From a8428de05cd6ad86dba8444de78a64294d4ddb5e Mon Sep 17 00:00:00 2001 From: Waldemar Brodkorb Date: Sat, 2 May 2015 15:24:16 -0500 Subject: update kernel versions, add separate modules-.mk files to abstract patch name changes --- target/linux/config/Config.in.misc | 6 + .../linux/patches/3.10.71/bsd-compatibility.patch | 2538 -- target/linux/patches/3.10.71/headers-install.patch | 11 - target/linux/patches/3.10.71/mkpiggy.patch | 28 - target/linux/patches/3.10.71/relocs.patch | 2693 -- target/linux/patches/3.10.71/sgidefs.patch | 18 - target/linux/patches/3.10.71/sortext.patch | 33 - target/linux/patches/3.10.71/startup.patch | 34 - target/linux/patches/3.10.71/yaffs2.patch | 16547 --------- .../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.12.38/bsd-compatibility.patch | 2538 -- target/linux/patches/3.12.38/cleankernel.patch | 11 - target/linux/patches/3.12.38/defaults.patch | 46 - .../linux/patches/3.12.38/disable-netfilter.patch | 160 - .../patches/3.12.38/export-symbol-for-exmap.patch | 11 - target/linux/patches/3.12.38/gemalto.patch | 11 - target/linux/patches/3.12.38/lemote-rfkill.patch | 21 - .../patches/3.12.38/microblaze-ethernet.patch | 11 - .../linux/patches/3.12.38/microblaze-setup.patch | 12 - target/linux/patches/3.12.38/mips-lzo-fix.patch | 12 - target/linux/patches/3.12.38/mkpiggy.patch | 28 - target/linux/patches/3.12.38/mtd-rootfs.patch | 26 - target/linux/patches/3.12.38/non-static.patch | 33 - .../linux/patches/3.12.38/ppc64-missing-zlib.patch | 11 - target/linux/patches/3.12.38/regmap-bool.patch | 20 - target/linux/patches/3.12.38/relocs.patch | 2693 -- target/linux/patches/3.12.38/sgidefs.patch | 18 - target/linux/patches/3.12.38/sortext.patch | 33 - target/linux/patches/3.12.38/startup.patch | 34 - .../linux/patches/3.12.38/usb-defaults-off.patch | 20 - .../patches/3.12.38/vga-cons-default-off.patch | 12 - target/linux/patches/3.12.38/wlan-cf.patch | 11 - target/linux/patches/3.12.38/xargs.patch | 12 - target/linux/patches/3.12.38/yaffs2.patch | 16547 --------- target/linux/patches/3.12.38/zlib-inflate.patch | 12 - .../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.14.35/bsd-compatibility.patch | 2538 -- target/linux/patches/3.14.35/cleankernel.patch | 11 - target/linux/patches/3.14.35/defaults.patch | 34 - .../linux/patches/3.14.35/disable-netfilter.patch | 160 - .../patches/3.14.35/export-symbol-for-exmap.patch | 11 - target/linux/patches/3.14.35/fblogo.patch | 2097 -- target/linux/patches/3.14.35/gemalto.patch | 11 - .../patches/3.14.35/initramfs-nosizelimit.patch | 57 - target/linux/patches/3.14.35/lemote-rfkill.patch | 21 - target/linux/patches/3.14.35/microblaze-axi.patch | 11 - .../patches/3.14.35/microblaze-ethernet.patch | 11 - target/linux/patches/3.14.35/mkpiggy.patch | 28 - target/linux/patches/3.14.35/mptcp.patch | 17203 --------- target/linux/patches/3.14.35/mtd-rootfs.patch | 26 - target/linux/patches/3.14.35/nfsv3-tcp.patch | 12 - target/linux/patches/3.14.35/non-static.patch | 33 - .../linux/patches/3.14.35/ppc64-missing-zlib.patch | 11 - target/linux/patches/3.14.35/regmap-boolean.patch | 24 - target/linux/patches/3.14.35/relocs.patch | 2709 -- target/linux/patches/3.14.35/sgidefs.patch | 18 - target/linux/patches/3.14.35/sortext.patch | 33 - target/linux/patches/3.14.35/startup.patch | 37 - target/linux/patches/3.14.35/wlan-cf.patch | 11 - target/linux/patches/3.14.35/xargs.patch | 12 - target/linux/patches/3.14.35/yaffs2.patch | 16547 --------- target/linux/patches/3.14.35/zlib-inflate.patch | 12 - .../linux/patches/3.14.40/bsd-compatibility.patch | 2538 ++ target/linux/patches/3.14.40/cleankernel.patch | 11 + target/linux/patches/3.14.40/defaults.patch | 34 + .../linux/patches/3.14.40/disable-netfilter.patch | 160 + .../patches/3.14.40/export-symbol-for-exmap.patch | 11 + target/linux/patches/3.14.40/fblogo.patch | 2097 ++ target/linux/patches/3.14.40/gemalto.patch | 11 + .../patches/3.14.40/initramfs-nosizelimit.patch | 57 + target/linux/patches/3.14.40/lemote-rfkill.patch | 21 + target/linux/patches/3.14.40/microblaze-axi.patch | 11 + .../patches/3.14.40/microblaze-ethernet.patch | 11 + target/linux/patches/3.14.40/mkpiggy.patch | 28 + target/linux/patches/3.14.40/mptcp.patch | 17203 +++++++++ target/linux/patches/3.14.40/mtd-rootfs.patch | 26 + target/linux/patches/3.14.40/nfsv3-tcp.patch | 12 + target/linux/patches/3.14.40/non-static.patch | 33 + .../linux/patches/3.14.40/ppc64-missing-zlib.patch | 11 + target/linux/patches/3.14.40/regmap-boolean.patch | 24 + target/linux/patches/3.14.40/relocs.patch | 2709 ++ target/linux/patches/3.14.40/sgidefs.patch | 18 + target/linux/patches/3.14.40/sortext.patch | 33 + target/linux/patches/3.14.40/startup.patch | 37 + target/linux/patches/3.14.40/wlan-cf.patch | 11 + target/linux/patches/3.14.40/xargs.patch | 12 + target/linux/patches/3.14.40/yaffs2.patch | 16547 +++++++++ target/linux/patches/3.14.40/zlib-inflate.patch | 12 + .../linux/patches/3.18.10/bsd-compatibility.patch | 2538 -- target/linux/patches/3.18.10/cleankernel.patch | 11 - target/linux/patches/3.18.10/cris-header.patch | 50 - target/linux/patches/3.18.10/defaults.patch | 46 - .../patches/3.18.10/export-symbol-for-exmap.patch | 11 - target/linux/patches/3.18.10/fblogo.patch | 2057 -- target/linux/patches/3.18.10/gemalto.patch | 11 - .../patches/3.18.10/initramfs-nosizelimit.patch | 57 - target/linux/patches/3.18.10/lemote-rfkill.patch | 21 - .../patches/3.18.10/microblaze-ethernet.patch | 11 - target/linux/patches/3.18.10/mkpiggy.patch | 28 - target/linux/patches/3.18.10/mtd-rootfs.patch | 26 - target/linux/patches/3.18.10/nfsv3-tcp.patch | 12 - target/linux/patches/3.18.10/non-static.patch | 33 - .../linux/patches/3.18.10/ppc64-missing-zlib.patch | 11 - target/linux/patches/3.18.10/realtime.patch | 24279 ------------- target/linux/patches/3.18.10/regmap-bool.patch | 27 - target/linux/patches/3.18.10/relocs.patch | 2709 -- target/linux/patches/3.18.10/sgidefs.patch | 18 - target/linux/patches/3.18.10/sortext.patch | 33 - target/linux/patches/3.18.10/startup.patch | 37 - target/linux/patches/3.18.10/wlan-cf.patch | 11 - target/linux/patches/3.18.10/xargs.patch | 12 - target/linux/patches/3.18.10/yaffs2.patch | 16551 --------- .../linux/patches/3.18.12/bsd-compatibility.patch | 2538 ++ target/linux/patches/3.18.12/cleankernel.patch | 11 + target/linux/patches/3.18.12/cris-header.patch | 50 + target/linux/patches/3.18.12/defaults.patch | 46 + .../patches/3.18.12/export-symbol-for-exmap.patch | 11 + target/linux/patches/3.18.12/fblogo.patch | 2057 ++ target/linux/patches/3.18.12/gemalto.patch | 11 + .../patches/3.18.12/initramfs-nosizelimit.patch | 57 + target/linux/patches/3.18.12/lemote-rfkill.patch | 21 + .../patches/3.18.12/microblaze-ethernet.patch | 11 + target/linux/patches/3.18.12/mkpiggy.patch | 28 + target/linux/patches/3.18.12/mtd-rootfs.patch | 26 + target/linux/patches/3.18.12/nfsv3-tcp.patch | 12 + target/linux/patches/3.18.12/non-static.patch | 33 + .../linux/patches/3.18.12/ppc64-missing-zlib.patch | 11 + target/linux/patches/3.18.12/realtime.patch | 36330 +++++++++++++++++++ target/linux/patches/3.18.12/regmap-bool.patch | 27 + target/linux/patches/3.18.12/relocs.patch | 2709 ++ target/linux/patches/3.18.12/sgidefs.patch | 18 + target/linux/patches/3.18.12/sortext.patch | 33 + target/linux/patches/3.18.12/startup.patch | 37 + target/linux/patches/3.18.12/wlan-cf.patch | 11 + target/linux/patches/3.18.12/xargs.patch | 12 + target/linux/patches/3.18.12/yaffs2.patch | 16551 +++++++++ .../linux/patches/3.19.1/bsd-compatibility.patch | 2538 -- target/linux/patches/3.19.1/cleankernel.patch | 11 - target/linux/patches/3.19.1/cris-header.patch | 12 - .../patches/3.19.1/export-symbol-for-exmap.patch | 11 - target/linux/patches/3.19.1/fblogo.patch | 2045 -- target/linux/patches/3.19.1/gemalto.patch | 11 - .../patches/3.19.1/initramfs-nosizelimit.patch | 57 - target/linux/patches/3.19.1/lemote-rfkill.patch | 21 - target/linux/patches/3.19.1/microblaze-axi.patch | 11 - .../linux/patches/3.19.1/microblaze-ethernet.patch | 11 - target/linux/patches/3.19.1/mkpiggy.patch | 28 - target/linux/patches/3.19.1/mtd-rootfs.patch | 26 - target/linux/patches/3.19.1/nfsv3-tcp.patch | 12 - target/linux/patches/3.19.1/non-static.patch | 33 - .../linux/patches/3.19.1/ppc64-missing-zlib.patch | 11 - target/linux/patches/3.19.1/regmap-bool.patch | 27 - target/linux/patches/3.19.1/relocs.patch | 2709 -- target/linux/patches/3.19.1/sgidefs.patch | 18 - target/linux/patches/3.19.1/sortext.patch | 33 - target/linux/patches/3.19.1/sparc-aout.patch | 24 - target/linux/patches/3.19.1/startup.patch | 37 - target/linux/patches/3.19.1/wlan-cf.patch | 11 - target/linux/patches/3.19.1/xargs.patch | 12 - target/linux/patches/3.19.1/yaffs2.patch | 16551 --------- .../linux/patches/3.19.5/bsd-compatibility.patch | 2538 ++ target/linux/patches/3.19.5/cleankernel.patch | 11 + target/linux/patches/3.19.5/cris-header.patch | 12 + .../patches/3.19.5/export-symbol-for-exmap.patch | 11 + target/linux/patches/3.19.5/fblogo.patch | 2045 ++ target/linux/patches/3.19.5/gemalto.patch | 11 + .../patches/3.19.5/initramfs-nosizelimit.patch | 57 + target/linux/patches/3.19.5/lemote-rfkill.patch | 21 + target/linux/patches/3.19.5/microblaze-axi.patch | 11 + .../linux/patches/3.19.5/microblaze-ethernet.patch | 11 + target/linux/patches/3.19.5/mkpiggy.patch | 28 + target/linux/patches/3.19.5/mtd-rootfs.patch | 26 + target/linux/patches/3.19.5/nfsv3-tcp.patch | 12 + target/linux/patches/3.19.5/non-static.patch | 33 + .../linux/patches/3.19.5/ppc64-missing-zlib.patch | 11 + target/linux/patches/3.19.5/regmap-bool.patch | 27 + target/linux/patches/3.19.5/relocs.patch | 2709 ++ target/linux/patches/3.19.5/sgidefs.patch | 18 + target/linux/patches/3.19.5/sortext.patch | 33 + target/linux/patches/3.19.5/sparc-aout.patch | 24 + target/linux/patches/3.19.5/startup.patch | 37 + target/linux/patches/3.19.5/wlan-cf.patch | 11 + target/linux/patches/3.19.5/xargs.patch | 12 + target/linux/patches/3.19.5/yaffs2.patch | 16551 +++++++++ target/linux/patches/3.4.106/aufs2.patch | 238 - .../linux/patches/3.4.106/bsd-compatibility.patch | 2512 -- target/linux/patches/3.4.106/defaults.patch | 22 - target/linux/patches/3.4.106/gemalto.patch | 11 - target/linux/patches/3.4.106/lemote-rfkill.patch | 21 - target/linux/patches/3.4.106/linux-gcc-check.patch | 18 - target/linux/patches/3.4.106/mips-error.patch | 254 - target/linux/patches/3.4.106/mkpiggy.patch | 28 - .../patches/3.4.106/module-alloc-size-check.patch | 21 - target/linux/patches/3.4.106/non-static.patch | 33 - target/linux/patches/3.4.106/relocs.patch | 3131 -- target/linux/patches/3.4.106/sparc-aout.patch | 24 - target/linux/patches/3.4.106/sparc-include.patch | 11 - target/linux/patches/3.4.106/startup.patch | 34 - .../linux/patches/3.4.106/usb-defaults-off.patch | 32 - .../patches/3.4.106/vga-cons-default-off.patch | 12 - target/linux/patches/3.4.106/wlan-cf.patch | 11 - target/linux/patches/3.4.106/yaffs2.patch | 16550 --------- target/linux/patches/3.4.106/zlib-inflate.patch | 12 - 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 + 253 files changed, 193815 insertions(+), 181758 deletions(-) delete mode 100644 target/linux/patches/3.10.71/bsd-compatibility.patch delete mode 100644 target/linux/patches/3.10.71/headers-install.patch delete mode 100644 target/linux/patches/3.10.71/mkpiggy.patch delete mode 100644 target/linux/patches/3.10.71/relocs.patch delete mode 100644 target/linux/patches/3.10.71/sgidefs.patch delete mode 100644 target/linux/patches/3.10.71/sortext.patch delete mode 100644 target/linux/patches/3.10.71/startup.patch delete mode 100644 target/linux/patches/3.10.71/yaffs2.patch create mode 100644 target/linux/patches/3.10.75/bsd-compatibility.patch create mode 100644 target/linux/patches/3.10.75/headers-install.patch create mode 100644 target/linux/patches/3.10.75/mkpiggy.patch create mode 100644 target/linux/patches/3.10.75/relocs.patch create mode 100644 target/linux/patches/3.10.75/sgidefs.patch create mode 100644 target/linux/patches/3.10.75/sortext.patch create mode 100644 target/linux/patches/3.10.75/startup.patch create mode 100644 target/linux/patches/3.10.75/yaffs2.patch delete mode 100644 target/linux/patches/3.12.38/bsd-compatibility.patch delete mode 100644 target/linux/patches/3.12.38/cleankernel.patch delete mode 100644 target/linux/patches/3.12.38/defaults.patch delete mode 100644 target/linux/patches/3.12.38/disable-netfilter.patch delete mode 100644 target/linux/patches/3.12.38/export-symbol-for-exmap.patch delete mode 100644 target/linux/patches/3.12.38/gemalto.patch delete mode 100644 target/linux/patches/3.12.38/lemote-rfkill.patch delete mode 100644 target/linux/patches/3.12.38/microblaze-ethernet.patch delete mode 100644 target/linux/patches/3.12.38/microblaze-setup.patch delete mode 100644 target/linux/patches/3.12.38/mips-lzo-fix.patch delete mode 100644 target/linux/patches/3.12.38/mkpiggy.patch delete mode 100644 target/linux/patches/3.12.38/mtd-rootfs.patch delete mode 100644 target/linux/patches/3.12.38/non-static.patch delete mode 100644 target/linux/patches/3.12.38/ppc64-missing-zlib.patch delete mode 100644 target/linux/patches/3.12.38/regmap-bool.patch delete mode 100644 target/linux/patches/3.12.38/relocs.patch delete mode 100644 target/linux/patches/3.12.38/sgidefs.patch delete mode 100644 target/linux/patches/3.12.38/sortext.patch delete mode 100644 target/linux/patches/3.12.38/startup.patch delete mode 100644 target/linux/patches/3.12.38/usb-defaults-off.patch delete mode 100644 target/linux/patches/3.12.38/vga-cons-default-off.patch delete mode 100644 target/linux/patches/3.12.38/wlan-cf.patch delete mode 100644 target/linux/patches/3.12.38/xargs.patch delete mode 100644 target/linux/patches/3.12.38/yaffs2.patch delete mode 100644 target/linux/patches/3.12.38/zlib-inflate.patch create mode 100644 target/linux/patches/3.12.40/bsd-compatibility.patch create mode 100644 target/linux/patches/3.12.40/cleankernel.patch create mode 100644 target/linux/patches/3.12.40/defaults.patch create mode 100644 target/linux/patches/3.12.40/disable-netfilter.patch create mode 100644 target/linux/patches/3.12.40/export-symbol-for-exmap.patch create mode 100644 target/linux/patches/3.12.40/gemalto.patch create mode 100644 target/linux/patches/3.12.40/lemote-rfkill.patch create mode 100644 target/linux/patches/3.12.40/microblaze-ethernet.patch create mode 100644 target/linux/patches/3.12.40/microblaze-setup.patch create mode 100644 target/linux/patches/3.12.40/mips-lzo-fix.patch create mode 100644 target/linux/patches/3.12.40/mkpiggy.patch create mode 100644 target/linux/patches/3.12.40/mtd-rootfs.patch create mode 100644 target/linux/patches/3.12.40/non-static.patch create mode 100644 target/linux/patches/3.12.40/ppc64-missing-zlib.patch create mode 100644 target/linux/patches/3.12.40/regmap-bool.patch create mode 100644 target/linux/patches/3.12.40/relocs.patch create mode 100644 target/linux/patches/3.12.40/sgidefs.patch create mode 100644 target/linux/patches/3.12.40/sortext.patch create mode 100644 target/linux/patches/3.12.40/startup.patch create mode 100644 target/linux/patches/3.12.40/usb-defaults-off.patch create mode 100644 target/linux/patches/3.12.40/vga-cons-default-off.patch create mode 100644 target/linux/patches/3.12.40/wlan-cf.patch create mode 100644 target/linux/patches/3.12.40/xargs.patch create mode 100644 target/linux/patches/3.12.40/yaffs2.patch create mode 100644 target/linux/patches/3.12.40/zlib-inflate.patch delete mode 100644 target/linux/patches/3.14.35/bsd-compatibility.patch delete mode 100644 target/linux/patches/3.14.35/cleankernel.patch delete mode 100644 target/linux/patches/3.14.35/defaults.patch delete mode 100644 target/linux/patches/3.14.35/disable-netfilter.patch delete mode 100644 target/linux/patches/3.14.35/export-symbol-for-exmap.patch delete mode 100644 target/linux/patches/3.14.35/fblogo.patch delete mode 100644 target/linux/patches/3.14.35/gemalto.patch delete mode 100644 target/linux/patches/3.14.35/initramfs-nosizelimit.patch delete mode 100644 target/linux/patches/3.14.35/lemote-rfkill.patch delete mode 100644 target/linux/patches/3.14.35/microblaze-axi.patch delete mode 100644 target/linux/patches/3.14.35/microblaze-ethernet.patch delete mode 100644 target/linux/patches/3.14.35/mkpiggy.patch delete mode 100644 target/linux/patches/3.14.35/mptcp.patch delete mode 100644 target/linux/patches/3.14.35/mtd-rootfs.patch delete mode 100644 target/linux/patches/3.14.35/nfsv3-tcp.patch delete mode 100644 target/linux/patches/3.14.35/non-static.patch delete mode 100644 target/linux/patches/3.14.35/ppc64-missing-zlib.patch delete mode 100644 target/linux/patches/3.14.35/regmap-boolean.patch delete mode 100644 target/linux/patches/3.14.35/relocs.patch delete mode 100644 target/linux/patches/3.14.35/sgidefs.patch delete mode 100644 target/linux/patches/3.14.35/sortext.patch delete mode 100644 target/linux/patches/3.14.35/startup.patch delete mode 100644 target/linux/patches/3.14.35/wlan-cf.patch delete mode 100644 target/linux/patches/3.14.35/xargs.patch delete mode 100644 target/linux/patches/3.14.35/yaffs2.patch delete mode 100644 target/linux/patches/3.14.35/zlib-inflate.patch create mode 100644 target/linux/patches/3.14.40/bsd-compatibility.patch create mode 100644 target/linux/patches/3.14.40/cleankernel.patch create mode 100644 target/linux/patches/3.14.40/defaults.patch create mode 100644 target/linux/patches/3.14.40/disable-netfilter.patch create mode 100644 target/linux/patches/3.14.40/export-symbol-for-exmap.patch create mode 100644 target/linux/patches/3.14.40/fblogo.patch create mode 100644 target/linux/patches/3.14.40/gemalto.patch create mode 100644 target/linux/patches/3.14.40/initramfs-nosizelimit.patch create mode 100644 target/linux/patches/3.14.40/lemote-rfkill.patch create mode 100644 target/linux/patches/3.14.40/microblaze-axi.patch create mode 100644 target/linux/patches/3.14.40/microblaze-ethernet.patch create mode 100644 target/linux/patches/3.14.40/mkpiggy.patch create mode 100644 target/linux/patches/3.14.40/mptcp.patch create mode 100644 target/linux/patches/3.14.40/mtd-rootfs.patch create mode 100644 target/linux/patches/3.14.40/nfsv3-tcp.patch create mode 100644 target/linux/patches/3.14.40/non-static.patch create mode 100644 target/linux/patches/3.14.40/ppc64-missing-zlib.patch create mode 100644 target/linux/patches/3.14.40/regmap-boolean.patch create mode 100644 target/linux/patches/3.14.40/relocs.patch create mode 100644 target/linux/patches/3.14.40/sgidefs.patch create mode 100644 target/linux/patches/3.14.40/sortext.patch create mode 100644 target/linux/patches/3.14.40/startup.patch create mode 100644 target/linux/patches/3.14.40/wlan-cf.patch create mode 100644 target/linux/patches/3.14.40/xargs.patch create mode 100644 target/linux/patches/3.14.40/yaffs2.patch create mode 100644 target/linux/patches/3.14.40/zlib-inflate.patch delete mode 100644 target/linux/patches/3.18.10/bsd-compatibility.patch delete mode 100644 target/linux/patches/3.18.10/cleankernel.patch delete mode 100644 target/linux/patches/3.18.10/cris-header.patch delete mode 100644 target/linux/patches/3.18.10/defaults.patch delete mode 100644 target/linux/patches/3.18.10/export-symbol-for-exmap.patch delete mode 100644 target/linux/patches/3.18.10/fblogo.patch delete mode 100644 target/linux/patches/3.18.10/gemalto.patch delete mode 100644 target/linux/patches/3.18.10/initramfs-nosizelimit.patch delete mode 100644 target/linux/patches/3.18.10/lemote-rfkill.patch delete mode 100644 target/linux/patches/3.18.10/microblaze-ethernet.patch delete mode 100644 target/linux/patches/3.18.10/mkpiggy.patch delete mode 100644 target/linux/patches/3.18.10/mtd-rootfs.patch delete mode 100644 target/linux/patches/3.18.10/nfsv3-tcp.patch delete mode 100644 target/linux/patches/3.18.10/non-static.patch delete mode 100644 target/linux/patches/3.18.10/ppc64-missing-zlib.patch delete mode 100644 target/linux/patches/3.18.10/realtime.patch delete mode 100644 target/linux/patches/3.18.10/regmap-bool.patch delete mode 100644 target/linux/patches/3.18.10/relocs.patch delete mode 100644 target/linux/patches/3.18.10/sgidefs.patch delete mode 100644 target/linux/patches/3.18.10/sortext.patch delete mode 100644 target/linux/patches/3.18.10/startup.patch delete mode 100644 target/linux/patches/3.18.10/wlan-cf.patch delete mode 100644 target/linux/patches/3.18.10/xargs.patch delete mode 100644 target/linux/patches/3.18.10/yaffs2.patch create mode 100644 target/linux/patches/3.18.12/bsd-compatibility.patch create mode 100644 target/linux/patches/3.18.12/cleankernel.patch create mode 100644 target/linux/patches/3.18.12/cris-header.patch create mode 100644 target/linux/patches/3.18.12/defaults.patch create mode 100644 target/linux/patches/3.18.12/export-symbol-for-exmap.patch create mode 100644 target/linux/patches/3.18.12/fblogo.patch create mode 100644 target/linux/patches/3.18.12/gemalto.patch create mode 100644 target/linux/patches/3.18.12/initramfs-nosizelimit.patch create mode 100644 target/linux/patches/3.18.12/lemote-rfkill.patch create mode 100644 target/linux/patches/3.18.12/microblaze-ethernet.patch create mode 100644 target/linux/patches/3.18.12/mkpiggy.patch create mode 100644 target/linux/patches/3.18.12/mtd-rootfs.patch create mode 100644 target/linux/patches/3.18.12/nfsv3-tcp.patch create mode 100644 target/linux/patches/3.18.12/non-static.patch create mode 100644 target/linux/patches/3.18.12/ppc64-missing-zlib.patch create mode 100644 target/linux/patches/3.18.12/realtime.patch create mode 100644 target/linux/patches/3.18.12/regmap-bool.patch create mode 100644 target/linux/patches/3.18.12/relocs.patch create mode 100644 target/linux/patches/3.18.12/sgidefs.patch create mode 100644 target/linux/patches/3.18.12/sortext.patch create mode 100644 target/linux/patches/3.18.12/startup.patch create mode 100644 target/linux/patches/3.18.12/wlan-cf.patch create mode 100644 target/linux/patches/3.18.12/xargs.patch create mode 100644 target/linux/patches/3.18.12/yaffs2.patch delete mode 100644 target/linux/patches/3.19.1/bsd-compatibility.patch delete mode 100644 target/linux/patches/3.19.1/cleankernel.patch delete mode 100644 target/linux/patches/3.19.1/cris-header.patch delete mode 100644 target/linux/patches/3.19.1/export-symbol-for-exmap.patch delete mode 100644 target/linux/patches/3.19.1/fblogo.patch delete mode 100644 target/linux/patches/3.19.1/gemalto.patch delete mode 100644 target/linux/patches/3.19.1/initramfs-nosizelimit.patch delete mode 100644 target/linux/patches/3.19.1/lemote-rfkill.patch delete mode 100644 target/linux/patches/3.19.1/microblaze-axi.patch delete mode 100644 target/linux/patches/3.19.1/microblaze-ethernet.patch delete mode 100644 target/linux/patches/3.19.1/mkpiggy.patch delete mode 100644 target/linux/patches/3.19.1/mtd-rootfs.patch delete mode 100644 target/linux/patches/3.19.1/nfsv3-tcp.patch delete mode 100644 target/linux/patches/3.19.1/non-static.patch delete mode 100644 target/linux/patches/3.19.1/ppc64-missing-zlib.patch delete mode 100644 target/linux/patches/3.19.1/regmap-bool.patch delete mode 100644 target/linux/patches/3.19.1/relocs.patch delete mode 100644 target/linux/patches/3.19.1/sgidefs.patch delete mode 100644 target/linux/patches/3.19.1/sortext.patch delete mode 100644 target/linux/patches/3.19.1/sparc-aout.patch delete mode 100644 target/linux/patches/3.19.1/startup.patch delete mode 100644 target/linux/patches/3.19.1/wlan-cf.patch delete mode 100644 target/linux/patches/3.19.1/xargs.patch delete mode 100644 target/linux/patches/3.19.1/yaffs2.patch create mode 100644 target/linux/patches/3.19.5/bsd-compatibility.patch create mode 100644 target/linux/patches/3.19.5/cleankernel.patch create mode 100644 target/linux/patches/3.19.5/cris-header.patch create mode 100644 target/linux/patches/3.19.5/export-symbol-for-exmap.patch create mode 100644 target/linux/patches/3.19.5/fblogo.patch create mode 100644 target/linux/patches/3.19.5/gemalto.patch create mode 100644 target/linux/patches/3.19.5/initramfs-nosizelimit.patch create mode 100644 target/linux/patches/3.19.5/lemote-rfkill.patch create mode 100644 target/linux/patches/3.19.5/microblaze-axi.patch create mode 100644 target/linux/patches/3.19.5/microblaze-ethernet.patch create mode 100644 target/linux/patches/3.19.5/mkpiggy.patch create mode 100644 target/linux/patches/3.19.5/mtd-rootfs.patch create mode 100644 target/linux/patches/3.19.5/nfsv3-tcp.patch create mode 100644 target/linux/patches/3.19.5/non-static.patch create mode 100644 target/linux/patches/3.19.5/ppc64-missing-zlib.patch create mode 100644 target/linux/patches/3.19.5/regmap-bool.patch create mode 100644 target/linux/patches/3.19.5/relocs.patch create mode 100644 target/linux/patches/3.19.5/sgidefs.patch create mode 100644 target/linux/patches/3.19.5/sortext.patch create mode 100644 target/linux/patches/3.19.5/sparc-aout.patch create mode 100644 target/linux/patches/3.19.5/startup.patch create mode 100644 target/linux/patches/3.19.5/wlan-cf.patch create mode 100644 target/linux/patches/3.19.5/xargs.patch create mode 100644 target/linux/patches/3.19.5/yaffs2.patch delete mode 100644 target/linux/patches/3.4.106/aufs2.patch delete mode 100644 target/linux/patches/3.4.106/bsd-compatibility.patch delete mode 100644 target/linux/patches/3.4.106/defaults.patch delete mode 100644 target/linux/patches/3.4.106/gemalto.patch delete mode 100644 target/linux/patches/3.4.106/lemote-rfkill.patch delete mode 100644 target/linux/patches/3.4.106/linux-gcc-check.patch delete mode 100644 target/linux/patches/3.4.106/mips-error.patch delete mode 100644 target/linux/patches/3.4.106/mkpiggy.patch delete mode 100644 target/linux/patches/3.4.106/module-alloc-size-check.patch delete mode 100644 target/linux/patches/3.4.106/non-static.patch delete mode 100644 target/linux/patches/3.4.106/relocs.patch delete mode 100644 target/linux/patches/3.4.106/sparc-aout.patch delete mode 100644 target/linux/patches/3.4.106/sparc-include.patch delete mode 100644 target/linux/patches/3.4.106/startup.patch delete mode 100644 target/linux/patches/3.4.106/usb-defaults-off.patch delete mode 100644 target/linux/patches/3.4.106/vga-cons-default-off.patch delete mode 100644 target/linux/patches/3.4.106/wlan-cf.patch delete mode 100644 target/linux/patches/3.4.106/yaffs2.patch delete mode 100644 target/linux/patches/3.4.106/zlib-inflate.patch create mode 100644 target/linux/patches/3.4.107/aufs2.patch create mode 100644 target/linux/patches/3.4.107/bsd-compatibility.patch create mode 100644 target/linux/patches/3.4.107/defaults.patch create mode 100644 target/linux/patches/3.4.107/gemalto.patch create mode 100644 target/linux/patches/3.4.107/lemote-rfkill.patch create mode 100644 target/linux/patches/3.4.107/linux-gcc-check.patch create mode 100644 target/linux/patches/3.4.107/mips-error.patch create mode 100644 target/linux/patches/3.4.107/mkpiggy.patch create mode 100644 target/linux/patches/3.4.107/module-alloc-size-check.patch create mode 100644 target/linux/patches/3.4.107/non-static.patch create mode 100644 target/linux/patches/3.4.107/relocs.patch create mode 100644 target/linux/patches/3.4.107/sparc-aout.patch create mode 100644 target/linux/patches/3.4.107/sparc-include.patch create mode 100644 target/linux/patches/3.4.107/startup.patch create mode 100644 target/linux/patches/3.4.107/usb-defaults-off.patch create mode 100644 target/linux/patches/3.4.107/vga-cons-default-off.patch create mode 100644 target/linux/patches/3.4.107/wlan-cf.patch create mode 100644 target/linux/patches/3.4.107/yaffs2.patch create mode 100644 target/linux/patches/3.4.107/zlib-inflate.patch (limited to 'target/linux') diff --git a/target/linux/config/Config.in.misc b/target/linux/config/Config.in.misc index 18de386da..26c5decec 100644 --- a/target/linux/config/Config.in.misc +++ b/target/linux/config/Config.in.misc @@ -29,6 +29,12 @@ config ADK_KERNEL_KEYS config ADK_KERNEL_SBUS bool +config ADK_KERNEL_FSL_OTP + bool + depends on ADK_TARGET_SYSTEM_SOLIDRUN_IMX6 + default y if ADK_TARGET_SYSTEM_SOLIDRUN_IMX6 + default n + config ADK_KERNEL_IMX_WEIM bool depends on ADK_TARGET_SYSTEM_SOLIDRUN_IMX6 diff --git a/target/linux/patches/3.10.71/bsd-compatibility.patch b/target/linux/patches/3.10.71/bsd-compatibility.patch deleted file mode 100644 index b954b658f..000000000 --- a/target/linux/patches/3.10.71/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.71/headers-install.patch b/target/linux/patches/3.10.71/headers-install.patch deleted file mode 100644 index 46dc9fe13..000000000 --- a/target/linux/patches/3.10.71/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.71/mkpiggy.patch b/target/linux/patches/3.10.71/mkpiggy.patch deleted file mode 100644 index d4e815cd2..000000000 --- a/target/linux/patches/3.10.71/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.71/relocs.patch b/target/linux/patches/3.10.71/relocs.patch deleted file mode 100644 index 649b9e73e..000000000 --- a/target/linux/patches/3.10.71/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.71/sgidefs.patch b/target/linux/patches/3.10.71/sgidefs.patch deleted file mode 100644 index f00a284d9..000000000 --- a/target/linux/patches/3.10.71/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.71/sortext.patch b/target/linux/patches/3.10.71/sortext.patch deleted file mode 100644 index 65bbbb64b..000000000 --- a/target/linux/patches/3.10.71/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.71/startup.patch b/target/linux/patches/3.10.71/startup.patch deleted file mode 100644 index 3ebc5db1e..000000000 --- a/target/linux/patches/3.10.71/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.71/yaffs2.patch b/target/linux/patches/3.10.71/yaffs2.patch deleted file mode 100644 index 172629530..000000000 --- a/target/linux/patches/3.10.71/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.75/bsd-compatibility.patch b/target/linux/patches/3.10.75/bsd-compatibility.patch new file mode 100644 index 000000000..b954b658f --- /dev/null +++ b/target/linux/patches/3.10.75/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.75/headers-install.patch b/target/linux/patches/3.10.75/headers-install.patch new file mode 100644 index 000000000..46dc9fe13 --- /dev/null +++ b/target/linux/patches/3.10.75/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.75/mkpiggy.patch b/target/linux/patches/3.10.75/mkpiggy.patch new file mode 100644 index 000000000..d4e815cd2 --- /dev/null +++ b/target/linux/patches/3.10.75/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.75/relocs.patch b/target/linux/patches/3.10.75/relocs.patch new file mode 100644 index 000000000..649b9e73e --- /dev/null +++ b/target/linux/patches/3.10.75/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.75/sgidefs.patch b/target/linux/patches/3.10.75/sgidefs.patch new file mode 100644 index 000000000..f00a284d9 --- /dev/null +++ b/target/linux/patches/3.10.75/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.75/sortext.patch b/target/linux/patches/3.10.75/sortext.patch new file mode 100644 index 000000000..65bbbb64b --- /dev/null +++ b/target/linux/patches/3.10.75/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.75/startup.patch b/target/linux/patches/3.10.75/startup.patch new file mode 100644 index 000000000..3ebc5db1e --- /dev/null +++ b/target/linux/patches/3.10.75/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.75/yaffs2.patch b/target/linux/patches/3.10.75/yaffs2.patch new file mode 100644 index 000000000..172629530 --- /dev/null +++ b/target/linux/patches/3.10.75/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.38/bsd-compatibility.patch b/target/linux/patches/3.12.38/bsd-compatibility.patch deleted file mode 100644 index b954b658f..000000000 --- a/target/linux/patches/3.12.38/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.38/cleankernel.patch b/target/linux/patches/3.12.38/cleankernel.patch deleted file mode 100644 index d8c055dc3..000000000 --- a/target/linux/patches/3.12.38/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.38/defaults.patch b/target/linux/patches/3.12.38/defaults.patch deleted file mode 100644 index 6cdca084e..000000000 --- a/target/linux/patches/3.12.38/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.38/disable-netfilter.patch b/target/linux/patches/3.12.38/disable-netfilter.patch deleted file mode 100644 index 7b1ca013a..000000000 --- a/target/linux/patches/3.12.38/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.38/export-symbol-for-exmap.patch b/target/linux/patches/3.12.38/export-symbol-for-exmap.patch deleted file mode 100644 index 4f0fc8449..000000000 --- a/target/linux/patches/3.12.38/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.38/gemalto.patch b/target/linux/patches/3.12.38/gemalto.patch deleted file mode 100644 index 65f7af1d7..000000000 --- a/target/linux/patches/3.12.38/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.38/lemote-rfkill.patch b/target/linux/patches/3.12.38/lemote-rfkill.patch deleted file mode 100644 index a61488434..000000000 --- a/target/linux/patches/3.12.38/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.38/microblaze-ethernet.patch b/target/linux/patches/3.12.38/microblaze-ethernet.patch deleted file mode 100644 index 742ab477e..000000000 --- a/target/linux/patches/3.12.38/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.38/microblaze-setup.patch b/target/linux/patches/3.12.38/microblaze-setup.patch deleted file mode 100644 index 43815f274..000000000 --- a/target/linux/patches/3.12.38/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.38/mips-lzo-fix.patch b/target/linux/patches/3.12.38/mips-lzo-fix.patch deleted file mode 100644 index 0740bdc6a..000000000 --- a/target/linux/patches/3.12.38/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.38/mkpiggy.patch b/target/linux/patches/3.12.38/mkpiggy.patch deleted file mode 100644 index d4e815cd2..000000000 --- a/target/linux/patches/3.12.38/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.38/mtd-rootfs.patch b/target/linux/patches/3.12.38/mtd-rootfs.patch deleted file mode 100644 index 775d5fc80..000000000 --- a/target/linux/patches/3.12.38/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.38/non-static.patch b/target/linux/patches/3.12.38/non-static.patch deleted file mode 100644 index a967703d0..000000000 --- a/target/linux/patches/3.12.38/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.38/ppc64-missing-zlib.patch b/target/linux/patches/3.12.38/ppc64-missing-zlib.patch deleted file mode 100644 index c6e0616be..000000000 --- a/target/linux/patches/3.12.38/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.38/regmap-bool.patch b/target/linux/patches/3.12.38/regmap-bool.patch deleted file mode 100644 index c3fd9a318..000000000 --- a/target/linux/patches/3.12.38/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.38/relocs.patch b/target/linux/patches/3.12.38/relocs.patch deleted file mode 100644 index 649b9e73e..000000000 --- a/target/linux/patches/3.12.38/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.38/sgidefs.patch b/target/linux/patches/3.12.38/sgidefs.patch deleted file mode 100644 index f00a284d9..000000000 --- a/target/linux/patches/3.12.38/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.38/sortext.patch b/target/linux/patches/3.12.38/sortext.patch deleted file mode 100644 index 8fd4e1d6b..000000000 --- a/target/linux/patches/3.12.38/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.38/startup.patch b/target/linux/patches/3.12.38/startup.patch deleted file mode 100644 index 4cd477da1..000000000 --- a/target/linux/patches/3.12.38/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.38/usb-defaults-off.patch b/target/linux/patches/3.12.38/usb-defaults-off.patch deleted file mode 100644 index 54dff2bd4..000000000 --- a/target/linux/patches/3.12.38/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.38/vga-cons-default-off.patch b/target/linux/patches/3.12.38/vga-cons-default-off.patch deleted file mode 100644 index 08a57f783..000000000 --- a/target/linux/patches/3.12.38/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.38/wlan-cf.patch b/target/linux/patches/3.12.38/wlan-cf.patch deleted file mode 100644 index fc20759e2..000000000 --- a/target/linux/patches/3.12.38/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.38/xargs.patch b/target/linux/patches/3.12.38/xargs.patch deleted file mode 100644 index 2c7b3df59..000000000 --- a/target/linux/patches/3.12.38/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.38/yaffs2.patch b/target/linux/patches/3.12.38/yaffs2.patch deleted file mode 100644 index 306814439..000000000 --- a/target/linux/patches/3.12.38/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.38/zlib-inflate.patch b/target/linux/patches/3.12.38/zlib-inflate.patch deleted file mode 100644 index 58e1f6d21..000000000 --- a/target/linux/patches/3.12.38/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.40/bsd-compatibility.patch b/target/linux/patches/3.12.40/bsd-compatibility.patch new file mode 100644 index 000000000..b954b658f --- /dev/null +++ b/target/linux/patches/3.12.40/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.40/cleankernel.patch b/target/linux/patches/3.12.40/cleankernel.patch new file mode 100644 index 000000000..d8c055dc3 --- /dev/null +++ b/target/linux/patches/3.12.40/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.40/defaults.patch b/target/linux/patches/3.12.40/defaults.patch new file mode 100644 index 000000000..6cdca084e --- /dev/null +++ b/target/linux/patches/3.12.40/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.40/disable-netfilter.patch b/target/linux/patches/3.12.40/disable-netfilter.patch new file mode 100644 index 000000000..7b1ca013a --- /dev/null +++ b/target/linux/patches/3.12.40/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.40/export-symbol-for-exmap.patch b/target/linux/patches/3.12.40/export-symbol-for-exmap.patch new file mode 100644 index 000000000..4f0fc8449 --- /dev/null +++ b/target/linux/patches/3.12.40/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.40/gemalto.patch b/target/linux/patches/3.12.40/gemalto.patch new file mode 100644 index 000000000..65f7af1d7 --- /dev/null +++ b/target/linux/patches/3.12.40/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.40/lemote-rfkill.patch b/target/linux/patches/3.12.40/lemote-rfkill.patch new file mode 100644 index 000000000..a61488434 --- /dev/null +++ b/target/linux/patches/3.12.40/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.40/microblaze-ethernet.patch b/target/linux/patches/3.12.40/microblaze-ethernet.patch new file mode 100644 index 000000000..742ab477e --- /dev/null +++ b/target/linux/patches/3.12.40/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.40/microblaze-setup.patch b/target/linux/patches/3.12.40/microblaze-setup.patch new file mode 100644 index 000000000..43815f274 --- /dev/null +++ b/target/linux/patches/3.12.40/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.40/mips-lzo-fix.patch b/target/linux/patches/3.12.40/mips-lzo-fix.patch new file mode 100644 index 000000000..0740bdc6a --- /dev/null +++ b/target/linux/patches/3.12.40/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.40/mkpiggy.patch b/target/linux/patches/3.12.40/mkpiggy.patch new file mode 100644 index 000000000..d4e815cd2 --- /dev/null +++ b/target/linux/patches/3.12.40/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.40/mtd-rootfs.patch b/target/linux/patches/3.12.40/mtd-rootfs.patch new file mode 100644 index 000000000..775d5fc80 --- /dev/null +++ b/target/linux/patches/3.12.40/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.40/non-static.patch b/target/linux/patches/3.12.40/non-static.patch new file mode 100644 index 000000000..a967703d0 --- /dev/null +++ b/target/linux/patches/3.12.40/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.40/ppc64-missing-zlib.patch b/target/linux/patches/3.12.40/ppc64-missing-zlib.patch new file mode 100644 index 000000000..c6e0616be --- /dev/null +++ b/target/linux/patches/3.12.40/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.40/regmap-bool.patch b/target/linux/patches/3.12.40/regmap-bool.patch new file mode 100644 index 000000000..c3fd9a318 --- /dev/null +++ b/target/linux/patches/3.12.40/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.40/relocs.patch b/target/linux/patches/3.12.40/relocs.patch new file mode 100644 index 000000000..649b9e73e --- /dev/null +++ b/target/linux/patches/3.12.40/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.40/sgidefs.patch b/target/linux/patches/3.12.40/sgidefs.patch new file mode 100644 index 000000000..f00a284d9 --- /dev/null +++ b/target/linux/patches/3.12.40/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.40/sortext.patch b/target/linux/patches/3.12.40/sortext.patch new file mode 100644 index 000000000..8fd4e1d6b --- /dev/null +++ b/target/linux/patches/3.12.40/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.40/startup.patch b/target/linux/patches/3.12.40/startup.patch new file mode 100644 index 000000000..4cd477da1 --- /dev/null +++ b/target/linux/patches/3.12.40/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.40/usb-defaults-off.patch b/target/linux/patches/3.12.40/usb-defaults-off.patch new file mode 100644 index 000000000..54dff2bd4 --- /dev/null +++ b/target/linux/patches/3.12.40/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.40/vga-cons-default-off.patch b/target/linux/patches/3.12.40/vga-cons-default-off.patch new file mode 100644 index 000000000..08a57f783 --- /dev/null +++ b/target/linux/patches/3.12.40/vga-cons-default-off.patch @@ -0,0 +1,12 @@ +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 new file mode 100644 index 000000000..fc20759e2 --- /dev/null +++ b/target/linux/patches/3.12.40/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.40/xargs.patch b/target/linux/patches/3.12.40/xargs.patch new file mode 100644 index 000000000..2c7b3df59 --- /dev/null +++ b/target/linux/patches/3.12.40/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.40/yaffs2.patch b/target/linux/patches/3.12.40/yaffs2.patch new file mode 100644 index 000000000..306814439 --- /dev/null +++ b/target/linux/patches/3.12.40/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.40/zlib-inflate.patch b/target/linux/patches/3.12.40/zlib-inflate.patch new file mode 100644 index 000000000..58e1f6d21 --- /dev/null +++ b/target/linux/patches/3.12.40/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.35/bsd-compatibility.patch b/target/linux/patches/3.14.35/bsd-compatibility.patch deleted file mode 100644 index b954b658f..000000000 --- a/target/linux/patches/3.14.35/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.35/cleankernel.patch b/target/linux/patches/3.14.35/cleankernel.patch deleted file mode 100644 index d8c055dc3..000000000 --- a/target/linux/patches/3.14.35/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.35/defaults.patch b/target/linux/patches/3.14.35/defaults.patch deleted file mode 100644 index f071fd1dd..000000000 --- a/target/linux/patches/3.14.35/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.35/disable-netfilter.patch b/target/linux/patches/3.14.35/disable-netfilter.patch deleted file mode 100644 index 7b1ca013a..000000000 --- a/target/linux/patches/3.14.35/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.35/export-symbol-for-exmap.patch b/target/linux/patches/3.14.35/export-symbol-for-exmap.patch deleted file mode 100644 index 4f0fc8449..000000000 --- a/target/linux/patches/3.14.35/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.35/fblogo.patch b/target/linux/patches/3.14.35/fblogo.patch deleted file mode 100644 index cbbb4216f..000000000 --- a/target/linux/patches/3.14.35/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.35/gemalto.patch b/target/linux/patches/3.14.35/gemalto.patch deleted file mode 100644 index 65f7af1d7..000000000 --- a/target/linux/patches/3.14.35/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.35/initramfs-nosizelimit.patch b/target/linux/patches/3.14.35/initramfs-nosizelimit.patch deleted file mode 100644 index 40d2f6bd8..000000000 --- a/target/linux/patches/3.14.35/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.35/lemote-rfkill.patch b/target/linux/patches/3.14.35/lemote-rfkill.patch deleted file mode 100644 index a61488434..000000000 --- a/target/linux/patches/3.14.35/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.35/microblaze-axi.patch b/target/linux/patches/3.14.35/microblaze-axi.patch deleted file mode 100644 index 1a4b17d8c..000000000 --- a/target/linux/patches/3.14.35/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.35/microblaze-ethernet.patch b/target/linux/patches/3.14.35/microblaze-ethernet.patch deleted file mode 100644 index 742ab477e..000000000 --- a/target/linux/patches/3.14.35/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.35/mkpiggy.patch b/target/linux/patches/3.14.35/mkpiggy.patch deleted file mode 100644 index 751678b74..000000000 --- a/target/linux/patches/3.14.35/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.35/mptcp.patch b/target/linux/patches/3.14.35/mptcp.patch deleted file mode 100644 index 9784e0577..000000000 --- a/target/linux/patches/3.14.35/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.35/mtd-rootfs.patch b/target/linux/patches/3.14.35/mtd-rootfs.patch deleted file mode 100644 index 775d5fc80..000000000 --- a/target/linux/patches/3.14.35/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.35/nfsv3-tcp.patch b/target/linux/patches/3.14.35/nfsv3-tcp.patch deleted file mode 100644 index 68ed95b08..000000000 --- a/target/linux/patches/3.14.35/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.35/non-static.patch b/target/linux/patches/3.14.35/non-static.patch deleted file mode 100644 index a967703d0..000000000 --- a/target/linux/patches/3.14.35/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.35/ppc64-missing-zlib.patch b/target/linux/patches/3.14.35/ppc64-missing-zlib.patch deleted file mode 100644 index c6e0616be..000000000 --- a/target/linux/patches/3.14.35/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.35/regmap-boolean.patch b/target/linux/patches/3.14.35/regmap-boolean.patch deleted file mode 100644 index d73620e4d..000000000 --- a/target/linux/patches/3.14.35/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.35/relocs.patch b/target/linux/patches/3.14.35/relocs.patch deleted file mode 100644 index 69a7c88a9..000000000 --- a/target/linux/patches/3.14.35/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.35/sgidefs.patch b/target/linux/patches/3.14.35/sgidefs.patch deleted file mode 100644 index f00a284d9..000000000 --- a/target/linux/patches/3.14.35/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.35/sortext.patch b/target/linux/patches/3.14.35/sortext.patch deleted file mode 100644 index 8fd4e1d6b..000000000 --- a/target/linux/patches/3.14.35/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.35/startup.patch b/target/linux/patches/3.14.35/startup.patch deleted file mode 100644 index d396b75e4..000000000 --- a/target/linux/patches/3.14.35/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.35/wlan-cf.patch b/target/linux/patches/3.14.35/wlan-cf.patch deleted file mode 100644 index fc20759e2..000000000 --- a/target/linux/patches/3.14.35/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.35/xargs.patch b/target/linux/patches/3.14.35/xargs.patch deleted file mode 100644 index 2c7b3df59..000000000 --- a/target/linux/patches/3.14.35/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.35/yaffs2.patch b/target/linux/patches/3.14.35/yaffs2.patch deleted file mode 100644 index f075aa658..000000000 --- a/target/linux/patches/3.14.35/yaffs2.patch +++ /dev/null @@ -1,16547 +0,0 @@ -diff -Nur linux-3.14.4.orig/fs/Kconfig linux-3.14.4/fs/Kconfig ---- linux-3.14.4.orig/fs/Kconfig 2014-05-13 13:33:14.000000000 +0200 -+++ linux-3.14.4/fs/Kconfig 2014-05-17 02:22:30.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.4.orig/fs/Makefile linux-3.14.4/fs/Makefile ---- linux-3.14.4.orig/fs/Makefile 2014-05-13 13:33:14.000000000 +0200 -+++ linux-3.14.4/fs/Makefile 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/Kconfig linux-3.14.4/fs/yaffs2/Kconfig ---- linux-3.14.4.orig/fs/yaffs2/Kconfig 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/Kconfig 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/Makefile linux-3.14.4/fs/yaffs2/Makefile ---- linux-3.14.4.orig/fs/yaffs2/Makefile 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/Makefile 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_allocator.c linux-3.14.4/fs/yaffs2/yaffs_allocator.c ---- linux-3.14.4.orig/fs/yaffs2/yaffs_allocator.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_allocator.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_allocator.h linux-3.14.4/fs/yaffs2/yaffs_allocator.h ---- linux-3.14.4.orig/fs/yaffs2/yaffs_allocator.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_allocator.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_attribs.c linux-3.14.4/fs/yaffs2/yaffs_attribs.c ---- linux-3.14.4.orig/fs/yaffs2/yaffs_attribs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_attribs.c 2014-05-17 02:22:30.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.14.4.orig/fs/yaffs2/yaffs_attribs.h linux-3.14.4/fs/yaffs2/yaffs_attribs.h ---- linux-3.14.4.orig/fs/yaffs2/yaffs_attribs.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_attribs.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_bitmap.c linux-3.14.4/fs/yaffs2/yaffs_bitmap.c ---- linux-3.14.4.orig/fs/yaffs2/yaffs_bitmap.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_bitmap.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_bitmap.h linux-3.14.4/fs/yaffs2/yaffs_bitmap.h ---- linux-3.14.4.orig/fs/yaffs2/yaffs_bitmap.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_bitmap.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_checkptrw.c linux-3.14.4/fs/yaffs2/yaffs_checkptrw.c ---- linux-3.14.4.orig/fs/yaffs2/yaffs_checkptrw.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_checkptrw.c 2014-05-17 02:22:30.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.14.4.orig/fs/yaffs2/yaffs_checkptrw.h linux-3.14.4/fs/yaffs2/yaffs_checkptrw.h ---- linux-3.14.4.orig/fs/yaffs2/yaffs_checkptrw.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_checkptrw.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_ecc.c linux-3.14.4/fs/yaffs2/yaffs_ecc.c ---- linux-3.14.4.orig/fs/yaffs2/yaffs_ecc.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_ecc.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_ecc.h linux-3.14.4/fs/yaffs2/yaffs_ecc.h ---- linux-3.14.4.orig/fs/yaffs2/yaffs_ecc.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_ecc.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_getblockinfo.h linux-3.14.4/fs/yaffs2/yaffs_getblockinfo.h ---- linux-3.14.4.orig/fs/yaffs2/yaffs_getblockinfo.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_getblockinfo.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_guts.c linux-3.14.4/fs/yaffs2/yaffs_guts.c ---- linux-3.14.4.orig/fs/yaffs2/yaffs_guts.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_guts.c 2014-05-17 02:22:30.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.14.4.orig/fs/yaffs2/yaffs_guts.h linux-3.14.4/fs/yaffs2/yaffs_guts.h ---- linux-3.14.4.orig/fs/yaffs2/yaffs_guts.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_guts.h 2014-05-17 02:22:30.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.14.4.orig/fs/yaffs2/yaffs_linux.h linux-3.14.4/fs/yaffs2/yaffs_linux.h ---- linux-3.14.4.orig/fs/yaffs2/yaffs_linux.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_linux.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_mtdif.c linux-3.14.4/fs/yaffs2/yaffs_mtdif.c ---- linux-3.14.4.orig/fs/yaffs2/yaffs_mtdif.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_mtdif.c 2014-05-17 02:22:30.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.14.4.orig/fs/yaffs2/yaffs_mtdif.h linux-3.14.4/fs/yaffs2/yaffs_mtdif.h ---- linux-3.14.4.orig/fs/yaffs2/yaffs_mtdif.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_mtdif.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_nameval.c linux-3.14.4/fs/yaffs2/yaffs_nameval.c ---- linux-3.14.4.orig/fs/yaffs2/yaffs_nameval.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_nameval.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_nameval.h linux-3.14.4/fs/yaffs2/yaffs_nameval.h ---- linux-3.14.4.orig/fs/yaffs2/yaffs_nameval.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_nameval.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_nand.c linux-3.14.4/fs/yaffs2/yaffs_nand.c ---- linux-3.14.4.orig/fs/yaffs2/yaffs_nand.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_nand.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_nand.h linux-3.14.4/fs/yaffs2/yaffs_nand.h ---- linux-3.14.4.orig/fs/yaffs2/yaffs_nand.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_nand.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_packedtags1.c linux-3.14.4/fs/yaffs2/yaffs_packedtags1.c ---- linux-3.14.4.orig/fs/yaffs2/yaffs_packedtags1.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_packedtags1.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_packedtags1.h linux-3.14.4/fs/yaffs2/yaffs_packedtags1.h ---- linux-3.14.4.orig/fs/yaffs2/yaffs_packedtags1.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_packedtags1.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_packedtags2.c linux-3.14.4/fs/yaffs2/yaffs_packedtags2.c ---- linux-3.14.4.orig/fs/yaffs2/yaffs_packedtags2.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_packedtags2.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_packedtags2.h linux-3.14.4/fs/yaffs2/yaffs_packedtags2.h ---- linux-3.14.4.orig/fs/yaffs2/yaffs_packedtags2.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_packedtags2.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_summary.c linux-3.14.4/fs/yaffs2/yaffs_summary.c ---- linux-3.14.4.orig/fs/yaffs2/yaffs_summary.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_summary.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_summary.h linux-3.14.4/fs/yaffs2/yaffs_summary.h ---- linux-3.14.4.orig/fs/yaffs2/yaffs_summary.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_summary.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_tagscompat.c linux-3.14.4/fs/yaffs2/yaffs_tagscompat.c ---- linux-3.14.4.orig/fs/yaffs2/yaffs_tagscompat.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_tagscompat.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_tagscompat.h linux-3.14.4/fs/yaffs2/yaffs_tagscompat.h ---- linux-3.14.4.orig/fs/yaffs2/yaffs_tagscompat.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_tagscompat.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_tagsmarshall.c linux-3.14.4/fs/yaffs2/yaffs_tagsmarshall.c ---- linux-3.14.4.orig/fs/yaffs2/yaffs_tagsmarshall.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_tagsmarshall.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_tagsmarshall.h linux-3.14.4/fs/yaffs2/yaffs_tagsmarshall.h ---- linux-3.14.4.orig/fs/yaffs2/yaffs_tagsmarshall.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_tagsmarshall.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_trace.h linux-3.14.4/fs/yaffs2/yaffs_trace.h ---- linux-3.14.4.orig/fs/yaffs2/yaffs_trace.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_trace.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_verify.c linux-3.14.4/fs/yaffs2/yaffs_verify.c ---- linux-3.14.4.orig/fs/yaffs2/yaffs_verify.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_verify.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_verify.h linux-3.14.4/fs/yaffs2/yaffs_verify.h ---- linux-3.14.4.orig/fs/yaffs2/yaffs_verify.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_verify.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_vfs.c linux-3.14.4/fs/yaffs2/yaffs_vfs.c ---- linux-3.14.4.orig/fs/yaffs2/yaffs_vfs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_vfs.c 2014-05-17 02:22:30.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.14.4.orig/fs/yaffs2/yaffs_yaffs1.c linux-3.14.4/fs/yaffs2/yaffs_yaffs1.c ---- linux-3.14.4.orig/fs/yaffs2/yaffs_yaffs1.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_yaffs1.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_yaffs1.h linux-3.14.4/fs/yaffs2/yaffs_yaffs1.h ---- linux-3.14.4.orig/fs/yaffs2/yaffs_yaffs1.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_yaffs1.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_yaffs2.c linux-3.14.4/fs/yaffs2/yaffs_yaffs2.c ---- linux-3.14.4.orig/fs/yaffs2/yaffs_yaffs2.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_yaffs2.c 2014-05-17 02:22:30.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.14.4.orig/fs/yaffs2/yaffs_yaffs2.h linux-3.14.4/fs/yaffs2/yaffs_yaffs2.h ---- linux-3.14.4.orig/fs/yaffs2/yaffs_yaffs2.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yaffs_yaffs2.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yportenv.h linux-3.14.4/fs/yaffs2/yportenv.h ---- linux-3.14.4.orig/fs/yaffs2/yportenv.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.4/fs/yaffs2/yportenv.h 2014-05-17 02:22:30.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.35/zlib-inflate.patch b/target/linux/patches/3.14.35/zlib-inflate.patch deleted file mode 100644 index 58e1f6d21..000000000 --- a/target/linux/patches/3.14.35/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.40/bsd-compatibility.patch b/target/linux/patches/3.14.40/bsd-compatibility.patch new file mode 100644 index 000000000..b954b658f --- /dev/null +++ b/target/linux/patches/3.14.40/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.40/cleankernel.patch b/target/linux/patches/3.14.40/cleankernel.patch new file mode 100644 index 000000000..d8c055dc3 --- /dev/null +++ b/target/linux/patches/3.14.40/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.40/defaults.patch b/target/linux/patches/3.14.40/defaults.patch new file mode 100644 index 000000000..f071fd1dd --- /dev/null +++ b/target/linux/patches/3.14.40/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.40/disable-netfilter.patch b/target/linux/patches/3.14.40/disable-netfilter.patch new file mode 100644 index 000000000..7b1ca013a --- /dev/null +++ b/target/linux/patches/3.14.40/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.40/export-symbol-for-exmap.patch b/target/linux/patches/3.14.40/export-symbol-for-exmap.patch new file mode 100644 index 000000000..4f0fc8449 --- /dev/null +++ b/target/linux/patches/3.14.40/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.40/fblogo.patch b/target/linux/patches/3.14.40/fblogo.patch new file mode 100644 index 000000000..cbbb4216f --- /dev/null +++ b/target/linux/patches/3.14.40/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.40/gemalto.patch b/target/linux/patches/3.14.40/gemalto.patch new file mode 100644 index 000000000..65f7af1d7 --- /dev/null +++ b/target/linux/patches/3.14.40/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.40/initramfs-nosizelimit.patch b/target/linux/patches/3.14.40/initramfs-nosizelimit.patch new file mode 100644 index 000000000..40d2f6bd8 --- /dev/null +++ b/target/linux/patches/3.14.40/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.40/lemote-rfkill.patch b/target/linux/patches/3.14.40/lemote-rfkill.patch new file mode 100644 index 000000000..a61488434 --- /dev/null +++ b/target/linux/patches/3.14.40/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.40/microblaze-axi.patch b/target/linux/patches/3.14.40/microblaze-axi.patch new file mode 100644 index 000000000..1a4b17d8c --- /dev/null +++ b/target/linux/patches/3.14.40/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.40/microblaze-ethernet.patch b/target/linux/patches/3.14.40/microblaze-ethernet.patch new file mode 100644 index 000000000..742ab477e --- /dev/null +++ b/target/linux/patches/3.14.40/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.40/mkpiggy.patch b/target/linux/patches/3.14.40/mkpiggy.patch new file mode 100644 index 000000000..751678b74 --- /dev/null +++ b/target/linux/patches/3.14.40/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.40/mptcp.patch b/target/linux/patches/3.14.40/mptcp.patch new file mode 100644 index 000000000..9784e0577 --- /dev/null +++ b/target/linux/patches/3.14.40/mptcp.patch @@ -0,0 +1,17203 @@ +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.40/mtd-rootfs.patch b/target/linux/patches/3.14.40/mtd-rootfs.patch new file mode 100644 index 000000000..775d5fc80 --- /dev/null +++ b/target/linux/patches/3.14.40/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.40/nfsv3-tcp.patch b/target/linux/patches/3.14.40/nfsv3-tcp.patch new file mode 100644 index 000000000..68ed95b08 --- /dev/null +++ b/target/linux/patches/3.14.40/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.40/non-static.patch b/target/linux/patches/3.14.40/non-static.patch new file mode 100644 index 000000000..a967703d0 --- /dev/null +++ b/target/linux/patches/3.14.40/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.40/ppc64-missing-zlib.patch b/target/linux/patches/3.14.40/ppc64-missing-zlib.patch new file mode 100644 index 000000000..c6e0616be --- /dev/null +++ b/target/linux/patches/3.14.40/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.40/regmap-boolean.patch b/target/linux/patches/3.14.40/regmap-boolean.patch new file mode 100644 index 000000000..d73620e4d --- /dev/null +++ b/target/linux/patches/3.14.40/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.40/relocs.patch b/target/linux/patches/3.14.40/relocs.patch new file mode 100644 index 000000000..69a7c88a9 --- /dev/null +++ b/target/linux/patches/3.14.40/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.40/sgidefs.patch b/target/linux/patches/3.14.40/sgidefs.patch new file mode 100644 index 000000000..f00a284d9 --- /dev/null +++ b/target/linux/patches/3.14.40/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.40/sortext.patch b/target/linux/patches/3.14.40/sortext.patch new file mode 100644 index 000000000..8fd4e1d6b --- /dev/null +++ b/target/linux/patches/3.14.40/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.40/startup.patch b/target/linux/patches/3.14.40/startup.patch new file mode 100644 index 000000000..d396b75e4 --- /dev/null +++ b/target/linux/patches/3.14.40/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.40/wlan-cf.patch b/target/linux/patches/3.14.40/wlan-cf.patch new file mode 100644 index 000000000..fc20759e2 --- /dev/null +++ b/target/linux/patches/3.14.40/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.40/xargs.patch b/target/linux/patches/3.14.40/xargs.patch new file mode 100644 index 000000000..2c7b3df59 --- /dev/null +++ b/target/linux/patches/3.14.40/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.40/yaffs2.patch b/target/linux/patches/3.14.40/yaffs2.patch new file mode 100644 index 000000000..f075aa658 --- /dev/null +++ b/target/linux/patches/3.14.40/yaffs2.patch @@ -0,0 +1,16547 @@ +diff -Nur linux-3.14.4.orig/fs/Kconfig linux-3.14.4/fs/Kconfig +--- linux-3.14.4.orig/fs/Kconfig 2014-05-13 13:33:14.000000000 +0200 ++++ linux-3.14.4/fs/Kconfig 2014-05-17 02:22:30.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.4.orig/fs/Makefile linux-3.14.4/fs/Makefile +--- linux-3.14.4.orig/fs/Makefile 2014-05-13 13:33:14.000000000 +0200 ++++ linux-3.14.4/fs/Makefile 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/Kconfig linux-3.14.4/fs/yaffs2/Kconfig +--- linux-3.14.4.orig/fs/yaffs2/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/Kconfig 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/Makefile linux-3.14.4/fs/yaffs2/Makefile +--- linux-3.14.4.orig/fs/yaffs2/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/Makefile 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_allocator.c linux-3.14.4/fs/yaffs2/yaffs_allocator.c +--- linux-3.14.4.orig/fs/yaffs2/yaffs_allocator.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_allocator.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_allocator.h linux-3.14.4/fs/yaffs2/yaffs_allocator.h +--- linux-3.14.4.orig/fs/yaffs2/yaffs_allocator.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_allocator.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_attribs.c linux-3.14.4/fs/yaffs2/yaffs_attribs.c +--- linux-3.14.4.orig/fs/yaffs2/yaffs_attribs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_attribs.c 2014-05-17 02:22:30.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.14.4.orig/fs/yaffs2/yaffs_attribs.h linux-3.14.4/fs/yaffs2/yaffs_attribs.h +--- linux-3.14.4.orig/fs/yaffs2/yaffs_attribs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_attribs.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_bitmap.c linux-3.14.4/fs/yaffs2/yaffs_bitmap.c +--- linux-3.14.4.orig/fs/yaffs2/yaffs_bitmap.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_bitmap.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_bitmap.h linux-3.14.4/fs/yaffs2/yaffs_bitmap.h +--- linux-3.14.4.orig/fs/yaffs2/yaffs_bitmap.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_bitmap.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_checkptrw.c linux-3.14.4/fs/yaffs2/yaffs_checkptrw.c +--- linux-3.14.4.orig/fs/yaffs2/yaffs_checkptrw.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_checkptrw.c 2014-05-17 02:22:30.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.14.4.orig/fs/yaffs2/yaffs_checkptrw.h linux-3.14.4/fs/yaffs2/yaffs_checkptrw.h +--- linux-3.14.4.orig/fs/yaffs2/yaffs_checkptrw.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_checkptrw.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_ecc.c linux-3.14.4/fs/yaffs2/yaffs_ecc.c +--- linux-3.14.4.orig/fs/yaffs2/yaffs_ecc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_ecc.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_ecc.h linux-3.14.4/fs/yaffs2/yaffs_ecc.h +--- linux-3.14.4.orig/fs/yaffs2/yaffs_ecc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_ecc.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_getblockinfo.h linux-3.14.4/fs/yaffs2/yaffs_getblockinfo.h +--- linux-3.14.4.orig/fs/yaffs2/yaffs_getblockinfo.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_getblockinfo.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_guts.c linux-3.14.4/fs/yaffs2/yaffs_guts.c +--- linux-3.14.4.orig/fs/yaffs2/yaffs_guts.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_guts.c 2014-05-17 02:22:30.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.14.4.orig/fs/yaffs2/yaffs_guts.h linux-3.14.4/fs/yaffs2/yaffs_guts.h +--- linux-3.14.4.orig/fs/yaffs2/yaffs_guts.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_guts.h 2014-05-17 02:22:30.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.14.4.orig/fs/yaffs2/yaffs_linux.h linux-3.14.4/fs/yaffs2/yaffs_linux.h +--- linux-3.14.4.orig/fs/yaffs2/yaffs_linux.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_linux.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_mtdif.c linux-3.14.4/fs/yaffs2/yaffs_mtdif.c +--- linux-3.14.4.orig/fs/yaffs2/yaffs_mtdif.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_mtdif.c 2014-05-17 02:22:30.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.14.4.orig/fs/yaffs2/yaffs_mtdif.h linux-3.14.4/fs/yaffs2/yaffs_mtdif.h +--- linux-3.14.4.orig/fs/yaffs2/yaffs_mtdif.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_mtdif.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_nameval.c linux-3.14.4/fs/yaffs2/yaffs_nameval.c +--- linux-3.14.4.orig/fs/yaffs2/yaffs_nameval.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_nameval.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_nameval.h linux-3.14.4/fs/yaffs2/yaffs_nameval.h +--- linux-3.14.4.orig/fs/yaffs2/yaffs_nameval.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_nameval.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_nand.c linux-3.14.4/fs/yaffs2/yaffs_nand.c +--- linux-3.14.4.orig/fs/yaffs2/yaffs_nand.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_nand.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_nand.h linux-3.14.4/fs/yaffs2/yaffs_nand.h +--- linux-3.14.4.orig/fs/yaffs2/yaffs_nand.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_nand.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_packedtags1.c linux-3.14.4/fs/yaffs2/yaffs_packedtags1.c +--- linux-3.14.4.orig/fs/yaffs2/yaffs_packedtags1.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_packedtags1.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_packedtags1.h linux-3.14.4/fs/yaffs2/yaffs_packedtags1.h +--- linux-3.14.4.orig/fs/yaffs2/yaffs_packedtags1.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_packedtags1.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_packedtags2.c linux-3.14.4/fs/yaffs2/yaffs_packedtags2.c +--- linux-3.14.4.orig/fs/yaffs2/yaffs_packedtags2.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_packedtags2.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_packedtags2.h linux-3.14.4/fs/yaffs2/yaffs_packedtags2.h +--- linux-3.14.4.orig/fs/yaffs2/yaffs_packedtags2.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_packedtags2.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_summary.c linux-3.14.4/fs/yaffs2/yaffs_summary.c +--- linux-3.14.4.orig/fs/yaffs2/yaffs_summary.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_summary.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_summary.h linux-3.14.4/fs/yaffs2/yaffs_summary.h +--- linux-3.14.4.orig/fs/yaffs2/yaffs_summary.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_summary.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_tagscompat.c linux-3.14.4/fs/yaffs2/yaffs_tagscompat.c +--- linux-3.14.4.orig/fs/yaffs2/yaffs_tagscompat.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_tagscompat.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_tagscompat.h linux-3.14.4/fs/yaffs2/yaffs_tagscompat.h +--- linux-3.14.4.orig/fs/yaffs2/yaffs_tagscompat.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_tagscompat.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_tagsmarshall.c linux-3.14.4/fs/yaffs2/yaffs_tagsmarshall.c +--- linux-3.14.4.orig/fs/yaffs2/yaffs_tagsmarshall.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_tagsmarshall.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_tagsmarshall.h linux-3.14.4/fs/yaffs2/yaffs_tagsmarshall.h +--- linux-3.14.4.orig/fs/yaffs2/yaffs_tagsmarshall.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_tagsmarshall.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_trace.h linux-3.14.4/fs/yaffs2/yaffs_trace.h +--- linux-3.14.4.orig/fs/yaffs2/yaffs_trace.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_trace.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_verify.c linux-3.14.4/fs/yaffs2/yaffs_verify.c +--- linux-3.14.4.orig/fs/yaffs2/yaffs_verify.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_verify.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_verify.h linux-3.14.4/fs/yaffs2/yaffs_verify.h +--- linux-3.14.4.orig/fs/yaffs2/yaffs_verify.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_verify.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_vfs.c linux-3.14.4/fs/yaffs2/yaffs_vfs.c +--- linux-3.14.4.orig/fs/yaffs2/yaffs_vfs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_vfs.c 2014-05-17 02:22:30.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.14.4.orig/fs/yaffs2/yaffs_yaffs1.c linux-3.14.4/fs/yaffs2/yaffs_yaffs1.c +--- linux-3.14.4.orig/fs/yaffs2/yaffs_yaffs1.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_yaffs1.c 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_yaffs1.h linux-3.14.4/fs/yaffs2/yaffs_yaffs1.h +--- linux-3.14.4.orig/fs/yaffs2/yaffs_yaffs1.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_yaffs1.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yaffs_yaffs2.c linux-3.14.4/fs/yaffs2/yaffs_yaffs2.c +--- linux-3.14.4.orig/fs/yaffs2/yaffs_yaffs2.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_yaffs2.c 2014-05-17 02:22:30.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.14.4.orig/fs/yaffs2/yaffs_yaffs2.h linux-3.14.4/fs/yaffs2/yaffs_yaffs2.h +--- linux-3.14.4.orig/fs/yaffs2/yaffs_yaffs2.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yaffs_yaffs2.h 2014-05-17 02:22:30.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.4.orig/fs/yaffs2/yportenv.h linux-3.14.4/fs/yaffs2/yportenv.h +--- linux-3.14.4.orig/fs/yaffs2/yportenv.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.4/fs/yaffs2/yportenv.h 2014-05-17 02:22:30.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.40/zlib-inflate.patch b/target/linux/patches/3.14.40/zlib-inflate.patch new file mode 100644 index 000000000..58e1f6d21 --- /dev/null +++ b/target/linux/patches/3.14.40/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.10/bsd-compatibility.patch b/target/linux/patches/3.18.10/bsd-compatibility.patch deleted file mode 100644 index b954b658f..000000000 --- a/target/linux/patches/3.18.10/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.10/cleankernel.patch b/target/linux/patches/3.18.10/cleankernel.patch deleted file mode 100644 index d8c055dc3..000000000 --- a/target/linux/patches/3.18.10/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.10/cris-header.patch b/target/linux/patches/3.18.10/cris-header.patch deleted file mode 100644 index 3db07e530..000000000 --- a/target/linux/patches/3.18.10/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.10/defaults.patch b/target/linux/patches/3.18.10/defaults.patch deleted file mode 100644 index 6cdca084e..000000000 --- a/target/linux/patches/3.18.10/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.10/export-symbol-for-exmap.patch b/target/linux/patches/3.18.10/export-symbol-for-exmap.patch deleted file mode 100644 index 4f0fc8449..000000000 --- a/target/linux/patches/3.18.10/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.10/fblogo.patch b/target/linux/patches/3.18.10/fblogo.patch deleted file mode 100644 index 5b9070242..000000000 --- a/target/linux/patches/3.18.10/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.10/gemalto.patch b/target/linux/patches/3.18.10/gemalto.patch deleted file mode 100644 index 65f7af1d7..000000000 --- a/target/linux/patches/3.18.10/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.10/initramfs-nosizelimit.patch b/target/linux/patches/3.18.10/initramfs-nosizelimit.patch deleted file mode 100644 index 40d2f6bd8..000000000 --- a/target/linux/patches/3.18.10/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.10/lemote-rfkill.patch b/target/linux/patches/3.18.10/lemote-rfkill.patch deleted file mode 100644 index a61488434..000000000 --- a/target/linux/patches/3.18.10/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.10/microblaze-ethernet.patch b/target/linux/patches/3.18.10/microblaze-ethernet.patch deleted file mode 100644 index 742ab477e..000000000 --- a/target/linux/patches/3.18.10/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.10/mkpiggy.patch b/target/linux/patches/3.18.10/mkpiggy.patch deleted file mode 100644 index 751678b74..000000000 --- a/target/linux/patches/3.18.10/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.10/mtd-rootfs.patch b/target/linux/patches/3.18.10/mtd-rootfs.patch deleted file mode 100644 index 775d5fc80..000000000 --- a/target/linux/patches/3.18.10/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.10/nfsv3-tcp.patch b/target/linux/patches/3.18.10/nfsv3-tcp.patch deleted file mode 100644 index d5e07e1c2..000000000 --- a/target/linux/patches/3.18.10/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.10/non-static.patch b/target/linux/patches/3.18.10/non-static.patch deleted file mode 100644 index a967703d0..000000000 --- a/target/linux/patches/3.18.10/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.10/ppc64-missing-zlib.patch b/target/linux/patches/3.18.10/ppc64-missing-zlib.patch deleted file mode 100644 index c6e0616be..000000000 --- a/target/linux/patches/3.18.10/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.10/realtime.patch b/target/linux/patches/3.18.10/realtime.patch deleted file mode 100644 index 87f669fbc..000000000 --- a/target/linux/patches/3.18.10/realtime.patch +++ /dev/null @@ -1,24279 +0,0 @@ -diff -Nur linux-3.18.10.orig/arch/alpha/mm/fault.c linux-3.18.10/arch/alpha/mm/fault.c ---- linux-3.18.10.orig/arch/alpha/mm/fault.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/alpha/mm/fault.c 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/arch/arm/include/asm/cmpxchg.h linux-3.18.10/arch/arm/include/asm/cmpxchg.h ---- linux-3.18.10.orig/arch/arm/include/asm/cmpxchg.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm/include/asm/cmpxchg.h 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/arch/arm/include/asm/futex.h linux-3.18.10/arch/arm/include/asm/futex.h ---- linux-3.18.10.orig/arch/arm/include/asm/futex.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm/include/asm/futex.h 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/arch/arm/include/asm/switch_to.h linux-3.18.10/arch/arm/include/asm/switch_to.h ---- linux-3.18.10.orig/arch/arm/include/asm/switch_to.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm/include/asm/switch_to.h 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/arch/arm/include/asm/thread_info.h linux-3.18.10/arch/arm/include/asm/thread_info.h ---- linux-3.18.10.orig/arch/arm/include/asm/thread_info.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm/include/asm/thread_info.h 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/arch/arm/Kconfig linux-3.18.10/arch/arm/Kconfig ---- linux-3.18.10.orig/arch/arm/Kconfig 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm/Kconfig 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/arch/arm/kernel/asm-offsets.c linux-3.18.10/arch/arm/kernel/asm-offsets.c ---- linux-3.18.10.orig/arch/arm/kernel/asm-offsets.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm/kernel/asm-offsets.c 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/arch/arm/kernel/entry-armv.S linux-3.18.10/arch/arm/kernel/entry-armv.S ---- linux-3.18.10.orig/arch/arm/kernel/entry-armv.S 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm/kernel/entry-armv.S 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/arch/arm/kernel/process.c linux-3.18.10/arch/arm/kernel/process.c ---- linux-3.18.10.orig/arch/arm/kernel/process.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm/kernel/process.c 2015-03-26 12:42:13.555582327 +0100 -@@ -431,6 +431,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.10.orig/arch/arm/kernel/signal.c linux-3.18.10/arch/arm/kernel/signal.c ---- linux-3.18.10.orig/arch/arm/kernel/signal.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm/kernel/signal.c 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/arch/arm/kernel/smp.c linux-3.18.10/arch/arm/kernel/smp.c ---- linux-3.18.10.orig/arch/arm/kernel/smp.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm/kernel/smp.c 2015-03-26 12:42:13.555582327 +0100 -@@ -506,12 +506,14 @@ - } - - #ifdef CONFIG_IRQ_WORK -+#ifndef CONFIG_PREEMPT_RT_FULL - void arch_irq_work_raise(void) - { - if (arch_irq_work_has_interrupt()) - smp_cross_call(cpumask_of(smp_processor_id()), IPI_IRQ_WORK); - } - #endif -+#endif - - #ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST - void tick_broadcast(const struct cpumask *mask) -diff -Nur linux-3.18.10.orig/arch/arm/kernel/unwind.c linux-3.18.10/arch/arm/kernel/unwind.c ---- linux-3.18.10.orig/arch/arm/kernel/unwind.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm/kernel/unwind.c 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/arch/arm/mach-at91/at91rm9200_time.c linux-3.18.10/arch/arm/mach-at91/at91rm9200_time.c ---- linux-3.18.10.orig/arch/arm/mach-at91/at91rm9200_time.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm/mach-at91/at91rm9200_time.c 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/arch/arm/mach-exynos/platsmp.c linux-3.18.10/arch/arm/mach-exynos/platsmp.c ---- linux-3.18.10.orig/arch/arm/mach-exynos/platsmp.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm/mach-exynos/platsmp.c 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/arch/arm/mach-hisi/platmcpm.c linux-3.18.10/arch/arm/mach-hisi/platmcpm.c ---- linux-3.18.10.orig/arch/arm/mach-hisi/platmcpm.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm/mach-hisi/platmcpm.c 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/arch/arm/mach-omap2/omap-smp.c linux-3.18.10/arch/arm/mach-omap2/omap-smp.c ---- linux-3.18.10.orig/arch/arm/mach-omap2/omap-smp.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm/mach-omap2/omap-smp.c 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/arch/arm/mach-prima2/platsmp.c linux-3.18.10/arch/arm/mach-prima2/platsmp.c ---- linux-3.18.10.orig/arch/arm/mach-prima2/platsmp.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm/mach-prima2/platsmp.c 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/arch/arm/mach-qcom/platsmp.c linux-3.18.10/arch/arm/mach-qcom/platsmp.c ---- linux-3.18.10.orig/arch/arm/mach-qcom/platsmp.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm/mach-qcom/platsmp.c 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/arch/arm/mach-spear/platsmp.c linux-3.18.10/arch/arm/mach-spear/platsmp.c ---- linux-3.18.10.orig/arch/arm/mach-spear/platsmp.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm/mach-spear/platsmp.c 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/arch/arm/mach-sti/platsmp.c linux-3.18.10/arch/arm/mach-sti/platsmp.c ---- linux-3.18.10.orig/arch/arm/mach-sti/platsmp.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm/mach-sti/platsmp.c 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/arch/arm/mach-ux500/platsmp.c linux-3.18.10/arch/arm/mach-ux500/platsmp.c ---- linux-3.18.10.orig/arch/arm/mach-ux500/platsmp.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm/mach-ux500/platsmp.c 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/arch/arm/mm/fault.c linux-3.18.10/arch/arm/mm/fault.c ---- linux-3.18.10.orig/arch/arm/mm/fault.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm/mm/fault.c 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/arch/arm/mm/highmem.c linux-3.18.10/arch/arm/mm/highmem.c ---- linux-3.18.10.orig/arch/arm/mm/highmem.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm/mm/highmem.c 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/arch/arm/plat-versatile/platsmp.c linux-3.18.10/arch/arm/plat-versatile/platsmp.c ---- linux-3.18.10.orig/arch/arm/plat-versatile/platsmp.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm/plat-versatile/platsmp.c 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/arch/arm64/kernel/smp.c linux-3.18.10/arch/arm64/kernel/smp.c ---- linux-3.18.10.orig/arch/arm64/kernel/smp.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/arm64/kernel/smp.c 2015-03-26 12:42:13.559582331 +0100 -@@ -529,12 +529,14 @@ - } - - #ifdef CONFIG_IRQ_WORK -+#ifndef CONFIG_PREEMPT_RT_FULL - void arch_irq_work_raise(void) - { - if (__smp_cross_call) - smp_cross_call(cpumask_of(smp_processor_id()), IPI_IRQ_WORK); - } - #endif -+#endif - - static DEFINE_RAW_SPINLOCK(stop_lock); - -diff -Nur linux-3.18.10.orig/arch/avr32/mm/fault.c linux-3.18.10/arch/avr32/mm/fault.c ---- linux-3.18.10.orig/arch/avr32/mm/fault.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/avr32/mm/fault.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/cris/mm/fault.c linux-3.18.10/arch/cris/mm/fault.c ---- linux-3.18.10.orig/arch/cris/mm/fault.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/cris/mm/fault.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/frv/mm/fault.c linux-3.18.10/arch/frv/mm/fault.c ---- linux-3.18.10.orig/arch/frv/mm/fault.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/frv/mm/fault.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/ia64/mm/fault.c linux-3.18.10/arch/ia64/mm/fault.c ---- linux-3.18.10.orig/arch/ia64/mm/fault.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/ia64/mm/fault.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/Kconfig linux-3.18.10/arch/Kconfig ---- linux-3.18.10.orig/arch/Kconfig 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/Kconfig 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/arch/m32r/mm/fault.c linux-3.18.10/arch/m32r/mm/fault.c ---- linux-3.18.10.orig/arch/m32r/mm/fault.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/m32r/mm/fault.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/m68k/mm/fault.c linux-3.18.10/arch/m68k/mm/fault.c ---- linux-3.18.10.orig/arch/m68k/mm/fault.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/m68k/mm/fault.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/microblaze/mm/fault.c linux-3.18.10/arch/microblaze/mm/fault.c ---- linux-3.18.10.orig/arch/microblaze/mm/fault.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/microblaze/mm/fault.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/mips/Kconfig linux-3.18.10/arch/mips/Kconfig ---- linux-3.18.10.orig/arch/mips/Kconfig 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/mips/Kconfig 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/mips/kernel/signal.c linux-3.18.10/arch/mips/kernel/signal.c ---- linux-3.18.10.orig/arch/mips/kernel/signal.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/mips/kernel/signal.c 2015-03-26 12:42:13.559582331 +0100 -@@ -613,6 +613,7 @@ - __u32 thread_info_flags) - { - local_irq_enable(); -+ preempt_check_resched(); - - user_exit(); - -diff -Nur linux-3.18.10.orig/arch/mips/mm/fault.c linux-3.18.10/arch/mips/mm/fault.c ---- linux-3.18.10.orig/arch/mips/mm/fault.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/mips/mm/fault.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/mips/mm/init.c linux-3.18.10/arch/mips/mm/init.c ---- linux-3.18.10.orig/arch/mips/mm/init.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/mips/mm/init.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/mn10300/mm/fault.c linux-3.18.10/arch/mn10300/mm/fault.c ---- linux-3.18.10.orig/arch/mn10300/mm/fault.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/mn10300/mm/fault.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/parisc/mm/fault.c linux-3.18.10/arch/parisc/mm/fault.c ---- linux-3.18.10.orig/arch/parisc/mm/fault.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/parisc/mm/fault.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/powerpc/include/asm/thread_info.h linux-3.18.10/arch/powerpc/include/asm/thread_info.h ---- linux-3.18.10.orig/arch/powerpc/include/asm/thread_info.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/powerpc/include/asm/thread_info.h 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/powerpc/kernel/misc_32.S linux-3.18.10/arch/powerpc/kernel/misc_32.S ---- linux-3.18.10.orig/arch/powerpc/kernel/misc_32.S 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/powerpc/kernel/misc_32.S 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/powerpc/kernel/misc_64.S linux-3.18.10/arch/powerpc/kernel/misc_64.S ---- linux-3.18.10.orig/arch/powerpc/kernel/misc_64.S 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/powerpc/kernel/misc_64.S 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/powerpc/kernel/time.c linux-3.18.10/arch/powerpc/kernel/time.c ---- linux-3.18.10.orig/arch/powerpc/kernel/time.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/powerpc/kernel/time.c 2015-03-26 12:42:13.559582331 +0100 -@@ -424,7 +424,7 @@ - EXPORT_SYMBOL(profile_pc); - #endif - --#ifdef CONFIG_IRQ_WORK -+#if defined(CONFIG_IRQ_WORK) && !defined(CONFIG_PREEMPT_RT_FULL) - - /* - * 64-bit uses a byte in the PACA, 32-bit uses a per-cpu variable... -diff -Nur linux-3.18.10.orig/arch/powerpc/mm/fault.c linux-3.18.10/arch/powerpc/mm/fault.c ---- linux-3.18.10.orig/arch/powerpc/mm/fault.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/powerpc/mm/fault.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/s390/mm/fault.c linux-3.18.10/arch/s390/mm/fault.c ---- linux-3.18.10.orig/arch/s390/mm/fault.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/s390/mm/fault.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/score/mm/fault.c linux-3.18.10/arch/score/mm/fault.c ---- linux-3.18.10.orig/arch/score/mm/fault.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/score/mm/fault.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/sh/kernel/irq.c linux-3.18.10/arch/sh/kernel/irq.c ---- linux-3.18.10.orig/arch/sh/kernel/irq.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/sh/kernel/irq.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/sh/mm/fault.c linux-3.18.10/arch/sh/mm/fault.c ---- linux-3.18.10.orig/arch/sh/mm/fault.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/sh/mm/fault.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/sparc/Kconfig linux-3.18.10/arch/sparc/Kconfig ---- linux-3.18.10.orig/arch/sparc/Kconfig 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/sparc/Kconfig 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/sparc/kernel/irq_64.c linux-3.18.10/arch/sparc/kernel/irq_64.c ---- linux-3.18.10.orig/arch/sparc/kernel/irq_64.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/sparc/kernel/irq_64.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/sparc/kernel/pcr.c linux-3.18.10/arch/sparc/kernel/pcr.c ---- linux-3.18.10.orig/arch/sparc/kernel/pcr.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/sparc/kernel/pcr.c 2015-03-26 12:42:13.559582331 +0100 -@@ -43,10 +43,12 @@ - set_irq_regs(old_regs); - } - -+#ifndef CONFIG_PREEMPT_RT_FULL - void arch_irq_work_raise(void) - { - set_softint(1 << PIL_DEFERRED_PCR_WORK); - } -+#endif - - const struct pcr_ops *pcr_ops; - EXPORT_SYMBOL_GPL(pcr_ops); -diff -Nur linux-3.18.10.orig/arch/sparc/kernel/setup_32.c linux-3.18.10/arch/sparc/kernel/setup_32.c ---- linux-3.18.10.orig/arch/sparc/kernel/setup_32.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/sparc/kernel/setup_32.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/sparc/kernel/setup_64.c linux-3.18.10/arch/sparc/kernel/setup_64.c ---- linux-3.18.10.orig/arch/sparc/kernel/setup_64.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/sparc/kernel/setup_64.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/sparc/mm/fault_32.c linux-3.18.10/arch/sparc/mm/fault_32.c ---- linux-3.18.10.orig/arch/sparc/mm/fault_32.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/sparc/mm/fault_32.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/sparc/mm/fault_64.c linux-3.18.10/arch/sparc/mm/fault_64.c ---- linux-3.18.10.orig/arch/sparc/mm/fault_64.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/sparc/mm/fault_64.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/tile/mm/fault.c linux-3.18.10/arch/tile/mm/fault.c ---- linux-3.18.10.orig/arch/tile/mm/fault.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/tile/mm/fault.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/um/kernel/trap.c linux-3.18.10/arch/um/kernel/trap.c ---- linux-3.18.10.orig/arch/um/kernel/trap.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/um/kernel/trap.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/x86/crypto/aesni-intel_glue.c linux-3.18.10/arch/x86/crypto/aesni-intel_glue.c ---- linux-3.18.10.orig/arch/x86/crypto/aesni-intel_glue.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/crypto/aesni-intel_glue.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/x86/crypto/cast5_avx_glue.c linux-3.18.10/arch/x86/crypto/cast5_avx_glue.c ---- linux-3.18.10.orig/arch/x86/crypto/cast5_avx_glue.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/crypto/cast5_avx_glue.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/x86/crypto/glue_helper.c linux-3.18.10/arch/x86/crypto/glue_helper.c ---- linux-3.18.10.orig/arch/x86/crypto/glue_helper.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/crypto/glue_helper.c 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/x86/include/asm/preempt.h linux-3.18.10/arch/x86/include/asm/preempt.h ---- linux-3.18.10.orig/arch/x86/include/asm/preempt.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/include/asm/preempt.h 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/x86/include/asm/signal.h linux-3.18.10/arch/x86/include/asm/signal.h ---- linux-3.18.10.orig/arch/x86/include/asm/signal.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/include/asm/signal.h 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/x86/include/asm/stackprotector.h linux-3.18.10/arch/x86/include/asm/stackprotector.h ---- linux-3.18.10.orig/arch/x86/include/asm/stackprotector.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/include/asm/stackprotector.h 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/x86/include/asm/thread_info.h linux-3.18.10/arch/x86/include/asm/thread_info.h ---- linux-3.18.10.orig/arch/x86/include/asm/thread_info.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/include/asm/thread_info.h 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/x86/include/asm/uv/uv_bau.h linux-3.18.10/arch/x86/include/asm/uv/uv_bau.h ---- linux-3.18.10.orig/arch/x86/include/asm/uv/uv_bau.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/include/asm/uv/uv_bau.h 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/arch/x86/include/asm/uv/uv_hub.h linux-3.18.10/arch/x86/include/asm/uv/uv_hub.h ---- linux-3.18.10.orig/arch/x86/include/asm/uv/uv_hub.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/include/asm/uv/uv_hub.h 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/arch/x86/Kconfig linux-3.18.10/arch/x86/Kconfig ---- linux-3.18.10.orig/arch/x86/Kconfig 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/Kconfig 2015-03-26 12:42:13.559582331 +0100 -@@ -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.10.orig/arch/x86/kernel/apic/io_apic.c linux-3.18.10/arch/x86/kernel/apic/io_apic.c ---- linux-3.18.10.orig/arch/x86/kernel/apic/io_apic.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/kernel/apic/io_apic.c 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/arch/x86/kernel/apic/x2apic_uv_x.c linux-3.18.10/arch/x86/kernel/apic/x2apic_uv_x.c ---- linux-3.18.10.orig/arch/x86/kernel/apic/x2apic_uv_x.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/kernel/apic/x2apic_uv_x.c 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/arch/x86/kernel/asm-offsets.c linux-3.18.10/arch/x86/kernel/asm-offsets.c ---- linux-3.18.10.orig/arch/x86/kernel/asm-offsets.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/kernel/asm-offsets.c 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/arch/x86/kernel/cpu/mcheck/mce.c linux-3.18.10/arch/x86/kernel/cpu/mcheck/mce.c ---- linux-3.18.10.orig/arch/x86/kernel/cpu/mcheck/mce.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/kernel/cpu/mcheck/mce.c 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/arch/x86/kernel/entry_32.S linux-3.18.10/arch/x86/kernel/entry_32.S ---- linux-3.18.10.orig/arch/x86/kernel/entry_32.S 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/kernel/entry_32.S 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/arch/x86/kernel/entry_64.S linux-3.18.10/arch/x86/kernel/entry_64.S ---- linux-3.18.10.orig/arch/x86/kernel/entry_64.S 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/kernel/entry_64.S 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/arch/x86/kernel/irq_32.c linux-3.18.10/arch/x86/kernel/irq_32.c ---- linux-3.18.10.orig/arch/x86/kernel/irq_32.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/kernel/irq_32.c 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/arch/x86/kernel/irq_work.c linux-3.18.10/arch/x86/kernel/irq_work.c ---- linux-3.18.10.orig/arch/x86/kernel/irq_work.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/kernel/irq_work.c 2015-03-26 12:42:13.563582336 +0100 -@@ -38,6 +38,7 @@ - exiting_irq(); - } - -+#ifndef CONFIG_PREEMPT_RT_FULL - void arch_irq_work_raise(void) - { - #ifdef CONFIG_X86_LOCAL_APIC -@@ -48,3 +49,4 @@ - apic_wait_icr_idle(); - #endif - } -+#endif -diff -Nur linux-3.18.10.orig/arch/x86/kernel/process_32.c linux-3.18.10/arch/x86/kernel/process_32.c ---- linux-3.18.10.orig/arch/x86/kernel/process_32.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/kernel/process_32.c 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/arch/x86/kernel/signal.c linux-3.18.10/arch/x86/kernel/signal.c ---- linux-3.18.10.orig/arch/x86/kernel/signal.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/kernel/signal.c 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/arch/x86/kernel/traps.c linux-3.18.10/arch/x86/kernel/traps.c ---- linux-3.18.10.orig/arch/x86/kernel/traps.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/kernel/traps.c 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/arch/x86/kvm/x86.c linux-3.18.10/arch/x86/kvm/x86.c ---- linux-3.18.10.orig/arch/x86/kvm/x86.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/kvm/x86.c 2015-03-26 12:42:13.563582336 +0100 -@@ -5773,6 +5773,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.10.orig/arch/x86/mm/fault.c linux-3.18.10/arch/x86/mm/fault.c ---- linux-3.18.10.orig/arch/x86/mm/fault.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/mm/fault.c 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/arch/x86/mm/highmem_32.c linux-3.18.10/arch/x86/mm/highmem_32.c ---- linux-3.18.10.orig/arch/x86/mm/highmem_32.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/mm/highmem_32.c 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/arch/x86/mm/iomap_32.c linux-3.18.10/arch/x86/mm/iomap_32.c ---- linux-3.18.10.orig/arch/x86/mm/iomap_32.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/mm/iomap_32.c 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/arch/x86/platform/uv/tlb_uv.c linux-3.18.10/arch/x86/platform/uv/tlb_uv.c ---- linux-3.18.10.orig/arch/x86/platform/uv/tlb_uv.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/platform/uv/tlb_uv.c 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/arch/x86/platform/uv/uv_time.c linux-3.18.10/arch/x86/platform/uv/uv_time.c ---- linux-3.18.10.orig/arch/x86/platform/uv/uv_time.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/x86/platform/uv/uv_time.c 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/arch/xtensa/mm/fault.c linux-3.18.10/arch/xtensa/mm/fault.c ---- linux-3.18.10.orig/arch/xtensa/mm/fault.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/arch/xtensa/mm/fault.c 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/block/blk-core.c linux-3.18.10/block/blk-core.c ---- linux-3.18.10.orig/block/blk-core.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/block/blk-core.c 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/block/blk-ioc.c linux-3.18.10/block/blk-ioc.c ---- linux-3.18.10.orig/block/blk-ioc.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/block/blk-ioc.c 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/block/blk-iopoll.c linux-3.18.10/block/blk-iopoll.c ---- linux-3.18.10.orig/block/blk-iopoll.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/block/blk-iopoll.c 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/block/blk-mq.c linux-3.18.10/block/blk-mq.c ---- linux-3.18.10.orig/block/blk-mq.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/block/blk-mq.c 2015-03-26 12:42:13.563582336 +0100 -@@ -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; - -+#if 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)) { -+#if 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.10.orig/block/blk-mq-cpu.c linux-3.18.10/block/blk-mq-cpu.c ---- linux-3.18.10.orig/block/blk-mq-cpu.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/block/blk-mq-cpu.c 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/block/blk-mq.h linux-3.18.10/block/blk-mq.h ---- linux-3.18.10.orig/block/blk-mq.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/block/blk-mq.h 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/block/blk-softirq.c linux-3.18.10/block/blk-softirq.c ---- linux-3.18.10.orig/block/blk-softirq.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/block/blk-softirq.c 2015-03-26 12:42:13.563582336 +0100 -@@ -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.10.orig/block/bounce.c linux-3.18.10/block/bounce.c ---- linux-3.18.10.orig/block/bounce.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/block/bounce.c 2015-03-26 12:42:13.567582340 +0100 -@@ -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.10.orig/crypto/algapi.c linux-3.18.10/crypto/algapi.c ---- linux-3.18.10.orig/crypto/algapi.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/crypto/algapi.c 2015-03-26 12:42:13.567582340 +0100 -@@ -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.10.orig/crypto/api.c linux-3.18.10/crypto/api.c ---- linux-3.18.10.orig/crypto/api.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/crypto/api.c 2015-03-26 12:42:13.567582340 +0100 -@@ -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.10.orig/crypto/internal.h linux-3.18.10/crypto/internal.h ---- linux-3.18.10.orig/crypto/internal.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/crypto/internal.h 2015-03-26 12:42:13.567582340 +0100 -@@ -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.10.orig/Documentation/hwlat_detector.txt linux-3.18.10/Documentation/hwlat_detector.txt ---- linux-3.18.10.orig/Documentation/hwlat_detector.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.10/Documentation/hwlat_detector.txt 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/Documentation/sysrq.txt linux-3.18.10/Documentation/sysrq.txt ---- linux-3.18.10.orig/Documentation/sysrq.txt 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/Documentation/sysrq.txt 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/Documentation/trace/histograms.txt linux-3.18.10/Documentation/trace/histograms.txt ---- linux-3.18.10.orig/Documentation/trace/histograms.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.10/Documentation/trace/histograms.txt 2015-03-26 12:42:13.555582327 +0100 -@@ -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.10.orig/drivers/acpi/acpica/acglobal.h linux-3.18.10/drivers/acpi/acpica/acglobal.h ---- linux-3.18.10.orig/drivers/acpi/acpica/acglobal.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/acpi/acpica/acglobal.h 2015-03-26 12:42:13.567582340 +0100 -@@ -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.10.orig/drivers/acpi/acpica/hwregs.c linux-3.18.10/drivers/acpi/acpica/hwregs.c ---- linux-3.18.10.orig/drivers/acpi/acpica/hwregs.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/acpi/acpica/hwregs.c 2015-03-26 12:42:13.567582340 +0100 -@@ -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.10.orig/drivers/acpi/acpica/hwxface.c linux-3.18.10/drivers/acpi/acpica/hwxface.c ---- linux-3.18.10.orig/drivers/acpi/acpica/hwxface.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/acpi/acpica/hwxface.c 2015-03-26 12:42:13.567582340 +0100 -@@ -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.10.orig/drivers/acpi/acpica/utmutex.c linux-3.18.10/drivers/acpi/acpica/utmutex.c ---- linux-3.18.10.orig/drivers/acpi/acpica/utmutex.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/acpi/acpica/utmutex.c 2015-03-26 12:42:13.567582340 +0100 -@@ -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.10.orig/drivers/ata/libata-sff.c linux-3.18.10/drivers/ata/libata-sff.c ---- linux-3.18.10.orig/drivers/ata/libata-sff.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/ata/libata-sff.c 2015-03-26 12:42:13.567582340 +0100 -@@ -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.10.orig/drivers/char/random.c linux-3.18.10/drivers/char/random.c ---- linux-3.18.10.orig/drivers/char/random.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/char/random.c 2015-03-26 12:42:13.567582340 +0100 -@@ -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.10.orig/drivers/clocksource/tcb_clksrc.c linux-3.18.10/drivers/clocksource/tcb_clksrc.c ---- linux-3.18.10.orig/drivers/clocksource/tcb_clksrc.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/clocksource/tcb_clksrc.c 2015-03-26 12:42:13.567582340 +0100 -@@ -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.10.orig/drivers/clocksource/timer-atmel-pit.c linux-3.18.10/drivers/clocksource/timer-atmel-pit.c ---- linux-3.18.10.orig/drivers/clocksource/timer-atmel-pit.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/clocksource/timer-atmel-pit.c 2015-03-26 12:42:13.567582340 +0100 -@@ -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.10.orig/drivers/gpio/gpio-omap.c linux-3.18.10/drivers/gpio/gpio-omap.c ---- linux-3.18.10.orig/drivers/gpio/gpio-omap.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/gpio/gpio-omap.c 2015-03-26 12:42:13.567582340 +0100 -@@ -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.10.orig/drivers/gpu/drm/i915/i915_gem.c linux-3.18.10/drivers/gpu/drm/i915/i915_gem.c ---- linux-3.18.10.orig/drivers/gpu/drm/i915/i915_gem.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/gpu/drm/i915/i915_gem.c 2015-03-26 12:42:13.567582340 +0100 -@@ -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.10.orig/drivers/gpu/drm/i915/i915_gem_execbuffer.c linux-3.18.10/drivers/gpu/drm/i915/i915_gem_execbuffer.c ---- linux-3.18.10.orig/drivers/gpu/drm/i915/i915_gem_execbuffer.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/gpu/drm/i915/i915_gem_execbuffer.c 2015-03-26 12:42:13.567582340 +0100 -@@ -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.10.orig/drivers/gpu/drm/radeon/evergreen.c linux-3.18.10/drivers/gpu/drm/radeon/evergreen.c ---- linux-3.18.10.orig/drivers/gpu/drm/radeon/evergreen.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/gpu/drm/radeon/evergreen.c 2015-03-26 12:42:16.827586167 +0100 -@@ -4589,6 +4589,9 @@ - WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, afmt5); - WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, afmt6); - -+ /* posting read */ -+ RREG32(SRBM_STATUS); -+ - return 0; - } - -diff -Nur linux-3.18.10.orig/drivers/gpu/drm/radeon/r600.c linux-3.18.10/drivers/gpu/drm/radeon/r600.c ---- linux-3.18.10.orig/drivers/gpu/drm/radeon/r600.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/gpu/drm/radeon/r600.c 2015-03-26 12:42:18.651588307 +0100 -@@ -3787,6 +3787,9 @@ - WREG32(RV770_CG_THERMAL_INT, thermal_int); - } - -+ /* posting read */ -+ RREG32(R_000E50_SRBM_STATUS); -+ - return 0; - } - -diff -Nur linux-3.18.10.orig/drivers/gpu/drm/radeon/radeon_fence.c linux-3.18.10/drivers/gpu/drm/radeon/radeon_fence.c ---- linux-3.18.10.orig/drivers/gpu/drm/radeon/radeon_fence.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/gpu/drm/radeon/radeon_fence.c 2015-03-26 12:42:18.651588307 +0100 -@@ -1029,37 +1029,59 @@ - return test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags); - } - -+struct radeon_wait_cb { -+ struct fence_cb base; -+ struct task_struct *task; -+}; -+ -+static void -+radeon_fence_wait_cb(struct fence *fence, struct fence_cb *cb) -+{ -+ struct radeon_wait_cb *wait = -+ container_of(cb, struct radeon_wait_cb, base); -+ -+ wake_up_process(wait->task); -+} -+ - static signed long radeon_fence_default_wait(struct fence *f, bool intr, - signed long t) - { - struct radeon_fence *fence = to_radeon_fence(f); - struct radeon_device *rdev = fence->rdev; -- bool signaled; -+ struct radeon_wait_cb cb; -+ -+ cb.task = current; -+ -+ if (fence_add_callback(f, &cb.base, radeon_fence_wait_cb)) -+ return t; - -- fence_enable_sw_signaling(&fence->base); -+ while (t > 0) { -+ if (intr) -+ set_current_state(TASK_INTERRUPTIBLE); -+ else -+ set_current_state(TASK_UNINTERRUPTIBLE); -+ -+ /* -+ * radeon_test_signaled must be called after -+ * set_current_state to prevent a race with wake_up_process -+ */ -+ if (radeon_test_signaled(fence)) -+ break; -+ -+ if (rdev->needs_reset) { -+ t = -EDEADLK; -+ break; -+ } -+ -+ t = schedule_timeout(t); -+ -+ if (t > 0 && intr && signal_pending(current)) -+ t = -ERESTARTSYS; -+ } - -- /* -- * This function has to return -EDEADLK, but cannot hold -- * exclusive_lock during the wait because some callers -- * may already hold it. This means checking needs_reset without -- * lock, and not fiddling with any gpu internals. -- * -- * The callback installed with fence_enable_sw_signaling will -- * run before our wait_event_*timeout call, so we will see -- * both the signaled fence and the changes to needs_reset. -- */ -- -- if (intr) -- t = wait_event_interruptible_timeout(rdev->fence_queue, -- ((signaled = radeon_test_signaled(fence)) || -- rdev->needs_reset), t); -- else -- t = wait_event_timeout(rdev->fence_queue, -- ((signaled = radeon_test_signaled(fence)) || -- rdev->needs_reset), t); -+ __set_current_state(TASK_RUNNING); -+ fence_remove_callback(f, &cb.base); - -- if (t > 0 && !signaled) -- return -EDEADLK; - return t; - } - -diff -Nur linux-3.18.10.orig/drivers/gpu/drm/radeon/rs600.c linux-3.18.10/drivers/gpu/drm/radeon/rs600.c ---- linux-3.18.10.orig/drivers/gpu/drm/radeon/rs600.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/gpu/drm/radeon/rs600.c 2015-03-26 12:42:18.651588307 +0100 -@@ -693,6 +693,10 @@ - WREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL, hpd2); - if (ASIC_IS_DCE2(rdev)) - WREG32(R_007408_HDMI0_AUDIO_PACKET_CONTROL, hdmi0); -+ -+ /* posting read */ -+ RREG32(R_000040_GEN_INT_CNTL); -+ - return 0; - } - -diff -Nur linux-3.18.10.orig/drivers/gpu/drm/radeon/si.c linux-3.18.10/drivers/gpu/drm/radeon/si.c ---- linux-3.18.10.orig/drivers/gpu/drm/radeon/si.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/gpu/drm/radeon/si.c 2015-03-26 12:42:18.655588312 +0100 -@@ -6192,6 +6192,9 @@ - - WREG32(CG_THERMAL_INT, thermal_int); - -+ /* posting read */ -+ RREG32(SRBM_STATUS); -+ - return 0; - } - -diff -Nur linux-3.18.10.orig/drivers/i2c/busses/i2c-omap.c linux-3.18.10/drivers/i2c/busses/i2c-omap.c ---- linux-3.18.10.orig/drivers/i2c/busses/i2c-omap.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/i2c/busses/i2c-omap.c 2015-03-26 12:42:18.655588312 +0100 -@@ -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.10.orig/drivers/ide/alim15x3.c linux-3.18.10/drivers/ide/alim15x3.c ---- linux-3.18.10.orig/drivers/ide/alim15x3.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/ide/alim15x3.c 2015-03-26 12:42:18.655588312 +0100 -@@ -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.10.orig/drivers/ide/hpt366.c linux-3.18.10/drivers/ide/hpt366.c ---- linux-3.18.10.orig/drivers/ide/hpt366.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/ide/hpt366.c 2015-03-26 12:42:18.655588312 +0100 -@@ -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.10.orig/drivers/ide/ide-io.c linux-3.18.10/drivers/ide/ide-io.c ---- linux-3.18.10.orig/drivers/ide/ide-io.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/ide/ide-io.c 2015-03-26 12:42:18.655588312 +0100 -@@ -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.10.orig/drivers/ide/ide-iops.c linux-3.18.10/drivers/ide/ide-iops.c ---- linux-3.18.10.orig/drivers/ide/ide-iops.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/ide/ide-iops.c 2015-03-26 12:42:18.655588312 +0100 -@@ -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.10.orig/drivers/ide/ide-io-std.c linux-3.18.10/drivers/ide/ide-io-std.c ---- linux-3.18.10.orig/drivers/ide/ide-io-std.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/ide/ide-io-std.c 2015-03-26 12:42:18.655588312 +0100 -@@ -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.10.orig/drivers/ide/ide-probe.c linux-3.18.10/drivers/ide/ide-probe.c ---- linux-3.18.10.orig/drivers/ide/ide-probe.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/ide/ide-probe.c 2015-03-26 12:42:18.655588312 +0100 -@@ -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.10.orig/drivers/ide/ide-taskfile.c linux-3.18.10/drivers/ide/ide-taskfile.c ---- linux-3.18.10.orig/drivers/ide/ide-taskfile.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/ide/ide-taskfile.c 2015-03-26 12:42:18.655588312 +0100 -@@ -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.10.orig/drivers/infiniband/ulp/ipoib/ipoib_multicast.c linux-3.18.10/drivers/infiniband/ulp/ipoib/ipoib_multicast.c ---- linux-3.18.10.orig/drivers/infiniband/ulp/ipoib/ipoib_multicast.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/infiniband/ulp/ipoib/ipoib_multicast.c 2015-03-26 12:42:18.655588312 +0100 -@@ -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.10.orig/drivers/input/gameport/gameport.c linux-3.18.10/drivers/input/gameport/gameport.c ---- linux-3.18.10.orig/drivers/input/gameport/gameport.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/input/gameport/gameport.c 2015-03-26 12:42:18.655588312 +0100 -@@ -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.10.orig/drivers/leds/trigger/Kconfig linux-3.18.10/drivers/leds/trigger/Kconfig ---- linux-3.18.10.orig/drivers/leds/trigger/Kconfig 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/leds/trigger/Kconfig 2015-03-26 12:42:18.655588312 +0100 -@@ -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.10.orig/drivers/md/bcache/Kconfig linux-3.18.10/drivers/md/bcache/Kconfig ---- linux-3.18.10.orig/drivers/md/bcache/Kconfig 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/md/bcache/Kconfig 2015-03-26 12:42:18.655588312 +0100 -@@ -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.10.orig/drivers/md/dm.c linux-3.18.10/drivers/md/dm.c ---- linux-3.18.10.orig/drivers/md/dm.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/md/dm.c 2015-03-26 12:42:18.655588312 +0100 -@@ -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.10.orig/drivers/md/raid5.c linux-3.18.10/drivers/md/raid5.c ---- linux-3.18.10.orig/drivers/md/raid5.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/md/raid5.c 2015-03-26 12:42:18.655588312 +0100 -@@ -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.10.orig/drivers/md/raid5.h linux-3.18.10/drivers/md/raid5.h ---- linux-3.18.10.orig/drivers/md/raid5.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/md/raid5.h 2015-03-26 12:42:18.655588312 +0100 -@@ -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.10.orig/drivers/misc/hwlat_detector.c linux-3.18.10/drivers/misc/hwlat_detector.c ---- linux-3.18.10.orig/drivers/misc/hwlat_detector.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.10/drivers/misc/hwlat_detector.c 2015-03-26 12:42:18.655588312 +0100 -@@ -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.10.orig/drivers/misc/Kconfig linux-3.18.10/drivers/misc/Kconfig ---- linux-3.18.10.orig/drivers/misc/Kconfig 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/misc/Kconfig 2015-03-26 12:42:18.655588312 +0100 -@@ -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.10.orig/drivers/misc/Makefile linux-3.18.10/drivers/misc/Makefile ---- linux-3.18.10.orig/drivers/misc/Makefile 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/misc/Makefile 2015-03-26 12:42:18.655588312 +0100 -@@ -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.10.orig/drivers/mmc/host/mmci.c linux-3.18.10/drivers/mmc/host/mmci.c ---- linux-3.18.10.orig/drivers/mmc/host/mmci.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/mmc/host/mmci.c 2015-03-26 12:42:18.655588312 +0100 -@@ -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.10.orig/drivers/mmc/host/sdhci.c linux-3.18.10/drivers/mmc/host/sdhci.c ---- linux-3.18.10.orig/drivers/mmc/host/sdhci.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/mmc/host/sdhci.c 2015-03-26 12:42:18.655588312 +0100 -@@ -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.10.orig/drivers/net/ethernet/3com/3c59x.c linux-3.18.10/drivers/net/ethernet/3com/3c59x.c ---- linux-3.18.10.orig/drivers/net/ethernet/3com/3c59x.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/net/ethernet/3com/3c59x.c 2015-03-26 12:42:18.655588312 +0100 -@@ -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.10.orig/drivers/net/ethernet/atheros/atl1c/atl1c_main.c linux-3.18.10/drivers/net/ethernet/atheros/atl1c/atl1c_main.c ---- linux-3.18.10.orig/drivers/net/ethernet/atheros/atl1c/atl1c_main.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/net/ethernet/atheros/atl1c/atl1c_main.c 2015-03-26 12:42:18.659588317 +0100 -@@ -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.10.orig/drivers/net/ethernet/atheros/atl1e/atl1e_main.c linux-3.18.10/drivers/net/ethernet/atheros/atl1e/atl1e_main.c ---- linux-3.18.10.orig/drivers/net/ethernet/atheros/atl1e/atl1e_main.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/net/ethernet/atheros/atl1e/atl1e_main.c 2015-03-26 12:42:18.659588317 +0100 -@@ -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.10.orig/drivers/net/ethernet/chelsio/cxgb/sge.c linux-3.18.10/drivers/net/ethernet/chelsio/cxgb/sge.c ---- linux-3.18.10.orig/drivers/net/ethernet/chelsio/cxgb/sge.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/net/ethernet/chelsio/cxgb/sge.c 2015-03-26 12:42:18.659588317 +0100 -@@ -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.10.orig/drivers/net/ethernet/freescale/gianfar.c linux-3.18.10/drivers/net/ethernet/freescale/gianfar.c ---- linux-3.18.10.orig/drivers/net/ethernet/freescale/gianfar.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/net/ethernet/freescale/gianfar.c 2015-03-26 12:42:18.659588317 +0100 -@@ -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.10.orig/drivers/net/ethernet/neterion/s2io.c linux-3.18.10/drivers/net/ethernet/neterion/s2io.c ---- linux-3.18.10.orig/drivers/net/ethernet/neterion/s2io.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/net/ethernet/neterion/s2io.c 2015-03-26 12:42:18.659588317 +0100 -@@ -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.10.orig/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c linux-3.18.10/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c ---- linux-3.18.10.orig/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c 2015-03-26 12:42:18.659588317 +0100 -@@ -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.10.orig/drivers/net/ethernet/realtek/8139too.c linux-3.18.10/drivers/net/ethernet/realtek/8139too.c ---- linux-3.18.10.orig/drivers/net/ethernet/realtek/8139too.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/net/ethernet/realtek/8139too.c 2015-03-26 12:42:18.659588317 +0100 -@@ -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.10.orig/drivers/net/ethernet/tehuti/tehuti.c linux-3.18.10/drivers/net/ethernet/tehuti/tehuti.c ---- linux-3.18.10.orig/drivers/net/ethernet/tehuti/tehuti.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/net/ethernet/tehuti/tehuti.c 2015-03-26 12:42:18.659588317 +0100 -@@ -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.10.orig/drivers/net/rionet.c linux-3.18.10/drivers/net/rionet.c ---- linux-3.18.10.orig/drivers/net/rionet.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/net/rionet.c 2015-03-26 12:42:18.659588317 +0100 -@@ -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.10.orig/drivers/net/wireless/orinoco/orinoco_usb.c linux-3.18.10/drivers/net/wireless/orinoco/orinoco_usb.c ---- linux-3.18.10.orig/drivers/net/wireless/orinoco/orinoco_usb.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/net/wireless/orinoco/orinoco_usb.c 2015-03-26 12:42:18.659588317 +0100 -@@ -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.10.orig/drivers/pci/access.c linux-3.18.10/drivers/pci/access.c ---- linux-3.18.10.orig/drivers/pci/access.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/pci/access.c 2015-03-26 12:42:18.659588317 +0100 -@@ -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.10.orig/drivers/scsi/fcoe/fcoe.c linux-3.18.10/drivers/scsi/fcoe/fcoe.c ---- linux-3.18.10.orig/drivers/scsi/fcoe/fcoe.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/scsi/fcoe/fcoe.c 2015-03-26 12:42:18.659588317 +0100 -@@ -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.10.orig/drivers/scsi/fcoe/fcoe_ctlr.c linux-3.18.10/drivers/scsi/fcoe/fcoe_ctlr.c ---- linux-3.18.10.orig/drivers/scsi/fcoe/fcoe_ctlr.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/scsi/fcoe/fcoe_ctlr.c 2015-03-26 12:42:18.659588317 +0100 -@@ -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.10.orig/drivers/scsi/libfc/fc_exch.c linux-3.18.10/drivers/scsi/libfc/fc_exch.c ---- linux-3.18.10.orig/drivers/scsi/libfc/fc_exch.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/scsi/libfc/fc_exch.c 2015-03-26 12:42:18.659588317 +0100 -@@ -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.10.orig/drivers/scsi/libsas/sas_ata.c linux-3.18.10/drivers/scsi/libsas/sas_ata.c ---- linux-3.18.10.orig/drivers/scsi/libsas/sas_ata.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/scsi/libsas/sas_ata.c 2015-03-26 12:42:18.659588317 +0100 -@@ -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.10.orig/drivers/scsi/qla2xxx/qla_inline.h linux-3.18.10/drivers/scsi/qla2xxx/qla_inline.h ---- linux-3.18.10.orig/drivers/scsi/qla2xxx/qla_inline.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/scsi/qla2xxx/qla_inline.h 2015-03-26 12:42:18.659588317 +0100 -@@ -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.10.orig/drivers/thermal/x86_pkg_temp_thermal.c linux-3.18.10/drivers/thermal/x86_pkg_temp_thermal.c ---- linux-3.18.10.orig/drivers/thermal/x86_pkg_temp_thermal.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/thermal/x86_pkg_temp_thermal.c 2015-03-26 12:42:18.659588317 +0100 -@@ -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.10.orig/drivers/tty/serial/8250/8250_core.c linux-3.18.10/drivers/tty/serial/8250/8250_core.c ---- linux-3.18.10.orig/drivers/tty/serial/8250/8250_core.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/tty/serial/8250/8250_core.c 2015-03-26 12:42:18.659588317 +0100 -@@ -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.10.orig/drivers/tty/serial/amba-pl011.c linux-3.18.10/drivers/tty/serial/amba-pl011.c ---- linux-3.18.10.orig/drivers/tty/serial/amba-pl011.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/tty/serial/amba-pl011.c 2015-03-26 12:42:18.659588317 +0100 -@@ -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.10.orig/drivers/tty/serial/omap-serial.c linux-3.18.10/drivers/tty/serial/omap-serial.c ---- linux-3.18.10.orig/drivers/tty/serial/omap-serial.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/tty/serial/omap-serial.c 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/drivers/usb/core/hcd.c linux-3.18.10/drivers/usb/core/hcd.c ---- linux-3.18.10.orig/drivers/usb/core/hcd.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/usb/core/hcd.c 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/drivers/usb/gadget/function/f_fs.c linux-3.18.10/drivers/usb/gadget/function/f_fs.c ---- linux-3.18.10.orig/drivers/usb/gadget/function/f_fs.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/usb/gadget/function/f_fs.c 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/drivers/usb/gadget/legacy/inode.c linux-3.18.10/drivers/usb/gadget/legacy/inode.c ---- linux-3.18.10.orig/drivers/usb/gadget/legacy/inode.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/drivers/usb/gadget/legacy/inode.c 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/fs/aio.c linux-3.18.10/fs/aio.c ---- linux-3.18.10.orig/fs/aio.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/fs/aio.c 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/fs/autofs4/autofs_i.h linux-3.18.10/fs/autofs4/autofs_i.h ---- linux-3.18.10.orig/fs/autofs4/autofs_i.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/fs/autofs4/autofs_i.h 2015-03-26 12:42:18.663588322 +0100 -@@ -34,6 +34,7 @@ - #include - #include - #include -+#include - #include - #include - -diff -Nur linux-3.18.10.orig/fs/autofs4/expire.c linux-3.18.10/fs/autofs4/expire.c ---- linux-3.18.10.orig/fs/autofs4/expire.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/fs/autofs4/expire.c 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/fs/buffer.c linux-3.18.10/fs/buffer.c ---- linux-3.18.10.orig/fs/buffer.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/fs/buffer.c 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/fs/dcache.c linux-3.18.10/fs/dcache.c ---- linux-3.18.10.orig/fs/dcache.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/fs/dcache.c 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/fs/eventpoll.c linux-3.18.10/fs/eventpoll.c ---- linux-3.18.10.orig/fs/eventpoll.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/fs/eventpoll.c 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/fs/exec.c linux-3.18.10/fs/exec.c ---- linux-3.18.10.orig/fs/exec.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/fs/exec.c 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/fs/jbd/checkpoint.c linux-3.18.10/fs/jbd/checkpoint.c ---- linux-3.18.10.orig/fs/jbd/checkpoint.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/fs/jbd/checkpoint.c 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/fs/jbd2/checkpoint.c linux-3.18.10/fs/jbd2/checkpoint.c ---- linux-3.18.10.orig/fs/jbd2/checkpoint.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/fs/jbd2/checkpoint.c 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/fs/namespace.c linux-3.18.10/fs/namespace.c ---- linux-3.18.10.orig/fs/namespace.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/fs/namespace.c 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/fs/ntfs/aops.c linux-3.18.10/fs/ntfs/aops.c ---- linux-3.18.10.orig/fs/ntfs/aops.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/fs/ntfs/aops.c 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/fs/timerfd.c linux-3.18.10/fs/timerfd.c ---- linux-3.18.10.orig/fs/timerfd.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/fs/timerfd.c 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/include/acpi/platform/aclinux.h linux-3.18.10/include/acpi/platform/aclinux.h ---- linux-3.18.10.orig/include/acpi/platform/aclinux.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/acpi/platform/aclinux.h 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/include/asm-generic/bug.h linux-3.18.10/include/asm-generic/bug.h ---- linux-3.18.10.orig/include/asm-generic/bug.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/asm-generic/bug.h 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/include/linux/blkdev.h linux-3.18.10/include/linux/blkdev.h ---- linux-3.18.10.orig/include/linux/blkdev.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/blkdev.h 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/include/linux/blk-mq.h linux-3.18.10/include/linux/blk-mq.h ---- linux-3.18.10.orig/include/linux/blk-mq.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/blk-mq.h 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/include/linux/bottom_half.h linux-3.18.10/include/linux/bottom_half.h ---- linux-3.18.10.orig/include/linux/bottom_half.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/bottom_half.h 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/include/linux/buffer_head.h linux-3.18.10/include/linux/buffer_head.h ---- linux-3.18.10.orig/include/linux/buffer_head.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/buffer_head.h 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/include/linux/cgroup.h linux-3.18.10/include/linux/cgroup.h ---- linux-3.18.10.orig/include/linux/cgroup.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/cgroup.h 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/include/linux/completion.h linux-3.18.10/include/linux/completion.h ---- linux-3.18.10.orig/include/linux/completion.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/completion.h 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/include/linux/cpu.h linux-3.18.10/include/linux/cpu.h ---- linux-3.18.10.orig/include/linux/cpu.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/cpu.h 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/include/linux/delay.h linux-3.18.10/include/linux/delay.h ---- linux-3.18.10.orig/include/linux/delay.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/delay.h 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/include/linux/ftrace_event.h linux-3.18.10/include/linux/ftrace_event.h ---- linux-3.18.10.orig/include/linux/ftrace_event.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/ftrace_event.h 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/include/linux/highmem.h linux-3.18.10/include/linux/highmem.h ---- linux-3.18.10.orig/include/linux/highmem.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/highmem.h 2015-03-26 12:42:18.663588322 +0100 -@@ -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.10.orig/include/linux/hrtimer.h linux-3.18.10/include/linux/hrtimer.h ---- linux-3.18.10.orig/include/linux/hrtimer.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/hrtimer.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/idr.h linux-3.18.10/include/linux/idr.h ---- linux-3.18.10.orig/include/linux/idr.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/idr.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/init_task.h linux-3.18.10/include/linux/init_task.h ---- linux-3.18.10.orig/include/linux/init_task.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/init_task.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/interrupt.h linux-3.18.10/include/linux/interrupt.h ---- linux-3.18.10.orig/include/linux/interrupt.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/interrupt.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/irqdesc.h linux-3.18.10/include/linux/irqdesc.h ---- linux-3.18.10.orig/include/linux/irqdesc.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/irqdesc.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/irqflags.h linux-3.18.10/include/linux/irqflags.h ---- linux-3.18.10.orig/include/linux/irqflags.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/irqflags.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/irq.h linux-3.18.10/include/linux/irq.h ---- linux-3.18.10.orig/include/linux/irq.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/irq.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/irq_work.h linux-3.18.10/include/linux/irq_work.h ---- linux-3.18.10.orig/include/linux/irq_work.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/irq_work.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/jbd_common.h linux-3.18.10/include/linux/jbd_common.h ---- linux-3.18.10.orig/include/linux/jbd_common.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/jbd_common.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/jump_label.h linux-3.18.10/include/linux/jump_label.h ---- linux-3.18.10.orig/include/linux/jump_label.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/jump_label.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/kdb.h linux-3.18.10/include/linux/kdb.h ---- linux-3.18.10.orig/include/linux/kdb.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/kdb.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/kernel.h linux-3.18.10/include/linux/kernel.h ---- linux-3.18.10.orig/include/linux/kernel.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/kernel.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/lglock.h linux-3.18.10/include/linux/lglock.h ---- linux-3.18.10.orig/include/linux/lglock.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/lglock.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/list_bl.h linux-3.18.10/include/linux/list_bl.h ---- linux-3.18.10.orig/include/linux/list_bl.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/list_bl.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/locallock.h linux-3.18.10/include/linux/locallock.h ---- linux-3.18.10.orig/include/linux/locallock.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.10/include/linux/locallock.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/mm_types.h linux-3.18.10/include/linux/mm_types.h ---- linux-3.18.10.orig/include/linux/mm_types.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/mm_types.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/mutex.h linux-3.18.10/include/linux/mutex.h ---- linux-3.18.10.orig/include/linux/mutex.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/mutex.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/mutex_rt.h linux-3.18.10/include/linux/mutex_rt.h ---- linux-3.18.10.orig/include/linux/mutex_rt.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.10/include/linux/mutex_rt.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/netdevice.h linux-3.18.10/include/linux/netdevice.h ---- linux-3.18.10.orig/include/linux/netdevice.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/netdevice.h 2015-03-26 12:42:18.667588326 +0100 -@@ -2345,6 +2345,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.10.orig/include/linux/netfilter/x_tables.h linux-3.18.10/include/linux/netfilter/x_tables.h ---- linux-3.18.10.orig/include/linux/netfilter/x_tables.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/netfilter/x_tables.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/notifier.h linux-3.18.10/include/linux/notifier.h ---- linux-3.18.10.orig/include/linux/notifier.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/notifier.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/percpu.h linux-3.18.10/include/linux/percpu.h ---- linux-3.18.10.orig/include/linux/percpu.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/percpu.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/pid.h linux-3.18.10/include/linux/pid.h ---- linux-3.18.10.orig/include/linux/pid.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/pid.h 2015-03-26 12:42:18.667588326 +0100 -@@ -2,6 +2,7 @@ - #define _LINUX_PID_H - - #include -+#include - - enum pid_type - { -diff -Nur linux-3.18.10.orig/include/linux/preempt.h linux-3.18.10/include/linux/preempt.h ---- linux-3.18.10.orig/include/linux/preempt.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/preempt.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/preempt_mask.h linux-3.18.10/include/linux/preempt_mask.h ---- linux-3.18.10.orig/include/linux/preempt_mask.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/preempt_mask.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/printk.h linux-3.18.10/include/linux/printk.h ---- linux-3.18.10.orig/include/linux/printk.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/printk.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/radix-tree.h linux-3.18.10/include/linux/radix-tree.h ---- linux-3.18.10.orig/include/linux/radix-tree.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/radix-tree.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/random.h linux-3.18.10/include/linux/random.h ---- linux-3.18.10.orig/include/linux/random.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/random.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/rcupdate.h linux-3.18.10/include/linux/rcupdate.h ---- linux-3.18.10.orig/include/linux/rcupdate.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/rcupdate.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/rcutree.h linux-3.18.10/include/linux/rcutree.h ---- linux-3.18.10.orig/include/linux/rcutree.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/rcutree.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/rtmutex.h linux-3.18.10/include/linux/rtmutex.h ---- linux-3.18.10.orig/include/linux/rtmutex.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/rtmutex.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/rwlock_rt.h linux-3.18.10/include/linux/rwlock_rt.h ---- linux-3.18.10.orig/include/linux/rwlock_rt.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.10/include/linux/rwlock_rt.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/rwlock_types.h linux-3.18.10/include/linux/rwlock_types.h ---- linux-3.18.10.orig/include/linux/rwlock_types.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/rwlock_types.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/rwlock_types_rt.h linux-3.18.10/include/linux/rwlock_types_rt.h ---- linux-3.18.10.orig/include/linux/rwlock_types_rt.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.10/include/linux/rwlock_types_rt.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/rwsem.h linux-3.18.10/include/linux/rwsem.h ---- linux-3.18.10.orig/include/linux/rwsem.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/rwsem.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/rwsem_rt.h linux-3.18.10/include/linux/rwsem_rt.h ---- linux-3.18.10.orig/include/linux/rwsem_rt.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.10/include/linux/rwsem_rt.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/sched.h linux-3.18.10/include/linux/sched.h ---- linux-3.18.10.orig/include/linux/sched.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/sched.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/seqlock.h linux-3.18.10/include/linux/seqlock.h ---- linux-3.18.10.orig/include/linux/seqlock.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/seqlock.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/signal.h linux-3.18.10/include/linux/signal.h ---- linux-3.18.10.orig/include/linux/signal.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/signal.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/skbuff.h linux-3.18.10/include/linux/skbuff.h ---- linux-3.18.10.orig/include/linux/skbuff.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/skbuff.h 2015-03-26 12:42:18.667588326 +0100 -@@ -172,6 +172,7 @@ - - __u32 qlen; - spinlock_t lock; -+ raw_spinlock_t raw_lock; - }; - - struct sk_buff; -@@ -1327,6 +1328,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.10.orig/include/linux/smp.h linux-3.18.10/include/linux/smp.h ---- linux-3.18.10.orig/include/linux/smp.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/smp.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/spinlock_api_smp.h linux-3.18.10/include/linux/spinlock_api_smp.h ---- linux-3.18.10.orig/include/linux/spinlock_api_smp.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/spinlock_api_smp.h 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/include/linux/spinlock.h linux-3.18.10/include/linux/spinlock.h ---- linux-3.18.10.orig/include/linux/spinlock.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/spinlock.h 2015-03-26 12:42:18.667588326 +0100 -@@ -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.10.orig/include/linux/spinlock_rt.h linux-3.18.10/include/linux/spinlock_rt.h ---- linux-3.18.10.orig/include/linux/spinlock_rt.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.10/include/linux/spinlock_rt.h 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/include/linux/spinlock_types.h linux-3.18.10/include/linux/spinlock_types.h ---- linux-3.18.10.orig/include/linux/spinlock_types.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/spinlock_types.h 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/include/linux/spinlock_types_nort.h linux-3.18.10/include/linux/spinlock_types_nort.h ---- linux-3.18.10.orig/include/linux/spinlock_types_nort.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.10/include/linux/spinlock_types_nort.h 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/include/linux/spinlock_types_raw.h linux-3.18.10/include/linux/spinlock_types_raw.h ---- linux-3.18.10.orig/include/linux/spinlock_types_raw.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.10/include/linux/spinlock_types_raw.h 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/include/linux/spinlock_types_rt.h linux-3.18.10/include/linux/spinlock_types_rt.h ---- linux-3.18.10.orig/include/linux/spinlock_types_rt.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.10/include/linux/spinlock_types_rt.h 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/include/linux/srcu.h linux-3.18.10/include/linux/srcu.h ---- linux-3.18.10.orig/include/linux/srcu.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/srcu.h 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/include/linux/swap.h linux-3.18.10/include/linux/swap.h ---- linux-3.18.10.orig/include/linux/swap.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/swap.h 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/include/linux/sysctl.h linux-3.18.10/include/linux/sysctl.h ---- linux-3.18.10.orig/include/linux/sysctl.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/sysctl.h 2015-03-26 12:42:18.671588332 +0100 -@@ -25,6 +25,7 @@ - #include - #include - #include -+#include - #include - - /* For the /proc/sys support */ -diff -Nur linux-3.18.10.orig/include/linux/thread_info.h linux-3.18.10/include/linux/thread_info.h ---- linux-3.18.10.orig/include/linux/thread_info.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/thread_info.h 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/include/linux/timer.h linux-3.18.10/include/linux/timer.h ---- linux-3.18.10.orig/include/linux/timer.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/timer.h 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/include/linux/uaccess.h linux-3.18.10/include/linux/uaccess.h ---- linux-3.18.10.orig/include/linux/uaccess.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/uaccess.h 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/include/linux/uprobes.h linux-3.18.10/include/linux/uprobes.h ---- linux-3.18.10.orig/include/linux/uprobes.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/uprobes.h 2015-03-26 12:42:18.671588332 +0100 -@@ -27,6 +27,7 @@ - #include - #include - #include -+#include - - struct vm_area_struct; - struct mm_struct; -diff -Nur linux-3.18.10.orig/include/linux/vmstat.h linux-3.18.10/include/linux/vmstat.h ---- linux-3.18.10.orig/include/linux/vmstat.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/vmstat.h 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/include/linux/wait.h linux-3.18.10/include/linux/wait.h ---- linux-3.18.10.orig/include/linux/wait.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/linux/wait.h 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/include/linux/wait-simple.h linux-3.18.10/include/linux/wait-simple.h ---- linux-3.18.10.orig/include/linux/wait-simple.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.10/include/linux/wait-simple.h 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/include/linux/work-simple.h linux-3.18.10/include/linux/work-simple.h ---- linux-3.18.10.orig/include/linux/work-simple.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.10/include/linux/work-simple.h 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/include/net/dst.h linux-3.18.10/include/net/dst.h ---- linux-3.18.10.orig/include/net/dst.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/net/dst.h 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/include/net/neighbour.h linux-3.18.10/include/net/neighbour.h ---- linux-3.18.10.orig/include/net/neighbour.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/net/neighbour.h 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/include/net/netns/ipv4.h linux-3.18.10/include/net/netns/ipv4.h ---- linux-3.18.10.orig/include/net/netns/ipv4.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/include/net/netns/ipv4.h 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/include/trace/events/hist.h linux-3.18.10/include/trace/events/hist.h ---- linux-3.18.10.orig/include/trace/events/hist.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.10/include/trace/events/hist.h 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/include/trace/events/latency_hist.h linux-3.18.10/include/trace/events/latency_hist.h ---- linux-3.18.10.orig/include/trace/events/latency_hist.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.10/include/trace/events/latency_hist.h 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/init/Kconfig linux-3.18.10/init/Kconfig ---- linux-3.18.10.orig/init/Kconfig 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/init/Kconfig 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/init/main.c linux-3.18.10/init/main.c ---- linux-3.18.10.orig/init/main.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/init/main.c 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/init/Makefile linux-3.18.10/init/Makefile ---- linux-3.18.10.orig/init/Makefile 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/init/Makefile 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/ipc/mqueue.c linux-3.18.10/ipc/mqueue.c ---- linux-3.18.10.orig/ipc/mqueue.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/ipc/mqueue.c 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/ipc/msg.c linux-3.18.10/ipc/msg.c ---- linux-3.18.10.orig/ipc/msg.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/ipc/msg.c 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/ipc/sem.c linux-3.18.10/ipc/sem.c ---- linux-3.18.10.orig/ipc/sem.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/ipc/sem.c 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/kernel/cgroup.c linux-3.18.10/kernel/cgroup.c ---- linux-3.18.10.orig/kernel/cgroup.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/cgroup.c 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/kernel/cpu.c linux-3.18.10/kernel/cpu.c ---- linux-3.18.10.orig/kernel/cpu.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/cpu.c 2015-03-26 12:42:18.671588332 +0100 -@@ -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; -+ do_set_cpus_allowed(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.10.orig/kernel/debug/kdb/kdb_io.c linux-3.18.10/kernel/debug/kdb/kdb_io.c ---- linux-3.18.10.orig/kernel/debug/kdb/kdb_io.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/debug/kdb/kdb_io.c 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/kernel/events/core.c linux-3.18.10/kernel/events/core.c ---- linux-3.18.10.orig/kernel/events/core.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/events/core.c 2015-03-26 12:42:18.675588336 +0100 -@@ -6336,6 +6336,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.10.orig/kernel/exit.c linux-3.18.10/kernel/exit.c ---- linux-3.18.10.orig/kernel/exit.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/exit.c 2015-03-26 12:42:18.675588336 +0100 -@@ -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.10.orig/kernel/fork.c linux-3.18.10/kernel/fork.c ---- linux-3.18.10.orig/kernel/fork.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/fork.c 2015-03-26 12:42:18.675588336 +0100 -@@ -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.10.orig/kernel/futex.c linux-3.18.10/kernel/futex.c ---- linux-3.18.10.orig/kernel/futex.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/futex.c 2015-03-26 12:42:18.675588336 +0100 -@@ -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.10.orig/kernel/irq/handle.c linux-3.18.10/kernel/irq/handle.c ---- linux-3.18.10.orig/kernel/irq/handle.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/irq/handle.c 2015-03-26 12:42:18.675588336 +0100 -@@ -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.10.orig/kernel/irq/manage.c linux-3.18.10/kernel/irq/manage.c ---- linux-3.18.10.orig/kernel/irq/manage.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/irq/manage.c 2015-03-26 12:42:18.675588336 +0100 -@@ -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.10.orig/kernel/irq/settings.h linux-3.18.10/kernel/irq/settings.h ---- linux-3.18.10.orig/kernel/irq/settings.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/irq/settings.h 2015-03-26 12:42:18.675588336 +0100 -@@ -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.10.orig/kernel/irq/spurious.c linux-3.18.10/kernel/irq/spurious.c ---- linux-3.18.10.orig/kernel/irq/spurious.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/irq/spurious.c 2015-03-26 12:42:18.675588336 +0100 -@@ -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.10.orig/kernel/irq_work.c linux-3.18.10/kernel/irq_work.c ---- linux-3.18.10.orig/kernel/irq_work.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/irq_work.c 2015-03-26 12:42:18.675588336 +0100 -@@ -22,7 +22,9 @@ - - static DEFINE_PER_CPU(struct llist_head, raised_list); - static DEFINE_PER_CPU(struct llist_head, lazy_list); -- -+#ifdef CONFIG_PREEMPT_RT_FULL -+static DEFINE_PER_CPU(struct llist_head, hirq_work_list); -+#endif - /* - * Claim the entry so that no one else will poke at it. - */ -@@ -49,7 +51,11 @@ - return true; - } - -+#ifdef CONFIG_PREEMPT_RT_FULL -+void arch_irq_work_raise(void) -+#else - void __weak arch_irq_work_raise(void) -+#endif - { - /* - * Lame architectures will get the timer tick callback -@@ -65,6 +71,8 @@ - */ - bool irq_work_queue_on(struct irq_work *work, int cpu) - { -+ bool raise_irqwork; -+ - /* All work should have been flushed before going offline */ - WARN_ON_ONCE(cpu_is_offline(cpu)); - -@@ -75,7 +83,19 @@ - if (!irq_work_claim(work)) - return false; - -- if (llist_add(&work->llnode, &per_cpu(raised_list, cpu))) -+#ifdef CONFIG_PREEMPT_RT_FULL -+ if (work->flags & IRQ_WORK_HARD_IRQ) -+ raise_irqwork = llist_add(&work->llnode, -+ &per_cpu(hirq_work_list, cpu)); -+ else -+ raise_irqwork = llist_add(&work->llnode, -+ &per_cpu(lazy_list, cpu)); -+#else -+ raise_irqwork = llist_add(&work->llnode, -+ &per_cpu(raised_list, cpu)); -+#endif -+ -+ if (raise_irqwork) - arch_send_call_function_single_ipi(cpu); - - return true; -@@ -93,7 +113,15 @@ - /* Queue the entry and raise the IPI if needed. */ - preempt_disable(); - -- /* If the work is "lazy", handle it from next tick if any */ -+#ifdef CONFIG_PREEMPT_RT_FULL -+ if (work->flags & IRQ_WORK_HARD_IRQ) { -+ if (llist_add(&work->llnode, this_cpu_ptr(&hirq_work_list))) -+ arch_irq_work_raise(); -+ } else { -+ if (llist_add(&work->llnode, this_cpu_ptr(&lazy_list))) -+ arch_irq_work_raise(); -+ } -+#else - if (work->flags & IRQ_WORK_LAZY) { - if (llist_add(&work->llnode, this_cpu_ptr(&lazy_list)) && - tick_nohz_tick_stopped()) -@@ -102,6 +130,7 @@ - if (llist_add(&work->llnode, this_cpu_ptr(&raised_list))) - arch_irq_work_raise(); - } -+#endif - - preempt_enable(); - -@@ -116,9 +145,12 @@ - raised = this_cpu_ptr(&raised_list); - lazy = this_cpu_ptr(&lazy_list); - -- if (llist_empty(raised) || arch_irq_work_has_interrupt()) -+ if (llist_empty(raised)) - if (llist_empty(lazy)) -- return false; -+#ifdef CONFIG_PREEMPT_RT_FULL -+ if (llist_empty(this_cpu_ptr(&hirq_work_list))) -+#endif -+ return false; - - /* All work should have been flushed before going offline */ - WARN_ON_ONCE(cpu_is_offline(smp_processor_id())); -@@ -132,7 +164,9 @@ - struct irq_work *work; - struct llist_node *llnode; - -+#ifndef CONFIG_PREEMPT_RT_FULL - BUG_ON(!irqs_disabled()); -+#endif - - if (llist_empty(list)) - return; -@@ -168,6 +202,12 @@ - */ - void irq_work_run(void) - { -+#ifdef CONFIG_PREEMPT_RT_FULL -+ if (in_irq()) { -+ irq_work_run_list(this_cpu_ptr(&hirq_work_list)); -+ return; -+ } -+#endif - irq_work_run_list(this_cpu_ptr(&raised_list)); - irq_work_run_list(this_cpu_ptr(&lazy_list)); - } -@@ -175,9 +215,16 @@ - - void irq_work_tick(void) - { -- struct llist_head *raised = &__get_cpu_var(raised_list); -+ struct llist_head *raised; - -- if (!llist_empty(raised) && !arch_irq_work_has_interrupt()) -+#ifdef CONFIG_PREEMPT_RT_FULL -+ if (in_irq()) { -+ irq_work_run_list(this_cpu_ptr(&hirq_work_list)); -+ return; -+ } -+#endif -+ raised = &__get_cpu_var(raised_list); -+ if (!llist_empty(raised)) - irq_work_run_list(raised); - irq_work_run_list(&__get_cpu_var(lazy_list)); - } -diff -Nur linux-3.18.10.orig/kernel/Kconfig.locks linux-3.18.10/kernel/Kconfig.locks ---- linux-3.18.10.orig/kernel/Kconfig.locks 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/Kconfig.locks 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/kernel/Kconfig.preempt linux-3.18.10/kernel/Kconfig.preempt ---- linux-3.18.10.orig/kernel/Kconfig.preempt 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/Kconfig.preempt 2015-03-26 12:42:18.671588332 +0100 -@@ -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.10.orig/kernel/ksysfs.c linux-3.18.10/kernel/ksysfs.c ---- linux-3.18.10.orig/kernel/ksysfs.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/ksysfs.c 2015-03-26 12:42:18.675588336 +0100 -@@ -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.10.orig/kernel/locking/lglock.c linux-3.18.10/kernel/locking/lglock.c ---- linux-3.18.10.orig/kernel/locking/lglock.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/locking/lglock.c 2015-03-26 12:42:18.675588336 +0100 -@@ -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.10.orig/kernel/locking/lockdep.c linux-3.18.10/kernel/locking/lockdep.c ---- linux-3.18.10.orig/kernel/locking/lockdep.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/locking/lockdep.c 2015-03-26 12:42:18.675588336 +0100 -@@ -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.10.orig/kernel/locking/Makefile linux-3.18.10/kernel/locking/Makefile ---- linux-3.18.10.orig/kernel/locking/Makefile 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/locking/Makefile 2015-03-26 12:42:18.675588336 +0100 -@@ -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.10.orig/kernel/locking/percpu-rwsem.c linux-3.18.10/kernel/locking/percpu-rwsem.c ---- linux-3.18.10.orig/kernel/locking/percpu-rwsem.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/locking/percpu-rwsem.c 2015-03-26 12:42:18.675588336 +0100 -@@ -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.10.orig/kernel/locking/rt.c linux-3.18.10/kernel/locking/rt.c ---- linux-3.18.10.orig/kernel/locking/rt.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.10/kernel/locking/rt.c 2015-03-26 12:42:18.675588336 +0100 -@@ -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.10.orig/kernel/locking/rtmutex.c linux-3.18.10/kernel/locking/rtmutex.c ---- linux-3.18.10.orig/kernel/locking/rtmutex.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/locking/rtmutex.c 2015-03-26 12:45:24.871805156 +0100 -@@ -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.10.orig/kernel/locking/rtmutex_common.h linux-3.18.10/kernel/locking/rtmutex_common.h ---- linux-3.18.10.orig/kernel/locking/rtmutex_common.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/locking/rtmutex_common.h 2015-03-26 12:42:18.675588336 +0100 -@@ -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.10.orig/kernel/locking/spinlock.c linux-3.18.10/kernel/locking/spinlock.c ---- linux-3.18.10.orig/kernel/locking/spinlock.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/locking/spinlock.c 2015-03-26 12:42:18.675588336 +0100 -@@ -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.10.orig/kernel/locking/spinlock_debug.c linux-3.18.10/kernel/locking/spinlock_debug.c ---- linux-3.18.10.orig/kernel/locking/spinlock_debug.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/locking/spinlock_debug.c 2015-03-26 12:42:18.675588336 +0100 -@@ -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.10.orig/kernel/panic.c linux-3.18.10/kernel/panic.c ---- linux-3.18.10.orig/kernel/panic.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/panic.c 2015-03-26 12:42:18.675588336 +0100 -@@ -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.10.orig/kernel/power/hibernate.c linux-3.18.10/kernel/power/hibernate.c ---- linux-3.18.10.orig/kernel/power/hibernate.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/power/hibernate.c 2015-03-26 12:42:18.675588336 +0100 -@@ -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.10.orig/kernel/power/suspend.c linux-3.18.10/kernel/power/suspend.c ---- linux-3.18.10.orig/kernel/power/suspend.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/power/suspend.c 2015-03-26 12:42:18.675588336 +0100 -@@ -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.10.orig/kernel/printk/printk.c linux-3.18.10/kernel/printk/printk.c ---- linux-3.18.10.orig/kernel/printk/printk.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/printk/printk.c 2015-03-26 12:42:18.675588336 +0100 -@@ -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.10.orig/kernel/ptrace.c linux-3.18.10/kernel/ptrace.c ---- linux-3.18.10.orig/kernel/ptrace.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/ptrace.c 2015-03-26 12:42:18.675588336 +0100 -@@ -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.10.orig/kernel/rcu/tiny.c linux-3.18.10/kernel/rcu/tiny.c ---- linux-3.18.10.orig/kernel/rcu/tiny.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/rcu/tiny.c 2015-03-26 12:42:18.675588336 +0100 -@@ -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.10.orig/kernel/rcu/tree.c linux-3.18.10/kernel/rcu/tree.c ---- linux-3.18.10.orig/kernel/rcu/tree.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/rcu/tree.c 2015-03-26 12:42:18.679588341 +0100 -@@ -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.10.orig/kernel/rcu/tree.h linux-3.18.10/kernel/rcu/tree.h ---- linux-3.18.10.orig/kernel/rcu/tree.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/rcu/tree.h 2015-03-26 12:42:18.679588341 +0100 -@@ -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.10.orig/kernel/rcu/tree_plugin.h linux-3.18.10/kernel/rcu/tree_plugin.h ---- linux-3.18.10.orig/kernel/rcu/tree_plugin.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/rcu/tree_plugin.h 2015-03-26 12:42:18.679588341 +0100 -@@ -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.10.orig/kernel/rcu/update.c linux-3.18.10/kernel/rcu/update.c ---- linux-3.18.10.orig/kernel/rcu/update.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/rcu/update.c 2015-03-26 12:42:18.679588341 +0100 -@@ -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.10.orig/kernel/relay.c linux-3.18.10/kernel/relay.c ---- linux-3.18.10.orig/kernel/relay.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/relay.c 2015-03-26 12:42:18.679588341 +0100 -@@ -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.10.orig/kernel/res_counter.c linux-3.18.10/kernel/res_counter.c ---- linux-3.18.10.orig/kernel/res_counter.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/res_counter.c 2015-03-26 12:42:18.679588341 +0100 -@@ -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.10.orig/kernel/sched/completion.c linux-3.18.10/kernel/sched/completion.c ---- linux-3.18.10.orig/kernel/sched/completion.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/sched/completion.c 2015-03-26 12:42:18.679588341 +0100 -@@ -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.10.orig/kernel/sched/core.c linux-3.18.10/kernel/sched/core.c ---- linux-3.18.10.orig/kernel/sched/core.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/sched/core.c 2015-03-26 12:42:18.679588341 +0100 -@@ -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; - } - /* -@@ -1198,6 +1239,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 +1295,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 +1310,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 +1536,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 +1749,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 +1812,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 +1825,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 +2032,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 +2318,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 +2748,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 +2978,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 +2987,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 +2996,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 +3026,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 +3047,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 +3109,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); - - /* -@@ -4234,9 +4438,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) -@@ -4277,6 +4488,7 @@ - } - EXPORT_SYMBOL(__cond_resched_lock); - -+#ifndef CONFIG_PREEMPT_RT_FULL - int __sched __cond_resched_softirq(void) - { - BUG_ON(!in_softirq()); -@@ -4290,6 +4502,7 @@ - return 0; - } - EXPORT_SYMBOL(__cond_resched_softirq); -+#endif - - /** - * yield - yield the current processor to other threads. -@@ -4651,7 +4864,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: - */ -@@ -4693,11 +4908,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; - } - - /* -@@ -4743,7 +5038,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); -@@ -4883,6 +5178,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. -@@ -4897,7 +5194,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; - } - - /* -@@ -5240,6 +5541,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 - } -@@ -7181,7 +7486,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.10.orig/kernel/sched/cputime.c linux-3.18.10/kernel/sched/cputime.c ---- linux-3.18.10.orig/kernel/sched/cputime.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/sched/cputime.c 2015-03-26 12:42:18.679588341 +0100 -@@ -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.10.orig/kernel/sched/deadline.c linux-3.18.10/kernel/sched/deadline.c ---- linux-3.18.10.orig/kernel/sched/deadline.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/sched/deadline.c 2015-03-26 12:42:18.679588341 +0100 -@@ -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.10.orig/kernel/sched/debug.c linux-3.18.10/kernel/sched/debug.c ---- linux-3.18.10.orig/kernel/sched/debug.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/sched/debug.c 2015-03-26 12:42:18.679588341 +0100 -@@ -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.10.orig/kernel/sched/fair.c linux-3.18.10/kernel/sched/fair.c ---- linux-3.18.10.orig/kernel/sched/fair.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/sched/fair.c 2015-03-26 12:42:18.679588341 +0100 -@@ -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.10.orig/kernel/sched/features.h linux-3.18.10/kernel/sched/features.h ---- linux-3.18.10.orig/kernel/sched/features.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/sched/features.h 2015-03-26 12:42:18.679588341 +0100 -@@ -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.10.orig/kernel/sched/Makefile linux-3.18.10/kernel/sched/Makefile ---- linux-3.18.10.orig/kernel/sched/Makefile 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/sched/Makefile 2015-03-26 12:42:18.679588341 +0100 -@@ -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.10.orig/kernel/sched/rt.c linux-3.18.10/kernel/sched/rt.c ---- linux-3.18.10.orig/kernel/sched/rt.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/sched/rt.c 2015-03-26 12:42:18.679588341 +0100 -@@ -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.10.orig/kernel/sched/sched.h linux-3.18.10/kernel/sched/sched.h ---- linux-3.18.10.orig/kernel/sched/sched.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/sched/sched.h 2015-03-26 12:42:18.679588341 +0100 -@@ -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.10.orig/kernel/sched/wait-simple.c linux-3.18.10/kernel/sched/wait-simple.c ---- linux-3.18.10.orig/kernel/sched/wait-simple.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.10/kernel/sched/wait-simple.c 2015-03-26 12:42:18.679588341 +0100 -@@ -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.10.orig/kernel/sched/work-simple.c linux-3.18.10/kernel/sched/work-simple.c ---- linux-3.18.10.orig/kernel/sched/work-simple.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.10/kernel/sched/work-simple.c 2015-03-26 12:42:18.683588345 +0100 -@@ -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.10.orig/kernel/signal.c linux-3.18.10/kernel/signal.c ---- linux-3.18.10.orig/kernel/signal.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/signal.c 2015-03-26 12:42:18.683588345 +0100 -@@ -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.10.orig/kernel/softirq.c linux-3.18.10/kernel/softirq.c ---- linux-3.18.10.orig/kernel/softirq.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/softirq.c 2015-03-26 12:42:18.683588345 +0100 -@@ -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.10.orig/kernel/stop_machine.c linux-3.18.10/kernel/stop_machine.c ---- linux-3.18.10.orig/kernel/stop_machine.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/stop_machine.c 2015-03-26 12:42:18.683588345 +0100 -@@ -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.10.orig/kernel/time/hrtimer.c linux-3.18.10/kernel/time/hrtimer.c ---- linux-3.18.10.orig/kernel/time/hrtimer.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/time/hrtimer.c 2015-03-26 12:42:18.683588345 +0100 -@@ -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.10.orig/kernel/time/itimer.c linux-3.18.10/kernel/time/itimer.c ---- linux-3.18.10.orig/kernel/time/itimer.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/time/itimer.c 2015-03-26 12:42:18.683588345 +0100 -@@ -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.10.orig/kernel/time/jiffies.c linux-3.18.10/kernel/time/jiffies.c ---- linux-3.18.10.orig/kernel/time/jiffies.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/time/jiffies.c 2015-03-26 12:42:18.683588345 +0100 -@@ -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.10.orig/kernel/time/ntp.c linux-3.18.10/kernel/time/ntp.c ---- linux-3.18.10.orig/kernel/time/ntp.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/time/ntp.c 2015-03-26 12:42:18.683588345 +0100 -@@ -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.10.orig/kernel/time/posix-cpu-timers.c linux-3.18.10/kernel/time/posix-cpu-timers.c ---- linux-3.18.10.orig/kernel/time/posix-cpu-timers.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/time/posix-cpu-timers.c 2015-03-26 12:42:18.683588345 +0100 -@@ -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.10.orig/kernel/time/posix-timers.c linux-3.18.10/kernel/time/posix-timers.c ---- linux-3.18.10.orig/kernel/time/posix-timers.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/time/posix-timers.c 2015-03-26 12:42:18.683588345 +0100 -@@ -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.10.orig/kernel/time/tick-common.c linux-3.18.10/kernel/time/tick-common.c ---- linux-3.18.10.orig/kernel/time/tick-common.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/time/tick-common.c 2015-03-26 12:42:18.683588345 +0100 -@@ -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.10.orig/kernel/time/tick-internal.h linux-3.18.10/kernel/time/tick-internal.h ---- linux-3.18.10.orig/kernel/time/tick-internal.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/time/tick-internal.h 2015-03-26 12:42:18.683588345 +0100 -@@ -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.10.orig/kernel/time/tick-sched.c linux-3.18.10/kernel/time/tick-sched.c ---- linux-3.18.10.orig/kernel/time/tick-sched.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/time/tick-sched.c 2015-03-26 12:42:18.683588345 +0100 -@@ -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; - } - -@@ -222,6 +227,7 @@ - - static DEFINE_PER_CPU(struct irq_work, nohz_full_kick_work) = { - .func = nohz_full_kick_work_func, -+ .flags = IRQ_WORK_HARD_IRQ, - }; - - /* -@@ -580,10 +586,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 +767,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 +1155,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.10.orig/kernel/time/timekeeping.c linux-3.18.10/kernel/time/timekeeping.c ---- linux-3.18.10.orig/kernel/time/timekeeping.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/time/timekeeping.c 2015-03-26 12:42:18.683588345 +0100 -@@ -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.10.orig/kernel/time/timer.c linux-3.18.10/kernel/time/timer.c ---- linux-3.18.10.orig/kernel/time/timer.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/time/timer.c 2015-03-26 12:42:18.683588345 +0100 -@@ -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(); - #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.10.orig/kernel/trace/Kconfig linux-3.18.10/kernel/trace/Kconfig ---- linux-3.18.10.orig/kernel/trace/Kconfig 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/trace/Kconfig 2015-03-26 12:42:18.683588345 +0100 -@@ -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.10.orig/kernel/trace/latency_hist.c linux-3.18.10/kernel/trace/latency_hist.c ---- linux-3.18.10.orig/kernel/trace/latency_hist.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.10/kernel/trace/latency_hist.c 2015-03-26 12:42:18.683588345 +0100 -@@ -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.10.orig/kernel/trace/Makefile linux-3.18.10/kernel/trace/Makefile ---- linux-3.18.10.orig/kernel/trace/Makefile 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/trace/Makefile 2015-03-26 12:42:18.683588345 +0100 -@@ -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.10.orig/kernel/trace/trace.c linux-3.18.10/kernel/trace/trace.c ---- linux-3.18.10.orig/kernel/trace/trace.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/trace/trace.c 2015-03-26 12:42:18.687588350 +0100 -@@ -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.10.orig/kernel/trace/trace_events.c linux-3.18.10/kernel/trace/trace_events.c ---- linux-3.18.10.orig/kernel/trace/trace_events.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/trace/trace_events.c 2015-03-26 12:42:18.687588350 +0100 -@@ -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.10.orig/kernel/trace/trace.h linux-3.18.10/kernel/trace/trace.h ---- linux-3.18.10.orig/kernel/trace/trace.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/trace/trace.h 2015-03-26 12:42:18.687588350 +0100 -@@ -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.10.orig/kernel/trace/trace_irqsoff.c linux-3.18.10/kernel/trace/trace_irqsoff.c ---- linux-3.18.10.orig/kernel/trace/trace_irqsoff.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/trace/trace_irqsoff.c 2015-03-26 12:42:18.687588350 +0100 -@@ -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.10.orig/kernel/trace/trace_output.c linux-3.18.10/kernel/trace/trace_output.c ---- linux-3.18.10.orig/kernel/trace/trace_output.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/trace/trace_output.c 2015-03-26 12:42:18.687588350 +0100 -@@ -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.10.orig/kernel/user.c linux-3.18.10/kernel/user.c ---- linux-3.18.10.orig/kernel/user.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/user.c 2015-03-26 12:42:18.687588350 +0100 -@@ -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.10.orig/kernel/watchdog.c linux-3.18.10/kernel/watchdog.c ---- linux-3.18.10.orig/kernel/watchdog.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/watchdog.c 2015-03-26 12:42:18.687588350 +0100 -@@ -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.10.orig/kernel/workqueue.c linux-3.18.10/kernel/workqueue.c ---- linux-3.18.10.orig/kernel/workqueue.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/workqueue.c 2015-03-26 12:42:18.687588350 +0100 -@@ -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; - } - -@@ -2727,7 +2763,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); -@@ -2772,10 +2808,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); -@@ -2810,7 +2846,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); -@@ -2996,7 +3032,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, -@@ -3004,7 +3041,8 @@ - delim = " "; - } - written += scnprintf(buf + written, PAGE_SIZE - written, "\n"); -- rcu_read_unlock_sched(); -+ rcu_read_unlock(); -+ put_online_cpus(); - - return written; - } -@@ -3372,7 +3410,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(). -@@ -3426,8 +3464,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); - } - - /** -@@ -3532,7 +3570,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 -@@ -4244,7 +4282,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(); -@@ -4255,7 +4294,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; - } -@@ -4281,16 +4321,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); -@@ -4719,16 +4758,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.10.orig/kernel/workqueue_internal.h linux-3.18.10/kernel/workqueue_internal.h ---- linux-3.18.10.orig/kernel/workqueue_internal.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/kernel/workqueue_internal.h 2015-03-26 12:42:18.687588350 +0100 -@@ -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.10.orig/lib/debugobjects.c linux-3.18.10/lib/debugobjects.c ---- linux-3.18.10.orig/lib/debugobjects.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/lib/debugobjects.c 2015-03-26 12:42:18.687588350 +0100 -@@ -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.10.orig/lib/idr.c linux-3.18.10/lib/idr.c ---- linux-3.18.10.orig/lib/idr.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/lib/idr.c 2015-03-26 12:42:18.687588350 +0100 -@@ -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.10.orig/lib/Kconfig linux-3.18.10/lib/Kconfig ---- linux-3.18.10.orig/lib/Kconfig 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/lib/Kconfig 2015-03-26 12:42:18.687588350 +0100 -@@ -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.10.orig/lib/Kconfig.debug linux-3.18.10/lib/Kconfig.debug ---- linux-3.18.10.orig/lib/Kconfig.debug 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/lib/Kconfig.debug 2015-03-26 12:42:18.687588350 +0100 -@@ -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.10.orig/lib/locking-selftest.c linux-3.18.10/lib/locking-selftest.c ---- linux-3.18.10.orig/lib/locking-selftest.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/lib/locking-selftest.c 2015-03-26 12:42:18.687588350 +0100 -@@ -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.10.orig/lib/percpu_ida.c linux-3.18.10/lib/percpu_ida.c ---- linux-3.18.10.orig/lib/percpu_ida.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/lib/percpu_ida.c 2015-03-26 12:42:18.687588350 +0100 -@@ -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.10.orig/lib/radix-tree.c linux-3.18.10/lib/radix-tree.c ---- linux-3.18.10.orig/lib/radix-tree.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/lib/radix-tree.c 2015-03-26 12:42:18.687588350 +0100 -@@ -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.10.orig/lib/scatterlist.c linux-3.18.10/lib/scatterlist.c ---- linux-3.18.10.orig/lib/scatterlist.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/lib/scatterlist.c 2015-03-26 12:42:18.687588350 +0100 -@@ -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.10.orig/lib/smp_processor_id.c linux-3.18.10/lib/smp_processor_id.c ---- linux-3.18.10.orig/lib/smp_processor_id.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/lib/smp_processor_id.c 2015-03-26 12:42:18.687588350 +0100 -@@ -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.10.orig/mm/filemap.c linux-3.18.10/mm/filemap.c ---- linux-3.18.10.orig/mm/filemap.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/mm/filemap.c 2015-03-26 12:42:18.687588350 +0100 -@@ -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.10.orig/mm/highmem.c linux-3.18.10/mm/highmem.c ---- linux-3.18.10.orig/mm/highmem.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/mm/highmem.c 2015-03-26 12:42:18.687588350 +0100 -@@ -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.10.orig/mm/Kconfig linux-3.18.10/mm/Kconfig ---- linux-3.18.10.orig/mm/Kconfig 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/mm/Kconfig 2015-03-26 12:42:18.687588350 +0100 -@@ -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.10.orig/mm/memcontrol.c linux-3.18.10/mm/memcontrol.c ---- linux-3.18.10.orig/mm/memcontrol.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/mm/memcontrol.c 2015-03-26 12:42:18.687588350 +0100 -@@ -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.10.orig/mm/memory.c linux-3.18.10/mm/memory.c ---- linux-3.18.10.orig/mm/memory.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/mm/memory.c 2015-03-26 12:42:18.691588355 +0100 -@@ -3258,6 +3258,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.10.orig/mm/mmu_context.c linux-3.18.10/mm/mmu_context.c ---- linux-3.18.10.orig/mm/mmu_context.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/mm/mmu_context.c 2015-03-26 12:42:18.691588355 +0100 -@@ -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.10.orig/mm/page_alloc.c linux-3.18.10/mm/page_alloc.c ---- linux-3.18.10.orig/mm/page_alloc.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/mm/page_alloc.c 2015-03-26 12:42:18.691588355 +0100 -@@ -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.10.orig/mm/slab.h linux-3.18.10/mm/slab.h ---- linux-3.18.10.orig/mm/slab.h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/mm/slab.h 2015-03-26 12:42:18.691588355 +0100 -@@ -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.10.orig/mm/slub.c linux-3.18.10/mm/slub.c ---- linux-3.18.10.orig/mm/slub.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/mm/slub.c 2015-03-26 12:42:18.691588355 +0100 -@@ -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.10.orig/mm/swap.c linux-3.18.10/mm/swap.c ---- linux-3.18.10.orig/mm/swap.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/mm/swap.c 2015-03-26 12:42:18.691588355 +0100 -@@ -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.10.orig/mm/truncate.c linux-3.18.10/mm/truncate.c ---- linux-3.18.10.orig/mm/truncate.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/mm/truncate.c 2015-03-26 12:42:18.691588355 +0100 -@@ -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.10.orig/mm/vmalloc.c linux-3.18.10/mm/vmalloc.c ---- linux-3.18.10.orig/mm/vmalloc.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/mm/vmalloc.c 2015-03-26 12:42:18.691588355 +0100 -@@ -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.10.orig/mm/vmstat.c linux-3.18.10/mm/vmstat.c ---- linux-3.18.10.orig/mm/vmstat.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/mm/vmstat.c 2015-03-26 12:42:18.691588355 +0100 -@@ -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.10.orig/mm/workingset.c linux-3.18.10/mm/workingset.c ---- linux-3.18.10.orig/mm/workingset.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/mm/workingset.c 2015-03-26 12:42:18.691588355 +0100 -@@ -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.10.orig/net/core/dev.c linux-3.18.10/net/core/dev.c ---- linux-3.18.10.orig/net/core/dev.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/net/core/dev.c 2015-03-26 12:42:18.691588355 +0100 -@@ -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); - -@@ -3336,6 +3344,7 @@ - rps_unlock(sd); - - local_irq_restore(flags); -+ preempt_check_resched_rt(); - - atomic_long_inc(&skb->dev->rx_dropped); - kfree_skb(skb); -@@ -3354,7 +3363,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); -@@ -3364,13 +3373,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; - } -@@ -3404,16 +3413,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); -@@ -3455,7 +3492,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); -@@ -3848,7 +3885,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); - } - } -@@ -3857,10 +3894,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) -@@ -4323,6 +4363,7 @@ - } else - #endif - local_irq_enable(); -+ preempt_check_resched_rt(); - } - - static int process_backlog(struct napi_struct *napi, int quota) -@@ -4394,6 +4435,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); - -@@ -4516,10 +4558,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; -@@ -7008,6 +7057,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))) { -@@ -7018,6 +7068,9 @@ - netif_rx_internal(skb); - input_queue_head_incr(oldsd); - } -+ while ((skb = __skb_dequeue(&oldsd->tofree_queue))) { -+ kfree_skb(skb); -+ } - - return NOTIFY_OK; - } -@@ -7319,8 +7372,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.10.orig/net/core/skbuff.c linux-3.18.10/net/core/skbuff.c ---- linux-3.18.10.orig/net/core/skbuff.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/net/core/skbuff.c 2015-03-26 12:42:18.691588355 +0100 -@@ -63,6 +63,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -336,6 +337,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) - { -@@ -344,7 +346,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: -@@ -389,7 +391,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.10.orig/net/core/sock.c linux-3.18.10/net/core/sock.c ---- linux-3.18.10.orig/net/core/sock.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/net/core/sock.c 2015-03-26 12:42:18.691588355 +0100 -@@ -2326,12 +2326,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.10.orig/net/ipv4/icmp.c linux-3.18.10/net/ipv4/icmp.c ---- linux-3.18.10.orig/net/ipv4/icmp.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/net/ipv4/icmp.c 2015-03-26 12:42:18.691588355 +0100 -@@ -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.10.orig/net/ipv4/sysctl_net_ipv4.c linux-3.18.10/net/ipv4/sysctl_net_ipv4.c ---- linux-3.18.10.orig/net/ipv4/sysctl_net_ipv4.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/net/ipv4/sysctl_net_ipv4.c 2015-03-26 12:42:18.691588355 +0100 -@@ -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.10.orig/net/mac80211/rx.c linux-3.18.10/net/mac80211/rx.c ---- linux-3.18.10.orig/net/mac80211/rx.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/net/mac80211/rx.c 2015-03-26 12:42:18.695588359 +0100 -@@ -3356,7 +3356,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.10.orig/net/netfilter/core.c linux-3.18.10/net/netfilter/core.c ---- linux-3.18.10.orig/net/netfilter/core.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/net/netfilter/core.c 2015-03-26 12:42:18.695588359 +0100 -@@ -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.10.orig/net/packet/af_packet.c linux-3.18.10/net/packet/af_packet.c ---- linux-3.18.10.orig/net/packet/af_packet.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/net/packet/af_packet.c 2015-03-26 12:42:18.695588359 +0100 -@@ -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.10.orig/net/rds/ib_rdma.c linux-3.18.10/net/rds/ib_rdma.c ---- linux-3.18.10.orig/net/rds/ib_rdma.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/net/rds/ib_rdma.c 2015-03-26 12:42:18.695588359 +0100 -@@ -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.10.orig/net/sched/sch_generic.c linux-3.18.10/net/sched/sch_generic.c ---- linux-3.18.10.orig/net/sched/sch_generic.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/net/sched/sch_generic.c 2015-03-26 12:42:18.695588359 +0100 -@@ -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.10.orig/net/sunrpc/svc_xprt.c linux-3.18.10/net/sunrpc/svc_xprt.c ---- linux-3.18.10.orig/net/sunrpc/svc_xprt.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/net/sunrpc/svc_xprt.c 2015-03-26 12:42:18.695588359 +0100 -@@ -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.10.orig/scripts/mkcompile_h linux-3.18.10/scripts/mkcompile_h ---- linux-3.18.10.orig/scripts/mkcompile_h 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/scripts/mkcompile_h 2015-03-26 12:42:18.695588359 +0100 -@@ -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.10.orig/sound/core/pcm_native.c linux-3.18.10/sound/core/pcm_native.c ---- linux-3.18.10.orig/sound/core/pcm_native.c 2015-03-24 02:05:12.000000000 +0100 -+++ linux-3.18.10/sound/core/pcm_native.c 2015-03-26 12:42:18.695588359 +0100 -@@ -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 --git a/target/linux/patches/3.18.10/regmap-bool.patch b/target/linux/patches/3.18.10/regmap-bool.patch deleted file mode 100644 index 5c0ff5e2c..000000000 --- a/target/linux/patches/3.18.10/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.10/relocs.patch b/target/linux/patches/3.18.10/relocs.patch deleted file mode 100644 index 69a7c88a9..000000000 --- a/target/linux/patches/3.18.10/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.10/sgidefs.patch b/target/linux/patches/3.18.10/sgidefs.patch deleted file mode 100644 index f00a284d9..000000000 --- a/target/linux/patches/3.18.10/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.10/sortext.patch b/target/linux/patches/3.18.10/sortext.patch deleted file mode 100644 index 8fd4e1d6b..000000000 --- a/target/linux/patches/3.18.10/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.10/startup.patch b/target/linux/patches/3.18.10/startup.patch deleted file mode 100644 index d396b75e4..000000000 --- a/target/linux/patches/3.18.10/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.10/wlan-cf.patch b/target/linux/patches/3.18.10/wlan-cf.patch deleted file mode 100644 index fc20759e2..000000000 --- a/target/linux/patches/3.18.10/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.10/xargs.patch b/target/linux/patches/3.18.10/xargs.patch deleted file mode 100644 index 2c7b3df59..000000000 --- a/target/linux/patches/3.18.10/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.10/yaffs2.patch b/target/linux/patches/3.18.10/yaffs2.patch deleted file mode 100644 index bb244c7ca..000000000 --- a/target/linux/patches/3.18.10/yaffs2.patch +++ /dev/null @@ -1,16551 +0,0 @@ -diff -Nur linux-3.15-rc5.orig/fs/Kconfig linux-3.15-rc5/fs/Kconfig ---- linux-3.15-rc5.orig/fs/Kconfig 2014-05-09 22:10:52.000000000 +0200 -+++ linux-3.15-rc5/fs/Kconfig 2014-05-17 01:53:17.000000000 +0200 -@@ -190,6 +190,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.15-rc5.orig/fs/Makefile linux-3.15-rc5/fs/Makefile ---- linux-3.15-rc5.orig/fs/Makefile 2014-05-09 22:10:52.000000000 +0200 -+++ linux-3.15-rc5/fs/Makefile 2014-05-17 01:53:25.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.15-rc5.orig/fs/yaffs2/Kconfig linux-3.15-rc5/fs/yaffs2/Kconfig ---- linux-3.15-rc5.orig/fs/yaffs2/Kconfig 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/Kconfig 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/Makefile linux-3.15-rc5/fs/yaffs2/Makefile ---- linux-3.15-rc5.orig/fs/yaffs2/Makefile 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/Makefile 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_allocator.c linux-3.15-rc5/fs/yaffs2/yaffs_allocator.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_allocator.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_allocator.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_allocator.h linux-3.15-rc5/fs/yaffs2/yaffs_allocator.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_allocator.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_allocator.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_attribs.c linux-3.15-rc5/fs/yaffs2/yaffs_attribs.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_attribs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_attribs.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_attribs.h linux-3.15-rc5/fs/yaffs2/yaffs_attribs.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_attribs.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_attribs.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_bitmap.c linux-3.15-rc5/fs/yaffs2/yaffs_bitmap.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_bitmap.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_bitmap.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_bitmap.h linux-3.15-rc5/fs/yaffs2/yaffs_bitmap.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_bitmap.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_bitmap.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_checkptrw.c linux-3.15-rc5/fs/yaffs2/yaffs_checkptrw.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_checkptrw.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_checkptrw.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_checkptrw.h linux-3.15-rc5/fs/yaffs2/yaffs_checkptrw.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_checkptrw.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_checkptrw.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_ecc.c linux-3.15-rc5/fs/yaffs2/yaffs_ecc.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_ecc.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_ecc.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_ecc.h linux-3.15-rc5/fs/yaffs2/yaffs_ecc.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_ecc.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_ecc.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_getblockinfo.h linux-3.15-rc5/fs/yaffs2/yaffs_getblockinfo.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_getblockinfo.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_getblockinfo.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_guts.c linux-3.15-rc5/fs/yaffs2/yaffs_guts.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_guts.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_guts.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_guts.h linux-3.15-rc5/fs/yaffs2/yaffs_guts.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_guts.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_guts.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_linux.h linux-3.15-rc5/fs/yaffs2/yaffs_linux.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_linux.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_linux.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_mtdif.c linux-3.15-rc5/fs/yaffs2/yaffs_mtdif.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_mtdif.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_mtdif.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_mtdif.h linux-3.15-rc5/fs/yaffs2/yaffs_mtdif.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_mtdif.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_mtdif.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_nameval.c linux-3.15-rc5/fs/yaffs2/yaffs_nameval.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_nameval.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_nameval.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_nameval.h linux-3.15-rc5/fs/yaffs2/yaffs_nameval.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_nameval.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_nameval.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_nand.c linux-3.15-rc5/fs/yaffs2/yaffs_nand.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_nand.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_nand.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_nand.h linux-3.15-rc5/fs/yaffs2/yaffs_nand.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_nand.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_nand.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_packedtags1.c linux-3.15-rc5/fs/yaffs2/yaffs_packedtags1.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_packedtags1.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_packedtags1.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_packedtags1.h linux-3.15-rc5/fs/yaffs2/yaffs_packedtags1.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_packedtags1.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_packedtags1.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_packedtags2.c linux-3.15-rc5/fs/yaffs2/yaffs_packedtags2.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_packedtags2.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_packedtags2.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_packedtags2.h linux-3.15-rc5/fs/yaffs2/yaffs_packedtags2.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_packedtags2.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_packedtags2.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_summary.c linux-3.15-rc5/fs/yaffs2/yaffs_summary.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_summary.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_summary.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_summary.h linux-3.15-rc5/fs/yaffs2/yaffs_summary.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_summary.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_summary.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_tagscompat.c linux-3.15-rc5/fs/yaffs2/yaffs_tagscompat.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_tagscompat.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_tagscompat.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_tagscompat.h linux-3.15-rc5/fs/yaffs2/yaffs_tagscompat.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_tagscompat.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_tagscompat.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_tagsmarshall.c linux-3.15-rc5/fs/yaffs2/yaffs_tagsmarshall.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_tagsmarshall.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_tagsmarshall.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_tagsmarshall.h linux-3.15-rc5/fs/yaffs2/yaffs_tagsmarshall.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_tagsmarshall.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_tagsmarshall.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_trace.h linux-3.15-rc5/fs/yaffs2/yaffs_trace.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_trace.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_trace.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_verify.c linux-3.15-rc5/fs/yaffs2/yaffs_verify.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_verify.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_verify.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_verify.h linux-3.15-rc5/fs/yaffs2/yaffs_verify.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_verify.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_verify.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_vfs.c linux-3.15-rc5/fs/yaffs2/yaffs_vfs.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_vfs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_vfs.c 2014-05-17 02:52:54.000000000 +0200 -@@ -0,0 +1,3604 @@ -+/* -+ * YAFFS: Yet Another Flash File System. 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; -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)) -+ ret = readlink_copy(buffer, buflen, alias); -+#else -+ ret = vfs_readlink(dentry, 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, 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.15-rc5.orig/fs/yaffs2/yaffs_yaffs1.c linux-3.15-rc5/fs/yaffs2/yaffs_yaffs1.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_yaffs1.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_yaffs1.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_yaffs1.h linux-3.15-rc5/fs/yaffs2/yaffs_yaffs1.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_yaffs1.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_yaffs1.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_yaffs2.c linux-3.15-rc5/fs/yaffs2/yaffs_yaffs2.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_yaffs2.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_yaffs2.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_yaffs2.h linux-3.15-rc5/fs/yaffs2/yaffs_yaffs2.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_yaffs2.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_yaffs2.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yportenv.h linux-3.15-rc5/fs/yaffs2/yportenv.h ---- linux-3.15-rc5.orig/fs/yaffs2/yportenv.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yportenv.h 2014-05-17 01:53:27.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.12/bsd-compatibility.patch b/target/linux/patches/3.18.12/bsd-compatibility.patch new file mode 100644 index 000000000..b954b658f --- /dev/null +++ b/target/linux/patches/3.18.12/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.12/cleankernel.patch b/target/linux/patches/3.18.12/cleankernel.patch new file mode 100644 index 000000000..d8c055dc3 --- /dev/null +++ b/target/linux/patches/3.18.12/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.12/cris-header.patch b/target/linux/patches/3.18.12/cris-header.patch new file mode 100644 index 000000000..3db07e530 --- /dev/null +++ b/target/linux/patches/3.18.12/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.12/defaults.patch b/target/linux/patches/3.18.12/defaults.patch new file mode 100644 index 000000000..6cdca084e --- /dev/null +++ b/target/linux/patches/3.18.12/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.12/export-symbol-for-exmap.patch b/target/linux/patches/3.18.12/export-symbol-for-exmap.patch new file mode 100644 index 000000000..4f0fc8449 --- /dev/null +++ b/target/linux/patches/3.18.12/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.12/fblogo.patch b/target/linux/patches/3.18.12/fblogo.patch new file mode 100644 index 000000000..5b9070242 --- /dev/null +++ b/target/linux/patches/3.18.12/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.12/gemalto.patch b/target/linux/patches/3.18.12/gemalto.patch new file mode 100644 index 000000000..65f7af1d7 --- /dev/null +++ b/target/linux/patches/3.18.12/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.12/initramfs-nosizelimit.patch b/target/linux/patches/3.18.12/initramfs-nosizelimit.patch new file mode 100644 index 000000000..40d2f6bd8 --- /dev/null +++ b/target/linux/patches/3.18.12/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.12/lemote-rfkill.patch b/target/linux/patches/3.18.12/lemote-rfkill.patch new file mode 100644 index 000000000..a61488434 --- /dev/null +++ b/target/linux/patches/3.18.12/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.12/microblaze-ethernet.patch b/target/linux/patches/3.18.12/microblaze-ethernet.patch new file mode 100644 index 000000000..742ab477e --- /dev/null +++ b/target/linux/patches/3.18.12/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.12/mkpiggy.patch b/target/linux/patches/3.18.12/mkpiggy.patch new file mode 100644 index 000000000..751678b74 --- /dev/null +++ b/target/linux/patches/3.18.12/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.12/mtd-rootfs.patch b/target/linux/patches/3.18.12/mtd-rootfs.patch new file mode 100644 index 000000000..775d5fc80 --- /dev/null +++ b/target/linux/patches/3.18.12/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.12/nfsv3-tcp.patch b/target/linux/patches/3.18.12/nfsv3-tcp.patch new file mode 100644 index 000000000..d5e07e1c2 --- /dev/null +++ b/target/linux/patches/3.18.12/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.12/non-static.patch b/target/linux/patches/3.18.12/non-static.patch new file mode 100644 index 000000000..a967703d0 --- /dev/null +++ b/target/linux/patches/3.18.12/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.12/ppc64-missing-zlib.patch b/target/linux/patches/3.18.12/ppc64-missing-zlib.patch new file mode 100644 index 000000000..c6e0616be --- /dev/null +++ b/target/linux/patches/3.18.12/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.12/realtime.patch b/target/linux/patches/3.18.12/realtime.patch new file mode 100644 index 000000000..e91381e07 --- /dev/null +++ b/target/linux/patches/3.18.12/realtime.patch @@ -0,0 +1,36330 @@ +diff -Nur linux-3.18.12.orig/arch/alpha/mm/fault.c linux-3.18.12/arch/alpha/mm/fault.c +--- linux-3.18.12.orig/arch/alpha/mm/fault.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/alpha/mm/fault.c 2015-04-26 13:32:22.351684003 -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.12.orig/arch/arm/include/asm/cmpxchg.h linux-3.18.12/arch/arm/include/asm/cmpxchg.h +--- linux-3.18.12.orig/arch/arm/include/asm/cmpxchg.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/include/asm/cmpxchg.h 2015-04-26 13:32:22.351684003 -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.12.orig/arch/arm/include/asm/futex.h linux-3.18.12/arch/arm/include/asm/futex.h +--- linux-3.18.12.orig/arch/arm/include/asm/futex.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/include/asm/futex.h 2015-04-26 13:32:22.351684003 -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.12.orig/arch/arm/include/asm/switch_to.h linux-3.18.12/arch/arm/include/asm/switch_to.h +--- linux-3.18.12.orig/arch/arm/include/asm/switch_to.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/include/asm/switch_to.h 2015-04-26 13:32:22.355684003 -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.12.orig/arch/arm/include/asm/thread_info.h linux-3.18.12/arch/arm/include/asm/thread_info.h +--- linux-3.18.12.orig/arch/arm/include/asm/thread_info.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/include/asm/thread_info.h 2015-04-26 13:32:22.355684003 -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.12.orig/arch/arm/Kconfig linux-3.18.12/arch/arm/Kconfig +--- linux-3.18.12.orig/arch/arm/Kconfig 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/Kconfig 2015-04-26 13:32:22.351684003 -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.12.orig/arch/arm/kernel/asm-offsets.c linux-3.18.12/arch/arm/kernel/asm-offsets.c +--- linux-3.18.12.orig/arch/arm/kernel/asm-offsets.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/kernel/asm-offsets.c 2015-04-26 13:32:22.355684003 -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.12.orig/arch/arm/kernel/entry-armv.S linux-3.18.12/arch/arm/kernel/entry-armv.S +--- linux-3.18.12.orig/arch/arm/kernel/entry-armv.S 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/kernel/entry-armv.S 2015-04-26 13:32:22.355684003 -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.12.orig/arch/arm/kernel/process.c linux-3.18.12/arch/arm/kernel/process.c +--- linux-3.18.12.orig/arch/arm/kernel/process.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/kernel/process.c 2015-04-26 13:32:22.355684003 -0500 +@@ -431,6 +431,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.12.orig/arch/arm/kernel/signal.c linux-3.18.12/arch/arm/kernel/signal.c +--- linux-3.18.12.orig/arch/arm/kernel/signal.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/kernel/signal.c 2015-04-26 13:32:22.359684003 -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.12.orig/arch/arm/kernel/unwind.c linux-3.18.12/arch/arm/kernel/unwind.c +--- linux-3.18.12.orig/arch/arm/kernel/unwind.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/kernel/unwind.c 2015-04-26 13:32:22.359684003 -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.12.orig/arch/arm/kvm/arm.c linux-3.18.12/arch/arm/kvm/arm.c +--- linux-3.18.12.orig/arch/arm/kvm/arm.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/kvm/arm.c 2015-04-26 13:32:22.359684003 -0500 +@@ -441,9 +441,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.12.orig/arch/arm/kvm/psci.c linux-3.18.12/arch/arm/kvm/psci.c +--- linux-3.18.12.orig/arch/arm/kvm/psci.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/kvm/psci.c 2015-04-26 13:32:22.359684003 -0500 +@@ -66,7 +66,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; +@@ -123,7 +123,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.12.orig/arch/arm/mach-at91/at91rm9200_time.c linux-3.18.12/arch/arm/mach-at91/at91rm9200_time.c +--- linux-3.18.12.orig/arch/arm/mach-at91/at91rm9200_time.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/mach-at91/at91rm9200_time.c 2015-04-26 13:32:22.359684003 -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.12.orig/arch/arm/mach-exynos/platsmp.c linux-3.18.12/arch/arm/mach-exynos/platsmp.c +--- linux-3.18.12.orig/arch/arm/mach-exynos/platsmp.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/mach-exynos/platsmp.c 2015-04-26 13:32:22.359684003 -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.12.orig/arch/arm/mach-hisi/platmcpm.c linux-3.18.12/arch/arm/mach-hisi/platmcpm.c +--- linux-3.18.12.orig/arch/arm/mach-hisi/platmcpm.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/mach-hisi/platmcpm.c 2015-04-26 13:32:22.363684003 -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.12.orig/arch/arm/mach-omap2/omap-smp.c linux-3.18.12/arch/arm/mach-omap2/omap-smp.c +--- linux-3.18.12.orig/arch/arm/mach-omap2/omap-smp.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/mach-omap2/omap-smp.c 2015-04-26 13:32:22.363684003 -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.12.orig/arch/arm/mach-prima2/platsmp.c linux-3.18.12/arch/arm/mach-prima2/platsmp.c +--- linux-3.18.12.orig/arch/arm/mach-prima2/platsmp.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/mach-prima2/platsmp.c 2015-04-26 13:32:22.363684003 -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.12.orig/arch/arm/mach-qcom/platsmp.c linux-3.18.12/arch/arm/mach-qcom/platsmp.c +--- linux-3.18.12.orig/arch/arm/mach-qcom/platsmp.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/mach-qcom/platsmp.c 2015-04-26 13:32:22.363684003 -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.12.orig/arch/arm/mach-spear/platsmp.c linux-3.18.12/arch/arm/mach-spear/platsmp.c +--- linux-3.18.12.orig/arch/arm/mach-spear/platsmp.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/mach-spear/platsmp.c 2015-04-26 13:32:22.363684003 -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.12.orig/arch/arm/mach-sti/platsmp.c linux-3.18.12/arch/arm/mach-sti/platsmp.c +--- linux-3.18.12.orig/arch/arm/mach-sti/platsmp.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/mach-sti/platsmp.c 2015-04-26 13:32:22.363684003 -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.12.orig/arch/arm/mach-ux500/platsmp.c linux-3.18.12/arch/arm/mach-ux500/platsmp.c +--- linux-3.18.12.orig/arch/arm/mach-ux500/platsmp.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/mach-ux500/platsmp.c 2015-04-26 13:32:22.363684003 -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.12.orig/arch/arm/mm/fault.c linux-3.18.12/arch/arm/mm/fault.c +--- linux-3.18.12.orig/arch/arm/mm/fault.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/mm/fault.c 2015-04-26 13:32:22.367684003 -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.12.orig/arch/arm/mm/highmem.c linux-3.18.12/arch/arm/mm/highmem.c +--- linux-3.18.12.orig/arch/arm/mm/highmem.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/mm/highmem.c 2015-04-26 13:32:22.367684003 -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.12.orig/arch/arm/plat-versatile/platsmp.c linux-3.18.12/arch/arm/plat-versatile/platsmp.c +--- linux-3.18.12.orig/arch/arm/plat-versatile/platsmp.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/arm/plat-versatile/platsmp.c 2015-04-26 13:32:22.367684003 -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.12.orig/arch/avr32/mm/fault.c linux-3.18.12/arch/avr32/mm/fault.c +--- linux-3.18.12.orig/arch/avr32/mm/fault.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/avr32/mm/fault.c 2015-04-26 13:32:22.367684003 -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.12.orig/arch/cris/mm/fault.c linux-3.18.12/arch/cris/mm/fault.c +--- linux-3.18.12.orig/arch/cris/mm/fault.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/cris/mm/fault.c 2015-04-26 13:32:22.367684003 -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.12.orig/arch/frv/mm/fault.c linux-3.18.12/arch/frv/mm/fault.c +--- linux-3.18.12.orig/arch/frv/mm/fault.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/frv/mm/fault.c 2015-04-26 13:32:22.367684003 -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.12.orig/arch/ia64/mm/fault.c linux-3.18.12/arch/ia64/mm/fault.c +--- linux-3.18.12.orig/arch/ia64/mm/fault.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/ia64/mm/fault.c 2015-04-26 13:32:22.367684003 -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.12.orig/arch/Kconfig linux-3.18.12/arch/Kconfig +--- linux-3.18.12.orig/arch/Kconfig 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/Kconfig 2015-04-26 13:32:22.351684003 -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.12.orig/arch/m32r/mm/fault.c linux-3.18.12/arch/m32r/mm/fault.c +--- linux-3.18.12.orig/arch/m32r/mm/fault.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/m32r/mm/fault.c 2015-04-26 13:32:22.367684003 -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.12.orig/arch/m68k/mm/fault.c linux-3.18.12/arch/m68k/mm/fault.c +--- linux-3.18.12.orig/arch/m68k/mm/fault.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/m68k/mm/fault.c 2015-04-26 13:32:22.367684003 -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.12.orig/arch/microblaze/mm/fault.c linux-3.18.12/arch/microblaze/mm/fault.c +--- linux-3.18.12.orig/arch/microblaze/mm/fault.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/microblaze/mm/fault.c 2015-04-26 13:32:22.367684003 -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.12.orig/arch/mips/Kconfig linux-3.18.12/arch/mips/Kconfig +--- linux-3.18.12.orig/arch/mips/Kconfig 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/mips/Kconfig 2015-04-26 13:32:22.367684003 -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.12.orig/arch/mips/kernel/signal.c linux-3.18.12/arch/mips/kernel/signal.c +--- linux-3.18.12.orig/arch/mips/kernel/signal.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/mips/kernel/signal.c 2015-04-26 13:32:22.367684003 -0500 +@@ -613,6 +613,7 @@ + __u32 thread_info_flags) + { + local_irq_enable(); ++ preempt_check_resched(); + + user_exit(); + +diff -Nur linux-3.18.12.orig/arch/mips/mm/fault.c linux-3.18.12/arch/mips/mm/fault.c +--- linux-3.18.12.orig/arch/mips/mm/fault.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/mips/mm/fault.c 2015-04-26 13:32:22.367684003 -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.12.orig/arch/mips/mm/init.c linux-3.18.12/arch/mips/mm/init.c +--- linux-3.18.12.orig/arch/mips/mm/init.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/mips/mm/init.c 2015-04-26 13:32:22.367684003 -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.12.orig/arch/mn10300/mm/fault.c linux-3.18.12/arch/mn10300/mm/fault.c +--- linux-3.18.12.orig/arch/mn10300/mm/fault.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/mn10300/mm/fault.c 2015-04-26 13:32:22.367684003 -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.12.orig/arch/parisc/mm/fault.c linux-3.18.12/arch/parisc/mm/fault.c +--- linux-3.18.12.orig/arch/parisc/mm/fault.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/parisc/mm/fault.c 2015-04-26 13:32:22.367684003 -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.12.orig/arch/powerpc/include/asm/kvm_host.h linux-3.18.12/arch/powerpc/include/asm/kvm_host.h +--- linux-3.18.12.orig/arch/powerpc/include/asm/kvm_host.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/powerpc/include/asm/kvm_host.h 2015-04-26 13:32:22.367684003 -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.12.orig/arch/powerpc/include/asm/thread_info.h linux-3.18.12/arch/powerpc/include/asm/thread_info.h +--- linux-3.18.12.orig/arch/powerpc/include/asm/thread_info.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/powerpc/include/asm/thread_info.h 2015-04-26 13:32:22.367684003 -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.12.orig/arch/powerpc/kernel/misc_32.S linux-3.18.12/arch/powerpc/kernel/misc_32.S +--- linux-3.18.12.orig/arch/powerpc/kernel/misc_32.S 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/powerpc/kernel/misc_32.S 2015-04-26 13:32:22.371684003 -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.12.orig/arch/powerpc/kernel/misc_64.S linux-3.18.12/arch/powerpc/kernel/misc_64.S +--- linux-3.18.12.orig/arch/powerpc/kernel/misc_64.S 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/powerpc/kernel/misc_64.S 2015-04-26 13:32:22.371684003 -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.12.orig/arch/powerpc/kernel/time.c linux-3.18.12/arch/powerpc/kernel/time.c +--- linux-3.18.12.orig/arch/powerpc/kernel/time.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/powerpc/kernel/time.c 2015-04-26 13:32:22.371684003 -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.12.orig/arch/powerpc/kvm/book3s_hv.c linux-3.18.12/arch/powerpc/kvm/book3s_hv.c +--- linux-3.18.12.orig/arch/powerpc/kvm/book3s_hv.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/powerpc/kvm/book3s_hv.c 2015-04-26 13:32:22.371684003 -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.12.orig/arch/powerpc/mm/fault.c linux-3.18.12/arch/powerpc/mm/fault.c +--- linux-3.18.12.orig/arch/powerpc/mm/fault.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/powerpc/mm/fault.c 2015-04-26 13:32:22.371684003 -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.12.orig/arch/s390/include/asm/kvm_host.h linux-3.18.12/arch/s390/include/asm/kvm_host.h +--- linux-3.18.12.orig/arch/s390/include/asm/kvm_host.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/s390/include/asm/kvm_host.h 2015-04-26 13:32:22.371684003 -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.12.orig/arch/s390/kvm/interrupt.c linux-3.18.12/arch/s390/kvm/interrupt.c +--- linux-3.18.12.orig/arch/s390/kvm/interrupt.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/s390/kvm/interrupt.c 2015-04-26 13:32:22.371684003 -0500 +@@ -619,13 +619,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++; + } + } +@@ -746,7 +746,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; + } +@@ -771,7 +771,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.12.orig/arch/s390/mm/fault.c linux-3.18.12/arch/s390/mm/fault.c +--- linux-3.18.12.orig/arch/s390/mm/fault.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/s390/mm/fault.c 2015-04-26 13:32:22.371684003 -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.12.orig/arch/score/mm/fault.c linux-3.18.12/arch/score/mm/fault.c +--- linux-3.18.12.orig/arch/score/mm/fault.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/score/mm/fault.c 2015-04-26 13:32:22.371684003 -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.12.orig/arch/sh/kernel/irq.c linux-3.18.12/arch/sh/kernel/irq.c +--- linux-3.18.12.orig/arch/sh/kernel/irq.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/sh/kernel/irq.c 2015-04-26 13:32:22.371684003 -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.12.orig/arch/sh/mm/fault.c linux-3.18.12/arch/sh/mm/fault.c +--- linux-3.18.12.orig/arch/sh/mm/fault.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/sh/mm/fault.c 2015-04-26 13:32:22.371684003 -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.12.orig/arch/sparc/Kconfig linux-3.18.12/arch/sparc/Kconfig +--- linux-3.18.12.orig/arch/sparc/Kconfig 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/sparc/Kconfig 2015-04-26 13:32:22.371684003 -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.12.orig/arch/sparc/kernel/irq_64.c linux-3.18.12/arch/sparc/kernel/irq_64.c +--- linux-3.18.12.orig/arch/sparc/kernel/irq_64.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/sparc/kernel/irq_64.c 2015-04-26 13:32:22.375684003 -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.12.orig/arch/sparc/kernel/setup_32.c linux-3.18.12/arch/sparc/kernel/setup_32.c +--- linux-3.18.12.orig/arch/sparc/kernel/setup_32.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/sparc/kernel/setup_32.c 2015-04-26 13:32:22.375684003 -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.12.orig/arch/sparc/kernel/setup_64.c linux-3.18.12/arch/sparc/kernel/setup_64.c +--- linux-3.18.12.orig/arch/sparc/kernel/setup_64.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/sparc/kernel/setup_64.c 2015-04-26 13:32:22.375684003 -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.12.orig/arch/sparc/mm/fault_32.c linux-3.18.12/arch/sparc/mm/fault_32.c +--- linux-3.18.12.orig/arch/sparc/mm/fault_32.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/sparc/mm/fault_32.c 2015-04-26 13:32:22.375684003 -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.12.orig/arch/sparc/mm/fault_64.c linux-3.18.12/arch/sparc/mm/fault_64.c +--- linux-3.18.12.orig/arch/sparc/mm/fault_64.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/sparc/mm/fault_64.c 2015-04-26 13:32:22.375684003 -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.12.orig/arch/tile/mm/fault.c linux-3.18.12/arch/tile/mm/fault.c +--- linux-3.18.12.orig/arch/tile/mm/fault.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/tile/mm/fault.c 2015-04-26 13:32:22.375684003 -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.12.orig/arch/um/kernel/trap.c linux-3.18.12/arch/um/kernel/trap.c +--- linux-3.18.12.orig/arch/um/kernel/trap.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/um/kernel/trap.c 2015-04-26 13:32:22.375684003 -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.12.orig/arch/x86/crypto/aesni-intel_glue.c linux-3.18.12/arch/x86/crypto/aesni-intel_glue.c +--- linux-3.18.12.orig/arch/x86/crypto/aesni-intel_glue.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/crypto/aesni-intel_glue.c 2015-04-26 13:32:22.375684003 -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.12.orig/arch/x86/crypto/cast5_avx_glue.c linux-3.18.12/arch/x86/crypto/cast5_avx_glue.c +--- linux-3.18.12.orig/arch/x86/crypto/cast5_avx_glue.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/crypto/cast5_avx_glue.c 2015-04-26 13:32:22.375684003 -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.12.orig/arch/x86/crypto/glue_helper.c linux-3.18.12/arch/x86/crypto/glue_helper.c +--- linux-3.18.12.orig/arch/x86/crypto/glue_helper.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/crypto/glue_helper.c 2015-04-26 13:32:22.375684003 -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.12.orig/arch/x86/include/asm/preempt.h linux-3.18.12/arch/x86/include/asm/preempt.h +--- linux-3.18.12.orig/arch/x86/include/asm/preempt.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/include/asm/preempt.h 2015-04-26 13:32:22.375684003 -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.12.orig/arch/x86/include/asm/signal.h linux-3.18.12/arch/x86/include/asm/signal.h +--- linux-3.18.12.orig/arch/x86/include/asm/signal.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/include/asm/signal.h 2015-04-26 13:32:22.375684003 -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.12.orig/arch/x86/include/asm/stackprotector.h linux-3.18.12/arch/x86/include/asm/stackprotector.h +--- linux-3.18.12.orig/arch/x86/include/asm/stackprotector.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/include/asm/stackprotector.h 2015-04-26 13:32:22.375684003 -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.12.orig/arch/x86/include/asm/thread_info.h linux-3.18.12/arch/x86/include/asm/thread_info.h +--- linux-3.18.12.orig/arch/x86/include/asm/thread_info.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/include/asm/thread_info.h 2015-04-26 13:32:22.375684003 -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.12.orig/arch/x86/include/asm/uv/uv_bau.h linux-3.18.12/arch/x86/include/asm/uv/uv_bau.h +--- linux-3.18.12.orig/arch/x86/include/asm/uv/uv_bau.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/include/asm/uv/uv_bau.h 2015-04-26 13:32:22.375684003 -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.12.orig/arch/x86/include/asm/uv/uv_hub.h linux-3.18.12/arch/x86/include/asm/uv/uv_hub.h +--- linux-3.18.12.orig/arch/x86/include/asm/uv/uv_hub.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/include/asm/uv/uv_hub.h 2015-04-26 13:32:22.375684003 -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.12.orig/arch/x86/Kconfig linux-3.18.12/arch/x86/Kconfig +--- linux-3.18.12.orig/arch/x86/Kconfig 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/Kconfig 2015-04-26 13:32:22.375684003 -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.12.orig/arch/x86/kernel/apic/io_apic.c linux-3.18.12/arch/x86/kernel/apic/io_apic.c +--- linux-3.18.12.orig/arch/x86/kernel/apic/io_apic.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/kernel/apic/io_apic.c 2015-04-26 13:32:22.379684003 -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.12.orig/arch/x86/kernel/apic/x2apic_uv_x.c linux-3.18.12/arch/x86/kernel/apic/x2apic_uv_x.c +--- linux-3.18.12.orig/arch/x86/kernel/apic/x2apic_uv_x.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/kernel/apic/x2apic_uv_x.c 2015-04-26 13:32:22.379684003 -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.12.orig/arch/x86/kernel/asm-offsets.c linux-3.18.12/arch/x86/kernel/asm-offsets.c +--- linux-3.18.12.orig/arch/x86/kernel/asm-offsets.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/kernel/asm-offsets.c 2015-04-26 13:32:22.379684003 -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.12.orig/arch/x86/kernel/cpu/mcheck/mce.c linux-3.18.12/arch/x86/kernel/cpu/mcheck/mce.c +--- linux-3.18.12.orig/arch/x86/kernel/cpu/mcheck/mce.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/kernel/cpu/mcheck/mce.c 2015-04-26 13:32:22.379684003 -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.12.orig/arch/x86/kernel/entry_32.S linux-3.18.12/arch/x86/kernel/entry_32.S +--- linux-3.18.12.orig/arch/x86/kernel/entry_32.S 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/kernel/entry_32.S 2015-04-26 13:32:22.379684003 -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.12.orig/arch/x86/kernel/entry_64.S linux-3.18.12/arch/x86/kernel/entry_64.S +--- linux-3.18.12.orig/arch/x86/kernel/entry_64.S 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/kernel/entry_64.S 2015-04-26 13:32:22.379684003 -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.12.orig/arch/x86/kernel/irq_32.c linux-3.18.12/arch/x86/kernel/irq_32.c +--- linux-3.18.12.orig/arch/x86/kernel/irq_32.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/kernel/irq_32.c 2015-04-26 13:32:22.379684003 -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.12.orig/arch/x86/kernel/process_32.c linux-3.18.12/arch/x86/kernel/process_32.c +--- linux-3.18.12.orig/arch/x86/kernel/process_32.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/kernel/process_32.c 2015-04-26 13:32:22.379684003 -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.12.orig/arch/x86/kernel/signal.c linux-3.18.12/arch/x86/kernel/signal.c +--- linux-3.18.12.orig/arch/x86/kernel/signal.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/kernel/signal.c 2015-04-26 13:32:22.379684003 -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.12.orig/arch/x86/kernel/traps.c linux-3.18.12/arch/x86/kernel/traps.c +--- linux-3.18.12.orig/arch/x86/kernel/traps.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/kernel/traps.c 2015-04-26 13:32:22.379684003 -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.12.orig/arch/x86/kvm/lapic.c linux-3.18.12/arch/x86/kvm/lapic.c +--- linux-3.18.12.orig/arch/x86/kvm/lapic.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/kvm/lapic.c 2015-04-26 13:32:22.379684003 -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.12.orig/arch/x86/kvm/x86.c linux-3.18.12/arch/x86/kvm/x86.c +--- linux-3.18.12.orig/arch/x86/kvm/x86.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/kvm/x86.c 2015-04-26 13:32:22.383684003 -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.12.orig/arch/x86/mm/fault.c linux-3.18.12/arch/x86/mm/fault.c +--- linux-3.18.12.orig/arch/x86/mm/fault.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/mm/fault.c 2015-04-26 13:32:22.383684003 -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.12.orig/arch/x86/mm/highmem_32.c linux-3.18.12/arch/x86/mm/highmem_32.c +--- linux-3.18.12.orig/arch/x86/mm/highmem_32.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/mm/highmem_32.c 2015-04-26 13:32:22.383684003 -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.12.orig/arch/x86/mm/iomap_32.c linux-3.18.12/arch/x86/mm/iomap_32.c +--- linux-3.18.12.orig/arch/x86/mm/iomap_32.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/mm/iomap_32.c 2015-04-26 13:32:22.383684003 -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.12.orig/arch/x86/platform/uv/tlb_uv.c linux-3.18.12/arch/x86/platform/uv/tlb_uv.c +--- linux-3.18.12.orig/arch/x86/platform/uv/tlb_uv.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/platform/uv/tlb_uv.c 2015-04-26 13:32:22.383684003 -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.12.orig/arch/x86/platform/uv/uv_time.c linux-3.18.12/arch/x86/platform/uv/uv_time.c +--- linux-3.18.12.orig/arch/x86/platform/uv/uv_time.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/x86/platform/uv/uv_time.c 2015-04-26 13:32:22.383684003 -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.12.orig/arch/xtensa/mm/fault.c linux-3.18.12/arch/xtensa/mm/fault.c +--- linux-3.18.12.orig/arch/xtensa/mm/fault.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/arch/xtensa/mm/fault.c 2015-04-26 13:32:22.383684003 -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.12.orig/block/blk-core.c linux-3.18.12/block/blk-core.c +--- linux-3.18.12.orig/block/blk-core.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/block/blk-core.c 2015-04-26 13:32:22.383684003 -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.12.orig/block/blk-ioc.c linux-3.18.12/block/blk-ioc.c +--- linux-3.18.12.orig/block/blk-ioc.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/block/blk-ioc.c 2015-04-26 13:32:22.383684003 -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.12.orig/block/blk-iopoll.c linux-3.18.12/block/blk-iopoll.c +--- linux-3.18.12.orig/block/blk-iopoll.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/block/blk-iopoll.c 2015-04-26 13:32:22.383684003 -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.12.orig/block/blk-mq.c linux-3.18.12/block/blk-mq.c +--- linux-3.18.12.orig/block/blk-mq.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/block/blk-mq.c 2015-04-26 13:32:22.383684003 -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.12.orig/block/blk-mq-cpu.c linux-3.18.12/block/blk-mq-cpu.c +--- linux-3.18.12.orig/block/blk-mq-cpu.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/block/blk-mq-cpu.c 2015-04-26 13:32:22.383684003 -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.12.orig/block/blk-mq.h linux-3.18.12/block/blk-mq.h +--- linux-3.18.12.orig/block/blk-mq.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/block/blk-mq.h 2015-04-26 13:32:22.383684003 -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.12.orig/block/blk-softirq.c linux-3.18.12/block/blk-softirq.c +--- linux-3.18.12.orig/block/blk-softirq.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/block/blk-softirq.c 2015-04-26 13:32:22.387684003 -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.12.orig/block/bounce.c linux-3.18.12/block/bounce.c +--- linux-3.18.12.orig/block/bounce.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/block/bounce.c 2015-04-26 13:32:22.387684003 -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.12.orig/crypto/algapi.c linux-3.18.12/crypto/algapi.c +--- linux-3.18.12.orig/crypto/algapi.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/crypto/algapi.c 2015-04-26 13:32:22.387684003 -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.12.orig/crypto/api.c linux-3.18.12/crypto/api.c +--- linux-3.18.12.orig/crypto/api.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/crypto/api.c 2015-04-26 13:32:22.387684003 -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.12.orig/crypto/internal.h linux-3.18.12/crypto/internal.h +--- linux-3.18.12.orig/crypto/internal.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/crypto/internal.h 2015-04-26 13:32:22.387684003 -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.12.orig/Documentation/hwlat_detector.txt linux-3.18.12/Documentation/hwlat_detector.txt +--- linux-3.18.12.orig/Documentation/hwlat_detector.txt 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.12/Documentation/hwlat_detector.txt 2015-04-26 13:32:22.347684003 -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.12.orig/Documentation/sysrq.txt linux-3.18.12/Documentation/sysrq.txt +--- linux-3.18.12.orig/Documentation/sysrq.txt 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/Documentation/sysrq.txt 2015-04-26 13:32:22.347684003 -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.12.orig/Documentation/trace/histograms.txt linux-3.18.12/Documentation/trace/histograms.txt +--- linux-3.18.12.orig/Documentation/trace/histograms.txt 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.12/Documentation/trace/histograms.txt 2015-04-26 13:32:22.351684003 -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.12.orig/drivers/acpi/acpica/acglobal.h linux-3.18.12/drivers/acpi/acpica/acglobal.h +--- linux-3.18.12.orig/drivers/acpi/acpica/acglobal.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/acpi/acpica/acglobal.h 2015-04-26 13:32:22.387684003 -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.12.orig/drivers/acpi/acpica/hwregs.c linux-3.18.12/drivers/acpi/acpica/hwregs.c +--- linux-3.18.12.orig/drivers/acpi/acpica/hwregs.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/acpi/acpica/hwregs.c 2015-04-26 13:32:22.387684003 -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.12.orig/drivers/acpi/acpica/hwxface.c linux-3.18.12/drivers/acpi/acpica/hwxface.c +--- linux-3.18.12.orig/drivers/acpi/acpica/hwxface.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/acpi/acpica/hwxface.c 2015-04-26 13:32:22.387684003 -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.12.orig/drivers/acpi/acpica/utmutex.c linux-3.18.12/drivers/acpi/acpica/utmutex.c +--- linux-3.18.12.orig/drivers/acpi/acpica/utmutex.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/acpi/acpica/utmutex.c 2015-04-26 13:32:22.387684003 -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.12.orig/drivers/ata/libata-sff.c linux-3.18.12/drivers/ata/libata-sff.c +--- linux-3.18.12.orig/drivers/ata/libata-sff.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/ata/libata-sff.c 2015-04-26 13:32:22.387684003 -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.12.orig/drivers/char/random.c linux-3.18.12/drivers/char/random.c +--- linux-3.18.12.orig/drivers/char/random.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/char/random.c 2015-04-26 13:32:22.387684003 -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.12.orig/drivers/clocksource/tcb_clksrc.c linux-3.18.12/drivers/clocksource/tcb_clksrc.c +--- linux-3.18.12.orig/drivers/clocksource/tcb_clksrc.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/clocksource/tcb_clksrc.c 2015-04-26 13:32:22.387684003 -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.12.orig/drivers/clocksource/timer-atmel-pit.c linux-3.18.12/drivers/clocksource/timer-atmel-pit.c +--- linux-3.18.12.orig/drivers/clocksource/timer-atmel-pit.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/clocksource/timer-atmel-pit.c 2015-04-26 13:32:22.387684003 -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.12.orig/drivers/cpufreq/Kconfig.x86 linux-3.18.12/drivers/cpufreq/Kconfig.x86 +--- linux-3.18.12.orig/drivers/cpufreq/Kconfig.x86 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/cpufreq/Kconfig.x86 2015-04-26 13:32:22.387684003 -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.12.orig/drivers/gpio/gpio-omap.c linux-3.18.12/drivers/gpio/gpio-omap.c +--- linux-3.18.12.orig/drivers/gpio/gpio-omap.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/gpio/gpio-omap.c 2015-04-26 13:32:22.387684003 -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.12.orig/drivers/gpu/drm/i915/i915_gem.c linux-3.18.12/drivers/gpu/drm/i915/i915_gem.c +--- linux-3.18.12.orig/drivers/gpu/drm/i915/i915_gem.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/gpu/drm/i915/i915_gem.c 2015-04-26 13:32:22.391684003 -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.12.orig/drivers/gpu/drm/i915/i915_gem_execbuffer.c linux-3.18.12/drivers/gpu/drm/i915/i915_gem_execbuffer.c +--- linux-3.18.12.orig/drivers/gpu/drm/i915/i915_gem_execbuffer.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/gpu/drm/i915/i915_gem_execbuffer.c 2015-04-26 13:32:22.391684003 -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.12.orig/drivers/i2c/busses/i2c-omap.c linux-3.18.12/drivers/i2c/busses/i2c-omap.c +--- linux-3.18.12.orig/drivers/i2c/busses/i2c-omap.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/i2c/busses/i2c-omap.c 2015-04-26 13:32:22.391684003 -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.12.orig/drivers/ide/alim15x3.c linux-3.18.12/drivers/ide/alim15x3.c +--- linux-3.18.12.orig/drivers/ide/alim15x3.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/ide/alim15x3.c 2015-04-26 13:32:22.391684003 -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.12.orig/drivers/ide/hpt366.c linux-3.18.12/drivers/ide/hpt366.c +--- linux-3.18.12.orig/drivers/ide/hpt366.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/ide/hpt366.c 2015-04-26 13:32:22.391684003 -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.12.orig/drivers/ide/ide-io.c linux-3.18.12/drivers/ide/ide-io.c +--- linux-3.18.12.orig/drivers/ide/ide-io.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/ide/ide-io.c 2015-04-26 13:32:22.391684003 -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.12.orig/drivers/ide/ide-iops.c linux-3.18.12/drivers/ide/ide-iops.c +--- linux-3.18.12.orig/drivers/ide/ide-iops.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/ide/ide-iops.c 2015-04-26 13:32:22.391684003 -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.12.orig/drivers/ide/ide-io-std.c linux-3.18.12/drivers/ide/ide-io-std.c +--- linux-3.18.12.orig/drivers/ide/ide-io-std.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/ide/ide-io-std.c 2015-04-26 13:32:22.391684003 -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.12.orig/drivers/ide/ide-probe.c linux-3.18.12/drivers/ide/ide-probe.c +--- linux-3.18.12.orig/drivers/ide/ide-probe.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/ide/ide-probe.c 2015-04-26 13:32:22.391684003 -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.12.orig/drivers/ide/ide-taskfile.c linux-3.18.12/drivers/ide/ide-taskfile.c +--- linux-3.18.12.orig/drivers/ide/ide-taskfile.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/ide/ide-taskfile.c 2015-04-26 13:32:22.391684003 -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.12.orig/drivers/infiniband/ulp/ipoib/ipoib_multicast.c linux-3.18.12/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +--- linux-3.18.12.orig/drivers/infiniband/ulp/ipoib/ipoib_multicast.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/infiniband/ulp/ipoib/ipoib_multicast.c 2015-04-26 13:32:22.391684003 -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.12.orig/drivers/input/gameport/gameport.c linux-3.18.12/drivers/input/gameport/gameport.c +--- linux-3.18.12.orig/drivers/input/gameport/gameport.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/input/gameport/gameport.c 2015-04-26 13:32:22.391684003 -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.12.orig/drivers/leds/trigger/Kconfig linux-3.18.12/drivers/leds/trigger/Kconfig +--- linux-3.18.12.orig/drivers/leds/trigger/Kconfig 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/leds/trigger/Kconfig 2015-04-26 13:32:22.391684003 -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.12.orig/drivers/md/bcache/Kconfig linux-3.18.12/drivers/md/bcache/Kconfig +--- linux-3.18.12.orig/drivers/md/bcache/Kconfig 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/md/bcache/Kconfig 2015-04-26 13:32:22.391684003 -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.12.orig/drivers/md/dm.c linux-3.18.12/drivers/md/dm.c +--- linux-3.18.12.orig/drivers/md/dm.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/md/dm.c 2015-04-26 13:32:22.395684003 -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.12.orig/drivers/md/raid5.c linux-3.18.12/drivers/md/raid5.c +--- linux-3.18.12.orig/drivers/md/raid5.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/md/raid5.c 2015-04-26 13:32:22.395684003 -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.12.orig/drivers/md/raid5.h linux-3.18.12/drivers/md/raid5.h +--- linux-3.18.12.orig/drivers/md/raid5.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/md/raid5.h 2015-04-26 13:32:22.395684003 -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.12.orig/drivers/misc/hwlat_detector.c linux-3.18.12/drivers/misc/hwlat_detector.c +--- linux-3.18.12.orig/drivers/misc/hwlat_detector.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.12/drivers/misc/hwlat_detector.c 2015-04-26 13:32:22.395684003 -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.12.orig/drivers/misc/Kconfig linux-3.18.12/drivers/misc/Kconfig +--- linux-3.18.12.orig/drivers/misc/Kconfig 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/misc/Kconfig 2015-04-26 13:32:22.395684003 -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.12.orig/drivers/misc/Makefile linux-3.18.12/drivers/misc/Makefile +--- linux-3.18.12.orig/drivers/misc/Makefile 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/misc/Makefile 2015-04-26 13:32:22.395684003 -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.12.orig/drivers/mmc/host/mmci.c linux-3.18.12/drivers/mmc/host/mmci.c +--- linux-3.18.12.orig/drivers/mmc/host/mmci.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/mmc/host/mmci.c 2015-04-26 13:32:22.395684003 -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.12.orig/drivers/mmc/host/sdhci.c linux-3.18.12/drivers/mmc/host/sdhci.c +--- linux-3.18.12.orig/drivers/mmc/host/sdhci.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/mmc/host/sdhci.c 2015-04-26 13:32:22.399684003 -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.12.orig/drivers/net/ethernet/3com/3c59x.c linux-3.18.12/drivers/net/ethernet/3com/3c59x.c +--- linux-3.18.12.orig/drivers/net/ethernet/3com/3c59x.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/net/ethernet/3com/3c59x.c 2015-04-26 13:32:22.399684003 -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.12.orig/drivers/net/ethernet/atheros/atl1c/atl1c_main.c linux-3.18.12/drivers/net/ethernet/atheros/atl1c/atl1c_main.c +--- linux-3.18.12.orig/drivers/net/ethernet/atheros/atl1c/atl1c_main.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/net/ethernet/atheros/atl1c/atl1c_main.c 2015-04-26 13:32:22.399684003 -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.12.orig/drivers/net/ethernet/atheros/atl1e/atl1e_main.c linux-3.18.12/drivers/net/ethernet/atheros/atl1e/atl1e_main.c +--- linux-3.18.12.orig/drivers/net/ethernet/atheros/atl1e/atl1e_main.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/net/ethernet/atheros/atl1e/atl1e_main.c 2015-04-26 13:32:22.399684003 -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.12.orig/drivers/net/ethernet/chelsio/cxgb/sge.c linux-3.18.12/drivers/net/ethernet/chelsio/cxgb/sge.c +--- linux-3.18.12.orig/drivers/net/ethernet/chelsio/cxgb/sge.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/net/ethernet/chelsio/cxgb/sge.c 2015-04-26 13:32:22.399684003 -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.12.orig/drivers/net/ethernet/freescale/gianfar.c linux-3.18.12/drivers/net/ethernet/freescale/gianfar.c +--- linux-3.18.12.orig/drivers/net/ethernet/freescale/gianfar.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/net/ethernet/freescale/gianfar.c 2015-04-26 13:32:22.399684003 -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.12.orig/drivers/net/ethernet/neterion/s2io.c linux-3.18.12/drivers/net/ethernet/neterion/s2io.c +--- linux-3.18.12.orig/drivers/net/ethernet/neterion/s2io.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/net/ethernet/neterion/s2io.c 2015-04-26 13:32:22.403684003 -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.12.orig/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c linux-3.18.12/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c +--- linux-3.18.12.orig/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c 2015-04-26 13:32:22.403684003 -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.12.orig/drivers/net/ethernet/realtek/8139too.c linux-3.18.12/drivers/net/ethernet/realtek/8139too.c +--- linux-3.18.12.orig/drivers/net/ethernet/realtek/8139too.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/net/ethernet/realtek/8139too.c 2015-04-26 13:32:22.403684003 -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.12.orig/drivers/net/ethernet/tehuti/tehuti.c linux-3.18.12/drivers/net/ethernet/tehuti/tehuti.c +--- linux-3.18.12.orig/drivers/net/ethernet/tehuti/tehuti.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/net/ethernet/tehuti/tehuti.c 2015-04-26 13:32:22.403684003 -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.12.orig/drivers/net/rionet.c linux-3.18.12/drivers/net/rionet.c +--- linux-3.18.12.orig/drivers/net/rionet.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/net/rionet.c 2015-04-26 13:32:22.403684003 -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.12.orig/drivers/net/wireless/orinoco/orinoco_usb.c linux-3.18.12/drivers/net/wireless/orinoco/orinoco_usb.c +--- linux-3.18.12.orig/drivers/net/wireless/orinoco/orinoco_usb.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/net/wireless/orinoco/orinoco_usb.c 2015-04-26 13:32:22.403684003 -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.12.orig/drivers/pci/access.c linux-3.18.12/drivers/pci/access.c +--- linux-3.18.12.orig/drivers/pci/access.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/pci/access.c 2015-04-26 13:32:22.403684003 -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.12.orig/drivers/scsi/fcoe/fcoe.c linux-3.18.12/drivers/scsi/fcoe/fcoe.c +--- linux-3.18.12.orig/drivers/scsi/fcoe/fcoe.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/scsi/fcoe/fcoe.c 2015-04-26 13:32:22.403684003 -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.12.orig/drivers/scsi/fcoe/fcoe_ctlr.c linux-3.18.12/drivers/scsi/fcoe/fcoe_ctlr.c +--- linux-3.18.12.orig/drivers/scsi/fcoe/fcoe_ctlr.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/scsi/fcoe/fcoe_ctlr.c 2015-04-26 13:32:22.403684003 -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.12.orig/drivers/scsi/libfc/fc_exch.c linux-3.18.12/drivers/scsi/libfc/fc_exch.c +--- linux-3.18.12.orig/drivers/scsi/libfc/fc_exch.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/scsi/libfc/fc_exch.c 2015-04-26 13:32:22.403684003 -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.12.orig/drivers/scsi/libsas/sas_ata.c linux-3.18.12/drivers/scsi/libsas/sas_ata.c +--- linux-3.18.12.orig/drivers/scsi/libsas/sas_ata.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/scsi/libsas/sas_ata.c 2015-04-26 13:32:22.407684003 -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.12.orig/drivers/scsi/qla2xxx/qla_inline.h linux-3.18.12/drivers/scsi/qla2xxx/qla_inline.h +--- linux-3.18.12.orig/drivers/scsi/qla2xxx/qla_inline.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/scsi/qla2xxx/qla_inline.h 2015-04-26 13:32:22.407684003 -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.12.orig/drivers/thermal/x86_pkg_temp_thermal.c linux-3.18.12/drivers/thermal/x86_pkg_temp_thermal.c +--- linux-3.18.12.orig/drivers/thermal/x86_pkg_temp_thermal.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/thermal/x86_pkg_temp_thermal.c 2015-04-26 13:32:22.407684003 -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.12.orig/drivers/tty/serial/8250/8250_core.c linux-3.18.12/drivers/tty/serial/8250/8250_core.c +--- linux-3.18.12.orig/drivers/tty/serial/8250/8250_core.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/tty/serial/8250/8250_core.c 2015-04-26 13:32:22.407684003 -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.12.orig/drivers/tty/serial/amba-pl011.c linux-3.18.12/drivers/tty/serial/amba-pl011.c +--- linux-3.18.12.orig/drivers/tty/serial/amba-pl011.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/tty/serial/amba-pl011.c 2015-04-26 13:32:22.407684003 -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.12.orig/drivers/tty/serial/omap-serial.c linux-3.18.12/drivers/tty/serial/omap-serial.c +--- linux-3.18.12.orig/drivers/tty/serial/omap-serial.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/tty/serial/omap-serial.c 2015-04-26 13:32:22.407684003 -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.12.orig/drivers/usb/core/hcd.c linux-3.18.12/drivers/usb/core/hcd.c +--- linux-3.18.12.orig/drivers/usb/core/hcd.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/usb/core/hcd.c 2015-04-26 13:32:22.407684003 -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.12.orig/drivers/usb/gadget/function/f_fs.c linux-3.18.12/drivers/usb/gadget/function/f_fs.c +--- linux-3.18.12.orig/drivers/usb/gadget/function/f_fs.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/usb/gadget/function/f_fs.c 2015-04-26 13:32:22.407684003 -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.12.orig/drivers/usb/gadget/legacy/inode.c linux-3.18.12/drivers/usb/gadget/legacy/inode.c +--- linux-3.18.12.orig/drivers/usb/gadget/legacy/inode.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/drivers/usb/gadget/legacy/inode.c 2015-04-26 13:32:22.407684003 -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.12.orig/fs/aio.c linux-3.18.12/fs/aio.c +--- linux-3.18.12.orig/fs/aio.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/fs/aio.c 2015-04-26 13:32:22.407684003 -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.12.orig/fs/autofs4/autofs_i.h linux-3.18.12/fs/autofs4/autofs_i.h +--- linux-3.18.12.orig/fs/autofs4/autofs_i.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/fs/autofs4/autofs_i.h 2015-04-26 13:32:22.411684003 -0500 +@@ -34,6 +34,7 @@ + #include + #include + #include ++#include + #include + #include + +diff -Nur linux-3.18.12.orig/fs/autofs4/expire.c linux-3.18.12/fs/autofs4/expire.c +--- linux-3.18.12.orig/fs/autofs4/expire.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/fs/autofs4/expire.c 2015-04-26 13:32:22.411684003 -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.12.orig/fs/buffer.c linux-3.18.12/fs/buffer.c +--- linux-3.18.12.orig/fs/buffer.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/fs/buffer.c 2015-04-26 13:32:22.411684003 -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.12.orig/fs/dcache.c linux-3.18.12/fs/dcache.c +--- linux-3.18.12.orig/fs/dcache.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/fs/dcache.c 2015-04-26 13:32:22.411684003 -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.12.orig/fs/eventpoll.c linux-3.18.12/fs/eventpoll.c +--- linux-3.18.12.orig/fs/eventpoll.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/fs/eventpoll.c 2015-04-26 13:32:22.411684003 -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.12.orig/fs/exec.c linux-3.18.12/fs/exec.c +--- linux-3.18.12.orig/fs/exec.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/fs/exec.c 2015-04-26 13:32:22.411684003 -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.12.orig/fs/jbd/checkpoint.c linux-3.18.12/fs/jbd/checkpoint.c +--- linux-3.18.12.orig/fs/jbd/checkpoint.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/fs/jbd/checkpoint.c 2015-04-26 13:32:22.411684003 -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.12.orig/fs/jbd2/checkpoint.c linux-3.18.12/fs/jbd2/checkpoint.c +--- linux-3.18.12.orig/fs/jbd2/checkpoint.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/fs/jbd2/checkpoint.c 2015-04-26 13:32:22.411684003 -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.12.orig/fs/namespace.c linux-3.18.12/fs/namespace.c +--- linux-3.18.12.orig/fs/namespace.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/fs/namespace.c 2015-04-26 13:32:22.411684003 -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.12.orig/fs/ntfs/aops.c linux-3.18.12/fs/ntfs/aops.c +--- linux-3.18.12.orig/fs/ntfs/aops.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/fs/ntfs/aops.c 2015-04-26 13:32:22.411684003 -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.12.orig/fs/timerfd.c linux-3.18.12/fs/timerfd.c +--- linux-3.18.12.orig/fs/timerfd.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/fs/timerfd.c 2015-04-26 13:32:22.411684003 -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.12.orig/include/acpi/platform/aclinux.h linux-3.18.12/include/acpi/platform/aclinux.h +--- linux-3.18.12.orig/include/acpi/platform/aclinux.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/acpi/platform/aclinux.h 2015-04-26 13:32:22.415684003 -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.12.orig/include/asm-generic/bug.h linux-3.18.12/include/asm-generic/bug.h +--- linux-3.18.12.orig/include/asm-generic/bug.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/asm-generic/bug.h 2015-04-26 13:32:22.415684003 -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.12.orig/include/linux/blkdev.h linux-3.18.12/include/linux/blkdev.h +--- linux-3.18.12.orig/include/linux/blkdev.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/blkdev.h 2015-04-26 13:32:22.415684003 -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.12.orig/include/linux/blk-mq.h linux-3.18.12/include/linux/blk-mq.h +--- linux-3.18.12.orig/include/linux/blk-mq.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/blk-mq.h 2015-04-26 13:32:22.415684003 -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.12.orig/include/linux/bottom_half.h linux-3.18.12/include/linux/bottom_half.h +--- linux-3.18.12.orig/include/linux/bottom_half.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/bottom_half.h 2015-04-26 13:32:22.415684003 -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.12.orig/include/linux/buffer_head.h linux-3.18.12/include/linux/buffer_head.h +--- linux-3.18.12.orig/include/linux/buffer_head.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/buffer_head.h 2015-04-26 13:32:22.415684003 -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.12.orig/include/linux/cgroup.h linux-3.18.12/include/linux/cgroup.h +--- linux-3.18.12.orig/include/linux/cgroup.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/cgroup.h 2015-04-26 13:32:22.415684003 -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.12.orig/include/linux/completion.h linux-3.18.12/include/linux/completion.h +--- linux-3.18.12.orig/include/linux/completion.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/completion.h 2015-04-26 13:32:22.415684003 -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.12.orig/include/linux/cpu.h linux-3.18.12/include/linux/cpu.h +--- linux-3.18.12.orig/include/linux/cpu.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/cpu.h 2015-04-26 13:32:22.415684003 -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.12.orig/include/linux/delay.h linux-3.18.12/include/linux/delay.h +--- linux-3.18.12.orig/include/linux/delay.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/delay.h 2015-04-26 13:32:22.415684003 -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.12.orig/include/linux/ftrace_event.h linux-3.18.12/include/linux/ftrace_event.h +--- linux-3.18.12.orig/include/linux/ftrace_event.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/ftrace_event.h 2015-04-26 13:32:22.415684003 -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.12.orig/include/linux/highmem.h linux-3.18.12/include/linux/highmem.h +--- linux-3.18.12.orig/include/linux/highmem.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/highmem.h 2015-04-26 13:32:22.415684003 -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.12.orig/include/linux/hrtimer.h linux-3.18.12/include/linux/hrtimer.h +--- linux-3.18.12.orig/include/linux/hrtimer.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/hrtimer.h 2015-04-26 13:32:22.415684003 -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.12.orig/include/linux/idr.h linux-3.18.12/include/linux/idr.h +--- linux-3.18.12.orig/include/linux/idr.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/idr.h 2015-04-26 13:32:22.415684003 -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.12.orig/include/linux/init_task.h linux-3.18.12/include/linux/init_task.h +--- linux-3.18.12.orig/include/linux/init_task.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/init_task.h 2015-04-26 13:32:22.415684003 -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.12.orig/include/linux/interrupt.h linux-3.18.12/include/linux/interrupt.h +--- linux-3.18.12.orig/include/linux/interrupt.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/interrupt.h 2015-04-26 13:32:22.415684003 -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.12.orig/include/linux/irqdesc.h linux-3.18.12/include/linux/irqdesc.h +--- linux-3.18.12.orig/include/linux/irqdesc.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/irqdesc.h 2015-04-26 13:32:22.415684003 -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.12.orig/include/linux/irqflags.h linux-3.18.12/include/linux/irqflags.h +--- linux-3.18.12.orig/include/linux/irqflags.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/irqflags.h 2015-04-26 13:32:22.415684003 -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.12.orig/include/linux/irq.h linux-3.18.12/include/linux/irq.h +--- linux-3.18.12.orig/include/linux/irq.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/irq.h 2015-04-26 13:32:22.415684003 -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.12.orig/include/linux/irq_work.h linux-3.18.12/include/linux/irq_work.h +--- linux-3.18.12.orig/include/linux/irq_work.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/irq_work.h 2015-04-26 13:32:22.415684003 -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.12.orig/include/linux/jbd_common.h linux-3.18.12/include/linux/jbd_common.h +--- linux-3.18.12.orig/include/linux/jbd_common.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/jbd_common.h 2015-04-26 13:32:22.415684003 -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.12.orig/include/linux/jump_label.h linux-3.18.12/include/linux/jump_label.h +--- linux-3.18.12.orig/include/linux/jump_label.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/jump_label.h 2015-04-26 13:32:22.419684003 -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.12.orig/include/linux/kdb.h linux-3.18.12/include/linux/kdb.h +--- linux-3.18.12.orig/include/linux/kdb.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/kdb.h 2015-04-26 13:32:22.419684003 -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.12.orig/include/linux/kernel.h linux-3.18.12/include/linux/kernel.h +--- linux-3.18.12.orig/include/linux/kernel.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/kernel.h 2015-04-26 13:32:22.419684003 -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.12.orig/include/linux/kvm_host.h linux-3.18.12/include/linux/kvm_host.h +--- linux-3.18.12.orig/include/linux/kvm_host.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/kvm_host.h 2015-04-26 13:32:22.419684003 -0500 +@@ -244,7 +244,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; +@@ -687,7 +687,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.12.orig/include/linux/lglock.h linux-3.18.12/include/linux/lglock.h +--- linux-3.18.12.orig/include/linux/lglock.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/lglock.h 2015-04-26 13:32:22.419684003 -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.12.orig/include/linux/list_bl.h linux-3.18.12/include/linux/list_bl.h +--- linux-3.18.12.orig/include/linux/list_bl.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/list_bl.h 2015-04-26 13:32:22.419684003 -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.12.orig/include/linux/locallock.h linux-3.18.12/include/linux/locallock.h +--- linux-3.18.12.orig/include/linux/locallock.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.12/include/linux/locallock.h 2015-04-26 13:32:22.419684003 -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.12.orig/include/linux/mm_types.h linux-3.18.12/include/linux/mm_types.h +--- linux-3.18.12.orig/include/linux/mm_types.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/mm_types.h 2015-04-26 13:32:22.419684003 -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.12.orig/include/linux/mutex.h linux-3.18.12/include/linux/mutex.h +--- linux-3.18.12.orig/include/linux/mutex.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/mutex.h 2015-04-26 13:32:22.419684003 -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.12.orig/include/linux/mutex_rt.h linux-3.18.12/include/linux/mutex_rt.h +--- linux-3.18.12.orig/include/linux/mutex_rt.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.12/include/linux/mutex_rt.h 2015-04-26 13:32:22.419684003 -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.12.orig/include/linux/netdevice.h linux-3.18.12/include/linux/netdevice.h +--- linux-3.18.12.orig/include/linux/netdevice.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/netdevice.h 2015-04-26 13:32:22.419684003 -0500 +@@ -2345,6 +2345,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.12.orig/include/linux/netfilter/x_tables.h linux-3.18.12/include/linux/netfilter/x_tables.h +--- linux-3.18.12.orig/include/linux/netfilter/x_tables.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/netfilter/x_tables.h 2015-04-26 13:32:22.419684003 -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.12.orig/include/linux/notifier.h linux-3.18.12/include/linux/notifier.h +--- linux-3.18.12.orig/include/linux/notifier.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/notifier.h 2015-04-26 13:32:22.419684003 -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.12.orig/include/linux/percpu.h linux-3.18.12/include/linux/percpu.h +--- linux-3.18.12.orig/include/linux/percpu.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/percpu.h 2015-04-26 13:32:22.419684003 -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.12.orig/include/linux/pid.h linux-3.18.12/include/linux/pid.h +--- linux-3.18.12.orig/include/linux/pid.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/pid.h 2015-04-26 13:32:22.419684003 -0500 +@@ -2,6 +2,7 @@ + #define _LINUX_PID_H + + #include ++#include + + enum pid_type + { +diff -Nur linux-3.18.12.orig/include/linux/preempt.h linux-3.18.12/include/linux/preempt.h +--- linux-3.18.12.orig/include/linux/preempt.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/preempt.h 2015-04-26 13:32:22.419684003 -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.12.orig/include/linux/preempt_mask.h linux-3.18.12/include/linux/preempt_mask.h +--- linux-3.18.12.orig/include/linux/preempt_mask.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/preempt_mask.h 2015-04-26 13:32:22.419684003 -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.12.orig/include/linux/printk.h linux-3.18.12/include/linux/printk.h +--- linux-3.18.12.orig/include/linux/printk.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/printk.h 2015-04-26 13:32:22.419684003 -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.12.orig/include/linux/radix-tree.h linux-3.18.12/include/linux/radix-tree.h +--- linux-3.18.12.orig/include/linux/radix-tree.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/radix-tree.h 2015-04-26 13:32:22.419684003 -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.12.orig/include/linux/random.h linux-3.18.12/include/linux/random.h +--- linux-3.18.12.orig/include/linux/random.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/random.h 2015-04-26 13:32:22.423684003 -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.12.orig/include/linux/rcupdate.h linux-3.18.12/include/linux/rcupdate.h +--- linux-3.18.12.orig/include/linux/rcupdate.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/rcupdate.h 2015-04-26 13:32:22.423684003 -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.12.orig/include/linux/rcutree.h linux-3.18.12/include/linux/rcutree.h +--- linux-3.18.12.orig/include/linux/rcutree.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/rcutree.h 2015-04-26 13:32:22.423684003 -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.12.orig/include/linux/rtmutex.h linux-3.18.12/include/linux/rtmutex.h +--- linux-3.18.12.orig/include/linux/rtmutex.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/rtmutex.h 2015-04-26 13:32:22.423684003 -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.12.orig/include/linux/rwlock_rt.h linux-3.18.12/include/linux/rwlock_rt.h +--- linux-3.18.12.orig/include/linux/rwlock_rt.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.12/include/linux/rwlock_rt.h 2015-04-26 13:32:22.423684003 -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.12.orig/include/linux/rwlock_types.h linux-3.18.12/include/linux/rwlock_types.h +--- linux-3.18.12.orig/include/linux/rwlock_types.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/rwlock_types.h 2015-04-26 13:32:22.423684003 -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.12.orig/include/linux/rwlock_types_rt.h linux-3.18.12/include/linux/rwlock_types_rt.h +--- linux-3.18.12.orig/include/linux/rwlock_types_rt.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.12/include/linux/rwlock_types_rt.h 2015-04-26 13:32:22.423684003 -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.12.orig/include/linux/rwsem.h linux-3.18.12/include/linux/rwsem.h +--- linux-3.18.12.orig/include/linux/rwsem.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/rwsem.h 2015-04-26 13:32:22.423684003 -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.12.orig/include/linux/rwsem_rt.h linux-3.18.12/include/linux/rwsem_rt.h +--- linux-3.18.12.orig/include/linux/rwsem_rt.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.12/include/linux/rwsem_rt.h 2015-04-26 13:32:22.423684003 -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.12.orig/include/linux/sched.h linux-3.18.12/include/linux/sched.h +--- linux-3.18.12.orig/include/linux/sched.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/sched.h 2015-04-26 13:32:22.423684003 -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.12.orig/include/linux/seqlock.h linux-3.18.12/include/linux/seqlock.h +--- linux-3.18.12.orig/include/linux/seqlock.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/seqlock.h 2015-04-26 13:32:22.423684003 -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.12.orig/include/linux/signal.h linux-3.18.12/include/linux/signal.h +--- linux-3.18.12.orig/include/linux/signal.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/signal.h 2015-04-26 13:32:22.423684003 -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.12.orig/include/linux/skbuff.h linux-3.18.12/include/linux/skbuff.h +--- linux-3.18.12.orig/include/linux/skbuff.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/skbuff.h 2015-04-26 13:32:22.423684003 -0500 +@@ -172,6 +172,7 @@ + + __u32 qlen; + spinlock_t lock; ++ raw_spinlock_t raw_lock; + }; + + struct sk_buff; +@@ -1327,6 +1328,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.12.orig/include/linux/smp.h linux-3.18.12/include/linux/smp.h +--- linux-3.18.12.orig/include/linux/smp.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/smp.h 2015-04-26 13:32:22.423684003 -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.12.orig/include/linux/spinlock_api_smp.h linux-3.18.12/include/linux/spinlock_api_smp.h +--- linux-3.18.12.orig/include/linux/spinlock_api_smp.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/spinlock_api_smp.h 2015-04-26 13:32:22.423684003 -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.12.orig/include/linux/spinlock.h linux-3.18.12/include/linux/spinlock.h +--- linux-3.18.12.orig/include/linux/spinlock.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/spinlock.h 2015-04-26 13:32:22.423684003 -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.12.orig/include/linux/spinlock_rt.h linux-3.18.12/include/linux/spinlock_rt.h +--- linux-3.18.12.orig/include/linux/spinlock_rt.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.12/include/linux/spinlock_rt.h 2015-04-26 13:32:22.423684003 -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.12.orig/include/linux/spinlock_types.h linux-3.18.12/include/linux/spinlock_types.h +--- linux-3.18.12.orig/include/linux/spinlock_types.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/spinlock_types.h 2015-04-26 13:32:22.423684003 -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.12.orig/include/linux/spinlock_types_nort.h linux-3.18.12/include/linux/spinlock_types_nort.h +--- linux-3.18.12.orig/include/linux/spinlock_types_nort.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.12/include/linux/spinlock_types_nort.h 2015-04-26 13:32:22.423684003 -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.12.orig/include/linux/spinlock_types_raw.h linux-3.18.12/include/linux/spinlock_types_raw.h +--- linux-3.18.12.orig/include/linux/spinlock_types_raw.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.12/include/linux/spinlock_types_raw.h 2015-04-26 13:32:22.423684003 -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.12.orig/include/linux/spinlock_types_rt.h linux-3.18.12/include/linux/spinlock_types_rt.h +--- linux-3.18.12.orig/include/linux/spinlock_types_rt.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.12/include/linux/spinlock_types_rt.h 2015-04-26 13:32:22.423684003 -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.12.orig/include/linux/srcu.h linux-3.18.12/include/linux/srcu.h +--- linux-3.18.12.orig/include/linux/srcu.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/srcu.h 2015-04-26 13:32:22.427684003 -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.12.orig/include/linux/swap.h linux-3.18.12/include/linux/swap.h +--- linux-3.18.12.orig/include/linux/swap.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/swap.h 2015-04-26 13:32:22.427684003 -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.12.orig/include/linux/sysctl.h linux-3.18.12/include/linux/sysctl.h +--- linux-3.18.12.orig/include/linux/sysctl.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/sysctl.h 2015-04-26 13:32:22.427684003 -0500 +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + #include + + /* For the /proc/sys support */ +diff -Nur linux-3.18.12.orig/include/linux/thread_info.h linux-3.18.12/include/linux/thread_info.h +--- linux-3.18.12.orig/include/linux/thread_info.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/thread_info.h 2015-04-26 13:32:22.427684003 -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.12.orig/include/linux/timer.h linux-3.18.12/include/linux/timer.h +--- linux-3.18.12.orig/include/linux/timer.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/timer.h 2015-04-26 13:32:22.427684003 -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.12.orig/include/linux/uaccess.h linux-3.18.12/include/linux/uaccess.h +--- linux-3.18.12.orig/include/linux/uaccess.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/uaccess.h 2015-04-26 13:32:22.427684003 -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.12.orig/include/linux/uprobes.h linux-3.18.12/include/linux/uprobes.h +--- linux-3.18.12.orig/include/linux/uprobes.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/uprobes.h 2015-04-26 13:32:22.427684003 -0500 +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + + struct vm_area_struct; + struct mm_struct; +diff -Nur linux-3.18.12.orig/include/linux/vmstat.h linux-3.18.12/include/linux/vmstat.h +--- linux-3.18.12.orig/include/linux/vmstat.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/vmstat.h 2015-04-26 13:32:22.427684003 -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.12.orig/include/linux/wait.h linux-3.18.12/include/linux/wait.h +--- linux-3.18.12.orig/include/linux/wait.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/linux/wait.h 2015-04-26 13:32:22.427684003 -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.12.orig/include/linux/wait-simple.h linux-3.18.12/include/linux/wait-simple.h +--- linux-3.18.12.orig/include/linux/wait-simple.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.12/include/linux/wait-simple.h 2015-04-26 13:32:22.427684003 -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.12.orig/include/linux/work-simple.h linux-3.18.12/include/linux/work-simple.h +--- linux-3.18.12.orig/include/linux/work-simple.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.12/include/linux/work-simple.h 2015-04-26 13:32:22.427684003 -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.12.orig/include/net/dst.h linux-3.18.12/include/net/dst.h +--- linux-3.18.12.orig/include/net/dst.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/net/dst.h 2015-04-26 13:32:22.427684003 -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.12.orig/include/net/neighbour.h linux-3.18.12/include/net/neighbour.h +--- linux-3.18.12.orig/include/net/neighbour.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/net/neighbour.h 2015-04-26 13:32:22.427684003 -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.12.orig/include/net/netns/ipv4.h linux-3.18.12/include/net/netns/ipv4.h +--- linux-3.18.12.orig/include/net/netns/ipv4.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/include/net/netns/ipv4.h 2015-04-26 13:32:22.427684003 -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.12.orig/include/trace/events/hist.h linux-3.18.12/include/trace/events/hist.h +--- linux-3.18.12.orig/include/trace/events/hist.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.12/include/trace/events/hist.h 2015-04-26 13:32:22.427684003 -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.12.orig/include/trace/events/latency_hist.h linux-3.18.12/include/trace/events/latency_hist.h +--- linux-3.18.12.orig/include/trace/events/latency_hist.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.12/include/trace/events/latency_hist.h 2015-04-26 13:32:22.427684003 -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.12.orig/init/Kconfig linux-3.18.12/init/Kconfig +--- linux-3.18.12.orig/init/Kconfig 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/init/Kconfig 2015-04-26 13:32:22.427684003 -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.12.orig/init/main.c linux-3.18.12/init/main.c +--- linux-3.18.12.orig/init/main.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/init/main.c 2015-04-26 13:32:22.427684003 -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.12.orig/init/Makefile linux-3.18.12/init/Makefile +--- linux-3.18.12.orig/init/Makefile 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/init/Makefile 2015-04-26 13:32:22.427684003 -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.12.orig/ipc/mqueue.c linux-3.18.12/ipc/mqueue.c +--- linux-3.18.12.orig/ipc/mqueue.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/ipc/mqueue.c 2015-04-26 13:32:22.427684003 -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.12.orig/ipc/msg.c linux-3.18.12/ipc/msg.c +--- linux-3.18.12.orig/ipc/msg.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/ipc/msg.c 2015-04-26 13:32:22.427684003 -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.12.orig/ipc/sem.c linux-3.18.12/ipc/sem.c +--- linux-3.18.12.orig/ipc/sem.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/ipc/sem.c 2015-04-26 13:32:22.431684003 -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.12.orig/kernel/cgroup.c linux-3.18.12/kernel/cgroup.c +--- linux-3.18.12.orig/kernel/cgroup.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/cgroup.c 2015-04-26 13:32:22.431684003 -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.12.orig/kernel/cpu.c linux-3.18.12/kernel/cpu.c +--- linux-3.18.12.orig/kernel/cpu.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/cpu.c 2015-04-26 13:32:22.431684003 -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.12.orig/kernel/debug/kdb/kdb_io.c linux-3.18.12/kernel/debug/kdb/kdb_io.c +--- linux-3.18.12.orig/kernel/debug/kdb/kdb_io.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/debug/kdb/kdb_io.c 2015-04-26 13:32:22.431684003 -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.12.orig/kernel/events/core.c linux-3.18.12/kernel/events/core.c +--- linux-3.18.12.orig/kernel/events/core.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/events/core.c 2015-04-26 13:32:22.431684003 -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.12.orig/kernel/events/core.c.orig linux-3.18.12/kernel/events/core.c.orig +--- linux-3.18.12.orig/kernel/events/core.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.12/kernel/events/core.c.orig 2015-04-20 14:48:02.000000000 -0500 +@@ -0,0 +1,8339 @@ ++/* ++ * Performance events core code: ++ * ++ * Copyright (C) 2008 Thomas Gleixner ++ * Copyright (C) 2008-2011 Red Hat, Inc., Ingo Molnar ++ * Copyright (C) 2008-2011 Red Hat, Inc., Peter Zijlstra ++ * Copyright © 2009 Paul Mackerras, IBM Corp. ++ * ++ * For licensing details see kernel-base/COPYING ++ */ ++ ++#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 ++#include ++ ++#include "internal.h" ++ ++#include ++ ++static struct workqueue_struct *perf_wq; ++ ++struct remote_function_call { ++ struct task_struct *p; ++ int (*func)(void *info); ++ void *info; ++ int ret; ++}; ++ ++static void remote_function(void *data) ++{ ++ struct remote_function_call *tfc = data; ++ struct task_struct *p = tfc->p; ++ ++ if (p) { ++ tfc->ret = -EAGAIN; ++ if (task_cpu(p) != smp_processor_id() || !task_curr(p)) ++ return; ++ } ++ ++ tfc->ret = tfc->func(tfc->info); ++} ++ ++/** ++ * task_function_call - call a function on the cpu on which a task runs ++ * @p: the task to evaluate ++ * @func: the function to be called ++ * @info: the function call argument ++ * ++ * Calls the function @func when the task is currently running. This might ++ * be on the current CPU, which just calls the function directly ++ * ++ * returns: @func return value, or ++ * -ESRCH - when the process isn't running ++ * -EAGAIN - when the process moved away ++ */ ++static int ++task_function_call(struct task_struct *p, int (*func) (void *info), void *info) ++{ ++ struct remote_function_call data = { ++ .p = p, ++ .func = func, ++ .info = info, ++ .ret = -ESRCH, /* No such (running) process */ ++ }; ++ ++ if (task_curr(p)) ++ smp_call_function_single(task_cpu(p), remote_function, &data, 1); ++ ++ return data.ret; ++} ++ ++/** ++ * cpu_function_call - call a function on the cpu ++ * @func: the function to be called ++ * @info: the function call argument ++ * ++ * Calls the function @func on the remote cpu. ++ * ++ * returns: @func return value or -ENXIO when the cpu is offline ++ */ ++static int cpu_function_call(int cpu, int (*func) (void *info), void *info) ++{ ++ struct remote_function_call data = { ++ .p = NULL, ++ .func = func, ++ .info = info, ++ .ret = -ENXIO, /* No such CPU */ ++ }; ++ ++ smp_call_function_single(cpu, remote_function, &data, 1); ++ ++ return data.ret; ++} ++ ++#define EVENT_OWNER_KERNEL ((void *) -1) ++ ++static bool is_kernel_event(struct perf_event *event) ++{ ++ return event->owner == EVENT_OWNER_KERNEL; ++} ++ ++#define PERF_FLAG_ALL (PERF_FLAG_FD_NO_GROUP |\ ++ PERF_FLAG_FD_OUTPUT |\ ++ PERF_FLAG_PID_CGROUP |\ ++ PERF_FLAG_FD_CLOEXEC) ++ ++/* ++ * branch priv levels that need permission checks ++ */ ++#define PERF_SAMPLE_BRANCH_PERM_PLM \ ++ (PERF_SAMPLE_BRANCH_KERNEL |\ ++ PERF_SAMPLE_BRANCH_HV) ++ ++enum event_type_t { ++ EVENT_FLEXIBLE = 0x1, ++ EVENT_PINNED = 0x2, ++ EVENT_ALL = EVENT_FLEXIBLE | EVENT_PINNED, ++}; ++ ++/* ++ * perf_sched_events : >0 events exist ++ * perf_cgroup_events: >0 per-cpu cgroup events exist on this cpu ++ */ ++struct static_key_deferred perf_sched_events __read_mostly; ++static DEFINE_PER_CPU(atomic_t, perf_cgroup_events); ++static DEFINE_PER_CPU(atomic_t, perf_branch_stack_events); ++ ++static atomic_t nr_mmap_events __read_mostly; ++static atomic_t nr_comm_events __read_mostly; ++static atomic_t nr_task_events __read_mostly; ++static atomic_t nr_freq_events __read_mostly; ++ ++static LIST_HEAD(pmus); ++static DEFINE_MUTEX(pmus_lock); ++static struct srcu_struct pmus_srcu; ++ ++/* ++ * perf event paranoia level: ++ * -1 - not paranoid at all ++ * 0 - disallow raw tracepoint access for unpriv ++ * 1 - disallow cpu events for unpriv ++ * 2 - disallow kernel profiling for unpriv ++ */ ++int sysctl_perf_event_paranoid __read_mostly = 1; ++ ++/* Minimum for 512 kiB + 1 user control page */ ++int sysctl_perf_event_mlock __read_mostly = 512 + (PAGE_SIZE / 1024); /* 'free' kiB per user */ ++ ++/* ++ * max perf event sample rate ++ */ ++#define DEFAULT_MAX_SAMPLE_RATE 100000 ++#define DEFAULT_SAMPLE_PERIOD_NS (NSEC_PER_SEC / DEFAULT_MAX_SAMPLE_RATE) ++#define DEFAULT_CPU_TIME_MAX_PERCENT 25 ++ ++int sysctl_perf_event_sample_rate __read_mostly = DEFAULT_MAX_SAMPLE_RATE; ++ ++static int max_samples_per_tick __read_mostly = DIV_ROUND_UP(DEFAULT_MAX_SAMPLE_RATE, HZ); ++static int perf_sample_period_ns __read_mostly = DEFAULT_SAMPLE_PERIOD_NS; ++ ++static int perf_sample_allowed_ns __read_mostly = ++ DEFAULT_SAMPLE_PERIOD_NS * DEFAULT_CPU_TIME_MAX_PERCENT / 100; ++ ++void update_perf_cpu_limits(void) ++{ ++ u64 tmp = perf_sample_period_ns; ++ ++ tmp *= sysctl_perf_cpu_time_max_percent; ++ do_div(tmp, 100); ++ ACCESS_ONCE(perf_sample_allowed_ns) = tmp; ++} ++ ++static int perf_rotate_context(struct perf_cpu_context *cpuctx); ++ ++int perf_proc_update_handler(struct ctl_table *table, int write, ++ void __user *buffer, size_t *lenp, ++ loff_t *ppos) ++{ ++ int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); ++ ++ if (ret || !write) ++ return ret; ++ ++ max_samples_per_tick = DIV_ROUND_UP(sysctl_perf_event_sample_rate, HZ); ++ perf_sample_period_ns = NSEC_PER_SEC / sysctl_perf_event_sample_rate; ++ update_perf_cpu_limits(); ++ ++ return 0; ++} ++ ++int sysctl_perf_cpu_time_max_percent __read_mostly = DEFAULT_CPU_TIME_MAX_PERCENT; ++ ++int perf_cpu_time_max_percent_handler(struct ctl_table *table, int write, ++ void __user *buffer, size_t *lenp, ++ loff_t *ppos) ++{ ++ int ret = proc_dointvec(table, write, buffer, lenp, ppos); ++ ++ if (ret || !write) ++ return ret; ++ ++ update_perf_cpu_limits(); ++ ++ return 0; ++} ++ ++/* ++ * perf samples are done in some very critical code paths (NMIs). ++ * If they take too much CPU time, the system can lock up and not ++ * get any real work done. This will drop the sample rate when ++ * we detect that events are taking too long. ++ */ ++#define NR_ACCUMULATED_SAMPLES 128 ++static DEFINE_PER_CPU(u64, running_sample_length); ++ ++static void perf_duration_warn(struct irq_work *w) ++{ ++ u64 allowed_ns = ACCESS_ONCE(perf_sample_allowed_ns); ++ u64 avg_local_sample_len; ++ u64 local_samples_len; ++ ++ local_samples_len = __this_cpu_read(running_sample_length); ++ avg_local_sample_len = local_samples_len/NR_ACCUMULATED_SAMPLES; ++ ++ printk_ratelimited(KERN_WARNING ++ "perf interrupt took too long (%lld > %lld), lowering " ++ "kernel.perf_event_max_sample_rate to %d\n", ++ avg_local_sample_len, allowed_ns >> 1, ++ sysctl_perf_event_sample_rate); ++} ++ ++static DEFINE_IRQ_WORK(perf_duration_work, perf_duration_warn); ++ ++void perf_sample_event_took(u64 sample_len_ns) ++{ ++ u64 allowed_ns = ACCESS_ONCE(perf_sample_allowed_ns); ++ u64 avg_local_sample_len; ++ u64 local_samples_len; ++ ++ if (allowed_ns == 0) ++ return; ++ ++ /* decay the counter by 1 average sample */ ++ local_samples_len = __this_cpu_read(running_sample_length); ++ local_samples_len -= local_samples_len/NR_ACCUMULATED_SAMPLES; ++ local_samples_len += sample_len_ns; ++ __this_cpu_write(running_sample_length, local_samples_len); ++ ++ /* ++ * note: this will be biased artifically low until we have ++ * seen NR_ACCUMULATED_SAMPLES. Doing it this way keeps us ++ * from having to maintain a count. ++ */ ++ avg_local_sample_len = local_samples_len/NR_ACCUMULATED_SAMPLES; ++ ++ if (avg_local_sample_len <= allowed_ns) ++ return; ++ ++ if (max_samples_per_tick <= 1) ++ return; ++ ++ max_samples_per_tick = DIV_ROUND_UP(max_samples_per_tick, 2); ++ sysctl_perf_event_sample_rate = max_samples_per_tick * HZ; ++ perf_sample_period_ns = NSEC_PER_SEC / sysctl_perf_event_sample_rate; ++ ++ update_perf_cpu_limits(); ++ ++ if (!irq_work_queue(&perf_duration_work)) { ++ early_printk("perf interrupt took too long (%lld > %lld), lowering " ++ "kernel.perf_event_max_sample_rate to %d\n", ++ avg_local_sample_len, allowed_ns >> 1, ++ sysctl_perf_event_sample_rate); ++ } ++} ++ ++static atomic64_t perf_event_id; ++ ++static void cpu_ctx_sched_out(struct perf_cpu_context *cpuctx, ++ enum event_type_t event_type); ++ ++static void cpu_ctx_sched_in(struct perf_cpu_context *cpuctx, ++ enum event_type_t event_type, ++ struct task_struct *task); ++ ++static void update_context_time(struct perf_event_context *ctx); ++static u64 perf_event_time(struct perf_event *event); ++ ++void __weak perf_event_print_debug(void) { } ++ ++extern __weak const char *perf_pmu_name(void) ++{ ++ return "pmu"; ++} ++ ++static inline u64 perf_clock(void) ++{ ++ return local_clock(); ++} ++ ++static inline struct perf_cpu_context * ++__get_cpu_context(struct perf_event_context *ctx) ++{ ++ return this_cpu_ptr(ctx->pmu->pmu_cpu_context); ++} ++ ++static void perf_ctx_lock(struct perf_cpu_context *cpuctx, ++ struct perf_event_context *ctx) ++{ ++ raw_spin_lock(&cpuctx->ctx.lock); ++ if (ctx) ++ raw_spin_lock(&ctx->lock); ++} ++ ++static void perf_ctx_unlock(struct perf_cpu_context *cpuctx, ++ struct perf_event_context *ctx) ++{ ++ if (ctx) ++ raw_spin_unlock(&ctx->lock); ++ raw_spin_unlock(&cpuctx->ctx.lock); ++} ++ ++#ifdef CONFIG_CGROUP_PERF ++ ++/* ++ * perf_cgroup_info keeps track of time_enabled for a cgroup. ++ * This is a per-cpu dynamically allocated data structure. ++ */ ++struct perf_cgroup_info { ++ u64 time; ++ u64 timestamp; ++}; ++ ++struct perf_cgroup { ++ struct cgroup_subsys_state css; ++ struct perf_cgroup_info __percpu *info; ++}; ++ ++/* ++ * Must ensure cgroup is pinned (css_get) before calling ++ * this function. In other words, we cannot call this function ++ * if there is no cgroup event for the current CPU context. ++ */ ++static inline struct perf_cgroup * ++perf_cgroup_from_task(struct task_struct *task) ++{ ++ return container_of(task_css(task, perf_event_cgrp_id), ++ struct perf_cgroup, css); ++} ++ ++static inline bool ++perf_cgroup_match(struct perf_event *event) ++{ ++ struct perf_event_context *ctx = event->ctx; ++ struct perf_cpu_context *cpuctx = __get_cpu_context(ctx); ++ ++ /* @event doesn't care about cgroup */ ++ if (!event->cgrp) ++ return true; ++ ++ /* wants specific cgroup scope but @cpuctx isn't associated with any */ ++ if (!cpuctx->cgrp) ++ return false; ++ ++ /* ++ * Cgroup scoping is recursive. An event enabled for a cgroup is ++ * also enabled for all its descendant cgroups. If @cpuctx's ++ * cgroup is a descendant of @event's (the test covers identity ++ * case), it's a match. ++ */ ++ return cgroup_is_descendant(cpuctx->cgrp->css.cgroup, ++ event->cgrp->css.cgroup); ++} ++ ++static inline void perf_detach_cgroup(struct perf_event *event) ++{ ++ css_put(&event->cgrp->css); ++ event->cgrp = NULL; ++} ++ ++static inline int is_cgroup_event(struct perf_event *event) ++{ ++ return event->cgrp != NULL; ++} ++ ++static inline u64 perf_cgroup_event_time(struct perf_event *event) ++{ ++ struct perf_cgroup_info *t; ++ ++ t = per_cpu_ptr(event->cgrp->info, event->cpu); ++ return t->time; ++} ++ ++static inline void __update_cgrp_time(struct perf_cgroup *cgrp) ++{ ++ struct perf_cgroup_info *info; ++ u64 now; ++ ++ now = perf_clock(); ++ ++ info = this_cpu_ptr(cgrp->info); ++ ++ info->time += now - info->timestamp; ++ info->timestamp = now; ++} ++ ++static inline void update_cgrp_time_from_cpuctx(struct perf_cpu_context *cpuctx) ++{ ++ struct perf_cgroup *cgrp_out = cpuctx->cgrp; ++ if (cgrp_out) ++ __update_cgrp_time(cgrp_out); ++} ++ ++static inline void update_cgrp_time_from_event(struct perf_event *event) ++{ ++ struct perf_cgroup *cgrp; ++ ++ /* ++ * ensure we access cgroup data only when needed and ++ * when we know the cgroup is pinned (css_get) ++ */ ++ if (!is_cgroup_event(event)) ++ return; ++ ++ cgrp = perf_cgroup_from_task(current); ++ /* ++ * Do not update time when cgroup is not active ++ */ ++ if (cgrp == event->cgrp) ++ __update_cgrp_time(event->cgrp); ++} ++ ++static inline void ++perf_cgroup_set_timestamp(struct task_struct *task, ++ struct perf_event_context *ctx) ++{ ++ struct perf_cgroup *cgrp; ++ struct perf_cgroup_info *info; ++ ++ /* ++ * ctx->lock held by caller ++ * ensure we do not access cgroup data ++ * unless we have the cgroup pinned (css_get) ++ */ ++ if (!task || !ctx->nr_cgroups) ++ return; ++ ++ cgrp = perf_cgroup_from_task(task); ++ info = this_cpu_ptr(cgrp->info); ++ info->timestamp = ctx->timestamp; ++} ++ ++#define PERF_CGROUP_SWOUT 0x1 /* cgroup switch out every event */ ++#define PERF_CGROUP_SWIN 0x2 /* cgroup switch in events based on task */ ++ ++/* ++ * reschedule events based on the cgroup constraint of task. ++ * ++ * mode SWOUT : schedule out everything ++ * mode SWIN : schedule in based on cgroup for next ++ */ ++void perf_cgroup_switch(struct task_struct *task, int mode) ++{ ++ struct perf_cpu_context *cpuctx; ++ struct pmu *pmu; ++ unsigned long flags; ++ ++ /* ++ * disable interrupts to avoid geting nr_cgroup ++ * changes via __perf_event_disable(). Also ++ * avoids preemption. ++ */ ++ local_irq_save(flags); ++ ++ /* ++ * we reschedule only in the presence of cgroup ++ * constrained events. ++ */ ++ rcu_read_lock(); ++ ++ list_for_each_entry_rcu(pmu, &pmus, entry) { ++ cpuctx = this_cpu_ptr(pmu->pmu_cpu_context); ++ if (cpuctx->unique_pmu != pmu) ++ continue; /* ensure we process each cpuctx once */ ++ ++ /* ++ * perf_cgroup_events says at least one ++ * context on this CPU has cgroup events. ++ * ++ * ctx->nr_cgroups reports the number of cgroup ++ * events for a context. ++ */ ++ if (cpuctx->ctx.nr_cgroups > 0) { ++ perf_ctx_lock(cpuctx, cpuctx->task_ctx); ++ perf_pmu_disable(cpuctx->ctx.pmu); ++ ++ if (mode & PERF_CGROUP_SWOUT) { ++ cpu_ctx_sched_out(cpuctx, EVENT_ALL); ++ /* ++ * must not be done before ctxswout due ++ * to event_filter_match() in event_sched_out() ++ */ ++ cpuctx->cgrp = NULL; ++ } ++ ++ if (mode & PERF_CGROUP_SWIN) { ++ WARN_ON_ONCE(cpuctx->cgrp); ++ /* ++ * set cgrp before ctxsw in to allow ++ * event_filter_match() to not have to pass ++ * task around ++ */ ++ cpuctx->cgrp = perf_cgroup_from_task(task); ++ cpu_ctx_sched_in(cpuctx, EVENT_ALL, task); ++ } ++ perf_pmu_enable(cpuctx->ctx.pmu); ++ perf_ctx_unlock(cpuctx, cpuctx->task_ctx); ++ } ++ } ++ ++ rcu_read_unlock(); ++ ++ local_irq_restore(flags); ++} ++ ++static inline void perf_cgroup_sched_out(struct task_struct *task, ++ struct task_struct *next) ++{ ++ struct perf_cgroup *cgrp1; ++ struct perf_cgroup *cgrp2 = NULL; ++ ++ /* ++ * we come here when we know perf_cgroup_events > 0 ++ */ ++ cgrp1 = perf_cgroup_from_task(task); ++ ++ /* ++ * next is NULL when called from perf_event_enable_on_exec() ++ * that will systematically cause a cgroup_switch() ++ */ ++ if (next) ++ cgrp2 = perf_cgroup_from_task(next); ++ ++ /* ++ * only schedule out current cgroup events if we know ++ * that we are switching to a different cgroup. Otherwise, ++ * do no touch the cgroup events. ++ */ ++ if (cgrp1 != cgrp2) ++ perf_cgroup_switch(task, PERF_CGROUP_SWOUT); ++} ++ ++static inline void perf_cgroup_sched_in(struct task_struct *prev, ++ struct task_struct *task) ++{ ++ struct perf_cgroup *cgrp1; ++ struct perf_cgroup *cgrp2 = NULL; ++ ++ /* ++ * we come here when we know perf_cgroup_events > 0 ++ */ ++ cgrp1 = perf_cgroup_from_task(task); ++ ++ /* prev can never be NULL */ ++ cgrp2 = perf_cgroup_from_task(prev); ++ ++ /* ++ * only need to schedule in cgroup events if we are changing ++ * cgroup during ctxsw. Cgroup events were not scheduled ++ * out of ctxsw out if that was not the case. ++ */ ++ if (cgrp1 != cgrp2) ++ perf_cgroup_switch(task, PERF_CGROUP_SWIN); ++} ++ ++static inline int perf_cgroup_connect(int fd, struct perf_event *event, ++ struct perf_event_attr *attr, ++ struct perf_event *group_leader) ++{ ++ struct perf_cgroup *cgrp; ++ struct cgroup_subsys_state *css; ++ struct fd f = fdget(fd); ++ int ret = 0; ++ ++ if (!f.file) ++ return -EBADF; ++ ++ css = css_tryget_online_from_dir(f.file->f_dentry, ++ &perf_event_cgrp_subsys); ++ if (IS_ERR(css)) { ++ ret = PTR_ERR(css); ++ goto out; ++ } ++ ++ cgrp = container_of(css, struct perf_cgroup, css); ++ event->cgrp = cgrp; ++ ++ /* ++ * all events in a group must monitor ++ * the same cgroup because a task belongs ++ * to only one perf cgroup at a time ++ */ ++ if (group_leader && group_leader->cgrp != cgrp) { ++ perf_detach_cgroup(event); ++ ret = -EINVAL; ++ } ++out: ++ fdput(f); ++ return ret; ++} ++ ++static inline void ++perf_cgroup_set_shadow_time(struct perf_event *event, u64 now) ++{ ++ struct perf_cgroup_info *t; ++ t = per_cpu_ptr(event->cgrp->info, event->cpu); ++ event->shadow_ctx_time = now - t->timestamp; ++} ++ ++static inline void ++perf_cgroup_defer_enabled(struct perf_event *event) ++{ ++ /* ++ * when the current task's perf cgroup does not match ++ * the event's, we need to remember to call the ++ * perf_mark_enable() function the first time a task with ++ * a matching perf cgroup is scheduled in. ++ */ ++ if (is_cgroup_event(event) && !perf_cgroup_match(event)) ++ event->cgrp_defer_enabled = 1; ++} ++ ++static inline void ++perf_cgroup_mark_enabled(struct perf_event *event, ++ struct perf_event_context *ctx) ++{ ++ struct perf_event *sub; ++ u64 tstamp = perf_event_time(event); ++ ++ if (!event->cgrp_defer_enabled) ++ return; ++ ++ event->cgrp_defer_enabled = 0; ++ ++ event->tstamp_enabled = tstamp - event->total_time_enabled; ++ list_for_each_entry(sub, &event->sibling_list, group_entry) { ++ if (sub->state >= PERF_EVENT_STATE_INACTIVE) { ++ sub->tstamp_enabled = tstamp - sub->total_time_enabled; ++ sub->cgrp_defer_enabled = 0; ++ } ++ } ++} ++#else /* !CONFIG_CGROUP_PERF */ ++ ++static inline bool ++perf_cgroup_match(struct perf_event *event) ++{ ++ return true; ++} ++ ++static inline void perf_detach_cgroup(struct perf_event *event) ++{} ++ ++static inline int is_cgroup_event(struct perf_event *event) ++{ ++ return 0; ++} ++ ++static inline u64 perf_cgroup_event_cgrp_time(struct perf_event *event) ++{ ++ return 0; ++} ++ ++static inline void update_cgrp_time_from_event(struct perf_event *event) ++{ ++} ++ ++static inline void update_cgrp_time_from_cpuctx(struct perf_cpu_context *cpuctx) ++{ ++} ++ ++static inline void perf_cgroup_sched_out(struct task_struct *task, ++ struct task_struct *next) ++{ ++} ++ ++static inline void perf_cgroup_sched_in(struct task_struct *prev, ++ struct task_struct *task) ++{ ++} ++ ++static inline int perf_cgroup_connect(pid_t pid, struct perf_event *event, ++ struct perf_event_attr *attr, ++ struct perf_event *group_leader) ++{ ++ return -EINVAL; ++} ++ ++static inline void ++perf_cgroup_set_timestamp(struct task_struct *task, ++ struct perf_event_context *ctx) ++{ ++} ++ ++void ++perf_cgroup_switch(struct task_struct *task, struct task_struct *next) ++{ ++} ++ ++static inline void ++perf_cgroup_set_shadow_time(struct perf_event *event, u64 now) ++{ ++} ++ ++static inline u64 perf_cgroup_event_time(struct perf_event *event) ++{ ++ return 0; ++} ++ ++static inline void ++perf_cgroup_defer_enabled(struct perf_event *event) ++{ ++} ++ ++static inline void ++perf_cgroup_mark_enabled(struct perf_event *event, ++ struct perf_event_context *ctx) ++{ ++} ++#endif ++ ++/* ++ * set default to be dependent on timer tick just ++ * like original code ++ */ ++#define PERF_CPU_HRTIMER (1000 / HZ) ++/* ++ * function must be called with interrupts disbled ++ */ ++static enum hrtimer_restart perf_cpu_hrtimer_handler(struct hrtimer *hr) ++{ ++ struct perf_cpu_context *cpuctx; ++ enum hrtimer_restart ret = HRTIMER_NORESTART; ++ int rotations = 0; ++ ++ WARN_ON(!irqs_disabled()); ++ ++ cpuctx = container_of(hr, struct perf_cpu_context, hrtimer); ++ ++ rotations = perf_rotate_context(cpuctx); ++ ++ /* ++ * arm timer if needed ++ */ ++ if (rotations) { ++ hrtimer_forward_now(hr, cpuctx->hrtimer_interval); ++ ret = HRTIMER_RESTART; ++ } ++ ++ return ret; ++} ++ ++/* CPU is going down */ ++void perf_cpu_hrtimer_cancel(int cpu) ++{ ++ struct perf_cpu_context *cpuctx; ++ struct pmu *pmu; ++ unsigned long flags; ++ ++ if (WARN_ON(cpu != smp_processor_id())) ++ return; ++ ++ local_irq_save(flags); ++ ++ rcu_read_lock(); ++ ++ list_for_each_entry_rcu(pmu, &pmus, entry) { ++ cpuctx = this_cpu_ptr(pmu->pmu_cpu_context); ++ ++ if (pmu->task_ctx_nr == perf_sw_context) ++ continue; ++ ++ hrtimer_cancel(&cpuctx->hrtimer); ++ } ++ ++ rcu_read_unlock(); ++ ++ local_irq_restore(flags); ++} ++ ++static void __perf_cpu_hrtimer_init(struct perf_cpu_context *cpuctx, int cpu) ++{ ++ struct hrtimer *hr = &cpuctx->hrtimer; ++ struct pmu *pmu = cpuctx->ctx.pmu; ++ int timer; ++ ++ /* no multiplexing needed for SW PMU */ ++ if (pmu->task_ctx_nr == perf_sw_context) ++ return; ++ ++ /* ++ * check default is sane, if not set then force to ++ * default interval (1/tick) ++ */ ++ timer = pmu->hrtimer_interval_ms; ++ if (timer < 1) ++ timer = pmu->hrtimer_interval_ms = PERF_CPU_HRTIMER; ++ ++ cpuctx->hrtimer_interval = ns_to_ktime(NSEC_PER_MSEC * timer); ++ ++ hrtimer_init(hr, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED); ++ hr->function = perf_cpu_hrtimer_handler; ++} ++ ++static void perf_cpu_hrtimer_restart(struct perf_cpu_context *cpuctx) ++{ ++ struct hrtimer *hr = &cpuctx->hrtimer; ++ struct pmu *pmu = cpuctx->ctx.pmu; ++ ++ /* not for SW PMU */ ++ if (pmu->task_ctx_nr == perf_sw_context) ++ return; ++ ++ if (hrtimer_active(hr)) ++ return; ++ ++ if (!hrtimer_callback_running(hr)) ++ __hrtimer_start_range_ns(hr, cpuctx->hrtimer_interval, ++ 0, HRTIMER_MODE_REL_PINNED, 0); ++} ++ ++void perf_pmu_disable(struct pmu *pmu) ++{ ++ int *count = this_cpu_ptr(pmu->pmu_disable_count); ++ if (!(*count)++) ++ pmu->pmu_disable(pmu); ++} ++ ++void perf_pmu_enable(struct pmu *pmu) ++{ ++ int *count = this_cpu_ptr(pmu->pmu_disable_count); ++ if (!--(*count)) ++ pmu->pmu_enable(pmu); ++} ++ ++static DEFINE_PER_CPU(struct list_head, rotation_list); ++ ++/* ++ * perf_pmu_rotate_start() and perf_rotate_context() are fully serialized ++ * because they're strictly cpu affine and rotate_start is called with IRQs ++ * disabled, while rotate_context is called from IRQ context. ++ */ ++static void perf_pmu_rotate_start(struct pmu *pmu) ++{ ++ struct perf_cpu_context *cpuctx = this_cpu_ptr(pmu->pmu_cpu_context); ++ struct list_head *head = this_cpu_ptr(&rotation_list); ++ ++ WARN_ON(!irqs_disabled()); ++ ++ if (list_empty(&cpuctx->rotation_list)) ++ list_add(&cpuctx->rotation_list, head); ++} ++ ++static void get_ctx(struct perf_event_context *ctx) ++{ ++ WARN_ON(!atomic_inc_not_zero(&ctx->refcount)); ++} ++ ++static void put_ctx(struct perf_event_context *ctx) ++{ ++ if (atomic_dec_and_test(&ctx->refcount)) { ++ if (ctx->parent_ctx) ++ put_ctx(ctx->parent_ctx); ++ if (ctx->task) ++ put_task_struct(ctx->task); ++ kfree_rcu(ctx, rcu_head); ++ } ++} ++ ++/* ++ * This must be done under the ctx->lock, such as to serialize against ++ * context_equiv(), therefore we cannot call put_ctx() since that might end up ++ * calling scheduler related locks and ctx->lock nests inside those. ++ */ ++static __must_check struct perf_event_context * ++unclone_ctx(struct perf_event_context *ctx) ++{ ++ struct perf_event_context *parent_ctx = ctx->parent_ctx; ++ ++ lockdep_assert_held(&ctx->lock); ++ ++ if (parent_ctx) ++ ctx->parent_ctx = NULL; ++ ctx->generation++; ++ ++ return parent_ctx; ++} ++ ++static u32 perf_event_pid(struct perf_event *event, struct task_struct *p) ++{ ++ /* ++ * only top level events have the pid namespace they were created in ++ */ ++ if (event->parent) ++ event = event->parent; ++ ++ return task_tgid_nr_ns(p, event->ns); ++} ++ ++static u32 perf_event_tid(struct perf_event *event, struct task_struct *p) ++{ ++ /* ++ * only top level events have the pid namespace they were created in ++ */ ++ if (event->parent) ++ event = event->parent; ++ ++ return task_pid_nr_ns(p, event->ns); ++} ++ ++/* ++ * If we inherit events we want to return the parent event id ++ * to userspace. ++ */ ++static u64 primary_event_id(struct perf_event *event) ++{ ++ u64 id = event->id; ++ ++ if (event->parent) ++ id = event->parent->id; ++ ++ return id; ++} ++ ++/* ++ * Get the perf_event_context for a task and lock it. ++ * This has to cope with with the fact that until it is locked, ++ * the context could get moved to another task. ++ */ ++static struct perf_event_context * ++perf_lock_task_context(struct task_struct *task, int ctxn, unsigned long *flags) ++{ ++ struct perf_event_context *ctx; ++ ++retry: ++ /* ++ * One of the few rules of preemptible RCU is that one cannot do ++ * rcu_read_unlock() while holding a scheduler (or nested) lock when ++ * part of the read side critical section was preemptible -- see ++ * rcu_read_unlock_special(). ++ * ++ * Since ctx->lock nests under rq->lock we must ensure the entire read ++ * side critical section is non-preemptible. ++ */ ++ preempt_disable(); ++ rcu_read_lock(); ++ ctx = rcu_dereference(task->perf_event_ctxp[ctxn]); ++ if (ctx) { ++ /* ++ * If this context is a clone of another, it might ++ * get swapped for another underneath us by ++ * perf_event_task_sched_out, though the ++ * rcu_read_lock() protects us from any context ++ * getting freed. Lock the context and check if it ++ * got swapped before we could get the lock, and retry ++ * if so. If we locked the right context, then it ++ * can't get swapped on us any more. ++ */ ++ raw_spin_lock_irqsave(&ctx->lock, *flags); ++ if (ctx != rcu_dereference(task->perf_event_ctxp[ctxn])) { ++ raw_spin_unlock_irqrestore(&ctx->lock, *flags); ++ rcu_read_unlock(); ++ preempt_enable(); ++ goto retry; ++ } ++ ++ if (!atomic_inc_not_zero(&ctx->refcount)) { ++ raw_spin_unlock_irqrestore(&ctx->lock, *flags); ++ ctx = NULL; ++ } ++ } ++ rcu_read_unlock(); ++ preempt_enable(); ++ return ctx; ++} ++ ++/* ++ * Get the context for a task and increment its pin_count so it ++ * can't get swapped to another task. This also increments its ++ * reference count so that the context can't get freed. ++ */ ++static struct perf_event_context * ++perf_pin_task_context(struct task_struct *task, int ctxn) ++{ ++ struct perf_event_context *ctx; ++ unsigned long flags; ++ ++ ctx = perf_lock_task_context(task, ctxn, &flags); ++ if (ctx) { ++ ++ctx->pin_count; ++ raw_spin_unlock_irqrestore(&ctx->lock, flags); ++ } ++ return ctx; ++} ++ ++static void perf_unpin_context(struct perf_event_context *ctx) ++{ ++ unsigned long flags; ++ ++ raw_spin_lock_irqsave(&ctx->lock, flags); ++ --ctx->pin_count; ++ raw_spin_unlock_irqrestore(&ctx->lock, flags); ++} ++ ++/* ++ * Update the record of the current time in a context. ++ */ ++static void update_context_time(struct perf_event_context *ctx) ++{ ++ u64 now = perf_clock(); ++ ++ ctx->time += now - ctx->timestamp; ++ ctx->timestamp = now; ++} ++ ++static u64 perf_event_time(struct perf_event *event) ++{ ++ struct perf_event_context *ctx = event->ctx; ++ ++ if (is_cgroup_event(event)) ++ return perf_cgroup_event_time(event); ++ ++ return ctx ? ctx->time : 0; ++} ++ ++/* ++ * Update the total_time_enabled and total_time_running fields for a event. ++ * The caller of this function needs to hold the ctx->lock. ++ */ ++static void update_event_times(struct perf_event *event) ++{ ++ struct perf_event_context *ctx = event->ctx; ++ u64 run_end; ++ ++ if (event->state < PERF_EVENT_STATE_INACTIVE || ++ event->group_leader->state < PERF_EVENT_STATE_INACTIVE) ++ return; ++ /* ++ * in cgroup mode, time_enabled represents ++ * the time the event was enabled AND active ++ * tasks were in the monitored cgroup. This is ++ * independent of the activity of the context as ++ * there may be a mix of cgroup and non-cgroup events. ++ * ++ * That is why we treat cgroup events differently ++ * here. ++ */ ++ if (is_cgroup_event(event)) ++ run_end = perf_cgroup_event_time(event); ++ else if (ctx->is_active) ++ run_end = ctx->time; ++ else ++ run_end = event->tstamp_stopped; ++ ++ event->total_time_enabled = run_end - event->tstamp_enabled; ++ ++ if (event->state == PERF_EVENT_STATE_INACTIVE) ++ run_end = event->tstamp_stopped; ++ else ++ run_end = perf_event_time(event); ++ ++ event->total_time_running = run_end - event->tstamp_running; ++ ++} ++ ++/* ++ * Update total_time_enabled and total_time_running for all events in a group. ++ */ ++static void update_group_times(struct perf_event *leader) ++{ ++ struct perf_event *event; ++ ++ update_event_times(leader); ++ list_for_each_entry(event, &leader->sibling_list, group_entry) ++ update_event_times(event); ++} ++ ++static struct list_head * ++ctx_group_list(struct perf_event *event, struct perf_event_context *ctx) ++{ ++ if (event->attr.pinned) ++ return &ctx->pinned_groups; ++ else ++ return &ctx->flexible_groups; ++} ++ ++/* ++ * Add a event from the lists for its context. ++ * Must be called with ctx->mutex and ctx->lock held. ++ */ ++static void ++list_add_event(struct perf_event *event, struct perf_event_context *ctx) ++{ ++ WARN_ON_ONCE(event->attach_state & PERF_ATTACH_CONTEXT); ++ event->attach_state |= PERF_ATTACH_CONTEXT; ++ ++ /* ++ * If we're a stand alone event or group leader, we go to the context ++ * list, group events are kept attached to the group so that ++ * perf_group_detach can, at all times, locate all siblings. ++ */ ++ if (event->group_leader == event) { ++ struct list_head *list; ++ ++ if (is_software_event(event)) ++ event->group_flags |= PERF_GROUP_SOFTWARE; ++ ++ list = ctx_group_list(event, ctx); ++ list_add_tail(&event->group_entry, list); ++ } ++ ++ if (is_cgroup_event(event)) ++ ctx->nr_cgroups++; ++ ++ if (has_branch_stack(event)) ++ ctx->nr_branch_stack++; ++ ++ list_add_rcu(&event->event_entry, &ctx->event_list); ++ if (!ctx->nr_events) ++ perf_pmu_rotate_start(ctx->pmu); ++ ctx->nr_events++; ++ if (event->attr.inherit_stat) ++ ctx->nr_stat++; ++ ++ ctx->generation++; ++} ++ ++/* ++ * Initialize event state based on the perf_event_attr::disabled. ++ */ ++static inline void perf_event__state_init(struct perf_event *event) ++{ ++ event->state = event->attr.disabled ? PERF_EVENT_STATE_OFF : ++ PERF_EVENT_STATE_INACTIVE; ++} ++ ++/* ++ * Called at perf_event creation and when events are attached/detached from a ++ * group. ++ */ ++static void perf_event__read_size(struct perf_event *event) ++{ ++ int entry = sizeof(u64); /* value */ ++ int size = 0; ++ int nr = 1; ++ ++ if (event->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) ++ size += sizeof(u64); ++ ++ if (event->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) ++ size += sizeof(u64); ++ ++ if (event->attr.read_format & PERF_FORMAT_ID) ++ entry += sizeof(u64); ++ ++ if (event->attr.read_format & PERF_FORMAT_GROUP) { ++ nr += event->group_leader->nr_siblings; ++ size += sizeof(u64); ++ } ++ ++ size += entry * nr; ++ event->read_size = size; ++} ++ ++static void perf_event__header_size(struct perf_event *event) ++{ ++ struct perf_sample_data *data; ++ u64 sample_type = event->attr.sample_type; ++ u16 size = 0; ++ ++ perf_event__read_size(event); ++ ++ if (sample_type & PERF_SAMPLE_IP) ++ size += sizeof(data->ip); ++ ++ if (sample_type & PERF_SAMPLE_ADDR) ++ size += sizeof(data->addr); ++ ++ if (sample_type & PERF_SAMPLE_PERIOD) ++ size += sizeof(data->period); ++ ++ if (sample_type & PERF_SAMPLE_WEIGHT) ++ size += sizeof(data->weight); ++ ++ if (sample_type & PERF_SAMPLE_READ) ++ size += event->read_size; ++ ++ if (sample_type & PERF_SAMPLE_DATA_SRC) ++ size += sizeof(data->data_src.val); ++ ++ if (sample_type & PERF_SAMPLE_TRANSACTION) ++ size += sizeof(data->txn); ++ ++ event->header_size = size; ++} ++ ++static void perf_event__id_header_size(struct perf_event *event) ++{ ++ struct perf_sample_data *data; ++ u64 sample_type = event->attr.sample_type; ++ u16 size = 0; ++ ++ if (sample_type & PERF_SAMPLE_TID) ++ size += sizeof(data->tid_entry); ++ ++ if (sample_type & PERF_SAMPLE_TIME) ++ size += sizeof(data->time); ++ ++ if (sample_type & PERF_SAMPLE_IDENTIFIER) ++ size += sizeof(data->id); ++ ++ if (sample_type & PERF_SAMPLE_ID) ++ size += sizeof(data->id); ++ ++ if (sample_type & PERF_SAMPLE_STREAM_ID) ++ size += sizeof(data->stream_id); ++ ++ if (sample_type & PERF_SAMPLE_CPU) ++ size += sizeof(data->cpu_entry); ++ ++ event->id_header_size = size; ++} ++ ++static void perf_group_attach(struct perf_event *event) ++{ ++ struct perf_event *group_leader = event->group_leader, *pos; ++ ++ /* ++ * We can have double attach due to group movement in perf_event_open. ++ */ ++ if (event->attach_state & PERF_ATTACH_GROUP) ++ return; ++ ++ event->attach_state |= PERF_ATTACH_GROUP; ++ ++ if (group_leader == event) ++ return; ++ ++ if (group_leader->group_flags & PERF_GROUP_SOFTWARE && ++ !is_software_event(event)) ++ group_leader->group_flags &= ~PERF_GROUP_SOFTWARE; ++ ++ list_add_tail(&event->group_entry, &group_leader->sibling_list); ++ group_leader->nr_siblings++; ++ ++ perf_event__header_size(group_leader); ++ ++ list_for_each_entry(pos, &group_leader->sibling_list, group_entry) ++ perf_event__header_size(pos); ++} ++ ++/* ++ * Remove a event from the lists for its context. ++ * Must be called with ctx->mutex and ctx->lock held. ++ */ ++static void ++list_del_event(struct perf_event *event, struct perf_event_context *ctx) ++{ ++ struct perf_cpu_context *cpuctx; ++ /* ++ * We can have double detach due to exit/hot-unplug + close. ++ */ ++ if (!(event->attach_state & PERF_ATTACH_CONTEXT)) ++ return; ++ ++ event->attach_state &= ~PERF_ATTACH_CONTEXT; ++ ++ if (is_cgroup_event(event)) { ++ ctx->nr_cgroups--; ++ cpuctx = __get_cpu_context(ctx); ++ /* ++ * if there are no more cgroup events ++ * then cler cgrp to avoid stale pointer ++ * in update_cgrp_time_from_cpuctx() ++ */ ++ if (!ctx->nr_cgroups) ++ cpuctx->cgrp = NULL; ++ } ++ ++ if (has_branch_stack(event)) ++ ctx->nr_branch_stack--; ++ ++ ctx->nr_events--; ++ if (event->attr.inherit_stat) ++ ctx->nr_stat--; ++ ++ list_del_rcu(&event->event_entry); ++ ++ if (event->group_leader == event) ++ list_del_init(&event->group_entry); ++ ++ update_group_times(event); ++ ++ /* ++ * If event was in error state, then keep it ++ * that way, otherwise bogus counts will be ++ * returned on read(). The only way to get out ++ * of error state is by explicit re-enabling ++ * of the event ++ */ ++ if (event->state > PERF_EVENT_STATE_OFF) ++ event->state = PERF_EVENT_STATE_OFF; ++ ++ ctx->generation++; ++} ++ ++static void perf_group_detach(struct perf_event *event) ++{ ++ struct perf_event *sibling, *tmp; ++ struct list_head *list = NULL; ++ ++ /* ++ * We can have double detach due to exit/hot-unplug + close. ++ */ ++ if (!(event->attach_state & PERF_ATTACH_GROUP)) ++ return; ++ ++ event->attach_state &= ~PERF_ATTACH_GROUP; ++ ++ /* ++ * If this is a sibling, remove it from its group. ++ */ ++ if (event->group_leader != event) { ++ list_del_init(&event->group_entry); ++ event->group_leader->nr_siblings--; ++ goto out; ++ } ++ ++ if (!list_empty(&event->group_entry)) ++ list = &event->group_entry; ++ ++ /* ++ * If this was a group event with sibling events then ++ * upgrade the siblings to singleton events by adding them ++ * to whatever list we are on. ++ */ ++ list_for_each_entry_safe(sibling, tmp, &event->sibling_list, group_entry) { ++ if (list) ++ list_move_tail(&sibling->group_entry, list); ++ sibling->group_leader = sibling; ++ ++ /* Inherit group flags from the previous leader */ ++ sibling->group_flags = event->group_flags; ++ } ++ ++out: ++ perf_event__header_size(event->group_leader); ++ ++ list_for_each_entry(tmp, &event->group_leader->sibling_list, group_entry) ++ perf_event__header_size(tmp); ++} ++ ++/* ++ * User event without the task. ++ */ ++static bool is_orphaned_event(struct perf_event *event) ++{ ++ return event && !is_kernel_event(event) && !event->owner; ++} ++ ++/* ++ * Event has a parent but parent's task finished and it's ++ * alive only because of children holding refference. ++ */ ++static bool is_orphaned_child(struct perf_event *event) ++{ ++ return is_orphaned_event(event->parent); ++} ++ ++static void orphans_remove_work(struct work_struct *work); ++ ++static void schedule_orphans_remove(struct perf_event_context *ctx) ++{ ++ if (!ctx->task || ctx->orphans_remove_sched || !perf_wq) ++ return; ++ ++ if (queue_delayed_work(perf_wq, &ctx->orphans_remove, 1)) { ++ get_ctx(ctx); ++ ctx->orphans_remove_sched = true; ++ } ++} ++ ++static int __init perf_workqueue_init(void) ++{ ++ perf_wq = create_singlethread_workqueue("perf"); ++ WARN(!perf_wq, "failed to create perf workqueue\n"); ++ return perf_wq ? 0 : -1; ++} ++ ++core_initcall(perf_workqueue_init); ++ ++static inline int ++event_filter_match(struct perf_event *event) ++{ ++ return (event->cpu == -1 || event->cpu == smp_processor_id()) ++ && perf_cgroup_match(event); ++} ++ ++static void ++event_sched_out(struct perf_event *event, ++ struct perf_cpu_context *cpuctx, ++ struct perf_event_context *ctx) ++{ ++ u64 tstamp = perf_event_time(event); ++ u64 delta; ++ /* ++ * An event which could not be activated because of ++ * filter mismatch still needs to have its timings ++ * maintained, otherwise bogus information is return ++ * via read() for time_enabled, time_running: ++ */ ++ if (event->state == PERF_EVENT_STATE_INACTIVE ++ && !event_filter_match(event)) { ++ delta = tstamp - event->tstamp_stopped; ++ event->tstamp_running += delta; ++ event->tstamp_stopped = tstamp; ++ } ++ ++ if (event->state != PERF_EVENT_STATE_ACTIVE) ++ return; ++ ++ perf_pmu_disable(event->pmu); ++ ++ event->state = PERF_EVENT_STATE_INACTIVE; ++ if (event->pending_disable) { ++ event->pending_disable = 0; ++ event->state = PERF_EVENT_STATE_OFF; ++ } ++ event->tstamp_stopped = tstamp; ++ event->pmu->del(event, 0); ++ event->oncpu = -1; ++ ++ if (!is_software_event(event)) ++ cpuctx->active_oncpu--; ++ ctx->nr_active--; ++ if (event->attr.freq && event->attr.sample_freq) ++ ctx->nr_freq--; ++ if (event->attr.exclusive || !cpuctx->active_oncpu) ++ cpuctx->exclusive = 0; ++ ++ if (is_orphaned_child(event)) ++ schedule_orphans_remove(ctx); ++ ++ perf_pmu_enable(event->pmu); ++} ++ ++static void ++group_sched_out(struct perf_event *group_event, ++ struct perf_cpu_context *cpuctx, ++ struct perf_event_context *ctx) ++{ ++ struct perf_event *event; ++ int state = group_event->state; ++ ++ event_sched_out(group_event, cpuctx, ctx); ++ ++ /* ++ * Schedule out siblings (if any): ++ */ ++ list_for_each_entry(event, &group_event->sibling_list, group_entry) ++ event_sched_out(event, cpuctx, ctx); ++ ++ if (state == PERF_EVENT_STATE_ACTIVE && group_event->attr.exclusive) ++ cpuctx->exclusive = 0; ++} ++ ++struct remove_event { ++ struct perf_event *event; ++ bool detach_group; ++}; ++ ++/* ++ * Cross CPU call to remove a performance event ++ * ++ * We disable the event on the hardware level first. After that we ++ * remove it from the context list. ++ */ ++static int __perf_remove_from_context(void *info) ++{ ++ struct remove_event *re = info; ++ struct perf_event *event = re->event; ++ struct perf_event_context *ctx = event->ctx; ++ struct perf_cpu_context *cpuctx = __get_cpu_context(ctx); ++ ++ raw_spin_lock(&ctx->lock); ++ event_sched_out(event, cpuctx, ctx); ++ if (re->detach_group) ++ perf_group_detach(event); ++ list_del_event(event, ctx); ++ if (!ctx->nr_events && cpuctx->task_ctx == ctx) { ++ ctx->is_active = 0; ++ cpuctx->task_ctx = NULL; ++ } ++ raw_spin_unlock(&ctx->lock); ++ ++ return 0; ++} ++ ++ ++/* ++ * Remove the event from a task's (or a CPU's) list of events. ++ * ++ * CPU events are removed with a smp call. For task events we only ++ * call when the task is on a CPU. ++ * ++ * If event->ctx is a cloned context, callers must make sure that ++ * every task struct that event->ctx->task could possibly point to ++ * remains valid. This is OK when called from perf_release since ++ * that only calls us on the top-level context, which can't be a clone. ++ * When called from perf_event_exit_task, it's OK because the ++ * context has been detached from its task. ++ */ ++static void perf_remove_from_context(struct perf_event *event, bool detach_group) ++{ ++ struct perf_event_context *ctx = event->ctx; ++ struct task_struct *task = ctx->task; ++ struct remove_event re = { ++ .event = event, ++ .detach_group = detach_group, ++ }; ++ ++ lockdep_assert_held(&ctx->mutex); ++ ++ if (!task) { ++ /* ++ * Per cpu events are removed via an smp call. The removal can ++ * fail if the CPU is currently offline, but in that case we ++ * already called __perf_remove_from_context from ++ * perf_event_exit_cpu. ++ */ ++ cpu_function_call(event->cpu, __perf_remove_from_context, &re); ++ return; ++ } ++ ++retry: ++ if (!task_function_call(task, __perf_remove_from_context, &re)) ++ return; ++ ++ raw_spin_lock_irq(&ctx->lock); ++ /* ++ * If we failed to find a running task, but find the context active now ++ * that we've acquired the ctx->lock, retry. ++ */ ++ if (ctx->is_active) { ++ raw_spin_unlock_irq(&ctx->lock); ++ /* ++ * Reload the task pointer, it might have been changed by ++ * a concurrent perf_event_context_sched_out(). ++ */ ++ task = ctx->task; ++ goto retry; ++ } ++ ++ /* ++ * Since the task isn't running, its safe to remove the event, us ++ * holding the ctx->lock ensures the task won't get scheduled in. ++ */ ++ if (detach_group) ++ perf_group_detach(event); ++ list_del_event(event, ctx); ++ raw_spin_unlock_irq(&ctx->lock); ++} ++ ++/* ++ * Cross CPU call to disable a performance event ++ */ ++int __perf_event_disable(void *info) ++{ ++ struct perf_event *event = info; ++ struct perf_event_context *ctx = event->ctx; ++ struct perf_cpu_context *cpuctx = __get_cpu_context(ctx); ++ ++ /* ++ * If this is a per-task event, need to check whether this ++ * event's task is the current task on this cpu. ++ * ++ * Can trigger due to concurrent perf_event_context_sched_out() ++ * flipping contexts around. ++ */ ++ if (ctx->task && cpuctx->task_ctx != ctx) ++ return -EINVAL; ++ ++ raw_spin_lock(&ctx->lock); ++ ++ /* ++ * If the event is on, turn it off. ++ * If it is in error state, leave it in error state. ++ */ ++ if (event->state >= PERF_EVENT_STATE_INACTIVE) { ++ update_context_time(ctx); ++ update_cgrp_time_from_event(event); ++ update_group_times(event); ++ if (event == event->group_leader) ++ group_sched_out(event, cpuctx, ctx); ++ else ++ event_sched_out(event, cpuctx, ctx); ++ event->state = PERF_EVENT_STATE_OFF; ++ } ++ ++ raw_spin_unlock(&ctx->lock); ++ ++ return 0; ++} ++ ++/* ++ * Disable a event. ++ * ++ * If event->ctx is a cloned context, callers must make sure that ++ * every task struct that event->ctx->task could possibly point to ++ * remains valid. This condition is satisifed when called through ++ * perf_event_for_each_child or perf_event_for_each because they ++ * hold the top-level event's child_mutex, so any descendant that ++ * goes to exit will block in sync_child_event. ++ * When called from perf_pending_event it's OK because event->ctx ++ * is the current context on this CPU and preemption is disabled, ++ * hence we can't get into perf_event_task_sched_out for this context. ++ */ ++void perf_event_disable(struct perf_event *event) ++{ ++ struct perf_event_context *ctx = event->ctx; ++ struct task_struct *task = ctx->task; ++ ++ if (!task) { ++ /* ++ * Disable the event on the cpu that it's on ++ */ ++ cpu_function_call(event->cpu, __perf_event_disable, event); ++ return; ++ } ++ ++retry: ++ if (!task_function_call(task, __perf_event_disable, event)) ++ return; ++ ++ raw_spin_lock_irq(&ctx->lock); ++ /* ++ * If the event is still active, we need to retry the cross-call. ++ */ ++ if (event->state == PERF_EVENT_STATE_ACTIVE) { ++ raw_spin_unlock_irq(&ctx->lock); ++ /* ++ * Reload the task pointer, it might have been changed by ++ * a concurrent perf_event_context_sched_out(). ++ */ ++ task = ctx->task; ++ goto retry; ++ } ++ ++ /* ++ * Since we have the lock this context can't be scheduled ++ * in, so we can change the state safely. ++ */ ++ if (event->state == PERF_EVENT_STATE_INACTIVE) { ++ update_group_times(event); ++ event->state = PERF_EVENT_STATE_OFF; ++ } ++ raw_spin_unlock_irq(&ctx->lock); ++} ++EXPORT_SYMBOL_GPL(perf_event_disable); ++ ++static void perf_set_shadow_time(struct perf_event *event, ++ struct perf_event_context *ctx, ++ u64 tstamp) ++{ ++ /* ++ * use the correct time source for the time snapshot ++ * ++ * We could get by without this by leveraging the ++ * fact that to get to this function, the caller ++ * has most likely already called update_context_time() ++ * and update_cgrp_time_xx() and thus both timestamp ++ * are identical (or very close). Given that tstamp is, ++ * already adjusted for cgroup, we could say that: ++ * tstamp - ctx->timestamp ++ * is equivalent to ++ * tstamp - cgrp->timestamp. ++ * ++ * Then, in perf_output_read(), the calculation would ++ * work with no changes because: ++ * - event is guaranteed scheduled in ++ * - no scheduled out in between ++ * - thus the timestamp would be the same ++ * ++ * But this is a bit hairy. ++ * ++ * So instead, we have an explicit cgroup call to remain ++ * within the time time source all along. We believe it ++ * is cleaner and simpler to understand. ++ */ ++ if (is_cgroup_event(event)) ++ perf_cgroup_set_shadow_time(event, tstamp); ++ else ++ event->shadow_ctx_time = tstamp - ctx->timestamp; ++} ++ ++#define MAX_INTERRUPTS (~0ULL) ++ ++static void perf_log_throttle(struct perf_event *event, int enable); ++ ++static int ++event_sched_in(struct perf_event *event, ++ struct perf_cpu_context *cpuctx, ++ struct perf_event_context *ctx) ++{ ++ u64 tstamp = perf_event_time(event); ++ int ret = 0; ++ ++ lockdep_assert_held(&ctx->lock); ++ ++ if (event->state <= PERF_EVENT_STATE_OFF) ++ return 0; ++ ++ event->state = PERF_EVENT_STATE_ACTIVE; ++ event->oncpu = smp_processor_id(); ++ ++ /* ++ * Unthrottle events, since we scheduled we might have missed several ++ * ticks already, also for a heavily scheduling task there is little ++ * guarantee it'll get a tick in a timely manner. ++ */ ++ if (unlikely(event->hw.interrupts == MAX_INTERRUPTS)) { ++ perf_log_throttle(event, 1); ++ event->hw.interrupts = 0; ++ } ++ ++ /* ++ * The new state must be visible before we turn it on in the hardware: ++ */ ++ smp_wmb(); ++ ++ perf_pmu_disable(event->pmu); ++ ++ if (event->pmu->add(event, PERF_EF_START)) { ++ event->state = PERF_EVENT_STATE_INACTIVE; ++ event->oncpu = -1; ++ ret = -EAGAIN; ++ goto out; ++ } ++ ++ event->tstamp_running += tstamp - event->tstamp_stopped; ++ ++ perf_set_shadow_time(event, ctx, tstamp); ++ ++ if (!is_software_event(event)) ++ cpuctx->active_oncpu++; ++ ctx->nr_active++; ++ if (event->attr.freq && event->attr.sample_freq) ++ ctx->nr_freq++; ++ ++ if (event->attr.exclusive) ++ cpuctx->exclusive = 1; ++ ++ if (is_orphaned_child(event)) ++ schedule_orphans_remove(ctx); ++ ++out: ++ perf_pmu_enable(event->pmu); ++ ++ return ret; ++} ++ ++static int ++group_sched_in(struct perf_event *group_event, ++ struct perf_cpu_context *cpuctx, ++ struct perf_event_context *ctx) ++{ ++ struct perf_event *event, *partial_group = NULL; ++ struct pmu *pmu = ctx->pmu; ++ u64 now = ctx->time; ++ bool simulate = false; ++ ++ if (group_event->state == PERF_EVENT_STATE_OFF) ++ return 0; ++ ++ pmu->start_txn(pmu); ++ ++ if (event_sched_in(group_event, cpuctx, ctx)) { ++ pmu->cancel_txn(pmu); ++ perf_cpu_hrtimer_restart(cpuctx); ++ return -EAGAIN; ++ } ++ ++ /* ++ * Schedule in siblings as one group (if any): ++ */ ++ list_for_each_entry(event, &group_event->sibling_list, group_entry) { ++ if (event_sched_in(event, cpuctx, ctx)) { ++ partial_group = event; ++ goto group_error; ++ } ++ } ++ ++ if (!pmu->commit_txn(pmu)) ++ return 0; ++ ++group_error: ++ /* ++ * Groups can be scheduled in as one unit only, so undo any ++ * partial group before returning: ++ * The events up to the failed event are scheduled out normally, ++ * tstamp_stopped will be updated. ++ * ++ * The failed events and the remaining siblings need to have ++ * their timings updated as if they had gone thru event_sched_in() ++ * and event_sched_out(). This is required to get consistent timings ++ * across the group. This also takes care of the case where the group ++ * could never be scheduled by ensuring tstamp_stopped is set to mark ++ * the time the event was actually stopped, such that time delta ++ * calculation in update_event_times() is correct. ++ */ ++ list_for_each_entry(event, &group_event->sibling_list, group_entry) { ++ if (event == partial_group) ++ simulate = true; ++ ++ if (simulate) { ++ event->tstamp_running += now - event->tstamp_stopped; ++ event->tstamp_stopped = now; ++ } else { ++ event_sched_out(event, cpuctx, ctx); ++ } ++ } ++ event_sched_out(group_event, cpuctx, ctx); ++ ++ pmu->cancel_txn(pmu); ++ ++ perf_cpu_hrtimer_restart(cpuctx); ++ ++ return -EAGAIN; ++} ++ ++/* ++ * Work out whether we can put this event group on the CPU now. ++ */ ++static int group_can_go_on(struct perf_event *event, ++ struct perf_cpu_context *cpuctx, ++ int can_add_hw) ++{ ++ /* ++ * Groups consisting entirely of software events can always go on. ++ */ ++ if (event->group_flags & PERF_GROUP_SOFTWARE) ++ return 1; ++ /* ++ * If an exclusive group is already on, no other hardware ++ * events can go on. ++ */ ++ if (cpuctx->exclusive) ++ return 0; ++ /* ++ * If this group is exclusive and there are already ++ * events on the CPU, it can't go on. ++ */ ++ if (event->attr.exclusive && cpuctx->active_oncpu) ++ return 0; ++ /* ++ * Otherwise, try to add it if all previous groups were able ++ * to go on. ++ */ ++ return can_add_hw; ++} ++ ++static void add_event_to_ctx(struct perf_event *event, ++ struct perf_event_context *ctx) ++{ ++ u64 tstamp = perf_event_time(event); ++ ++ list_add_event(event, ctx); ++ perf_group_attach(event); ++ event->tstamp_enabled = tstamp; ++ event->tstamp_running = tstamp; ++ event->tstamp_stopped = tstamp; ++} ++ ++static void task_ctx_sched_out(struct perf_event_context *ctx); ++static void ++ctx_sched_in(struct perf_event_context *ctx, ++ struct perf_cpu_context *cpuctx, ++ enum event_type_t event_type, ++ struct task_struct *task); ++ ++static void perf_event_sched_in(struct perf_cpu_context *cpuctx, ++ struct perf_event_context *ctx, ++ struct task_struct *task) ++{ ++ cpu_ctx_sched_in(cpuctx, EVENT_PINNED, task); ++ if (ctx) ++ ctx_sched_in(ctx, cpuctx, EVENT_PINNED, task); ++ cpu_ctx_sched_in(cpuctx, EVENT_FLEXIBLE, task); ++ if (ctx) ++ ctx_sched_in(ctx, cpuctx, EVENT_FLEXIBLE, task); ++} ++ ++/* ++ * Cross CPU call to install and enable a performance event ++ * ++ * Must be called with ctx->mutex held ++ */ ++static int __perf_install_in_context(void *info) ++{ ++ struct perf_event *event = info; ++ struct perf_event_context *ctx = event->ctx; ++ struct perf_cpu_context *cpuctx = __get_cpu_context(ctx); ++ struct perf_event_context *task_ctx = cpuctx->task_ctx; ++ struct task_struct *task = current; ++ ++ perf_ctx_lock(cpuctx, task_ctx); ++ perf_pmu_disable(cpuctx->ctx.pmu); ++ ++ /* ++ * If there was an active task_ctx schedule it out. ++ */ ++ if (task_ctx) ++ task_ctx_sched_out(task_ctx); ++ ++ /* ++ * If the context we're installing events in is not the ++ * active task_ctx, flip them. ++ */ ++ if (ctx->task && task_ctx != ctx) { ++ if (task_ctx) ++ raw_spin_unlock(&task_ctx->lock); ++ raw_spin_lock(&ctx->lock); ++ task_ctx = ctx; ++ } ++ ++ if (task_ctx) { ++ cpuctx->task_ctx = task_ctx; ++ task = task_ctx->task; ++ } ++ ++ cpu_ctx_sched_out(cpuctx, EVENT_ALL); ++ ++ update_context_time(ctx); ++ /* ++ * update cgrp time only if current cgrp ++ * matches event->cgrp. Must be done before ++ * calling add_event_to_ctx() ++ */ ++ update_cgrp_time_from_event(event); ++ ++ add_event_to_ctx(event, ctx); ++ ++ /* ++ * Schedule everything back in ++ */ ++ perf_event_sched_in(cpuctx, task_ctx, task); ++ ++ perf_pmu_enable(cpuctx->ctx.pmu); ++ perf_ctx_unlock(cpuctx, task_ctx); ++ ++ return 0; ++} ++ ++/* ++ * Attach a performance event to a context ++ * ++ * First we add the event to the list with the hardware enable bit ++ * in event->hw_config cleared. ++ * ++ * If the event is attached to a task which is on a CPU we use a smp ++ * call to enable it in the task context. The task might have been ++ * scheduled away, but we check this in the smp call again. ++ */ ++static void ++perf_install_in_context(struct perf_event_context *ctx, ++ struct perf_event *event, ++ int cpu) ++{ ++ struct task_struct *task = ctx->task; ++ ++ lockdep_assert_held(&ctx->mutex); ++ ++ event->ctx = ctx; ++ if (event->cpu != -1) ++ event->cpu = cpu; ++ ++ if (!task) { ++ /* ++ * Per cpu events are installed via an smp call and ++ * the install is always successful. ++ */ ++ cpu_function_call(cpu, __perf_install_in_context, event); ++ return; ++ } ++ ++retry: ++ if (!task_function_call(task, __perf_install_in_context, event)) ++ return; ++ ++ raw_spin_lock_irq(&ctx->lock); ++ /* ++ * If we failed to find a running task, but find the context active now ++ * that we've acquired the ctx->lock, retry. ++ */ ++ if (ctx->is_active) { ++ raw_spin_unlock_irq(&ctx->lock); ++ /* ++ * Reload the task pointer, it might have been changed by ++ * a concurrent perf_event_context_sched_out(). ++ */ ++ task = ctx->task; ++ goto retry; ++ } ++ ++ /* ++ * Since the task isn't running, its safe to add the event, us holding ++ * the ctx->lock ensures the task won't get scheduled in. ++ */ ++ add_event_to_ctx(event, ctx); ++ raw_spin_unlock_irq(&ctx->lock); ++} ++ ++/* ++ * Put a event into inactive state and update time fields. ++ * Enabling the leader of a group effectively enables all ++ * the group members that aren't explicitly disabled, so we ++ * have to update their ->tstamp_enabled also. ++ * Note: this works for group members as well as group leaders ++ * since the non-leader members' sibling_lists will be empty. ++ */ ++static void __perf_event_mark_enabled(struct perf_event *event) ++{ ++ struct perf_event *sub; ++ u64 tstamp = perf_event_time(event); ++ ++ event->state = PERF_EVENT_STATE_INACTIVE; ++ event->tstamp_enabled = tstamp - event->total_time_enabled; ++ list_for_each_entry(sub, &event->sibling_list, group_entry) { ++ if (sub->state >= PERF_EVENT_STATE_INACTIVE) ++ sub->tstamp_enabled = tstamp - sub->total_time_enabled; ++ } ++} ++ ++/* ++ * Cross CPU call to enable a performance event ++ */ ++static int __perf_event_enable(void *info) ++{ ++ struct perf_event *event = info; ++ struct perf_event_context *ctx = event->ctx; ++ struct perf_event *leader = event->group_leader; ++ struct perf_cpu_context *cpuctx = __get_cpu_context(ctx); ++ int err; ++ ++ /* ++ * There's a time window between 'ctx->is_active' check ++ * in perf_event_enable function and this place having: ++ * - IRQs on ++ * - ctx->lock unlocked ++ * ++ * where the task could be killed and 'ctx' deactivated ++ * by perf_event_exit_task. ++ */ ++ if (!ctx->is_active) ++ return -EINVAL; ++ ++ raw_spin_lock(&ctx->lock); ++ update_context_time(ctx); ++ ++ if (event->state >= PERF_EVENT_STATE_INACTIVE) ++ goto unlock; ++ ++ /* ++ * set current task's cgroup time reference point ++ */ ++ perf_cgroup_set_timestamp(current, ctx); ++ ++ __perf_event_mark_enabled(event); ++ ++ if (!event_filter_match(event)) { ++ if (is_cgroup_event(event)) ++ perf_cgroup_defer_enabled(event); ++ goto unlock; ++ } ++ ++ /* ++ * If the event is in a group and isn't the group leader, ++ * then don't put it on unless the group is on. ++ */ ++ if (leader != event && leader->state != PERF_EVENT_STATE_ACTIVE) ++ goto unlock; ++ ++ if (!group_can_go_on(event, cpuctx, 1)) { ++ err = -EEXIST; ++ } else { ++ if (event == leader) ++ err = group_sched_in(event, cpuctx, ctx); ++ else ++ err = event_sched_in(event, cpuctx, ctx); ++ } ++ ++ if (err) { ++ /* ++ * If this event can't go on and it's part of a ++ * group, then the whole group has to come off. ++ */ ++ if (leader != event) { ++ group_sched_out(leader, cpuctx, ctx); ++ perf_cpu_hrtimer_restart(cpuctx); ++ } ++ if (leader->attr.pinned) { ++ update_group_times(leader); ++ leader->state = PERF_EVENT_STATE_ERROR; ++ } ++ } ++ ++unlock: ++ raw_spin_unlock(&ctx->lock); ++ ++ return 0; ++} ++ ++/* ++ * Enable a event. ++ * ++ * If event->ctx is a cloned context, callers must make sure that ++ * every task struct that event->ctx->task could possibly point to ++ * remains valid. This condition is satisfied when called through ++ * perf_event_for_each_child or perf_event_for_each as described ++ * for perf_event_disable. ++ */ ++void perf_event_enable(struct perf_event *event) ++{ ++ struct perf_event_context *ctx = event->ctx; ++ struct task_struct *task = ctx->task; ++ ++ if (!task) { ++ /* ++ * Enable the event on the cpu that it's on ++ */ ++ cpu_function_call(event->cpu, __perf_event_enable, event); ++ return; ++ } ++ ++ raw_spin_lock_irq(&ctx->lock); ++ if (event->state >= PERF_EVENT_STATE_INACTIVE) ++ goto out; ++ ++ /* ++ * If the event is in error state, clear that first. ++ * That way, if we see the event in error state below, we ++ * know that it has gone back into error state, as distinct ++ * from the task having been scheduled away before the ++ * cross-call arrived. ++ */ ++ if (event->state == PERF_EVENT_STATE_ERROR) ++ event->state = PERF_EVENT_STATE_OFF; ++ ++retry: ++ if (!ctx->is_active) { ++ __perf_event_mark_enabled(event); ++ goto out; ++ } ++ ++ raw_spin_unlock_irq(&ctx->lock); ++ ++ if (!task_function_call(task, __perf_event_enable, event)) ++ return; ++ ++ raw_spin_lock_irq(&ctx->lock); ++ ++ /* ++ * If the context is active and the event is still off, ++ * we need to retry the cross-call. ++ */ ++ if (ctx->is_active && event->state == PERF_EVENT_STATE_OFF) { ++ /* ++ * task could have been flipped by a concurrent ++ * perf_event_context_sched_out() ++ */ ++ task = ctx->task; ++ goto retry; ++ } ++ ++out: ++ raw_spin_unlock_irq(&ctx->lock); ++} ++EXPORT_SYMBOL_GPL(perf_event_enable); ++ ++int perf_event_refresh(struct perf_event *event, int refresh) ++{ ++ /* ++ * not supported on inherited events ++ */ ++ if (event->attr.inherit || !is_sampling_event(event)) ++ return -EINVAL; ++ ++ atomic_add(refresh, &event->event_limit); ++ perf_event_enable(event); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(perf_event_refresh); ++ ++static void ctx_sched_out(struct perf_event_context *ctx, ++ struct perf_cpu_context *cpuctx, ++ enum event_type_t event_type) ++{ ++ struct perf_event *event; ++ int is_active = ctx->is_active; ++ ++ ctx->is_active &= ~event_type; ++ if (likely(!ctx->nr_events)) ++ return; ++ ++ update_context_time(ctx); ++ update_cgrp_time_from_cpuctx(cpuctx); ++ if (!ctx->nr_active) ++ return; ++ ++ perf_pmu_disable(ctx->pmu); ++ if ((is_active & EVENT_PINNED) && (event_type & EVENT_PINNED)) { ++ list_for_each_entry(event, &ctx->pinned_groups, group_entry) ++ group_sched_out(event, cpuctx, ctx); ++ } ++ ++ if ((is_active & EVENT_FLEXIBLE) && (event_type & EVENT_FLEXIBLE)) { ++ list_for_each_entry(event, &ctx->flexible_groups, group_entry) ++ group_sched_out(event, cpuctx, ctx); ++ } ++ perf_pmu_enable(ctx->pmu); ++} ++ ++/* ++ * Test whether two contexts are equivalent, i.e. whether they have both been ++ * cloned from the same version of the same context. ++ * ++ * Equivalence is measured using a generation number in the context that is ++ * incremented on each modification to it; see unclone_ctx(), list_add_event() ++ * and list_del_event(). ++ */ ++static int context_equiv(struct perf_event_context *ctx1, ++ struct perf_event_context *ctx2) ++{ ++ lockdep_assert_held(&ctx1->lock); ++ lockdep_assert_held(&ctx2->lock); ++ ++ /* Pinning disables the swap optimization */ ++ if (ctx1->pin_count || ctx2->pin_count) ++ return 0; ++ ++ /* If ctx1 is the parent of ctx2 */ ++ if (ctx1 == ctx2->parent_ctx && ctx1->generation == ctx2->parent_gen) ++ return 1; ++ ++ /* If ctx2 is the parent of ctx1 */ ++ if (ctx1->parent_ctx == ctx2 && ctx1->parent_gen == ctx2->generation) ++ return 1; ++ ++ /* ++ * If ctx1 and ctx2 have the same parent; we flatten the parent ++ * hierarchy, see perf_event_init_context(). ++ */ ++ if (ctx1->parent_ctx && ctx1->parent_ctx == ctx2->parent_ctx && ++ ctx1->parent_gen == ctx2->parent_gen) ++ return 1; ++ ++ /* Unmatched */ ++ return 0; ++} ++ ++static void __perf_event_sync_stat(struct perf_event *event, ++ struct perf_event *next_event) ++{ ++ u64 value; ++ ++ if (!event->attr.inherit_stat) ++ return; ++ ++ /* ++ * Update the event value, we cannot use perf_event_read() ++ * because we're in the middle of a context switch and have IRQs ++ * disabled, which upsets smp_call_function_single(), however ++ * we know the event must be on the current CPU, therefore we ++ * don't need to use it. ++ */ ++ switch (event->state) { ++ case PERF_EVENT_STATE_ACTIVE: ++ event->pmu->read(event); ++ /* fall-through */ ++ ++ case PERF_EVENT_STATE_INACTIVE: ++ update_event_times(event); ++ break; ++ ++ default: ++ break; ++ } ++ ++ /* ++ * In order to keep per-task stats reliable we need to flip the event ++ * values when we flip the contexts. ++ */ ++ value = local64_read(&next_event->count); ++ value = local64_xchg(&event->count, value); ++ local64_set(&next_event->count, value); ++ ++ swap(event->total_time_enabled, next_event->total_time_enabled); ++ swap(event->total_time_running, next_event->total_time_running); ++ ++ /* ++ * Since we swizzled the values, update the user visible data too. ++ */ ++ perf_event_update_userpage(event); ++ perf_event_update_userpage(next_event); ++} ++ ++static void perf_event_sync_stat(struct perf_event_context *ctx, ++ struct perf_event_context *next_ctx) ++{ ++ struct perf_event *event, *next_event; ++ ++ if (!ctx->nr_stat) ++ return; ++ ++ update_context_time(ctx); ++ ++ event = list_first_entry(&ctx->event_list, ++ struct perf_event, event_entry); ++ ++ next_event = list_first_entry(&next_ctx->event_list, ++ struct perf_event, event_entry); ++ ++ while (&event->event_entry != &ctx->event_list && ++ &next_event->event_entry != &next_ctx->event_list) { ++ ++ __perf_event_sync_stat(event, next_event); ++ ++ event = list_next_entry(event, event_entry); ++ next_event = list_next_entry(next_event, event_entry); ++ } ++} ++ ++static void perf_event_context_sched_out(struct task_struct *task, int ctxn, ++ struct task_struct *next) ++{ ++ struct perf_event_context *ctx = task->perf_event_ctxp[ctxn]; ++ struct perf_event_context *next_ctx; ++ struct perf_event_context *parent, *next_parent; ++ struct perf_cpu_context *cpuctx; ++ int do_switch = 1; ++ ++ if (likely(!ctx)) ++ return; ++ ++ cpuctx = __get_cpu_context(ctx); ++ if (!cpuctx->task_ctx) ++ return; ++ ++ rcu_read_lock(); ++ next_ctx = next->perf_event_ctxp[ctxn]; ++ if (!next_ctx) ++ goto unlock; ++ ++ parent = rcu_dereference(ctx->parent_ctx); ++ next_parent = rcu_dereference(next_ctx->parent_ctx); ++ ++ /* If neither context have a parent context; they cannot be clones. */ ++ if (!parent && !next_parent) ++ goto unlock; ++ ++ if (next_parent == ctx || next_ctx == parent || next_parent == parent) { ++ /* ++ * Looks like the two contexts are clones, so we might be ++ * able to optimize the context switch. We lock both ++ * contexts and check that they are clones under the ++ * lock (including re-checking that neither has been ++ * uncloned in the meantime). It doesn't matter which ++ * order we take the locks because no other cpu could ++ * be trying to lock both of these tasks. ++ */ ++ raw_spin_lock(&ctx->lock); ++ raw_spin_lock_nested(&next_ctx->lock, SINGLE_DEPTH_NESTING); ++ if (context_equiv(ctx, next_ctx)) { ++ /* ++ * XXX do we need a memory barrier of sorts ++ * wrt to rcu_dereference() of perf_event_ctxp ++ */ ++ task->perf_event_ctxp[ctxn] = next_ctx; ++ next->perf_event_ctxp[ctxn] = ctx; ++ ctx->task = next; ++ next_ctx->task = task; ++ do_switch = 0; ++ ++ perf_event_sync_stat(ctx, next_ctx); ++ } ++ raw_spin_unlock(&next_ctx->lock); ++ raw_spin_unlock(&ctx->lock); ++ } ++unlock: ++ rcu_read_unlock(); ++ ++ if (do_switch) { ++ raw_spin_lock(&ctx->lock); ++ ctx_sched_out(ctx, cpuctx, EVENT_ALL); ++ cpuctx->task_ctx = NULL; ++ raw_spin_unlock(&ctx->lock); ++ } ++} ++ ++#define for_each_task_context_nr(ctxn) \ ++ for ((ctxn) = 0; (ctxn) < perf_nr_task_contexts; (ctxn)++) ++ ++/* ++ * Called from scheduler to remove the events of the current task, ++ * with interrupts disabled. ++ * ++ * We stop each event and update the event value in event->count. ++ * ++ * This does not protect us against NMI, but disable() ++ * sets the disabled bit in the control field of event _before_ ++ * accessing the event control register. If a NMI hits, then it will ++ * not restart the event. ++ */ ++void __perf_event_task_sched_out(struct task_struct *task, ++ struct task_struct *next) ++{ ++ int ctxn; ++ ++ for_each_task_context_nr(ctxn) ++ perf_event_context_sched_out(task, ctxn, next); ++ ++ /* ++ * if cgroup events exist on this CPU, then we need ++ * to check if we have to switch out PMU state. ++ * cgroup event are system-wide mode only ++ */ ++ if (atomic_read(this_cpu_ptr(&perf_cgroup_events))) ++ perf_cgroup_sched_out(task, next); ++} ++ ++static void task_ctx_sched_out(struct perf_event_context *ctx) ++{ ++ struct perf_cpu_context *cpuctx = __get_cpu_context(ctx); ++ ++ if (!cpuctx->task_ctx) ++ return; ++ ++ if (WARN_ON_ONCE(ctx != cpuctx->task_ctx)) ++ return; ++ ++ ctx_sched_out(ctx, cpuctx, EVENT_ALL); ++ cpuctx->task_ctx = NULL; ++} ++ ++/* ++ * Called with IRQs disabled ++ */ ++static void cpu_ctx_sched_out(struct perf_cpu_context *cpuctx, ++ enum event_type_t event_type) ++{ ++ ctx_sched_out(&cpuctx->ctx, cpuctx, event_type); ++} ++ ++static void ++ctx_pinned_sched_in(struct perf_event_context *ctx, ++ struct perf_cpu_context *cpuctx) ++{ ++ struct perf_event *event; ++ ++ list_for_each_entry(event, &ctx->pinned_groups, group_entry) { ++ if (event->state <= PERF_EVENT_STATE_OFF) ++ continue; ++ if (!event_filter_match(event)) ++ continue; ++ ++ /* may need to reset tstamp_enabled */ ++ if (is_cgroup_event(event)) ++ perf_cgroup_mark_enabled(event, ctx); ++ ++ if (group_can_go_on(event, cpuctx, 1)) ++ group_sched_in(event, cpuctx, ctx); ++ ++ /* ++ * If this pinned group hasn't been scheduled, ++ * put it in error state. ++ */ ++ if (event->state == PERF_EVENT_STATE_INACTIVE) { ++ update_group_times(event); ++ event->state = PERF_EVENT_STATE_ERROR; ++ } ++ } ++} ++ ++static void ++ctx_flexible_sched_in(struct perf_event_context *ctx, ++ struct perf_cpu_context *cpuctx) ++{ ++ struct perf_event *event; ++ int can_add_hw = 1; ++ ++ list_for_each_entry(event, &ctx->flexible_groups, group_entry) { ++ /* Ignore events in OFF or ERROR state */ ++ if (event->state <= PERF_EVENT_STATE_OFF) ++ continue; ++ /* ++ * Listen to the 'cpu' scheduling filter constraint ++ * of events: ++ */ ++ if (!event_filter_match(event)) ++ continue; ++ ++ /* may need to reset tstamp_enabled */ ++ if (is_cgroup_event(event)) ++ perf_cgroup_mark_enabled(event, ctx); ++ ++ if (group_can_go_on(event, cpuctx, can_add_hw)) { ++ if (group_sched_in(event, cpuctx, ctx)) ++ can_add_hw = 0; ++ } ++ } ++} ++ ++static void ++ctx_sched_in(struct perf_event_context *ctx, ++ struct perf_cpu_context *cpuctx, ++ enum event_type_t event_type, ++ struct task_struct *task) ++{ ++ u64 now; ++ int is_active = ctx->is_active; ++ ++ ctx->is_active |= event_type; ++ if (likely(!ctx->nr_events)) ++ return; ++ ++ now = perf_clock(); ++ ctx->timestamp = now; ++ perf_cgroup_set_timestamp(task, ctx); ++ /* ++ * First go through the list and put on any pinned groups ++ * in order to give them the best chance of going on. ++ */ ++ if (!(is_active & EVENT_PINNED) && (event_type & EVENT_PINNED)) ++ ctx_pinned_sched_in(ctx, cpuctx); ++ ++ /* Then walk through the lower prio flexible groups */ ++ if (!(is_active & EVENT_FLEXIBLE) && (event_type & EVENT_FLEXIBLE)) ++ ctx_flexible_sched_in(ctx, cpuctx); ++} ++ ++static void cpu_ctx_sched_in(struct perf_cpu_context *cpuctx, ++ enum event_type_t event_type, ++ struct task_struct *task) ++{ ++ struct perf_event_context *ctx = &cpuctx->ctx; ++ ++ ctx_sched_in(ctx, cpuctx, event_type, task); ++} ++ ++static void perf_event_context_sched_in(struct perf_event_context *ctx, ++ struct task_struct *task) ++{ ++ struct perf_cpu_context *cpuctx; ++ ++ cpuctx = __get_cpu_context(ctx); ++ if (cpuctx->task_ctx == ctx) ++ return; ++ ++ perf_ctx_lock(cpuctx, ctx); ++ perf_pmu_disable(ctx->pmu); ++ /* ++ * We want to keep the following priority order: ++ * cpu pinned (that don't need to move), task pinned, ++ * cpu flexible, task flexible. ++ */ ++ cpu_ctx_sched_out(cpuctx, EVENT_FLEXIBLE); ++ ++ if (ctx->nr_events) ++ cpuctx->task_ctx = ctx; ++ ++ perf_event_sched_in(cpuctx, cpuctx->task_ctx, task); ++ ++ perf_pmu_enable(ctx->pmu); ++ perf_ctx_unlock(cpuctx, ctx); ++ ++ /* ++ * Since these rotations are per-cpu, we need to ensure the ++ * cpu-context we got scheduled on is actually rotating. ++ */ ++ perf_pmu_rotate_start(ctx->pmu); ++} ++ ++/* ++ * When sampling the branck stack in system-wide, it may be necessary ++ * to flush the stack on context switch. This happens when the branch ++ * stack does not tag its entries with the pid of the current task. ++ * Otherwise it becomes impossible to associate a branch entry with a ++ * task. This ambiguity is more likely to appear when the branch stack ++ * supports priv level filtering and the user sets it to monitor only ++ * at the user level (which could be a useful measurement in system-wide ++ * mode). In that case, the risk is high of having a branch stack with ++ * branch from multiple tasks. Flushing may mean dropping the existing ++ * entries or stashing them somewhere in the PMU specific code layer. ++ * ++ * This function provides the context switch callback to the lower code ++ * layer. It is invoked ONLY when there is at least one system-wide context ++ * with at least one active event using taken branch sampling. ++ */ ++static void perf_branch_stack_sched_in(struct task_struct *prev, ++ struct task_struct *task) ++{ ++ struct perf_cpu_context *cpuctx; ++ struct pmu *pmu; ++ unsigned long flags; ++ ++ /* no need to flush branch stack if not changing task */ ++ if (prev == task) ++ return; ++ ++ local_irq_save(flags); ++ ++ rcu_read_lock(); ++ ++ list_for_each_entry_rcu(pmu, &pmus, entry) { ++ cpuctx = this_cpu_ptr(pmu->pmu_cpu_context); ++ ++ /* ++ * check if the context has at least one ++ * event using PERF_SAMPLE_BRANCH_STACK ++ */ ++ if (cpuctx->ctx.nr_branch_stack > 0 ++ && pmu->flush_branch_stack) { ++ ++ perf_ctx_lock(cpuctx, cpuctx->task_ctx); ++ ++ perf_pmu_disable(pmu); ++ ++ pmu->flush_branch_stack(); ++ ++ perf_pmu_enable(pmu); ++ ++ perf_ctx_unlock(cpuctx, cpuctx->task_ctx); ++ } ++ } ++ ++ rcu_read_unlock(); ++ ++ local_irq_restore(flags); ++} ++ ++/* ++ * Called from scheduler to add the events of the current task ++ * with interrupts disabled. ++ * ++ * We restore the event value and then enable it. ++ * ++ * This does not protect us against NMI, but enable() ++ * sets the enabled bit in the control field of event _before_ ++ * accessing the event control register. If a NMI hits, then it will ++ * keep the event running. ++ */ ++void __perf_event_task_sched_in(struct task_struct *prev, ++ struct task_struct *task) ++{ ++ struct perf_event_context *ctx; ++ int ctxn; ++ ++ for_each_task_context_nr(ctxn) { ++ ctx = task->perf_event_ctxp[ctxn]; ++ if (likely(!ctx)) ++ continue; ++ ++ perf_event_context_sched_in(ctx, task); ++ } ++ /* ++ * if cgroup events exist on this CPU, then we need ++ * to check if we have to switch in PMU state. ++ * cgroup event are system-wide mode only ++ */ ++ if (atomic_read(this_cpu_ptr(&perf_cgroup_events))) ++ perf_cgroup_sched_in(prev, task); ++ ++ /* check for system-wide branch_stack events */ ++ if (atomic_read(this_cpu_ptr(&perf_branch_stack_events))) ++ perf_branch_stack_sched_in(prev, task); ++} ++ ++static u64 perf_calculate_period(struct perf_event *event, u64 nsec, u64 count) ++{ ++ u64 frequency = event->attr.sample_freq; ++ u64 sec = NSEC_PER_SEC; ++ u64 divisor, dividend; ++ ++ int count_fls, nsec_fls, frequency_fls, sec_fls; ++ ++ count_fls = fls64(count); ++ nsec_fls = fls64(nsec); ++ frequency_fls = fls64(frequency); ++ sec_fls = 30; ++ ++ /* ++ * We got @count in @nsec, with a target of sample_freq HZ ++ * the target period becomes: ++ * ++ * @count * 10^9 ++ * period = ------------------- ++ * @nsec * sample_freq ++ * ++ */ ++ ++ /* ++ * Reduce accuracy by one bit such that @a and @b converge ++ * to a similar magnitude. ++ */ ++#define REDUCE_FLS(a, b) \ ++do { \ ++ if (a##_fls > b##_fls) { \ ++ a >>= 1; \ ++ a##_fls--; \ ++ } else { \ ++ b >>= 1; \ ++ b##_fls--; \ ++ } \ ++} while (0) ++ ++ /* ++ * Reduce accuracy until either term fits in a u64, then proceed with ++ * the other, so that finally we can do a u64/u64 division. ++ */ ++ while (count_fls + sec_fls > 64 && nsec_fls + frequency_fls > 64) { ++ REDUCE_FLS(nsec, frequency); ++ REDUCE_FLS(sec, count); ++ } ++ ++ if (count_fls + sec_fls > 64) { ++ divisor = nsec * frequency; ++ ++ while (count_fls + sec_fls > 64) { ++ REDUCE_FLS(count, sec); ++ divisor >>= 1; ++ } ++ ++ dividend = count * sec; ++ } else { ++ dividend = count * sec; ++ ++ while (nsec_fls + frequency_fls > 64) { ++ REDUCE_FLS(nsec, frequency); ++ dividend >>= 1; ++ } ++ ++ divisor = nsec * frequency; ++ } ++ ++ if (!divisor) ++ return dividend; ++ ++ return div64_u64(dividend, divisor); ++} ++ ++static DEFINE_PER_CPU(int, perf_throttled_count); ++static DEFINE_PER_CPU(u64, perf_throttled_seq); ++ ++static void perf_adjust_period(struct perf_event *event, u64 nsec, u64 count, bool disable) ++{ ++ struct hw_perf_event *hwc = &event->hw; ++ s64 period, sample_period; ++ s64 delta; ++ ++ period = perf_calculate_period(event, nsec, count); ++ ++ delta = (s64)(period - hwc->sample_period); ++ delta = (delta + 7) / 8; /* low pass filter */ ++ ++ sample_period = hwc->sample_period + delta; ++ ++ if (!sample_period) ++ sample_period = 1; ++ ++ hwc->sample_period = sample_period; ++ ++ if (local64_read(&hwc->period_left) > 8*sample_period) { ++ if (disable) ++ event->pmu->stop(event, PERF_EF_UPDATE); ++ ++ local64_set(&hwc->period_left, 0); ++ ++ if (disable) ++ event->pmu->start(event, PERF_EF_RELOAD); ++ } ++} ++ ++/* ++ * combine freq adjustment with unthrottling to avoid two passes over the ++ * events. At the same time, make sure, having freq events does not change ++ * the rate of unthrottling as that would introduce bias. ++ */ ++static void perf_adjust_freq_unthr_context(struct perf_event_context *ctx, ++ int needs_unthr) ++{ ++ struct perf_event *event; ++ struct hw_perf_event *hwc; ++ u64 now, period = TICK_NSEC; ++ s64 delta; ++ ++ /* ++ * only need to iterate over all events iff: ++ * - context have events in frequency mode (needs freq adjust) ++ * - there are events to unthrottle on this cpu ++ */ ++ if (!(ctx->nr_freq || needs_unthr)) ++ return; ++ ++ raw_spin_lock(&ctx->lock); ++ perf_pmu_disable(ctx->pmu); ++ ++ list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { ++ if (event->state != PERF_EVENT_STATE_ACTIVE) ++ continue; ++ ++ if (!event_filter_match(event)) ++ continue; ++ ++ perf_pmu_disable(event->pmu); ++ ++ hwc = &event->hw; ++ ++ if (hwc->interrupts == MAX_INTERRUPTS) { ++ hwc->interrupts = 0; ++ perf_log_throttle(event, 1); ++ event->pmu->start(event, 0); ++ } ++ ++ if (!event->attr.freq || !event->attr.sample_freq) ++ goto next; ++ ++ /* ++ * stop the event and update event->count ++ */ ++ event->pmu->stop(event, PERF_EF_UPDATE); ++ ++ now = local64_read(&event->count); ++ delta = now - hwc->freq_count_stamp; ++ hwc->freq_count_stamp = now; ++ ++ /* ++ * restart the event ++ * reload only if value has changed ++ * we have stopped the event so tell that ++ * to perf_adjust_period() to avoid stopping it ++ * twice. ++ */ ++ if (delta > 0) ++ perf_adjust_period(event, period, delta, false); ++ ++ event->pmu->start(event, delta > 0 ? PERF_EF_RELOAD : 0); ++ next: ++ perf_pmu_enable(event->pmu); ++ } ++ ++ perf_pmu_enable(ctx->pmu); ++ raw_spin_unlock(&ctx->lock); ++} ++ ++/* ++ * Round-robin a context's events: ++ */ ++static void rotate_ctx(struct perf_event_context *ctx) ++{ ++ /* ++ * Rotate the first entry last of non-pinned groups. Rotation might be ++ * disabled by the inheritance code. ++ */ ++ if (!ctx->rotate_disable) ++ list_rotate_left(&ctx->flexible_groups); ++} ++ ++/* ++ * perf_pmu_rotate_start() and perf_rotate_context() are fully serialized ++ * because they're strictly cpu affine and rotate_start is called with IRQs ++ * disabled, while rotate_context is called from IRQ context. ++ */ ++static int perf_rotate_context(struct perf_cpu_context *cpuctx) ++{ ++ struct perf_event_context *ctx = NULL; ++ int rotate = 0, remove = 1; ++ ++ if (cpuctx->ctx.nr_events) { ++ remove = 0; ++ if (cpuctx->ctx.nr_events != cpuctx->ctx.nr_active) ++ rotate = 1; ++ } ++ ++ ctx = cpuctx->task_ctx; ++ if (ctx && ctx->nr_events) { ++ remove = 0; ++ if (ctx->nr_events != ctx->nr_active) ++ rotate = 1; ++ } ++ ++ if (!rotate) ++ goto done; ++ ++ perf_ctx_lock(cpuctx, cpuctx->task_ctx); ++ perf_pmu_disable(cpuctx->ctx.pmu); ++ ++ cpu_ctx_sched_out(cpuctx, EVENT_FLEXIBLE); ++ if (ctx) ++ ctx_sched_out(ctx, cpuctx, EVENT_FLEXIBLE); ++ ++ rotate_ctx(&cpuctx->ctx); ++ if (ctx) ++ rotate_ctx(ctx); ++ ++ perf_event_sched_in(cpuctx, ctx, current); ++ ++ perf_pmu_enable(cpuctx->ctx.pmu); ++ perf_ctx_unlock(cpuctx, cpuctx->task_ctx); ++done: ++ if (remove) ++ list_del_init(&cpuctx->rotation_list); ++ ++ return rotate; ++} ++ ++#ifdef CONFIG_NO_HZ_FULL ++bool perf_event_can_stop_tick(void) ++{ ++ if (atomic_read(&nr_freq_events) || ++ __this_cpu_read(perf_throttled_count)) ++ return false; ++ else ++ return true; ++} ++#endif ++ ++void perf_event_task_tick(void) ++{ ++ struct list_head *head = this_cpu_ptr(&rotation_list); ++ struct perf_cpu_context *cpuctx, *tmp; ++ struct perf_event_context *ctx; ++ int throttled; ++ ++ WARN_ON(!irqs_disabled()); ++ ++ __this_cpu_inc(perf_throttled_seq); ++ throttled = __this_cpu_xchg(perf_throttled_count, 0); ++ ++ list_for_each_entry_safe(cpuctx, tmp, head, rotation_list) { ++ ctx = &cpuctx->ctx; ++ perf_adjust_freq_unthr_context(ctx, throttled); ++ ++ ctx = cpuctx->task_ctx; ++ if (ctx) ++ perf_adjust_freq_unthr_context(ctx, throttled); ++ } ++} ++ ++static int event_enable_on_exec(struct perf_event *event, ++ struct perf_event_context *ctx) ++{ ++ if (!event->attr.enable_on_exec) ++ return 0; ++ ++ event->attr.enable_on_exec = 0; ++ if (event->state >= PERF_EVENT_STATE_INACTIVE) ++ return 0; ++ ++ __perf_event_mark_enabled(event); ++ ++ return 1; ++} ++ ++/* ++ * Enable all of a task's events that have been marked enable-on-exec. ++ * This expects task == current. ++ */ ++static void perf_event_enable_on_exec(struct perf_event_context *ctx) ++{ ++ struct perf_event_context *clone_ctx = NULL; ++ struct perf_event *event; ++ unsigned long flags; ++ int enabled = 0; ++ int ret; ++ ++ local_irq_save(flags); ++ if (!ctx || !ctx->nr_events) ++ goto out; ++ ++ /* ++ * We must ctxsw out cgroup events to avoid conflict ++ * when invoking perf_task_event_sched_in() later on ++ * in this function. Otherwise we end up trying to ++ * ctxswin cgroup events which are already scheduled ++ * in. ++ */ ++ perf_cgroup_sched_out(current, NULL); ++ ++ raw_spin_lock(&ctx->lock); ++ task_ctx_sched_out(ctx); ++ ++ list_for_each_entry(event, &ctx->event_list, event_entry) { ++ ret = event_enable_on_exec(event, ctx); ++ if (ret) ++ enabled = 1; ++ } ++ ++ /* ++ * Unclone this context if we enabled any event. ++ */ ++ if (enabled) ++ clone_ctx = unclone_ctx(ctx); ++ ++ raw_spin_unlock(&ctx->lock); ++ ++ /* ++ * Also calls ctxswin for cgroup events, if any: ++ */ ++ perf_event_context_sched_in(ctx, ctx->task); ++out: ++ local_irq_restore(flags); ++ ++ if (clone_ctx) ++ put_ctx(clone_ctx); ++} ++ ++void perf_event_exec(void) ++{ ++ struct perf_event_context *ctx; ++ int ctxn; ++ ++ rcu_read_lock(); ++ for_each_task_context_nr(ctxn) { ++ ctx = current->perf_event_ctxp[ctxn]; ++ if (!ctx) ++ continue; ++ ++ perf_event_enable_on_exec(ctx); ++ } ++ rcu_read_unlock(); ++} ++ ++/* ++ * Cross CPU call to read the hardware event ++ */ ++static void __perf_event_read(void *info) ++{ ++ struct perf_event *event = info; ++ struct perf_event_context *ctx = event->ctx; ++ struct perf_cpu_context *cpuctx = __get_cpu_context(ctx); ++ ++ /* ++ * If this is a task context, we need to check whether it is ++ * the current task context of this cpu. If not it has been ++ * scheduled out before the smp call arrived. In that case ++ * event->count would have been updated to a recent sample ++ * when the event was scheduled out. ++ */ ++ if (ctx->task && cpuctx->task_ctx != ctx) ++ return; ++ ++ raw_spin_lock(&ctx->lock); ++ if (ctx->is_active) { ++ update_context_time(ctx); ++ update_cgrp_time_from_event(event); ++ } ++ update_event_times(event); ++ if (event->state == PERF_EVENT_STATE_ACTIVE) ++ event->pmu->read(event); ++ raw_spin_unlock(&ctx->lock); ++} ++ ++static inline u64 perf_event_count(struct perf_event *event) ++{ ++ return local64_read(&event->count) + atomic64_read(&event->child_count); ++} ++ ++static u64 perf_event_read(struct perf_event *event) ++{ ++ /* ++ * If event is enabled and currently active on a CPU, update the ++ * value in the event structure: ++ */ ++ if (event->state == PERF_EVENT_STATE_ACTIVE) { ++ smp_call_function_single(event->oncpu, ++ __perf_event_read, event, 1); ++ } else if (event->state == PERF_EVENT_STATE_INACTIVE) { ++ struct perf_event_context *ctx = event->ctx; ++ unsigned long flags; ++ ++ raw_spin_lock_irqsave(&ctx->lock, flags); ++ /* ++ * may read while context is not active ++ * (e.g., thread is blocked), in that case ++ * we cannot update context time ++ */ ++ if (ctx->is_active) { ++ update_context_time(ctx); ++ update_cgrp_time_from_event(event); ++ } ++ update_event_times(event); ++ raw_spin_unlock_irqrestore(&ctx->lock, flags); ++ } ++ ++ return perf_event_count(event); ++} ++ ++/* ++ * Initialize the perf_event context in a task_struct: ++ */ ++static void __perf_event_init_context(struct perf_event_context *ctx) ++{ ++ raw_spin_lock_init(&ctx->lock); ++ mutex_init(&ctx->mutex); ++ INIT_LIST_HEAD(&ctx->pinned_groups); ++ INIT_LIST_HEAD(&ctx->flexible_groups); ++ INIT_LIST_HEAD(&ctx->event_list); ++ atomic_set(&ctx->refcount, 1); ++ INIT_DELAYED_WORK(&ctx->orphans_remove, orphans_remove_work); ++} ++ ++static struct perf_event_context * ++alloc_perf_context(struct pmu *pmu, struct task_struct *task) ++{ ++ struct perf_event_context *ctx; ++ ++ ctx = kzalloc(sizeof(struct perf_event_context), GFP_KERNEL); ++ if (!ctx) ++ return NULL; ++ ++ __perf_event_init_context(ctx); ++ if (task) { ++ ctx->task = task; ++ get_task_struct(task); ++ } ++ ctx->pmu = pmu; ++ ++ return ctx; ++} ++ ++static struct task_struct * ++find_lively_task_by_vpid(pid_t vpid) ++{ ++ struct task_struct *task; ++ int err; ++ ++ rcu_read_lock(); ++ if (!vpid) ++ task = current; ++ else ++ task = find_task_by_vpid(vpid); ++ if (task) ++ get_task_struct(task); ++ rcu_read_unlock(); ++ ++ if (!task) ++ return ERR_PTR(-ESRCH); ++ ++ /* Reuse ptrace permission checks for now. */ ++ err = -EACCES; ++ if (!ptrace_may_access(task, PTRACE_MODE_READ)) ++ goto errout; ++ ++ return task; ++errout: ++ put_task_struct(task); ++ return ERR_PTR(err); ++ ++} ++ ++/* ++ * Returns a matching context with refcount and pincount. ++ */ ++static struct perf_event_context * ++find_get_context(struct pmu *pmu, struct task_struct *task, int cpu) ++{ ++ struct perf_event_context *ctx, *clone_ctx = NULL; ++ struct perf_cpu_context *cpuctx; ++ unsigned long flags; ++ int ctxn, err; ++ ++ if (!task) { ++ /* Must be root to operate on a CPU event: */ ++ if (perf_paranoid_cpu() && !capable(CAP_SYS_ADMIN)) ++ return ERR_PTR(-EACCES); ++ ++ /* ++ * We could be clever and allow to attach a event to an ++ * offline CPU and activate it when the CPU comes up, but ++ * that's for later. ++ */ ++ if (!cpu_online(cpu)) ++ return ERR_PTR(-ENODEV); ++ ++ cpuctx = per_cpu_ptr(pmu->pmu_cpu_context, cpu); ++ ctx = &cpuctx->ctx; ++ get_ctx(ctx); ++ ++ctx->pin_count; ++ ++ return ctx; ++ } ++ ++ err = -EINVAL; ++ ctxn = pmu->task_ctx_nr; ++ if (ctxn < 0) ++ goto errout; ++ ++retry: ++ ctx = perf_lock_task_context(task, ctxn, &flags); ++ if (ctx) { ++ clone_ctx = unclone_ctx(ctx); ++ ++ctx->pin_count; ++ raw_spin_unlock_irqrestore(&ctx->lock, flags); ++ ++ if (clone_ctx) ++ put_ctx(clone_ctx); ++ } else { ++ ctx = alloc_perf_context(pmu, task); ++ err = -ENOMEM; ++ if (!ctx) ++ goto errout; ++ ++ err = 0; ++ mutex_lock(&task->perf_event_mutex); ++ /* ++ * If it has already passed perf_event_exit_task(). ++ * we must see PF_EXITING, it takes this mutex too. ++ */ ++ if (task->flags & PF_EXITING) ++ err = -ESRCH; ++ else if (task->perf_event_ctxp[ctxn]) ++ err = -EAGAIN; ++ else { ++ get_ctx(ctx); ++ ++ctx->pin_count; ++ rcu_assign_pointer(task->perf_event_ctxp[ctxn], ctx); ++ } ++ mutex_unlock(&task->perf_event_mutex); ++ ++ if (unlikely(err)) { ++ put_ctx(ctx); ++ ++ if (err == -EAGAIN) ++ goto retry; ++ goto errout; ++ } ++ } ++ ++ return ctx; ++ ++errout: ++ return ERR_PTR(err); ++} ++ ++static void perf_event_free_filter(struct perf_event *event); ++ ++static void free_event_rcu(struct rcu_head *head) ++{ ++ struct perf_event *event; ++ ++ event = container_of(head, struct perf_event, rcu_head); ++ if (event->ns) ++ put_pid_ns(event->ns); ++ perf_event_free_filter(event); ++ kfree(event); ++} ++ ++static void ring_buffer_put(struct ring_buffer *rb); ++static void ring_buffer_attach(struct perf_event *event, ++ struct ring_buffer *rb); ++ ++static void unaccount_event_cpu(struct perf_event *event, int cpu) ++{ ++ if (event->parent) ++ return; ++ ++ if (has_branch_stack(event)) { ++ if (!(event->attach_state & PERF_ATTACH_TASK)) ++ atomic_dec(&per_cpu(perf_branch_stack_events, cpu)); ++ } ++ if (is_cgroup_event(event)) ++ atomic_dec(&per_cpu(perf_cgroup_events, cpu)); ++} ++ ++static void unaccount_event(struct perf_event *event) ++{ ++ if (event->parent) ++ return; ++ ++ if (event->attach_state & PERF_ATTACH_TASK) ++ static_key_slow_dec_deferred(&perf_sched_events); ++ if (event->attr.mmap || event->attr.mmap_data) ++ atomic_dec(&nr_mmap_events); ++ if (event->attr.comm) ++ atomic_dec(&nr_comm_events); ++ if (event->attr.task) ++ atomic_dec(&nr_task_events); ++ if (event->attr.freq) ++ atomic_dec(&nr_freq_events); ++ if (is_cgroup_event(event)) ++ static_key_slow_dec_deferred(&perf_sched_events); ++ if (has_branch_stack(event)) ++ static_key_slow_dec_deferred(&perf_sched_events); ++ ++ unaccount_event_cpu(event, event->cpu); ++} ++ ++static void __free_event(struct perf_event *event) ++{ ++ if (!event->parent) { ++ if (event->attr.sample_type & PERF_SAMPLE_CALLCHAIN) ++ put_callchain_buffers(); ++ } ++ ++ if (event->destroy) ++ event->destroy(event); ++ ++ if (event->ctx) ++ put_ctx(event->ctx); ++ ++ if (event->pmu) ++ module_put(event->pmu->module); ++ ++ call_rcu(&event->rcu_head, free_event_rcu); ++} ++ ++static void _free_event(struct perf_event *event) ++{ ++ irq_work_sync(&event->pending); ++ ++ unaccount_event(event); ++ ++ if (event->rb) { ++ /* ++ * Can happen when we close an event with re-directed output. ++ * ++ * Since we have a 0 refcount, perf_mmap_close() will skip ++ * over us; possibly making our ring_buffer_put() the last. ++ */ ++ mutex_lock(&event->mmap_mutex); ++ ring_buffer_attach(event, NULL); ++ mutex_unlock(&event->mmap_mutex); ++ } ++ ++ if (is_cgroup_event(event)) ++ perf_detach_cgroup(event); ++ ++ __free_event(event); ++} ++ ++/* ++ * Used to free events which have a known refcount of 1, such as in error paths ++ * where the event isn't exposed yet and inherited events. ++ */ ++static void free_event(struct perf_event *event) ++{ ++ if (WARN(atomic_long_cmpxchg(&event->refcount, 1, 0) != 1, ++ "unexpected event refcount: %ld; ptr=%p\n", ++ atomic_long_read(&event->refcount), event)) { ++ /* leak to avoid use-after-free */ ++ return; ++ } ++ ++ _free_event(event); ++} ++ ++/* ++ * Remove user event from the owner task. ++ */ ++static void perf_remove_from_owner(struct perf_event *event) ++{ ++ struct task_struct *owner; ++ ++ rcu_read_lock(); ++ owner = ACCESS_ONCE(event->owner); ++ /* ++ * Matches the smp_wmb() in perf_event_exit_task(). If we observe ++ * !owner it means the list deletion is complete and we can indeed ++ * free this event, otherwise we need to serialize on ++ * owner->perf_event_mutex. ++ */ ++ smp_read_barrier_depends(); ++ if (owner) { ++ /* ++ * Since delayed_put_task_struct() also drops the last ++ * task reference we can safely take a new reference ++ * while holding the rcu_read_lock(). ++ */ ++ get_task_struct(owner); ++ } ++ rcu_read_unlock(); ++ ++ if (owner) { ++ mutex_lock(&owner->perf_event_mutex); ++ /* ++ * We have to re-check the event->owner field, if it is cleared ++ * we raced with perf_event_exit_task(), acquiring the mutex ++ * ensured they're done, and we can proceed with freeing the ++ * event. ++ */ ++ if (event->owner) ++ list_del_init(&event->owner_entry); ++ mutex_unlock(&owner->perf_event_mutex); ++ put_task_struct(owner); ++ } ++} ++ ++/* ++ * Called when the last reference to the file is gone. ++ */ ++static void put_event(struct perf_event *event) ++{ ++ struct perf_event_context *ctx = event->ctx; ++ ++ if (!atomic_long_dec_and_test(&event->refcount)) ++ return; ++ ++ if (!is_kernel_event(event)) ++ perf_remove_from_owner(event); ++ ++ WARN_ON_ONCE(ctx->parent_ctx); ++ /* ++ * There are two ways this annotation is useful: ++ * ++ * 1) there is a lock recursion from perf_event_exit_task ++ * see the comment there. ++ * ++ * 2) there is a lock-inversion with mmap_sem through ++ * perf_event_read_group(), which takes faults while ++ * holding ctx->mutex, however this is called after ++ * the last filedesc died, so there is no possibility ++ * to trigger the AB-BA case. ++ */ ++ mutex_lock_nested(&ctx->mutex, SINGLE_DEPTH_NESTING); ++ perf_remove_from_context(event, true); ++ mutex_unlock(&ctx->mutex); ++ ++ _free_event(event); ++} ++ ++int perf_event_release_kernel(struct perf_event *event) ++{ ++ put_event(event); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(perf_event_release_kernel); ++ ++static int perf_release(struct inode *inode, struct file *file) ++{ ++ put_event(file->private_data); ++ return 0; ++} ++ ++/* ++ * Remove all orphanes events from the context. ++ */ ++static void orphans_remove_work(struct work_struct *work) ++{ ++ struct perf_event_context *ctx; ++ struct perf_event *event, *tmp; ++ ++ ctx = container_of(work, struct perf_event_context, ++ orphans_remove.work); ++ ++ mutex_lock(&ctx->mutex); ++ list_for_each_entry_safe(event, tmp, &ctx->event_list, event_entry) { ++ struct perf_event *parent_event = event->parent; ++ ++ if (!is_orphaned_child(event)) ++ continue; ++ ++ perf_remove_from_context(event, true); ++ ++ mutex_lock(&parent_event->child_mutex); ++ list_del_init(&event->child_list); ++ mutex_unlock(&parent_event->child_mutex); ++ ++ free_event(event); ++ put_event(parent_event); ++ } ++ ++ raw_spin_lock_irq(&ctx->lock); ++ ctx->orphans_remove_sched = false; ++ raw_spin_unlock_irq(&ctx->lock); ++ mutex_unlock(&ctx->mutex); ++ ++ put_ctx(ctx); ++} ++ ++u64 perf_event_read_value(struct perf_event *event, u64 *enabled, u64 *running) ++{ ++ struct perf_event *child; ++ u64 total = 0; ++ ++ *enabled = 0; ++ *running = 0; ++ ++ mutex_lock(&event->child_mutex); ++ total += perf_event_read(event); ++ *enabled += event->total_time_enabled + ++ atomic64_read(&event->child_total_time_enabled); ++ *running += event->total_time_running + ++ atomic64_read(&event->child_total_time_running); ++ ++ list_for_each_entry(child, &event->child_list, child_list) { ++ total += perf_event_read(child); ++ *enabled += child->total_time_enabled; ++ *running += child->total_time_running; ++ } ++ mutex_unlock(&event->child_mutex); ++ ++ return total; ++} ++EXPORT_SYMBOL_GPL(perf_event_read_value); ++ ++static int perf_event_read_group(struct perf_event *event, ++ u64 read_format, char __user *buf) ++{ ++ struct perf_event *leader = event->group_leader, *sub; ++ int n = 0, size = 0, ret = -EFAULT; ++ struct perf_event_context *ctx = leader->ctx; ++ u64 values[5]; ++ u64 count, enabled, running; ++ ++ mutex_lock(&ctx->mutex); ++ count = perf_event_read_value(leader, &enabled, &running); ++ ++ values[n++] = 1 + leader->nr_siblings; ++ if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) ++ values[n++] = enabled; ++ if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) ++ values[n++] = running; ++ values[n++] = count; ++ if (read_format & PERF_FORMAT_ID) ++ values[n++] = primary_event_id(leader); ++ ++ size = n * sizeof(u64); ++ ++ if (copy_to_user(buf, values, size)) ++ goto unlock; ++ ++ ret = size; ++ ++ list_for_each_entry(sub, &leader->sibling_list, group_entry) { ++ n = 0; ++ ++ values[n++] = perf_event_read_value(sub, &enabled, &running); ++ if (read_format & PERF_FORMAT_ID) ++ values[n++] = primary_event_id(sub); ++ ++ size = n * sizeof(u64); ++ ++ if (copy_to_user(buf + ret, values, size)) { ++ ret = -EFAULT; ++ goto unlock; ++ } ++ ++ ret += size; ++ } ++unlock: ++ mutex_unlock(&ctx->mutex); ++ ++ return ret; ++} ++ ++static int perf_event_read_one(struct perf_event *event, ++ u64 read_format, char __user *buf) ++{ ++ u64 enabled, running; ++ u64 values[4]; ++ int n = 0; ++ ++ values[n++] = perf_event_read_value(event, &enabled, &running); ++ if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) ++ values[n++] = enabled; ++ if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) ++ values[n++] = running; ++ if (read_format & PERF_FORMAT_ID) ++ values[n++] = primary_event_id(event); ++ ++ if (copy_to_user(buf, values, n * sizeof(u64))) ++ return -EFAULT; ++ ++ return n * sizeof(u64); ++} ++ ++static bool is_event_hup(struct perf_event *event) ++{ ++ bool no_children; ++ ++ if (event->state != PERF_EVENT_STATE_EXIT) ++ return false; ++ ++ mutex_lock(&event->child_mutex); ++ no_children = list_empty(&event->child_list); ++ mutex_unlock(&event->child_mutex); ++ return no_children; ++} ++ ++/* ++ * Read the performance event - simple non blocking version for now ++ */ ++static ssize_t ++perf_read_hw(struct perf_event *event, char __user *buf, size_t count) ++{ ++ u64 read_format = event->attr.read_format; ++ int ret; ++ ++ /* ++ * Return end-of-file for a read on a event that is in ++ * error state (i.e. because it was pinned but it couldn't be ++ * scheduled on to the CPU at some point). ++ */ ++ if (event->state == PERF_EVENT_STATE_ERROR) ++ return 0; ++ ++ if (count < event->read_size) ++ return -ENOSPC; ++ ++ WARN_ON_ONCE(event->ctx->parent_ctx); ++ if (read_format & PERF_FORMAT_GROUP) ++ ret = perf_event_read_group(event, read_format, buf); ++ else ++ ret = perf_event_read_one(event, read_format, buf); ++ ++ return ret; ++} ++ ++static ssize_t ++perf_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) ++{ ++ struct perf_event *event = file->private_data; ++ ++ return perf_read_hw(event, buf, count); ++} ++ ++static unsigned int perf_poll(struct file *file, poll_table *wait) ++{ ++ struct perf_event *event = file->private_data; ++ struct ring_buffer *rb; ++ unsigned int events = POLLHUP; ++ ++ poll_wait(file, &event->waitq, wait); ++ ++ if (is_event_hup(event)) ++ return events; ++ ++ /* ++ * Pin the event->rb by taking event->mmap_mutex; otherwise ++ * perf_event_set_output() can swizzle our rb and make us miss wakeups. ++ */ ++ mutex_lock(&event->mmap_mutex); ++ rb = event->rb; ++ if (rb) ++ events = atomic_xchg(&rb->poll, 0); ++ mutex_unlock(&event->mmap_mutex); ++ return events; ++} ++ ++static void perf_event_reset(struct perf_event *event) ++{ ++ (void)perf_event_read(event); ++ local64_set(&event->count, 0); ++ perf_event_update_userpage(event); ++} ++ ++/* ++ * Holding the top-level event's child_mutex means that any ++ * descendant process that has inherited this event will block ++ * in sync_child_event if it goes to exit, thus satisfying the ++ * task existence requirements of perf_event_enable/disable. ++ */ ++static void perf_event_for_each_child(struct perf_event *event, ++ void (*func)(struct perf_event *)) ++{ ++ struct perf_event *child; ++ ++ WARN_ON_ONCE(event->ctx->parent_ctx); ++ mutex_lock(&event->child_mutex); ++ func(event); ++ list_for_each_entry(child, &event->child_list, child_list) ++ func(child); ++ mutex_unlock(&event->child_mutex); ++} ++ ++static void perf_event_for_each(struct perf_event *event, ++ void (*func)(struct perf_event *)) ++{ ++ struct perf_event_context *ctx = event->ctx; ++ struct perf_event *sibling; ++ ++ WARN_ON_ONCE(ctx->parent_ctx); ++ mutex_lock(&ctx->mutex); ++ event = event->group_leader; ++ ++ perf_event_for_each_child(event, func); ++ list_for_each_entry(sibling, &event->sibling_list, group_entry) ++ perf_event_for_each_child(sibling, func); ++ mutex_unlock(&ctx->mutex); ++} ++ ++static int perf_event_period(struct perf_event *event, u64 __user *arg) ++{ ++ struct perf_event_context *ctx = event->ctx; ++ int ret = 0, active; ++ u64 value; ++ ++ if (!is_sampling_event(event)) ++ return -EINVAL; ++ ++ if (copy_from_user(&value, arg, sizeof(value))) ++ return -EFAULT; ++ ++ if (!value) ++ return -EINVAL; ++ ++ raw_spin_lock_irq(&ctx->lock); ++ if (event->attr.freq) { ++ if (value > sysctl_perf_event_sample_rate) { ++ ret = -EINVAL; ++ goto unlock; ++ } ++ ++ event->attr.sample_freq = value; ++ } else { ++ event->attr.sample_period = value; ++ event->hw.sample_period = value; ++ } ++ ++ active = (event->state == PERF_EVENT_STATE_ACTIVE); ++ if (active) { ++ perf_pmu_disable(ctx->pmu); ++ event->pmu->stop(event, PERF_EF_UPDATE); ++ } ++ ++ local64_set(&event->hw.period_left, 0); ++ ++ if (active) { ++ event->pmu->start(event, PERF_EF_RELOAD); ++ perf_pmu_enable(ctx->pmu); ++ } ++ ++unlock: ++ raw_spin_unlock_irq(&ctx->lock); ++ ++ return ret; ++} ++ ++static const struct file_operations perf_fops; ++ ++static inline int perf_fget_light(int fd, struct fd *p) ++{ ++ struct fd f = fdget(fd); ++ if (!f.file) ++ return -EBADF; ++ ++ if (f.file->f_op != &perf_fops) { ++ fdput(f); ++ return -EBADF; ++ } ++ *p = f; ++ return 0; ++} ++ ++static int perf_event_set_output(struct perf_event *event, ++ struct perf_event *output_event); ++static int perf_event_set_filter(struct perf_event *event, void __user *arg); ++ ++static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ struct perf_event *event = file->private_data; ++ void (*func)(struct perf_event *); ++ u32 flags = arg; ++ ++ switch (cmd) { ++ case PERF_EVENT_IOC_ENABLE: ++ func = perf_event_enable; ++ break; ++ case PERF_EVENT_IOC_DISABLE: ++ func = perf_event_disable; ++ break; ++ case PERF_EVENT_IOC_RESET: ++ func = perf_event_reset; ++ break; ++ ++ case PERF_EVENT_IOC_REFRESH: ++ return perf_event_refresh(event, arg); ++ ++ case PERF_EVENT_IOC_PERIOD: ++ return perf_event_period(event, (u64 __user *)arg); ++ ++ case PERF_EVENT_IOC_ID: ++ { ++ u64 id = primary_event_id(event); ++ ++ if (copy_to_user((void __user *)arg, &id, sizeof(id))) ++ return -EFAULT; ++ return 0; ++ } ++ ++ case PERF_EVENT_IOC_SET_OUTPUT: ++ { ++ int ret; ++ if (arg != -1) { ++ struct perf_event *output_event; ++ struct fd output; ++ ret = perf_fget_light(arg, &output); ++ if (ret) ++ return ret; ++ output_event = output.file->private_data; ++ ret = perf_event_set_output(event, output_event); ++ fdput(output); ++ } else { ++ ret = perf_event_set_output(event, NULL); ++ } ++ return ret; ++ } ++ ++ case PERF_EVENT_IOC_SET_FILTER: ++ return perf_event_set_filter(event, (void __user *)arg); ++ ++ default: ++ return -ENOTTY; ++ } ++ ++ if (flags & PERF_IOC_FLAG_GROUP) ++ perf_event_for_each(event, func); ++ else ++ perf_event_for_each_child(event, func); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_COMPAT ++static long perf_compat_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ switch (_IOC_NR(cmd)) { ++ case _IOC_NR(PERF_EVENT_IOC_SET_FILTER): ++ case _IOC_NR(PERF_EVENT_IOC_ID): ++ /* Fix up pointer size (usually 4 -> 8 in 32-on-64-bit case */ ++ if (_IOC_SIZE(cmd) == sizeof(compat_uptr_t)) { ++ cmd &= ~IOCSIZE_MASK; ++ cmd |= sizeof(void *) << IOCSIZE_SHIFT; ++ } ++ break; ++ } ++ return perf_ioctl(file, cmd, arg); ++} ++#else ++# define perf_compat_ioctl NULL ++#endif ++ ++int perf_event_task_enable(void) ++{ ++ struct perf_event *event; ++ ++ mutex_lock(¤t->perf_event_mutex); ++ list_for_each_entry(event, ¤t->perf_event_list, owner_entry) ++ perf_event_for_each_child(event, perf_event_enable); ++ mutex_unlock(¤t->perf_event_mutex); ++ ++ return 0; ++} ++ ++int perf_event_task_disable(void) ++{ ++ struct perf_event *event; ++ ++ mutex_lock(¤t->perf_event_mutex); ++ list_for_each_entry(event, ¤t->perf_event_list, owner_entry) ++ perf_event_for_each_child(event, perf_event_disable); ++ mutex_unlock(¤t->perf_event_mutex); ++ ++ return 0; ++} ++ ++static int perf_event_index(struct perf_event *event) ++{ ++ if (event->hw.state & PERF_HES_STOPPED) ++ return 0; ++ ++ if (event->state != PERF_EVENT_STATE_ACTIVE) ++ return 0; ++ ++ return event->pmu->event_idx(event); ++} ++ ++static void calc_timer_values(struct perf_event *event, ++ u64 *now, ++ u64 *enabled, ++ u64 *running) ++{ ++ u64 ctx_time; ++ ++ *now = perf_clock(); ++ ctx_time = event->shadow_ctx_time + *now; ++ *enabled = ctx_time - event->tstamp_enabled; ++ *running = ctx_time - event->tstamp_running; ++} ++ ++static void perf_event_init_userpage(struct perf_event *event) ++{ ++ struct perf_event_mmap_page *userpg; ++ struct ring_buffer *rb; ++ ++ rcu_read_lock(); ++ rb = rcu_dereference(event->rb); ++ if (!rb) ++ goto unlock; ++ ++ userpg = rb->user_page; ++ ++ /* Allow new userspace to detect that bit 0 is deprecated */ ++ userpg->cap_bit0_is_deprecated = 1; ++ userpg->size = offsetof(struct perf_event_mmap_page, __reserved); ++ ++unlock: ++ rcu_read_unlock(); ++} ++ ++void __weak arch_perf_update_userpage(struct perf_event_mmap_page *userpg, u64 now) ++{ ++} ++ ++/* ++ * Callers need to ensure there can be no nesting of this function, otherwise ++ * the seqlock logic goes bad. We can not serialize this because the arch ++ * code calls this from NMI context. ++ */ ++void perf_event_update_userpage(struct perf_event *event) ++{ ++ struct perf_event_mmap_page *userpg; ++ struct ring_buffer *rb; ++ u64 enabled, running, now; ++ ++ rcu_read_lock(); ++ rb = rcu_dereference(event->rb); ++ if (!rb) ++ goto unlock; ++ ++ /* ++ * compute total_time_enabled, total_time_running ++ * based on snapshot values taken when the event ++ * was last scheduled in. ++ * ++ * we cannot simply called update_context_time() ++ * because of locking issue as we can be called in ++ * NMI context ++ */ ++ calc_timer_values(event, &now, &enabled, &running); ++ ++ userpg = rb->user_page; ++ /* ++ * Disable preemption so as to not let the corresponding user-space ++ * spin too long if we get preempted. ++ */ ++ preempt_disable(); ++ ++userpg->lock; ++ barrier(); ++ userpg->index = perf_event_index(event); ++ userpg->offset = perf_event_count(event); ++ if (userpg->index) ++ userpg->offset -= local64_read(&event->hw.prev_count); ++ ++ userpg->time_enabled = enabled + ++ atomic64_read(&event->child_total_time_enabled); ++ ++ userpg->time_running = running + ++ atomic64_read(&event->child_total_time_running); ++ ++ arch_perf_update_userpage(userpg, now); ++ ++ barrier(); ++ ++userpg->lock; ++ preempt_enable(); ++unlock: ++ rcu_read_unlock(); ++} ++ ++static int perf_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) ++{ ++ struct perf_event *event = vma->vm_file->private_data; ++ struct ring_buffer *rb; ++ int ret = VM_FAULT_SIGBUS; ++ ++ if (vmf->flags & FAULT_FLAG_MKWRITE) { ++ if (vmf->pgoff == 0) ++ ret = 0; ++ return ret; ++ } ++ ++ rcu_read_lock(); ++ rb = rcu_dereference(event->rb); ++ if (!rb) ++ goto unlock; ++ ++ if (vmf->pgoff && (vmf->flags & FAULT_FLAG_WRITE)) ++ goto unlock; ++ ++ vmf->page = perf_mmap_to_page(rb, vmf->pgoff); ++ if (!vmf->page) ++ goto unlock; ++ ++ get_page(vmf->page); ++ vmf->page->mapping = vma->vm_file->f_mapping; ++ vmf->page->index = vmf->pgoff; ++ ++ ret = 0; ++unlock: ++ rcu_read_unlock(); ++ ++ return ret; ++} ++ ++static void ring_buffer_attach(struct perf_event *event, ++ struct ring_buffer *rb) ++{ ++ struct ring_buffer *old_rb = NULL; ++ unsigned long flags; ++ ++ if (event->rb) { ++ /* ++ * Should be impossible, we set this when removing ++ * event->rb_entry and wait/clear when adding event->rb_entry. ++ */ ++ WARN_ON_ONCE(event->rcu_pending); ++ ++ old_rb = event->rb; ++ event->rcu_batches = get_state_synchronize_rcu(); ++ event->rcu_pending = 1; ++ ++ spin_lock_irqsave(&old_rb->event_lock, flags); ++ list_del_rcu(&event->rb_entry); ++ spin_unlock_irqrestore(&old_rb->event_lock, flags); ++ } ++ ++ if (event->rcu_pending && rb) { ++ cond_synchronize_rcu(event->rcu_batches); ++ event->rcu_pending = 0; ++ } ++ ++ if (rb) { ++ spin_lock_irqsave(&rb->event_lock, flags); ++ list_add_rcu(&event->rb_entry, &rb->event_list); ++ spin_unlock_irqrestore(&rb->event_lock, flags); ++ } ++ ++ rcu_assign_pointer(event->rb, rb); ++ ++ if (old_rb) { ++ ring_buffer_put(old_rb); ++ /* ++ * Since we detached before setting the new rb, so that we ++ * could attach the new rb, we could have missed a wakeup. ++ * Provide it now. ++ */ ++ wake_up_all(&event->waitq); ++ } ++} ++ ++static void ring_buffer_wakeup(struct perf_event *event) ++{ ++ struct ring_buffer *rb; ++ ++ rcu_read_lock(); ++ rb = rcu_dereference(event->rb); ++ if (rb) { ++ list_for_each_entry_rcu(event, &rb->event_list, rb_entry) ++ wake_up_all(&event->waitq); ++ } ++ rcu_read_unlock(); ++} ++ ++static void rb_free_rcu(struct rcu_head *rcu_head) ++{ ++ struct ring_buffer *rb; ++ ++ rb = container_of(rcu_head, struct ring_buffer, rcu_head); ++ rb_free(rb); ++} ++ ++static struct ring_buffer *ring_buffer_get(struct perf_event *event) ++{ ++ struct ring_buffer *rb; ++ ++ rcu_read_lock(); ++ rb = rcu_dereference(event->rb); ++ if (rb) { ++ if (!atomic_inc_not_zero(&rb->refcount)) ++ rb = NULL; ++ } ++ rcu_read_unlock(); ++ ++ return rb; ++} ++ ++static void ring_buffer_put(struct ring_buffer *rb) ++{ ++ if (!atomic_dec_and_test(&rb->refcount)) ++ return; ++ ++ WARN_ON_ONCE(!list_empty(&rb->event_list)); ++ ++ call_rcu(&rb->rcu_head, rb_free_rcu); ++} ++ ++static void perf_mmap_open(struct vm_area_struct *vma) ++{ ++ struct perf_event *event = vma->vm_file->private_data; ++ ++ atomic_inc(&event->mmap_count); ++ atomic_inc(&event->rb->mmap_count); ++} ++ ++/* ++ * A buffer can be mmap()ed multiple times; either directly through the same ++ * event, or through other events by use of perf_event_set_output(). ++ * ++ * In order to undo the VM accounting done by perf_mmap() we need to destroy ++ * the buffer here, where we still have a VM context. This means we need ++ * to detach all events redirecting to us. ++ */ ++static void perf_mmap_close(struct vm_area_struct *vma) ++{ ++ struct perf_event *event = vma->vm_file->private_data; ++ ++ struct ring_buffer *rb = ring_buffer_get(event); ++ struct user_struct *mmap_user = rb->mmap_user; ++ int mmap_locked = rb->mmap_locked; ++ unsigned long size = perf_data_size(rb); ++ ++ atomic_dec(&rb->mmap_count); ++ ++ if (!atomic_dec_and_mutex_lock(&event->mmap_count, &event->mmap_mutex)) ++ goto out_put; ++ ++ ring_buffer_attach(event, NULL); ++ mutex_unlock(&event->mmap_mutex); ++ ++ /* If there's still other mmap()s of this buffer, we're done. */ ++ if (atomic_read(&rb->mmap_count)) ++ goto out_put; ++ ++ /* ++ * No other mmap()s, detach from all other events that might redirect ++ * into the now unreachable buffer. Somewhat complicated by the ++ * fact that rb::event_lock otherwise nests inside mmap_mutex. ++ */ ++again: ++ rcu_read_lock(); ++ list_for_each_entry_rcu(event, &rb->event_list, rb_entry) { ++ if (!atomic_long_inc_not_zero(&event->refcount)) { ++ /* ++ * This event is en-route to free_event() which will ++ * detach it and remove it from the list. ++ */ ++ continue; ++ } ++ rcu_read_unlock(); ++ ++ mutex_lock(&event->mmap_mutex); ++ /* ++ * Check we didn't race with perf_event_set_output() which can ++ * swizzle the rb from under us while we were waiting to ++ * acquire mmap_mutex. ++ * ++ * If we find a different rb; ignore this event, a next ++ * iteration will no longer find it on the list. We have to ++ * still restart the iteration to make sure we're not now ++ * iterating the wrong list. ++ */ ++ if (event->rb == rb) ++ ring_buffer_attach(event, NULL); ++ ++ mutex_unlock(&event->mmap_mutex); ++ put_event(event); ++ ++ /* ++ * Restart the iteration; either we're on the wrong list or ++ * destroyed its integrity by doing a deletion. ++ */ ++ goto again; ++ } ++ rcu_read_unlock(); ++ ++ /* ++ * It could be there's still a few 0-ref events on the list; they'll ++ * get cleaned up by free_event() -- they'll also still have their ++ * ref on the rb and will free it whenever they are done with it. ++ * ++ * Aside from that, this buffer is 'fully' detached and unmapped, ++ * undo the VM accounting. ++ */ ++ ++ atomic_long_sub((size >> PAGE_SHIFT) + 1, &mmap_user->locked_vm); ++ vma->vm_mm->pinned_vm -= mmap_locked; ++ free_uid(mmap_user); ++ ++out_put: ++ ring_buffer_put(rb); /* could be last */ ++} ++ ++static const struct vm_operations_struct perf_mmap_vmops = { ++ .open = perf_mmap_open, ++ .close = perf_mmap_close, ++ .fault = perf_mmap_fault, ++ .page_mkwrite = perf_mmap_fault, ++}; ++ ++static int perf_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct perf_event *event = file->private_data; ++ unsigned long user_locked, user_lock_limit; ++ struct user_struct *user = current_user(); ++ unsigned long locked, lock_limit; ++ struct ring_buffer *rb; ++ unsigned long vma_size; ++ unsigned long nr_pages; ++ long user_extra, extra; ++ int ret = 0, flags = 0; ++ ++ /* ++ * Don't allow mmap() of inherited per-task counters. This would ++ * create a performance issue due to all children writing to the ++ * same rb. ++ */ ++ if (event->cpu == -1 && event->attr.inherit) ++ return -EINVAL; ++ ++ if (!(vma->vm_flags & VM_SHARED)) ++ return -EINVAL; ++ ++ vma_size = vma->vm_end - vma->vm_start; ++ nr_pages = (vma_size / PAGE_SIZE) - 1; ++ ++ /* ++ * If we have rb pages ensure they're a power-of-two number, so we ++ * can do bitmasks instead of modulo. ++ */ ++ if (nr_pages != 0 && !is_power_of_2(nr_pages)) ++ return -EINVAL; ++ ++ if (vma_size != PAGE_SIZE * (1 + nr_pages)) ++ return -EINVAL; ++ ++ if (vma->vm_pgoff != 0) ++ return -EINVAL; ++ ++ WARN_ON_ONCE(event->ctx->parent_ctx); ++again: ++ mutex_lock(&event->mmap_mutex); ++ if (event->rb) { ++ if (event->rb->nr_pages != nr_pages) { ++ ret = -EINVAL; ++ goto unlock; ++ } ++ ++ if (!atomic_inc_not_zero(&event->rb->mmap_count)) { ++ /* ++ * Raced against perf_mmap_close() through ++ * perf_event_set_output(). Try again, hope for better ++ * luck. ++ */ ++ mutex_unlock(&event->mmap_mutex); ++ goto again; ++ } ++ ++ goto unlock; ++ } ++ ++ user_extra = nr_pages + 1; ++ user_lock_limit = sysctl_perf_event_mlock >> (PAGE_SHIFT - 10); ++ ++ /* ++ * Increase the limit linearly with more CPUs: ++ */ ++ user_lock_limit *= num_online_cpus(); ++ ++ user_locked = atomic_long_read(&user->locked_vm) + user_extra; ++ ++ extra = 0; ++ if (user_locked > user_lock_limit) ++ extra = user_locked - user_lock_limit; ++ ++ lock_limit = rlimit(RLIMIT_MEMLOCK); ++ lock_limit >>= PAGE_SHIFT; ++ locked = vma->vm_mm->pinned_vm + extra; ++ ++ if ((locked > lock_limit) && perf_paranoid_tracepoint_raw() && ++ !capable(CAP_IPC_LOCK)) { ++ ret = -EPERM; ++ goto unlock; ++ } ++ ++ WARN_ON(event->rb); ++ ++ if (vma->vm_flags & VM_WRITE) ++ flags |= RING_BUFFER_WRITABLE; ++ ++ rb = rb_alloc(nr_pages, ++ event->attr.watermark ? event->attr.wakeup_watermark : 0, ++ event->cpu, flags); ++ ++ if (!rb) { ++ ret = -ENOMEM; ++ goto unlock; ++ } ++ ++ atomic_set(&rb->mmap_count, 1); ++ rb->mmap_locked = extra; ++ rb->mmap_user = get_current_user(); ++ ++ atomic_long_add(user_extra, &user->locked_vm); ++ vma->vm_mm->pinned_vm += extra; ++ ++ ring_buffer_attach(event, rb); ++ ++ perf_event_init_userpage(event); ++ perf_event_update_userpage(event); ++ ++unlock: ++ if (!ret) ++ atomic_inc(&event->mmap_count); ++ mutex_unlock(&event->mmap_mutex); ++ ++ /* ++ * Since pinned accounting is per vm we cannot allow fork() to copy our ++ * vma. ++ */ ++ vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP; ++ vma->vm_ops = &perf_mmap_vmops; ++ ++ return ret; ++} ++ ++static int perf_fasync(int fd, struct file *filp, int on) ++{ ++ struct inode *inode = file_inode(filp); ++ struct perf_event *event = filp->private_data; ++ int retval; ++ ++ mutex_lock(&inode->i_mutex); ++ retval = fasync_helper(fd, filp, on, &event->fasync); ++ mutex_unlock(&inode->i_mutex); ++ ++ if (retval < 0) ++ return retval; ++ ++ return 0; ++} ++ ++static const struct file_operations perf_fops = { ++ .llseek = no_llseek, ++ .release = perf_release, ++ .read = perf_read, ++ .poll = perf_poll, ++ .unlocked_ioctl = perf_ioctl, ++ .compat_ioctl = perf_compat_ioctl, ++ .mmap = perf_mmap, ++ .fasync = perf_fasync, ++}; ++ ++/* ++ * Perf event wakeup ++ * ++ * If there's data, ensure we set the poll() state and publish everything ++ * to user-space before waking everybody up. ++ */ ++ ++void perf_event_wakeup(struct perf_event *event) ++{ ++ ring_buffer_wakeup(event); ++ ++ if (event->pending_kill) { ++ kill_fasync(&event->fasync, SIGIO, event->pending_kill); ++ event->pending_kill = 0; ++ } ++} ++ ++static void perf_pending_event(struct irq_work *entry) ++{ ++ struct perf_event *event = container_of(entry, ++ struct perf_event, pending); ++ int rctx; ++ ++ rctx = perf_swevent_get_recursion_context(); ++ /* ++ * If we 'fail' here, that's OK, it means recursion is already disabled ++ * and we won't recurse 'further'. ++ */ ++ ++ if (event->pending_disable) { ++ event->pending_disable = 0; ++ __perf_event_disable(event); ++ } ++ ++ if (event->pending_wakeup) { ++ event->pending_wakeup = 0; ++ perf_event_wakeup(event); ++ } ++ ++ if (rctx >= 0) ++ perf_swevent_put_recursion_context(rctx); ++} ++ ++/* ++ * We assume there is only KVM supporting the callbacks. ++ * Later on, we might change it to a list if there is ++ * another virtualization implementation supporting the callbacks. ++ */ ++struct perf_guest_info_callbacks *perf_guest_cbs; ++ ++int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *cbs) ++{ ++ perf_guest_cbs = cbs; ++ return 0; ++} ++EXPORT_SYMBOL_GPL(perf_register_guest_info_callbacks); ++ ++int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *cbs) ++{ ++ perf_guest_cbs = NULL; ++ return 0; ++} ++EXPORT_SYMBOL_GPL(perf_unregister_guest_info_callbacks); ++ ++static void ++perf_output_sample_regs(struct perf_output_handle *handle, ++ struct pt_regs *regs, u64 mask) ++{ ++ int bit; ++ ++ for_each_set_bit(bit, (const unsigned long *) &mask, ++ sizeof(mask) * BITS_PER_BYTE) { ++ u64 val; ++ ++ val = perf_reg_value(regs, bit); ++ perf_output_put(handle, val); ++ } ++} ++ ++static void perf_sample_regs_user(struct perf_regs_user *regs_user, ++ struct pt_regs *regs) ++{ ++ if (!user_mode(regs)) { ++ if (current->mm) ++ regs = task_pt_regs(current); ++ else ++ regs = NULL; ++ } ++ ++ if (regs) { ++ regs_user->regs = regs; ++ regs_user->abi = perf_reg_abi(current); ++ } ++} ++ ++/* ++ * Get remaining task size from user stack pointer. ++ * ++ * It'd be better to take stack vma map and limit this more ++ * precisly, but there's no way to get it safely under interrupt, ++ * so using TASK_SIZE as limit. ++ */ ++static u64 perf_ustack_task_size(struct pt_regs *regs) ++{ ++ unsigned long addr = perf_user_stack_pointer(regs); ++ ++ if (!addr || addr >= TASK_SIZE) ++ return 0; ++ ++ return TASK_SIZE - addr; ++} ++ ++static u16 ++perf_sample_ustack_size(u16 stack_size, u16 header_size, ++ struct pt_regs *regs) ++{ ++ u64 task_size; ++ ++ /* No regs, no stack pointer, no dump. */ ++ if (!regs) ++ return 0; ++ ++ /* ++ * Check if we fit in with the requested stack size into the: ++ * - TASK_SIZE ++ * If we don't, we limit the size to the TASK_SIZE. ++ * ++ * - remaining sample size ++ * If we don't, we customize the stack size to ++ * fit in to the remaining sample size. ++ */ ++ ++ task_size = min((u64) USHRT_MAX, perf_ustack_task_size(regs)); ++ stack_size = min(stack_size, (u16) task_size); ++ ++ /* Current header size plus static size and dynamic size. */ ++ header_size += 2 * sizeof(u64); ++ ++ /* Do we fit in with the current stack dump size? */ ++ if ((u16) (header_size + stack_size) < header_size) { ++ /* ++ * If we overflow the maximum size for the sample, ++ * we customize the stack dump size to fit in. ++ */ ++ stack_size = USHRT_MAX - header_size - sizeof(u64); ++ stack_size = round_up(stack_size, sizeof(u64)); ++ } ++ ++ return stack_size; ++} ++ ++static void ++perf_output_sample_ustack(struct perf_output_handle *handle, u64 dump_size, ++ struct pt_regs *regs) ++{ ++ /* Case of a kernel thread, nothing to dump */ ++ if (!regs) { ++ u64 size = 0; ++ perf_output_put(handle, size); ++ } else { ++ unsigned long sp; ++ unsigned int rem; ++ u64 dyn_size; ++ ++ /* ++ * We dump: ++ * static size ++ * - the size requested by user or the best one we can fit ++ * in to the sample max size ++ * data ++ * - user stack dump data ++ * dynamic size ++ * - the actual dumped size ++ */ ++ ++ /* Static size. */ ++ perf_output_put(handle, dump_size); ++ ++ /* Data. */ ++ sp = perf_user_stack_pointer(regs); ++ rem = __output_copy_user(handle, (void *) sp, dump_size); ++ dyn_size = dump_size - rem; ++ ++ perf_output_skip(handle, rem); ++ ++ /* Dynamic size. */ ++ perf_output_put(handle, dyn_size); ++ } ++} ++ ++static void __perf_event_header__init_id(struct perf_event_header *header, ++ struct perf_sample_data *data, ++ struct perf_event *event) ++{ ++ u64 sample_type = event->attr.sample_type; ++ ++ data->type = sample_type; ++ header->size += event->id_header_size; ++ ++ if (sample_type & PERF_SAMPLE_TID) { ++ /* namespace issues */ ++ data->tid_entry.pid = perf_event_pid(event, current); ++ data->tid_entry.tid = perf_event_tid(event, current); ++ } ++ ++ if (sample_type & PERF_SAMPLE_TIME) ++ data->time = perf_clock(); ++ ++ if (sample_type & (PERF_SAMPLE_ID | PERF_SAMPLE_IDENTIFIER)) ++ data->id = primary_event_id(event); ++ ++ if (sample_type & PERF_SAMPLE_STREAM_ID) ++ data->stream_id = event->id; ++ ++ if (sample_type & PERF_SAMPLE_CPU) { ++ data->cpu_entry.cpu = raw_smp_processor_id(); ++ data->cpu_entry.reserved = 0; ++ } ++} ++ ++void perf_event_header__init_id(struct perf_event_header *header, ++ struct perf_sample_data *data, ++ struct perf_event *event) ++{ ++ if (event->attr.sample_id_all) ++ __perf_event_header__init_id(header, data, event); ++} ++ ++static void __perf_event__output_id_sample(struct perf_output_handle *handle, ++ struct perf_sample_data *data) ++{ ++ u64 sample_type = data->type; ++ ++ if (sample_type & PERF_SAMPLE_TID) ++ perf_output_put(handle, data->tid_entry); ++ ++ if (sample_type & PERF_SAMPLE_TIME) ++ perf_output_put(handle, data->time); ++ ++ if (sample_type & PERF_SAMPLE_ID) ++ perf_output_put(handle, data->id); ++ ++ if (sample_type & PERF_SAMPLE_STREAM_ID) ++ perf_output_put(handle, data->stream_id); ++ ++ if (sample_type & PERF_SAMPLE_CPU) ++ perf_output_put(handle, data->cpu_entry); ++ ++ if (sample_type & PERF_SAMPLE_IDENTIFIER) ++ perf_output_put(handle, data->id); ++} ++ ++void perf_event__output_id_sample(struct perf_event *event, ++ struct perf_output_handle *handle, ++ struct perf_sample_data *sample) ++{ ++ if (event->attr.sample_id_all) ++ __perf_event__output_id_sample(handle, sample); ++} ++ ++static void perf_output_read_one(struct perf_output_handle *handle, ++ struct perf_event *event, ++ u64 enabled, u64 running) ++{ ++ u64 read_format = event->attr.read_format; ++ u64 values[4]; ++ int n = 0; ++ ++ values[n++] = perf_event_count(event); ++ if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) { ++ values[n++] = enabled + ++ atomic64_read(&event->child_total_time_enabled); ++ } ++ if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) { ++ values[n++] = running + ++ atomic64_read(&event->child_total_time_running); ++ } ++ if (read_format & PERF_FORMAT_ID) ++ values[n++] = primary_event_id(event); ++ ++ __output_copy(handle, values, n * sizeof(u64)); ++} ++ ++/* ++ * XXX PERF_FORMAT_GROUP vs inherited events seems difficult. ++ */ ++static void perf_output_read_group(struct perf_output_handle *handle, ++ struct perf_event *event, ++ u64 enabled, u64 running) ++{ ++ struct perf_event *leader = event->group_leader, *sub; ++ u64 read_format = event->attr.read_format; ++ u64 values[5]; ++ int n = 0; ++ ++ values[n++] = 1 + leader->nr_siblings; ++ ++ if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) ++ values[n++] = enabled; ++ ++ if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) ++ values[n++] = running; ++ ++ if (leader != event) ++ leader->pmu->read(leader); ++ ++ values[n++] = perf_event_count(leader); ++ if (read_format & PERF_FORMAT_ID) ++ values[n++] = primary_event_id(leader); ++ ++ __output_copy(handle, values, n * sizeof(u64)); ++ ++ list_for_each_entry(sub, &leader->sibling_list, group_entry) { ++ n = 0; ++ ++ if ((sub != event) && ++ (sub->state == PERF_EVENT_STATE_ACTIVE)) ++ sub->pmu->read(sub); ++ ++ values[n++] = perf_event_count(sub); ++ if (read_format & PERF_FORMAT_ID) ++ values[n++] = primary_event_id(sub); ++ ++ __output_copy(handle, values, n * sizeof(u64)); ++ } ++} ++ ++#define PERF_FORMAT_TOTAL_TIMES (PERF_FORMAT_TOTAL_TIME_ENABLED|\ ++ PERF_FORMAT_TOTAL_TIME_RUNNING) ++ ++static void perf_output_read(struct perf_output_handle *handle, ++ struct perf_event *event) ++{ ++ u64 enabled = 0, running = 0, now; ++ u64 read_format = event->attr.read_format; ++ ++ /* ++ * compute total_time_enabled, total_time_running ++ * based on snapshot values taken when the event ++ * was last scheduled in. ++ * ++ * we cannot simply called update_context_time() ++ * because of locking issue as we are called in ++ * NMI context ++ */ ++ if (read_format & PERF_FORMAT_TOTAL_TIMES) ++ calc_timer_values(event, &now, &enabled, &running); ++ ++ if (event->attr.read_format & PERF_FORMAT_GROUP) ++ perf_output_read_group(handle, event, enabled, running); ++ else ++ perf_output_read_one(handle, event, enabled, running); ++} ++ ++void perf_output_sample(struct perf_output_handle *handle, ++ struct perf_event_header *header, ++ struct perf_sample_data *data, ++ struct perf_event *event) ++{ ++ u64 sample_type = data->type; ++ ++ perf_output_put(handle, *header); ++ ++ if (sample_type & PERF_SAMPLE_IDENTIFIER) ++ perf_output_put(handle, data->id); ++ ++ if (sample_type & PERF_SAMPLE_IP) ++ perf_output_put(handle, data->ip); ++ ++ if (sample_type & PERF_SAMPLE_TID) ++ perf_output_put(handle, data->tid_entry); ++ ++ if (sample_type & PERF_SAMPLE_TIME) ++ perf_output_put(handle, data->time); ++ ++ if (sample_type & PERF_SAMPLE_ADDR) ++ perf_output_put(handle, data->addr); ++ ++ if (sample_type & PERF_SAMPLE_ID) ++ perf_output_put(handle, data->id); ++ ++ if (sample_type & PERF_SAMPLE_STREAM_ID) ++ perf_output_put(handle, data->stream_id); ++ ++ if (sample_type & PERF_SAMPLE_CPU) ++ perf_output_put(handle, data->cpu_entry); ++ ++ if (sample_type & PERF_SAMPLE_PERIOD) ++ perf_output_put(handle, data->period); ++ ++ if (sample_type & PERF_SAMPLE_READ) ++ perf_output_read(handle, event); ++ ++ if (sample_type & PERF_SAMPLE_CALLCHAIN) { ++ if (data->callchain) { ++ int size = 1; ++ ++ if (data->callchain) ++ size += data->callchain->nr; ++ ++ size *= sizeof(u64); ++ ++ __output_copy(handle, data->callchain, size); ++ } else { ++ u64 nr = 0; ++ perf_output_put(handle, nr); ++ } ++ } ++ ++ if (sample_type & PERF_SAMPLE_RAW) { ++ if (data->raw) { ++ perf_output_put(handle, data->raw->size); ++ __output_copy(handle, data->raw->data, ++ data->raw->size); ++ } else { ++ struct { ++ u32 size; ++ u32 data; ++ } raw = { ++ .size = sizeof(u32), ++ .data = 0, ++ }; ++ perf_output_put(handle, raw); ++ } ++ } ++ ++ if (sample_type & PERF_SAMPLE_BRANCH_STACK) { ++ if (data->br_stack) { ++ size_t size; ++ ++ size = data->br_stack->nr ++ * sizeof(struct perf_branch_entry); ++ ++ perf_output_put(handle, data->br_stack->nr); ++ perf_output_copy(handle, data->br_stack->entries, size); ++ } else { ++ /* ++ * we always store at least the value of nr ++ */ ++ u64 nr = 0; ++ perf_output_put(handle, nr); ++ } ++ } ++ ++ if (sample_type & PERF_SAMPLE_REGS_USER) { ++ u64 abi = data->regs_user.abi; ++ ++ /* ++ * If there are no regs to dump, notice it through ++ * first u64 being zero (PERF_SAMPLE_REGS_ABI_NONE). ++ */ ++ perf_output_put(handle, abi); ++ ++ if (abi) { ++ u64 mask = event->attr.sample_regs_user; ++ perf_output_sample_regs(handle, ++ data->regs_user.regs, ++ mask); ++ } ++ } ++ ++ if (sample_type & PERF_SAMPLE_STACK_USER) { ++ perf_output_sample_ustack(handle, ++ data->stack_user_size, ++ data->regs_user.regs); ++ } ++ ++ if (sample_type & PERF_SAMPLE_WEIGHT) ++ perf_output_put(handle, data->weight); ++ ++ if (sample_type & PERF_SAMPLE_DATA_SRC) ++ perf_output_put(handle, data->data_src.val); ++ ++ if (sample_type & PERF_SAMPLE_TRANSACTION) ++ perf_output_put(handle, data->txn); ++ ++ if (!event->attr.watermark) { ++ int wakeup_events = event->attr.wakeup_events; ++ ++ if (wakeup_events) { ++ struct ring_buffer *rb = handle->rb; ++ int events = local_inc_return(&rb->events); ++ ++ if (events >= wakeup_events) { ++ local_sub(wakeup_events, &rb->events); ++ local_inc(&rb->wakeup); ++ } ++ } ++ } ++} ++ ++void perf_prepare_sample(struct perf_event_header *header, ++ struct perf_sample_data *data, ++ struct perf_event *event, ++ struct pt_regs *regs) ++{ ++ u64 sample_type = event->attr.sample_type; ++ ++ header->type = PERF_RECORD_SAMPLE; ++ header->size = sizeof(*header) + event->header_size; ++ ++ header->misc = 0; ++ header->misc |= perf_misc_flags(regs); ++ ++ __perf_event_header__init_id(header, data, event); ++ ++ if (sample_type & PERF_SAMPLE_IP) ++ data->ip = perf_instruction_pointer(regs); ++ ++ if (sample_type & PERF_SAMPLE_CALLCHAIN) { ++ int size = 1; ++ ++ data->callchain = perf_callchain(event, regs); ++ ++ if (data->callchain) ++ size += data->callchain->nr; ++ ++ header->size += size * sizeof(u64); ++ } ++ ++ if (sample_type & PERF_SAMPLE_RAW) { ++ int size = sizeof(u32); ++ ++ if (data->raw) ++ size += data->raw->size; ++ else ++ size += sizeof(u32); ++ ++ WARN_ON_ONCE(size & (sizeof(u64)-1)); ++ header->size += size; ++ } ++ ++ if (sample_type & PERF_SAMPLE_BRANCH_STACK) { ++ int size = sizeof(u64); /* nr */ ++ if (data->br_stack) { ++ size += data->br_stack->nr ++ * sizeof(struct perf_branch_entry); ++ } ++ header->size += size; ++ } ++ ++ if (sample_type & PERF_SAMPLE_REGS_USER) { ++ /* regs dump ABI info */ ++ int size = sizeof(u64); ++ ++ perf_sample_regs_user(&data->regs_user, regs); ++ ++ if (data->regs_user.regs) { ++ u64 mask = event->attr.sample_regs_user; ++ size += hweight64(mask) * sizeof(u64); ++ } ++ ++ header->size += size; ++ } ++ ++ if (sample_type & PERF_SAMPLE_STACK_USER) { ++ /* ++ * Either we need PERF_SAMPLE_STACK_USER bit to be allways ++ * processed as the last one or have additional check added ++ * in case new sample type is added, because we could eat ++ * up the rest of the sample size. ++ */ ++ struct perf_regs_user *uregs = &data->regs_user; ++ u16 stack_size = event->attr.sample_stack_user; ++ u16 size = sizeof(u64); ++ ++ if (!uregs->abi) ++ perf_sample_regs_user(uregs, regs); ++ ++ stack_size = perf_sample_ustack_size(stack_size, header->size, ++ uregs->regs); ++ ++ /* ++ * If there is something to dump, add space for the dump ++ * itself and for the field that tells the dynamic size, ++ * which is how many have been actually dumped. ++ */ ++ if (stack_size) ++ size += sizeof(u64) + stack_size; ++ ++ data->stack_user_size = stack_size; ++ header->size += size; ++ } ++} ++ ++static void perf_event_output(struct perf_event *event, ++ struct perf_sample_data *data, ++ struct pt_regs *regs) ++{ ++ struct perf_output_handle handle; ++ struct perf_event_header header; ++ ++ /* protect the callchain buffers */ ++ rcu_read_lock(); ++ ++ perf_prepare_sample(&header, data, event, regs); ++ ++ if (perf_output_begin(&handle, event, header.size)) ++ goto exit; ++ ++ perf_output_sample(&handle, &header, data, event); ++ ++ perf_output_end(&handle); ++ ++exit: ++ rcu_read_unlock(); ++} ++ ++/* ++ * read event_id ++ */ ++ ++struct perf_read_event { ++ struct perf_event_header header; ++ ++ u32 pid; ++ u32 tid; ++}; ++ ++static void ++perf_event_read_event(struct perf_event *event, ++ struct task_struct *task) ++{ ++ struct perf_output_handle handle; ++ struct perf_sample_data sample; ++ struct perf_read_event read_event = { ++ .header = { ++ .type = PERF_RECORD_READ, ++ .misc = 0, ++ .size = sizeof(read_event) + event->read_size, ++ }, ++ .pid = perf_event_pid(event, task), ++ .tid = perf_event_tid(event, task), ++ }; ++ int ret; ++ ++ perf_event_header__init_id(&read_event.header, &sample, event); ++ ret = perf_output_begin(&handle, event, read_event.header.size); ++ if (ret) ++ return; ++ ++ perf_output_put(&handle, read_event); ++ perf_output_read(&handle, event); ++ perf_event__output_id_sample(event, &handle, &sample); ++ ++ perf_output_end(&handle); ++} ++ ++typedef void (perf_event_aux_output_cb)(struct perf_event *event, void *data); ++ ++static void ++perf_event_aux_ctx(struct perf_event_context *ctx, ++ perf_event_aux_output_cb output, ++ void *data) ++{ ++ struct perf_event *event; ++ ++ list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { ++ if (event->state < PERF_EVENT_STATE_INACTIVE) ++ continue; ++ if (!event_filter_match(event)) ++ continue; ++ output(event, data); ++ } ++} ++ ++static void ++perf_event_aux(perf_event_aux_output_cb output, void *data, ++ struct perf_event_context *task_ctx) ++{ ++ struct perf_cpu_context *cpuctx; ++ struct perf_event_context *ctx; ++ struct pmu *pmu; ++ int ctxn; ++ ++ rcu_read_lock(); ++ list_for_each_entry_rcu(pmu, &pmus, entry) { ++ cpuctx = get_cpu_ptr(pmu->pmu_cpu_context); ++ if (cpuctx->unique_pmu != pmu) ++ goto next; ++ perf_event_aux_ctx(&cpuctx->ctx, output, data); ++ if (task_ctx) ++ goto next; ++ ctxn = pmu->task_ctx_nr; ++ if (ctxn < 0) ++ goto next; ++ ctx = rcu_dereference(current->perf_event_ctxp[ctxn]); ++ if (ctx) ++ perf_event_aux_ctx(ctx, output, data); ++next: ++ put_cpu_ptr(pmu->pmu_cpu_context); ++ } ++ ++ if (task_ctx) { ++ preempt_disable(); ++ perf_event_aux_ctx(task_ctx, output, data); ++ preempt_enable(); ++ } ++ rcu_read_unlock(); ++} ++ ++/* ++ * task tracking -- fork/exit ++ * ++ * enabled by: attr.comm | attr.mmap | attr.mmap2 | attr.mmap_data | attr.task ++ */ ++ ++struct perf_task_event { ++ struct task_struct *task; ++ struct perf_event_context *task_ctx; ++ ++ struct { ++ struct perf_event_header header; ++ ++ u32 pid; ++ u32 ppid; ++ u32 tid; ++ u32 ptid; ++ u64 time; ++ } event_id; ++}; ++ ++static int perf_event_task_match(struct perf_event *event) ++{ ++ return event->attr.comm || event->attr.mmap || ++ event->attr.mmap2 || event->attr.mmap_data || ++ event->attr.task; ++} ++ ++static void perf_event_task_output(struct perf_event *event, ++ void *data) ++{ ++ struct perf_task_event *task_event = data; ++ struct perf_output_handle handle; ++ struct perf_sample_data sample; ++ struct task_struct *task = task_event->task; ++ int ret, size = task_event->event_id.header.size; ++ ++ if (!perf_event_task_match(event)) ++ return; ++ ++ perf_event_header__init_id(&task_event->event_id.header, &sample, event); ++ ++ ret = perf_output_begin(&handle, event, ++ task_event->event_id.header.size); ++ if (ret) ++ goto out; ++ ++ task_event->event_id.pid = perf_event_pid(event, task); ++ task_event->event_id.ppid = perf_event_pid(event, current); ++ ++ task_event->event_id.tid = perf_event_tid(event, task); ++ task_event->event_id.ptid = perf_event_tid(event, current); ++ ++ perf_output_put(&handle, task_event->event_id); ++ ++ perf_event__output_id_sample(event, &handle, &sample); ++ ++ perf_output_end(&handle); ++out: ++ task_event->event_id.header.size = size; ++} ++ ++static void perf_event_task(struct task_struct *task, ++ struct perf_event_context *task_ctx, ++ int new) ++{ ++ struct perf_task_event task_event; ++ ++ if (!atomic_read(&nr_comm_events) && ++ !atomic_read(&nr_mmap_events) && ++ !atomic_read(&nr_task_events)) ++ return; ++ ++ task_event = (struct perf_task_event){ ++ .task = task, ++ .task_ctx = task_ctx, ++ .event_id = { ++ .header = { ++ .type = new ? PERF_RECORD_FORK : PERF_RECORD_EXIT, ++ .misc = 0, ++ .size = sizeof(task_event.event_id), ++ }, ++ /* .pid */ ++ /* .ppid */ ++ /* .tid */ ++ /* .ptid */ ++ .time = perf_clock(), ++ }, ++ }; ++ ++ perf_event_aux(perf_event_task_output, ++ &task_event, ++ task_ctx); ++} ++ ++void perf_event_fork(struct task_struct *task) ++{ ++ perf_event_task(task, NULL, 1); ++} ++ ++/* ++ * comm tracking ++ */ ++ ++struct perf_comm_event { ++ struct task_struct *task; ++ char *comm; ++ int comm_size; ++ ++ struct { ++ struct perf_event_header header; ++ ++ u32 pid; ++ u32 tid; ++ } event_id; ++}; ++ ++static int perf_event_comm_match(struct perf_event *event) ++{ ++ return event->attr.comm; ++} ++ ++static void perf_event_comm_output(struct perf_event *event, ++ void *data) ++{ ++ struct perf_comm_event *comm_event = data; ++ struct perf_output_handle handle; ++ struct perf_sample_data sample; ++ int size = comm_event->event_id.header.size; ++ int ret; ++ ++ if (!perf_event_comm_match(event)) ++ return; ++ ++ perf_event_header__init_id(&comm_event->event_id.header, &sample, event); ++ ret = perf_output_begin(&handle, event, ++ comm_event->event_id.header.size); ++ ++ if (ret) ++ goto out; ++ ++ comm_event->event_id.pid = perf_event_pid(event, comm_event->task); ++ comm_event->event_id.tid = perf_event_tid(event, comm_event->task); ++ ++ perf_output_put(&handle, comm_event->event_id); ++ __output_copy(&handle, comm_event->comm, ++ comm_event->comm_size); ++ ++ perf_event__output_id_sample(event, &handle, &sample); ++ ++ perf_output_end(&handle); ++out: ++ comm_event->event_id.header.size = size; ++} ++ ++static void perf_event_comm_event(struct perf_comm_event *comm_event) ++{ ++ char comm[TASK_COMM_LEN]; ++ unsigned int size; ++ ++ memset(comm, 0, sizeof(comm)); ++ strlcpy(comm, comm_event->task->comm, sizeof(comm)); ++ size = ALIGN(strlen(comm)+1, sizeof(u64)); ++ ++ comm_event->comm = comm; ++ comm_event->comm_size = size; ++ ++ comm_event->event_id.header.size = sizeof(comm_event->event_id) + size; ++ ++ perf_event_aux(perf_event_comm_output, ++ comm_event, ++ NULL); ++} ++ ++void perf_event_comm(struct task_struct *task, bool exec) ++{ ++ struct perf_comm_event comm_event; ++ ++ if (!atomic_read(&nr_comm_events)) ++ return; ++ ++ comm_event = (struct perf_comm_event){ ++ .task = task, ++ /* .comm */ ++ /* .comm_size */ ++ .event_id = { ++ .header = { ++ .type = PERF_RECORD_COMM, ++ .misc = exec ? PERF_RECORD_MISC_COMM_EXEC : 0, ++ /* .size */ ++ }, ++ /* .pid */ ++ /* .tid */ ++ }, ++ }; ++ ++ perf_event_comm_event(&comm_event); ++} ++ ++/* ++ * mmap tracking ++ */ ++ ++struct perf_mmap_event { ++ struct vm_area_struct *vma; ++ ++ const char *file_name; ++ int file_size; ++ int maj, min; ++ u64 ino; ++ u64 ino_generation; ++ u32 prot, flags; ++ ++ struct { ++ struct perf_event_header header; ++ ++ u32 pid; ++ u32 tid; ++ u64 start; ++ u64 len; ++ u64 pgoff; ++ } event_id; ++}; ++ ++static int perf_event_mmap_match(struct perf_event *event, ++ void *data) ++{ ++ struct perf_mmap_event *mmap_event = data; ++ struct vm_area_struct *vma = mmap_event->vma; ++ int executable = vma->vm_flags & VM_EXEC; ++ ++ return (!executable && event->attr.mmap_data) || ++ (executable && (event->attr.mmap || event->attr.mmap2)); ++} ++ ++static void perf_event_mmap_output(struct perf_event *event, ++ void *data) ++{ ++ struct perf_mmap_event *mmap_event = data; ++ struct perf_output_handle handle; ++ struct perf_sample_data sample; ++ int size = mmap_event->event_id.header.size; ++ int ret; ++ ++ if (!perf_event_mmap_match(event, data)) ++ return; ++ ++ if (event->attr.mmap2) { ++ mmap_event->event_id.header.type = PERF_RECORD_MMAP2; ++ mmap_event->event_id.header.size += sizeof(mmap_event->maj); ++ mmap_event->event_id.header.size += sizeof(mmap_event->min); ++ mmap_event->event_id.header.size += sizeof(mmap_event->ino); ++ mmap_event->event_id.header.size += sizeof(mmap_event->ino_generation); ++ mmap_event->event_id.header.size += sizeof(mmap_event->prot); ++ mmap_event->event_id.header.size += sizeof(mmap_event->flags); ++ } ++ ++ perf_event_header__init_id(&mmap_event->event_id.header, &sample, event); ++ ret = perf_output_begin(&handle, event, ++ mmap_event->event_id.header.size); ++ if (ret) ++ goto out; ++ ++ mmap_event->event_id.pid = perf_event_pid(event, current); ++ mmap_event->event_id.tid = perf_event_tid(event, current); ++ ++ perf_output_put(&handle, mmap_event->event_id); ++ ++ if (event->attr.mmap2) { ++ perf_output_put(&handle, mmap_event->maj); ++ perf_output_put(&handle, mmap_event->min); ++ perf_output_put(&handle, mmap_event->ino); ++ perf_output_put(&handle, mmap_event->ino_generation); ++ perf_output_put(&handle, mmap_event->prot); ++ perf_output_put(&handle, mmap_event->flags); ++ } ++ ++ __output_copy(&handle, mmap_event->file_name, ++ mmap_event->file_size); ++ ++ perf_event__output_id_sample(event, &handle, &sample); ++ ++ perf_output_end(&handle); ++out: ++ mmap_event->event_id.header.size = size; ++} ++ ++static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) ++{ ++ struct vm_area_struct *vma = mmap_event->vma; ++ struct file *file = vma->vm_file; ++ int maj = 0, min = 0; ++ u64 ino = 0, gen = 0; ++ u32 prot = 0, flags = 0; ++ unsigned int size; ++ char tmp[16]; ++ char *buf = NULL; ++ char *name; ++ ++ if (file) { ++ struct inode *inode; ++ dev_t dev; ++ ++ buf = kmalloc(PATH_MAX, GFP_KERNEL); ++ if (!buf) { ++ name = "//enomem"; ++ goto cpy_name; ++ } ++ /* ++ * d_path() works from the end of the rb backwards, so we ++ * need to add enough zero bytes after the string to handle ++ * the 64bit alignment we do later. ++ */ ++ name = d_path(&file->f_path, buf, PATH_MAX - sizeof(u64)); ++ if (IS_ERR(name)) { ++ name = "//toolong"; ++ goto cpy_name; ++ } ++ inode = file_inode(vma->vm_file); ++ dev = inode->i_sb->s_dev; ++ ino = inode->i_ino; ++ gen = inode->i_generation; ++ maj = MAJOR(dev); ++ min = MINOR(dev); ++ ++ if (vma->vm_flags & VM_READ) ++ prot |= PROT_READ; ++ if (vma->vm_flags & VM_WRITE) ++ prot |= PROT_WRITE; ++ if (vma->vm_flags & VM_EXEC) ++ prot |= PROT_EXEC; ++ ++ if (vma->vm_flags & VM_MAYSHARE) ++ flags = MAP_SHARED; ++ else ++ flags = MAP_PRIVATE; ++ ++ if (vma->vm_flags & VM_DENYWRITE) ++ flags |= MAP_DENYWRITE; ++ if (vma->vm_flags & VM_MAYEXEC) ++ flags |= MAP_EXECUTABLE; ++ if (vma->vm_flags & VM_LOCKED) ++ flags |= MAP_LOCKED; ++ if (vma->vm_flags & VM_HUGETLB) ++ flags |= MAP_HUGETLB; ++ ++ goto got_name; ++ } else { ++ if (vma->vm_ops && vma->vm_ops->name) { ++ name = (char *) vma->vm_ops->name(vma); ++ if (name) ++ goto cpy_name; ++ } ++ ++ name = (char *)arch_vma_name(vma); ++ if (name) ++ goto cpy_name; ++ ++ if (vma->vm_start <= vma->vm_mm->start_brk && ++ vma->vm_end >= vma->vm_mm->brk) { ++ name = "[heap]"; ++ goto cpy_name; ++ } ++ if (vma->vm_start <= vma->vm_mm->start_stack && ++ vma->vm_end >= vma->vm_mm->start_stack) { ++ name = "[stack]"; ++ goto cpy_name; ++ } ++ ++ name = "//anon"; ++ goto cpy_name; ++ } ++ ++cpy_name: ++ strlcpy(tmp, name, sizeof(tmp)); ++ name = tmp; ++got_name: ++ /* ++ * Since our buffer works in 8 byte units we need to align our string ++ * size to a multiple of 8. However, we must guarantee the tail end is ++ * zero'd out to avoid leaking random bits to userspace. ++ */ ++ size = strlen(name)+1; ++ while (!IS_ALIGNED(size, sizeof(u64))) ++ name[size++] = '\0'; ++ ++ mmap_event->file_name = name; ++ mmap_event->file_size = size; ++ mmap_event->maj = maj; ++ mmap_event->min = min; ++ mmap_event->ino = ino; ++ mmap_event->ino_generation = gen; ++ mmap_event->prot = prot; ++ mmap_event->flags = flags; ++ ++ if (!(vma->vm_flags & VM_EXEC)) ++ mmap_event->event_id.header.misc |= PERF_RECORD_MISC_MMAP_DATA; ++ ++ mmap_event->event_id.header.size = sizeof(mmap_event->event_id) + size; ++ ++ perf_event_aux(perf_event_mmap_output, ++ mmap_event, ++ NULL); ++ ++ kfree(buf); ++} ++ ++void perf_event_mmap(struct vm_area_struct *vma) ++{ ++ struct perf_mmap_event mmap_event; ++ ++ if (!atomic_read(&nr_mmap_events)) ++ return; ++ ++ mmap_event = (struct perf_mmap_event){ ++ .vma = vma, ++ /* .file_name */ ++ /* .file_size */ ++ .event_id = { ++ .header = { ++ .type = PERF_RECORD_MMAP, ++ .misc = PERF_RECORD_MISC_USER, ++ /* .size */ ++ }, ++ /* .pid */ ++ /* .tid */ ++ .start = vma->vm_start, ++ .len = vma->vm_end - vma->vm_start, ++ .pgoff = (u64)vma->vm_pgoff << PAGE_SHIFT, ++ }, ++ /* .maj (attr_mmap2 only) */ ++ /* .min (attr_mmap2 only) */ ++ /* .ino (attr_mmap2 only) */ ++ /* .ino_generation (attr_mmap2 only) */ ++ /* .prot (attr_mmap2 only) */ ++ /* .flags (attr_mmap2 only) */ ++ }; ++ ++ perf_event_mmap_event(&mmap_event); ++} ++ ++/* ++ * IRQ throttle logging ++ */ ++ ++static void perf_log_throttle(struct perf_event *event, int enable) ++{ ++ struct perf_output_handle handle; ++ struct perf_sample_data sample; ++ int ret; ++ ++ struct { ++ struct perf_event_header header; ++ u64 time; ++ u64 id; ++ u64 stream_id; ++ } throttle_event = { ++ .header = { ++ .type = PERF_RECORD_THROTTLE, ++ .misc = 0, ++ .size = sizeof(throttle_event), ++ }, ++ .time = perf_clock(), ++ .id = primary_event_id(event), ++ .stream_id = event->id, ++ }; ++ ++ if (enable) ++ throttle_event.header.type = PERF_RECORD_UNTHROTTLE; ++ ++ perf_event_header__init_id(&throttle_event.header, &sample, event); ++ ++ ret = perf_output_begin(&handle, event, ++ throttle_event.header.size); ++ if (ret) ++ return; ++ ++ perf_output_put(&handle, throttle_event); ++ perf_event__output_id_sample(event, &handle, &sample); ++ perf_output_end(&handle); ++} ++ ++/* ++ * Generic event overflow handling, sampling. ++ */ ++ ++static int __perf_event_overflow(struct perf_event *event, ++ int throttle, struct perf_sample_data *data, ++ struct pt_regs *regs) ++{ ++ int events = atomic_read(&event->event_limit); ++ struct hw_perf_event *hwc = &event->hw; ++ u64 seq; ++ int ret = 0; ++ ++ /* ++ * Non-sampling counters might still use the PMI to fold short ++ * hardware counters, ignore those. ++ */ ++ if (unlikely(!is_sampling_event(event))) ++ return 0; ++ ++ seq = __this_cpu_read(perf_throttled_seq); ++ if (seq != hwc->interrupts_seq) { ++ hwc->interrupts_seq = seq; ++ hwc->interrupts = 1; ++ } else { ++ hwc->interrupts++; ++ if (unlikely(throttle ++ && hwc->interrupts >= max_samples_per_tick)) { ++ __this_cpu_inc(perf_throttled_count); ++ hwc->interrupts = MAX_INTERRUPTS; ++ perf_log_throttle(event, 0); ++ tick_nohz_full_kick(); ++ ret = 1; ++ } ++ } ++ ++ if (event->attr.freq) { ++ u64 now = perf_clock(); ++ s64 delta = now - hwc->freq_time_stamp; ++ ++ hwc->freq_time_stamp = now; ++ ++ if (delta > 0 && delta < 2*TICK_NSEC) ++ perf_adjust_period(event, delta, hwc->last_period, true); ++ } ++ ++ /* ++ * XXX event_limit might not quite work as expected on inherited ++ * events ++ */ ++ ++ event->pending_kill = POLL_IN; ++ if (events && atomic_dec_and_test(&event->event_limit)) { ++ ret = 1; ++ event->pending_kill = POLL_HUP; ++ event->pending_disable = 1; ++ irq_work_queue(&event->pending); ++ } ++ ++ if (event->overflow_handler) ++ event->overflow_handler(event, data, regs); ++ else ++ perf_event_output(event, data, regs); ++ ++ if (event->fasync && event->pending_kill) { ++ event->pending_wakeup = 1; ++ irq_work_queue(&event->pending); ++ } ++ ++ return ret; ++} ++ ++int perf_event_overflow(struct perf_event *event, ++ struct perf_sample_data *data, ++ struct pt_regs *regs) ++{ ++ return __perf_event_overflow(event, 1, data, regs); ++} ++ ++/* ++ * Generic software event infrastructure ++ */ ++ ++struct swevent_htable { ++ struct swevent_hlist *swevent_hlist; ++ struct mutex hlist_mutex; ++ int hlist_refcount; ++ ++ /* Recursion avoidance in each contexts */ ++ int recursion[PERF_NR_CONTEXTS]; ++ ++ /* Keeps track of cpu being initialized/exited */ ++ bool online; ++}; ++ ++static DEFINE_PER_CPU(struct swevent_htable, swevent_htable); ++ ++/* ++ * We directly increment event->count and keep a second value in ++ * event->hw.period_left to count intervals. This period event ++ * is kept in the range [-sample_period, 0] so that we can use the ++ * sign as trigger. ++ */ ++ ++u64 perf_swevent_set_period(struct perf_event *event) ++{ ++ struct hw_perf_event *hwc = &event->hw; ++ u64 period = hwc->last_period; ++ u64 nr, offset; ++ s64 old, val; ++ ++ hwc->last_period = hwc->sample_period; ++ ++again: ++ old = val = local64_read(&hwc->period_left); ++ if (val < 0) ++ return 0; ++ ++ nr = div64_u64(period + val, period); ++ offset = nr * period; ++ val -= offset; ++ if (local64_cmpxchg(&hwc->period_left, old, val) != old) ++ goto again; ++ ++ return nr; ++} ++ ++static void perf_swevent_overflow(struct perf_event *event, u64 overflow, ++ struct perf_sample_data *data, ++ struct pt_regs *regs) ++{ ++ struct hw_perf_event *hwc = &event->hw; ++ int throttle = 0; ++ ++ if (!overflow) ++ overflow = perf_swevent_set_period(event); ++ ++ if (hwc->interrupts == MAX_INTERRUPTS) ++ return; ++ ++ for (; overflow; overflow--) { ++ if (__perf_event_overflow(event, throttle, ++ data, regs)) { ++ /* ++ * We inhibit the overflow from happening when ++ * hwc->interrupts == MAX_INTERRUPTS. ++ */ ++ break; ++ } ++ throttle = 1; ++ } ++} ++ ++static void perf_swevent_event(struct perf_event *event, u64 nr, ++ struct perf_sample_data *data, ++ struct pt_regs *regs) ++{ ++ struct hw_perf_event *hwc = &event->hw; ++ ++ local64_add(nr, &event->count); ++ ++ if (!regs) ++ return; ++ ++ if (!is_sampling_event(event)) ++ return; ++ ++ if ((event->attr.sample_type & PERF_SAMPLE_PERIOD) && !event->attr.freq) { ++ data->period = nr; ++ return perf_swevent_overflow(event, 1, data, regs); ++ } else ++ data->period = event->hw.last_period; ++ ++ if (nr == 1 && hwc->sample_period == 1 && !event->attr.freq) ++ return perf_swevent_overflow(event, 1, data, regs); ++ ++ if (local64_add_negative(nr, &hwc->period_left)) ++ return; ++ ++ perf_swevent_overflow(event, 0, data, regs); ++} ++ ++static int perf_exclude_event(struct perf_event *event, ++ struct pt_regs *regs) ++{ ++ if (event->hw.state & PERF_HES_STOPPED) ++ return 1; ++ ++ if (regs) { ++ if (event->attr.exclude_user && user_mode(regs)) ++ return 1; ++ ++ if (event->attr.exclude_kernel && !user_mode(regs)) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static int perf_swevent_match(struct perf_event *event, ++ enum perf_type_id type, ++ u32 event_id, ++ struct perf_sample_data *data, ++ struct pt_regs *regs) ++{ ++ if (event->attr.type != type) ++ return 0; ++ ++ if (event->attr.config != event_id) ++ return 0; ++ ++ if (perf_exclude_event(event, regs)) ++ return 0; ++ ++ return 1; ++} ++ ++static inline u64 swevent_hash(u64 type, u32 event_id) ++{ ++ u64 val = event_id | (type << 32); ++ ++ return hash_64(val, SWEVENT_HLIST_BITS); ++} ++ ++static inline struct hlist_head * ++__find_swevent_head(struct swevent_hlist *hlist, u64 type, u32 event_id) ++{ ++ u64 hash = swevent_hash(type, event_id); ++ ++ return &hlist->heads[hash]; ++} ++ ++/* For the read side: events when they trigger */ ++static inline struct hlist_head * ++find_swevent_head_rcu(struct swevent_htable *swhash, u64 type, u32 event_id) ++{ ++ struct swevent_hlist *hlist; ++ ++ hlist = rcu_dereference(swhash->swevent_hlist); ++ if (!hlist) ++ return NULL; ++ ++ return __find_swevent_head(hlist, type, event_id); ++} ++ ++/* For the event head insertion and removal in the hlist */ ++static inline struct hlist_head * ++find_swevent_head(struct swevent_htable *swhash, struct perf_event *event) ++{ ++ struct swevent_hlist *hlist; ++ u32 event_id = event->attr.config; ++ u64 type = event->attr.type; ++ ++ /* ++ * Event scheduling is always serialized against hlist allocation ++ * and release. Which makes the protected version suitable here. ++ * The context lock guarantees that. ++ */ ++ hlist = rcu_dereference_protected(swhash->swevent_hlist, ++ lockdep_is_held(&event->ctx->lock)); ++ if (!hlist) ++ return NULL; ++ ++ return __find_swevent_head(hlist, type, event_id); ++} ++ ++static void do_perf_sw_event(enum perf_type_id type, u32 event_id, ++ u64 nr, ++ struct perf_sample_data *data, ++ struct pt_regs *regs) ++{ ++ struct swevent_htable *swhash = this_cpu_ptr(&swevent_htable); ++ struct perf_event *event; ++ struct hlist_head *head; ++ ++ rcu_read_lock(); ++ head = find_swevent_head_rcu(swhash, type, event_id); ++ if (!head) ++ goto end; ++ ++ hlist_for_each_entry_rcu(event, head, hlist_entry) { ++ if (perf_swevent_match(event, type, event_id, data, regs)) ++ perf_swevent_event(event, nr, data, regs); ++ } ++end: ++ rcu_read_unlock(); ++} ++ ++int perf_swevent_get_recursion_context(void) ++{ ++ struct swevent_htable *swhash = this_cpu_ptr(&swevent_htable); ++ ++ return get_recursion_context(swhash->recursion); ++} ++EXPORT_SYMBOL_GPL(perf_swevent_get_recursion_context); ++ ++inline void perf_swevent_put_recursion_context(int rctx) ++{ ++ struct swevent_htable *swhash = this_cpu_ptr(&swevent_htable); ++ ++ put_recursion_context(swhash->recursion, rctx); ++} ++ ++void __perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr) ++{ ++ struct perf_sample_data data; ++ int rctx; ++ ++ preempt_disable_notrace(); ++ rctx = perf_swevent_get_recursion_context(); ++ if (rctx < 0) ++ return; ++ ++ perf_sample_data_init(&data, addr, 0); ++ ++ do_perf_sw_event(PERF_TYPE_SOFTWARE, event_id, nr, &data, regs); ++ ++ perf_swevent_put_recursion_context(rctx); ++ preempt_enable_notrace(); ++} ++ ++static void perf_swevent_read(struct perf_event *event) ++{ ++} ++ ++static int perf_swevent_add(struct perf_event *event, int flags) ++{ ++ struct swevent_htable *swhash = this_cpu_ptr(&swevent_htable); ++ struct hw_perf_event *hwc = &event->hw; ++ struct hlist_head *head; ++ ++ if (is_sampling_event(event)) { ++ hwc->last_period = hwc->sample_period; ++ perf_swevent_set_period(event); ++ } ++ ++ hwc->state = !(flags & PERF_EF_START); ++ ++ head = find_swevent_head(swhash, event); ++ if (!head) { ++ /* ++ * We can race with cpu hotplug code. Do not ++ * WARN if the cpu just got unplugged. ++ */ ++ WARN_ON_ONCE(swhash->online); ++ return -EINVAL; ++ } ++ ++ hlist_add_head_rcu(&event->hlist_entry, head); ++ ++ return 0; ++} ++ ++static void perf_swevent_del(struct perf_event *event, int flags) ++{ ++ hlist_del_rcu(&event->hlist_entry); ++} ++ ++static void perf_swevent_start(struct perf_event *event, int flags) ++{ ++ event->hw.state = 0; ++} ++ ++static void perf_swevent_stop(struct perf_event *event, int flags) ++{ ++ event->hw.state = PERF_HES_STOPPED; ++} ++ ++/* Deref the hlist from the update side */ ++static inline struct swevent_hlist * ++swevent_hlist_deref(struct swevent_htable *swhash) ++{ ++ return rcu_dereference_protected(swhash->swevent_hlist, ++ lockdep_is_held(&swhash->hlist_mutex)); ++} ++ ++static void swevent_hlist_release(struct swevent_htable *swhash) ++{ ++ struct swevent_hlist *hlist = swevent_hlist_deref(swhash); ++ ++ if (!hlist) ++ return; ++ ++ RCU_INIT_POINTER(swhash->swevent_hlist, NULL); ++ kfree_rcu(hlist, rcu_head); ++} ++ ++static void swevent_hlist_put_cpu(struct perf_event *event, int cpu) ++{ ++ struct swevent_htable *swhash = &per_cpu(swevent_htable, cpu); ++ ++ mutex_lock(&swhash->hlist_mutex); ++ ++ if (!--swhash->hlist_refcount) ++ swevent_hlist_release(swhash); ++ ++ mutex_unlock(&swhash->hlist_mutex); ++} ++ ++static void swevent_hlist_put(struct perf_event *event) ++{ ++ int cpu; ++ ++ for_each_possible_cpu(cpu) ++ swevent_hlist_put_cpu(event, cpu); ++} ++ ++static int swevent_hlist_get_cpu(struct perf_event *event, int cpu) ++{ ++ struct swevent_htable *swhash = &per_cpu(swevent_htable, cpu); ++ int err = 0; ++ ++ mutex_lock(&swhash->hlist_mutex); ++ ++ if (!swevent_hlist_deref(swhash) && cpu_online(cpu)) { ++ struct swevent_hlist *hlist; ++ ++ hlist = kzalloc(sizeof(*hlist), GFP_KERNEL); ++ if (!hlist) { ++ err = -ENOMEM; ++ goto exit; ++ } ++ rcu_assign_pointer(swhash->swevent_hlist, hlist); ++ } ++ swhash->hlist_refcount++; ++exit: ++ mutex_unlock(&swhash->hlist_mutex); ++ ++ return err; ++} ++ ++static int swevent_hlist_get(struct perf_event *event) ++{ ++ int err; ++ int cpu, failed_cpu; ++ ++ get_online_cpus(); ++ for_each_possible_cpu(cpu) { ++ err = swevent_hlist_get_cpu(event, cpu); ++ if (err) { ++ failed_cpu = cpu; ++ goto fail; ++ } ++ } ++ put_online_cpus(); ++ ++ return 0; ++fail: ++ for_each_possible_cpu(cpu) { ++ if (cpu == failed_cpu) ++ break; ++ swevent_hlist_put_cpu(event, cpu); ++ } ++ ++ put_online_cpus(); ++ return err; ++} ++ ++struct static_key perf_swevent_enabled[PERF_COUNT_SW_MAX]; ++ ++static void sw_perf_event_destroy(struct perf_event *event) ++{ ++ u64 event_id = event->attr.config; ++ ++ WARN_ON(event->parent); ++ ++ static_key_slow_dec(&perf_swevent_enabled[event_id]); ++ swevent_hlist_put(event); ++} ++ ++static int perf_swevent_init(struct perf_event *event) ++{ ++ u64 event_id = event->attr.config; ++ ++ if (event->attr.type != PERF_TYPE_SOFTWARE) ++ return -ENOENT; ++ ++ /* ++ * no branch sampling for software events ++ */ ++ if (has_branch_stack(event)) ++ return -EOPNOTSUPP; ++ ++ switch (event_id) { ++ case PERF_COUNT_SW_CPU_CLOCK: ++ case PERF_COUNT_SW_TASK_CLOCK: ++ return -ENOENT; ++ ++ default: ++ break; ++ } ++ ++ if (event_id >= PERF_COUNT_SW_MAX) ++ return -ENOENT; ++ ++ if (!event->parent) { ++ int err; ++ ++ err = swevent_hlist_get(event); ++ if (err) ++ return err; ++ ++ static_key_slow_inc(&perf_swevent_enabled[event_id]); ++ event->destroy = sw_perf_event_destroy; ++ } ++ ++ return 0; ++} ++ ++static struct pmu perf_swevent = { ++ .task_ctx_nr = perf_sw_context, ++ ++ .event_init = perf_swevent_init, ++ .add = perf_swevent_add, ++ .del = perf_swevent_del, ++ .start = perf_swevent_start, ++ .stop = perf_swevent_stop, ++ .read = perf_swevent_read, ++}; ++ ++#ifdef CONFIG_EVENT_TRACING ++ ++static int perf_tp_filter_match(struct perf_event *event, ++ struct perf_sample_data *data) ++{ ++ void *record = data->raw->data; ++ ++ if (likely(!event->filter) || filter_match_preds(event->filter, record)) ++ return 1; ++ return 0; ++} ++ ++static int perf_tp_event_match(struct perf_event *event, ++ struct perf_sample_data *data, ++ struct pt_regs *regs) ++{ ++ if (event->hw.state & PERF_HES_STOPPED) ++ return 0; ++ /* ++ * All tracepoints are from kernel-space. ++ */ ++ if (event->attr.exclude_kernel) ++ return 0; ++ ++ if (!perf_tp_filter_match(event, data)) ++ return 0; ++ ++ return 1; ++} ++ ++void perf_tp_event(u64 addr, u64 count, void *record, int entry_size, ++ struct pt_regs *regs, struct hlist_head *head, int rctx, ++ struct task_struct *task) ++{ ++ struct perf_sample_data data; ++ struct perf_event *event; ++ ++ struct perf_raw_record raw = { ++ .size = entry_size, ++ .data = record, ++ }; ++ ++ perf_sample_data_init(&data, addr, 0); ++ data.raw = &raw; ++ ++ hlist_for_each_entry_rcu(event, head, hlist_entry) { ++ if (perf_tp_event_match(event, &data, regs)) ++ perf_swevent_event(event, count, &data, regs); ++ } ++ ++ /* ++ * If we got specified a target task, also iterate its context and ++ * deliver this event there too. ++ */ ++ if (task && task != current) { ++ struct perf_event_context *ctx; ++ struct trace_entry *entry = record; ++ ++ rcu_read_lock(); ++ ctx = rcu_dereference(task->perf_event_ctxp[perf_sw_context]); ++ if (!ctx) ++ goto unlock; ++ ++ list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { ++ if (event->attr.type != PERF_TYPE_TRACEPOINT) ++ continue; ++ if (event->attr.config != entry->type) ++ continue; ++ if (perf_tp_event_match(event, &data, regs)) ++ perf_swevent_event(event, count, &data, regs); ++ } ++unlock: ++ rcu_read_unlock(); ++ } ++ ++ perf_swevent_put_recursion_context(rctx); ++} ++EXPORT_SYMBOL_GPL(perf_tp_event); ++ ++static void tp_perf_event_destroy(struct perf_event *event) ++{ ++ perf_trace_destroy(event); ++} ++ ++static int perf_tp_event_init(struct perf_event *event) ++{ ++ int err; ++ ++ if (event->attr.type != PERF_TYPE_TRACEPOINT) ++ return -ENOENT; ++ ++ /* ++ * no branch sampling for tracepoint events ++ */ ++ if (has_branch_stack(event)) ++ return -EOPNOTSUPP; ++ ++ err = perf_trace_init(event); ++ if (err) ++ return err; ++ ++ event->destroy = tp_perf_event_destroy; ++ ++ return 0; ++} ++ ++static struct pmu perf_tracepoint = { ++ .task_ctx_nr = perf_sw_context, ++ ++ .event_init = perf_tp_event_init, ++ .add = perf_trace_add, ++ .del = perf_trace_del, ++ .start = perf_swevent_start, ++ .stop = perf_swevent_stop, ++ .read = perf_swevent_read, ++}; ++ ++static inline void perf_tp_register(void) ++{ ++ perf_pmu_register(&perf_tracepoint, "tracepoint", PERF_TYPE_TRACEPOINT); ++} ++ ++static int perf_event_set_filter(struct perf_event *event, void __user *arg) ++{ ++ char *filter_str; ++ int ret; ++ ++ if (event->attr.type != PERF_TYPE_TRACEPOINT) ++ return -EINVAL; ++ ++ filter_str = strndup_user(arg, PAGE_SIZE); ++ if (IS_ERR(filter_str)) ++ return PTR_ERR(filter_str); ++ ++ ret = ftrace_profile_set_filter(event, event->attr.config, filter_str); ++ ++ kfree(filter_str); ++ return ret; ++} ++ ++static void perf_event_free_filter(struct perf_event *event) ++{ ++ ftrace_profile_free_filter(event); ++} ++ ++#else ++ ++static inline void perf_tp_register(void) ++{ ++} ++ ++static int perf_event_set_filter(struct perf_event *event, void __user *arg) ++{ ++ return -ENOENT; ++} ++ ++static void perf_event_free_filter(struct perf_event *event) ++{ ++} ++ ++#endif /* CONFIG_EVENT_TRACING */ ++ ++#ifdef CONFIG_HAVE_HW_BREAKPOINT ++void perf_bp_event(struct perf_event *bp, void *data) ++{ ++ struct perf_sample_data sample; ++ struct pt_regs *regs = data; ++ ++ perf_sample_data_init(&sample, bp->attr.bp_addr, 0); ++ ++ if (!bp->hw.state && !perf_exclude_event(bp, regs)) ++ perf_swevent_event(bp, 1, &sample, regs); ++} ++#endif ++ ++/* ++ * hrtimer based swevent callback ++ */ ++ ++static enum hrtimer_restart perf_swevent_hrtimer(struct hrtimer *hrtimer) ++{ ++ enum hrtimer_restart ret = HRTIMER_RESTART; ++ struct perf_sample_data data; ++ struct pt_regs *regs; ++ struct perf_event *event; ++ u64 period; ++ ++ event = container_of(hrtimer, struct perf_event, hw.hrtimer); ++ ++ if (event->state != PERF_EVENT_STATE_ACTIVE) ++ return HRTIMER_NORESTART; ++ ++ event->pmu->read(event); ++ ++ perf_sample_data_init(&data, 0, event->hw.last_period); ++ regs = get_irq_regs(); ++ ++ if (regs && !perf_exclude_event(event, regs)) { ++ if (!(event->attr.exclude_idle && is_idle_task(current))) ++ if (__perf_event_overflow(event, 1, &data, regs)) ++ ret = HRTIMER_NORESTART; ++ } ++ ++ period = max_t(u64, 10000, event->hw.sample_period); ++ hrtimer_forward_now(hrtimer, ns_to_ktime(period)); ++ ++ return ret; ++} ++ ++static void perf_swevent_start_hrtimer(struct perf_event *event) ++{ ++ struct hw_perf_event *hwc = &event->hw; ++ s64 period; ++ ++ if (!is_sampling_event(event)) ++ return; ++ ++ period = local64_read(&hwc->period_left); ++ if (period) { ++ if (period < 0) ++ period = 10000; ++ ++ local64_set(&hwc->period_left, 0); ++ } else { ++ period = max_t(u64, 10000, hwc->sample_period); ++ } ++ __hrtimer_start_range_ns(&hwc->hrtimer, ++ ns_to_ktime(period), 0, ++ HRTIMER_MODE_REL_PINNED, 0); ++} ++ ++static void perf_swevent_cancel_hrtimer(struct perf_event *event) ++{ ++ struct hw_perf_event *hwc = &event->hw; ++ ++ if (is_sampling_event(event)) { ++ ktime_t remaining = hrtimer_get_remaining(&hwc->hrtimer); ++ local64_set(&hwc->period_left, ktime_to_ns(remaining)); ++ ++ hrtimer_cancel(&hwc->hrtimer); ++ } ++} ++ ++static void perf_swevent_init_hrtimer(struct perf_event *event) ++{ ++ struct hw_perf_event *hwc = &event->hw; ++ ++ if (!is_sampling_event(event)) ++ return; ++ ++ hrtimer_init(&hwc->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ hwc->hrtimer.function = perf_swevent_hrtimer; ++ ++ /* ++ * Since hrtimers have a fixed rate, we can do a static freq->period ++ * mapping and avoid the whole period adjust feedback stuff. ++ */ ++ if (event->attr.freq) { ++ long freq = event->attr.sample_freq; ++ ++ event->attr.sample_period = NSEC_PER_SEC / freq; ++ hwc->sample_period = event->attr.sample_period; ++ local64_set(&hwc->period_left, hwc->sample_period); ++ hwc->last_period = hwc->sample_period; ++ event->attr.freq = 0; ++ } ++} ++ ++/* ++ * Software event: cpu wall time clock ++ */ ++ ++static void cpu_clock_event_update(struct perf_event *event) ++{ ++ s64 prev; ++ u64 now; ++ ++ now = local_clock(); ++ prev = local64_xchg(&event->hw.prev_count, now); ++ local64_add(now - prev, &event->count); ++} ++ ++static void cpu_clock_event_start(struct perf_event *event, int flags) ++{ ++ local64_set(&event->hw.prev_count, local_clock()); ++ perf_swevent_start_hrtimer(event); ++} ++ ++static void cpu_clock_event_stop(struct perf_event *event, int flags) ++{ ++ perf_swevent_cancel_hrtimer(event); ++ cpu_clock_event_update(event); ++} ++ ++static int cpu_clock_event_add(struct perf_event *event, int flags) ++{ ++ if (flags & PERF_EF_START) ++ cpu_clock_event_start(event, flags); ++ ++ return 0; ++} ++ ++static void cpu_clock_event_del(struct perf_event *event, int flags) ++{ ++ cpu_clock_event_stop(event, flags); ++} ++ ++static void cpu_clock_event_read(struct perf_event *event) ++{ ++ cpu_clock_event_update(event); ++} ++ ++static int cpu_clock_event_init(struct perf_event *event) ++{ ++ if (event->attr.type != PERF_TYPE_SOFTWARE) ++ return -ENOENT; ++ ++ if (event->attr.config != PERF_COUNT_SW_CPU_CLOCK) ++ return -ENOENT; ++ ++ /* ++ * no branch sampling for software events ++ */ ++ if (has_branch_stack(event)) ++ return -EOPNOTSUPP; ++ ++ perf_swevent_init_hrtimer(event); ++ ++ return 0; ++} ++ ++static struct pmu perf_cpu_clock = { ++ .task_ctx_nr = perf_sw_context, ++ ++ .event_init = cpu_clock_event_init, ++ .add = cpu_clock_event_add, ++ .del = cpu_clock_event_del, ++ .start = cpu_clock_event_start, ++ .stop = cpu_clock_event_stop, ++ .read = cpu_clock_event_read, ++}; ++ ++/* ++ * Software event: task time clock ++ */ ++ ++static void task_clock_event_update(struct perf_event *event, u64 now) ++{ ++ u64 prev; ++ s64 delta; ++ ++ prev = local64_xchg(&event->hw.prev_count, now); ++ delta = now - prev; ++ local64_add(delta, &event->count); ++} ++ ++static void task_clock_event_start(struct perf_event *event, int flags) ++{ ++ local64_set(&event->hw.prev_count, event->ctx->time); ++ perf_swevent_start_hrtimer(event); ++} ++ ++static void task_clock_event_stop(struct perf_event *event, int flags) ++{ ++ perf_swevent_cancel_hrtimer(event); ++ task_clock_event_update(event, event->ctx->time); ++} ++ ++static int task_clock_event_add(struct perf_event *event, int flags) ++{ ++ if (flags & PERF_EF_START) ++ task_clock_event_start(event, flags); ++ ++ return 0; ++} ++ ++static void task_clock_event_del(struct perf_event *event, int flags) ++{ ++ task_clock_event_stop(event, PERF_EF_UPDATE); ++} ++ ++static void task_clock_event_read(struct perf_event *event) ++{ ++ u64 now = perf_clock(); ++ u64 delta = now - event->ctx->timestamp; ++ u64 time = event->ctx->time + delta; ++ ++ task_clock_event_update(event, time); ++} ++ ++static int task_clock_event_init(struct perf_event *event) ++{ ++ if (event->attr.type != PERF_TYPE_SOFTWARE) ++ return -ENOENT; ++ ++ if (event->attr.config != PERF_COUNT_SW_TASK_CLOCK) ++ return -ENOENT; ++ ++ /* ++ * no branch sampling for software events ++ */ ++ if (has_branch_stack(event)) ++ return -EOPNOTSUPP; ++ ++ perf_swevent_init_hrtimer(event); ++ ++ return 0; ++} ++ ++static struct pmu perf_task_clock = { ++ .task_ctx_nr = perf_sw_context, ++ ++ .event_init = task_clock_event_init, ++ .add = task_clock_event_add, ++ .del = task_clock_event_del, ++ .start = task_clock_event_start, ++ .stop = task_clock_event_stop, ++ .read = task_clock_event_read, ++}; ++ ++static void perf_pmu_nop_void(struct pmu *pmu) ++{ ++} ++ ++static int perf_pmu_nop_int(struct pmu *pmu) ++{ ++ return 0; ++} ++ ++static void perf_pmu_start_txn(struct pmu *pmu) ++{ ++ perf_pmu_disable(pmu); ++} ++ ++static int perf_pmu_commit_txn(struct pmu *pmu) ++{ ++ perf_pmu_enable(pmu); ++ return 0; ++} ++ ++static void perf_pmu_cancel_txn(struct pmu *pmu) ++{ ++ perf_pmu_enable(pmu); ++} ++ ++static int perf_event_idx_default(struct perf_event *event) ++{ ++ return 0; ++} ++ ++/* ++ * Ensures all contexts with the same task_ctx_nr have the same ++ * pmu_cpu_context too. ++ */ ++static struct perf_cpu_context __percpu *find_pmu_context(int ctxn) ++{ ++ struct pmu *pmu; ++ ++ if (ctxn < 0) ++ return NULL; ++ ++ list_for_each_entry(pmu, &pmus, entry) { ++ if (pmu->task_ctx_nr == ctxn) ++ return pmu->pmu_cpu_context; ++ } ++ ++ return NULL; ++} ++ ++static void update_pmu_context(struct pmu *pmu, struct pmu *old_pmu) ++{ ++ int cpu; ++ ++ for_each_possible_cpu(cpu) { ++ struct perf_cpu_context *cpuctx; ++ ++ cpuctx = per_cpu_ptr(pmu->pmu_cpu_context, cpu); ++ ++ if (cpuctx->unique_pmu == old_pmu) ++ cpuctx->unique_pmu = pmu; ++ } ++} ++ ++static void free_pmu_context(struct pmu *pmu) ++{ ++ struct pmu *i; ++ ++ mutex_lock(&pmus_lock); ++ /* ++ * Like a real lame refcount. ++ */ ++ list_for_each_entry(i, &pmus, entry) { ++ if (i->pmu_cpu_context == pmu->pmu_cpu_context) { ++ update_pmu_context(i, pmu); ++ goto out; ++ } ++ } ++ ++ free_percpu(pmu->pmu_cpu_context); ++out: ++ mutex_unlock(&pmus_lock); ++} ++static struct idr pmu_idr; ++ ++static ssize_t ++type_show(struct device *dev, struct device_attribute *attr, char *page) ++{ ++ struct pmu *pmu = dev_get_drvdata(dev); ++ ++ return snprintf(page, PAGE_SIZE-1, "%d\n", pmu->type); ++} ++static DEVICE_ATTR_RO(type); ++ ++static ssize_t ++perf_event_mux_interval_ms_show(struct device *dev, ++ struct device_attribute *attr, ++ char *page) ++{ ++ struct pmu *pmu = dev_get_drvdata(dev); ++ ++ return snprintf(page, PAGE_SIZE-1, "%d\n", pmu->hrtimer_interval_ms); ++} ++ ++static ssize_t ++perf_event_mux_interval_ms_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct pmu *pmu = dev_get_drvdata(dev); ++ int timer, cpu, ret; ++ ++ ret = kstrtoint(buf, 0, &timer); ++ if (ret) ++ return ret; ++ ++ if (timer < 1) ++ return -EINVAL; ++ ++ /* same value, noting to do */ ++ if (timer == pmu->hrtimer_interval_ms) ++ return count; ++ ++ pmu->hrtimer_interval_ms = timer; ++ ++ /* update all cpuctx for this PMU */ ++ for_each_possible_cpu(cpu) { ++ struct perf_cpu_context *cpuctx; ++ cpuctx = per_cpu_ptr(pmu->pmu_cpu_context, cpu); ++ cpuctx->hrtimer_interval = ns_to_ktime(NSEC_PER_MSEC * timer); ++ ++ if (hrtimer_active(&cpuctx->hrtimer)) ++ hrtimer_forward_now(&cpuctx->hrtimer, cpuctx->hrtimer_interval); ++ } ++ ++ return count; ++} ++static DEVICE_ATTR_RW(perf_event_mux_interval_ms); ++ ++static struct attribute *pmu_dev_attrs[] = { ++ &dev_attr_type.attr, ++ &dev_attr_perf_event_mux_interval_ms.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(pmu_dev); ++ ++static int pmu_bus_running; ++static struct bus_type pmu_bus = { ++ .name = "event_source", ++ .dev_groups = pmu_dev_groups, ++}; ++ ++static void pmu_dev_release(struct device *dev) ++{ ++ kfree(dev); ++} ++ ++static int pmu_dev_alloc(struct pmu *pmu) ++{ ++ int ret = -ENOMEM; ++ ++ pmu->dev = kzalloc(sizeof(struct device), GFP_KERNEL); ++ if (!pmu->dev) ++ goto out; ++ ++ pmu->dev->groups = pmu->attr_groups; ++ device_initialize(pmu->dev); ++ ret = dev_set_name(pmu->dev, "%s", pmu->name); ++ if (ret) ++ goto free_dev; ++ ++ dev_set_drvdata(pmu->dev, pmu); ++ pmu->dev->bus = &pmu_bus; ++ pmu->dev->release = pmu_dev_release; ++ ret = device_add(pmu->dev); ++ if (ret) ++ goto free_dev; ++ ++out: ++ return ret; ++ ++free_dev: ++ put_device(pmu->dev); ++ goto out; ++} ++ ++static struct lock_class_key cpuctx_mutex; ++static struct lock_class_key cpuctx_lock; ++ ++int perf_pmu_register(struct pmu *pmu, const char *name, int type) ++{ ++ int cpu, ret; ++ ++ mutex_lock(&pmus_lock); ++ ret = -ENOMEM; ++ pmu->pmu_disable_count = alloc_percpu(int); ++ if (!pmu->pmu_disable_count) ++ goto unlock; ++ ++ pmu->type = -1; ++ if (!name) ++ goto skip_type; ++ pmu->name = name; ++ ++ if (type < 0) { ++ type = idr_alloc(&pmu_idr, pmu, PERF_TYPE_MAX, 0, GFP_KERNEL); ++ if (type < 0) { ++ ret = type; ++ goto free_pdc; ++ } ++ } ++ pmu->type = type; ++ ++ if (pmu_bus_running) { ++ ret = pmu_dev_alloc(pmu); ++ if (ret) ++ goto free_idr; ++ } ++ ++skip_type: ++ pmu->pmu_cpu_context = find_pmu_context(pmu->task_ctx_nr); ++ if (pmu->pmu_cpu_context) ++ goto got_cpu_context; ++ ++ ret = -ENOMEM; ++ pmu->pmu_cpu_context = alloc_percpu(struct perf_cpu_context); ++ if (!pmu->pmu_cpu_context) ++ goto free_dev; ++ ++ for_each_possible_cpu(cpu) { ++ struct perf_cpu_context *cpuctx; ++ ++ cpuctx = per_cpu_ptr(pmu->pmu_cpu_context, cpu); ++ __perf_event_init_context(&cpuctx->ctx); ++ lockdep_set_class(&cpuctx->ctx.mutex, &cpuctx_mutex); ++ lockdep_set_class(&cpuctx->ctx.lock, &cpuctx_lock); ++ cpuctx->ctx.type = cpu_context; ++ cpuctx->ctx.pmu = pmu; ++ ++ __perf_cpu_hrtimer_init(cpuctx, cpu); ++ ++ INIT_LIST_HEAD(&cpuctx->rotation_list); ++ cpuctx->unique_pmu = pmu; ++ } ++ ++got_cpu_context: ++ if (!pmu->start_txn) { ++ if (pmu->pmu_enable) { ++ /* ++ * If we have pmu_enable/pmu_disable calls, install ++ * transaction stubs that use that to try and batch ++ * hardware accesses. ++ */ ++ pmu->start_txn = perf_pmu_start_txn; ++ pmu->commit_txn = perf_pmu_commit_txn; ++ pmu->cancel_txn = perf_pmu_cancel_txn; ++ } else { ++ pmu->start_txn = perf_pmu_nop_void; ++ pmu->commit_txn = perf_pmu_nop_int; ++ pmu->cancel_txn = perf_pmu_nop_void; ++ } ++ } ++ ++ if (!pmu->pmu_enable) { ++ pmu->pmu_enable = perf_pmu_nop_void; ++ pmu->pmu_disable = perf_pmu_nop_void; ++ } ++ ++ if (!pmu->event_idx) ++ pmu->event_idx = perf_event_idx_default; ++ ++ list_add_rcu(&pmu->entry, &pmus); ++ ret = 0; ++unlock: ++ mutex_unlock(&pmus_lock); ++ ++ return ret; ++ ++free_dev: ++ device_del(pmu->dev); ++ put_device(pmu->dev); ++ ++free_idr: ++ if (pmu->type >= PERF_TYPE_MAX) ++ idr_remove(&pmu_idr, pmu->type); ++ ++free_pdc: ++ free_percpu(pmu->pmu_disable_count); ++ goto unlock; ++} ++EXPORT_SYMBOL_GPL(perf_pmu_register); ++ ++void perf_pmu_unregister(struct pmu *pmu) ++{ ++ mutex_lock(&pmus_lock); ++ list_del_rcu(&pmu->entry); ++ mutex_unlock(&pmus_lock); ++ ++ /* ++ * We dereference the pmu list under both SRCU and regular RCU, so ++ * synchronize against both of those. ++ */ ++ synchronize_srcu(&pmus_srcu); ++ synchronize_rcu(); ++ ++ free_percpu(pmu->pmu_disable_count); ++ if (pmu->type >= PERF_TYPE_MAX) ++ idr_remove(&pmu_idr, pmu->type); ++ device_del(pmu->dev); ++ put_device(pmu->dev); ++ free_pmu_context(pmu); ++} ++EXPORT_SYMBOL_GPL(perf_pmu_unregister); ++ ++struct pmu *perf_init_event(struct perf_event *event) ++{ ++ struct pmu *pmu = NULL; ++ int idx; ++ int ret; ++ ++ idx = srcu_read_lock(&pmus_srcu); ++ ++ rcu_read_lock(); ++ pmu = idr_find(&pmu_idr, event->attr.type); ++ rcu_read_unlock(); ++ if (pmu) { ++ if (!try_module_get(pmu->module)) { ++ pmu = ERR_PTR(-ENODEV); ++ goto unlock; ++ } ++ event->pmu = pmu; ++ ret = pmu->event_init(event); ++ if (ret) ++ pmu = ERR_PTR(ret); ++ goto unlock; ++ } ++ ++ list_for_each_entry_rcu(pmu, &pmus, entry) { ++ if (!try_module_get(pmu->module)) { ++ pmu = ERR_PTR(-ENODEV); ++ goto unlock; ++ } ++ event->pmu = pmu; ++ ret = pmu->event_init(event); ++ if (!ret) ++ goto unlock; ++ ++ if (ret != -ENOENT) { ++ pmu = ERR_PTR(ret); ++ goto unlock; ++ } ++ } ++ pmu = ERR_PTR(-ENOENT); ++unlock: ++ srcu_read_unlock(&pmus_srcu, idx); ++ ++ return pmu; ++} ++ ++static void account_event_cpu(struct perf_event *event, int cpu) ++{ ++ if (event->parent) ++ return; ++ ++ if (has_branch_stack(event)) { ++ if (!(event->attach_state & PERF_ATTACH_TASK)) ++ atomic_inc(&per_cpu(perf_branch_stack_events, cpu)); ++ } ++ if (is_cgroup_event(event)) ++ atomic_inc(&per_cpu(perf_cgroup_events, cpu)); ++} ++ ++static void account_event(struct perf_event *event) ++{ ++ if (event->parent) ++ return; ++ ++ if (event->attach_state & PERF_ATTACH_TASK) ++ static_key_slow_inc(&perf_sched_events.key); ++ if (event->attr.mmap || event->attr.mmap_data) ++ atomic_inc(&nr_mmap_events); ++ if (event->attr.comm) ++ atomic_inc(&nr_comm_events); ++ if (event->attr.task) ++ atomic_inc(&nr_task_events); ++ if (event->attr.freq) { ++ if (atomic_inc_return(&nr_freq_events) == 1) ++ tick_nohz_full_kick_all(); ++ } ++ if (has_branch_stack(event)) ++ static_key_slow_inc(&perf_sched_events.key); ++ if (is_cgroup_event(event)) ++ static_key_slow_inc(&perf_sched_events.key); ++ ++ account_event_cpu(event, event->cpu); ++} ++ ++/* ++ * Allocate and initialize a event structure ++ */ ++static struct perf_event * ++perf_event_alloc(struct perf_event_attr *attr, int cpu, ++ struct task_struct *task, ++ struct perf_event *group_leader, ++ struct perf_event *parent_event, ++ perf_overflow_handler_t overflow_handler, ++ void *context) ++{ ++ struct pmu *pmu; ++ struct perf_event *event; ++ struct hw_perf_event *hwc; ++ long err = -EINVAL; ++ ++ if ((unsigned)cpu >= nr_cpu_ids) { ++ if (!task || cpu != -1) ++ return ERR_PTR(-EINVAL); ++ } ++ ++ event = kzalloc(sizeof(*event), GFP_KERNEL); ++ if (!event) ++ return ERR_PTR(-ENOMEM); ++ ++ /* ++ * Single events are their own group leaders, with an ++ * empty sibling list: ++ */ ++ if (!group_leader) ++ group_leader = event; ++ ++ mutex_init(&event->child_mutex); ++ INIT_LIST_HEAD(&event->child_list); ++ ++ INIT_LIST_HEAD(&event->group_entry); ++ INIT_LIST_HEAD(&event->event_entry); ++ INIT_LIST_HEAD(&event->sibling_list); ++ INIT_LIST_HEAD(&event->rb_entry); ++ INIT_LIST_HEAD(&event->active_entry); ++ INIT_HLIST_NODE(&event->hlist_entry); ++ ++ ++ init_waitqueue_head(&event->waitq); ++ init_irq_work(&event->pending, perf_pending_event); ++ ++ mutex_init(&event->mmap_mutex); ++ ++ atomic_long_set(&event->refcount, 1); ++ event->cpu = cpu; ++ event->attr = *attr; ++ event->group_leader = group_leader; ++ event->pmu = NULL; ++ event->oncpu = -1; ++ ++ event->parent = parent_event; ++ ++ event->ns = get_pid_ns(task_active_pid_ns(current)); ++ event->id = atomic64_inc_return(&perf_event_id); ++ ++ event->state = PERF_EVENT_STATE_INACTIVE; ++ ++ if (task) { ++ event->attach_state = PERF_ATTACH_TASK; ++ ++ if (attr->type == PERF_TYPE_TRACEPOINT) ++ event->hw.tp_target = task; ++#ifdef CONFIG_HAVE_HW_BREAKPOINT ++ /* ++ * hw_breakpoint is a bit difficult here.. ++ */ ++ else if (attr->type == PERF_TYPE_BREAKPOINT) ++ event->hw.bp_target = task; ++#endif ++ } ++ ++ if (!overflow_handler && parent_event) { ++ overflow_handler = parent_event->overflow_handler; ++ context = parent_event->overflow_handler_context; ++ } ++ ++ event->overflow_handler = overflow_handler; ++ event->overflow_handler_context = context; ++ ++ perf_event__state_init(event); ++ ++ pmu = NULL; ++ ++ hwc = &event->hw; ++ hwc->sample_period = attr->sample_period; ++ if (attr->freq && attr->sample_freq) ++ hwc->sample_period = 1; ++ hwc->last_period = hwc->sample_period; ++ ++ local64_set(&hwc->period_left, hwc->sample_period); ++ ++ /* ++ * we currently do not support PERF_FORMAT_GROUP on inherited events ++ */ ++ if (attr->inherit && (attr->read_format & PERF_FORMAT_GROUP)) ++ goto err_ns; ++ ++ pmu = perf_init_event(event); ++ if (!pmu) ++ goto err_ns; ++ else if (IS_ERR(pmu)) { ++ err = PTR_ERR(pmu); ++ goto err_ns; ++ } ++ ++ if (!event->parent) { ++ if (event->attr.sample_type & PERF_SAMPLE_CALLCHAIN) { ++ err = get_callchain_buffers(); ++ if (err) ++ goto err_pmu; ++ } ++ } ++ ++ return event; ++ ++err_pmu: ++ if (event->destroy) ++ event->destroy(event); ++ module_put(pmu->module); ++err_ns: ++ if (event->ns) ++ put_pid_ns(event->ns); ++ kfree(event); ++ ++ return ERR_PTR(err); ++} ++ ++static int perf_copy_attr(struct perf_event_attr __user *uattr, ++ struct perf_event_attr *attr) ++{ ++ u32 size; ++ int ret; ++ ++ if (!access_ok(VERIFY_WRITE, uattr, PERF_ATTR_SIZE_VER0)) ++ return -EFAULT; ++ ++ /* ++ * zero the full structure, so that a short copy will be nice. ++ */ ++ memset(attr, 0, sizeof(*attr)); ++ ++ ret = get_user(size, &uattr->size); ++ if (ret) ++ return ret; ++ ++ if (size > PAGE_SIZE) /* silly large */ ++ goto err_size; ++ ++ if (!size) /* abi compat */ ++ size = PERF_ATTR_SIZE_VER0; ++ ++ if (size < PERF_ATTR_SIZE_VER0) ++ goto err_size; ++ ++ /* ++ * If we're handed a bigger struct than we know of, ++ * ensure all the unknown bits are 0 - i.e. new ++ * user-space does not rely on any kernel feature ++ * extensions we dont know about yet. ++ */ ++ if (size > sizeof(*attr)) { ++ unsigned char __user *addr; ++ unsigned char __user *end; ++ unsigned char val; ++ ++ addr = (void __user *)uattr + sizeof(*attr); ++ end = (void __user *)uattr + size; ++ ++ for (; addr < end; addr++) { ++ ret = get_user(val, addr); ++ if (ret) ++ return ret; ++ if (val) ++ goto err_size; ++ } ++ size = sizeof(*attr); ++ } ++ ++ ret = copy_from_user(attr, uattr, size); ++ if (ret) ++ return -EFAULT; ++ ++ if (attr->__reserved_1) ++ return -EINVAL; ++ ++ if (attr->sample_type & ~(PERF_SAMPLE_MAX-1)) ++ return -EINVAL; ++ ++ if (attr->read_format & ~(PERF_FORMAT_MAX-1)) ++ return -EINVAL; ++ ++ if (attr->sample_type & PERF_SAMPLE_BRANCH_STACK) { ++ u64 mask = attr->branch_sample_type; ++ ++ /* only using defined bits */ ++ if (mask & ~(PERF_SAMPLE_BRANCH_MAX-1)) ++ return -EINVAL; ++ ++ /* at least one branch bit must be set */ ++ if (!(mask & ~PERF_SAMPLE_BRANCH_PLM_ALL)) ++ return -EINVAL; ++ ++ /* propagate priv level, when not set for branch */ ++ if (!(mask & PERF_SAMPLE_BRANCH_PLM_ALL)) { ++ ++ /* exclude_kernel checked on syscall entry */ ++ if (!attr->exclude_kernel) ++ mask |= PERF_SAMPLE_BRANCH_KERNEL; ++ ++ if (!attr->exclude_user) ++ mask |= PERF_SAMPLE_BRANCH_USER; ++ ++ if (!attr->exclude_hv) ++ mask |= PERF_SAMPLE_BRANCH_HV; ++ /* ++ * adjust user setting (for HW filter setup) ++ */ ++ attr->branch_sample_type = mask; ++ } ++ /* privileged levels capture (kernel, hv): check permissions */ ++ if ((mask & PERF_SAMPLE_BRANCH_PERM_PLM) ++ && perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN)) ++ return -EACCES; ++ } ++ ++ if (attr->sample_type & PERF_SAMPLE_REGS_USER) { ++ ret = perf_reg_validate(attr->sample_regs_user); ++ if (ret) ++ return ret; ++ } ++ ++ if (attr->sample_type & PERF_SAMPLE_STACK_USER) { ++ if (!arch_perf_have_user_stack_dump()) ++ return -ENOSYS; ++ ++ /* ++ * We have __u32 type for the size, but so far ++ * we can only use __u16 as maximum due to the ++ * __u16 sample size limit. ++ */ ++ if (attr->sample_stack_user >= USHRT_MAX) ++ ret = -EINVAL; ++ else if (!IS_ALIGNED(attr->sample_stack_user, sizeof(u64))) ++ ret = -EINVAL; ++ } ++ ++out: ++ return ret; ++ ++err_size: ++ put_user(sizeof(*attr), &uattr->size); ++ ret = -E2BIG; ++ goto out; ++} ++ ++static int ++perf_event_set_output(struct perf_event *event, struct perf_event *output_event) ++{ ++ struct ring_buffer *rb = NULL; ++ int ret = -EINVAL; ++ ++ if (!output_event) ++ goto set; ++ ++ /* don't allow circular references */ ++ if (event == output_event) ++ goto out; ++ ++ /* ++ * Don't allow cross-cpu buffers ++ */ ++ if (output_event->cpu != event->cpu) ++ goto out; ++ ++ /* ++ * If its not a per-cpu rb, it must be the same task. ++ */ ++ if (output_event->cpu == -1 && output_event->ctx != event->ctx) ++ goto out; ++ ++set: ++ mutex_lock(&event->mmap_mutex); ++ /* Can't redirect output if we've got an active mmap() */ ++ if (atomic_read(&event->mmap_count)) ++ goto unlock; ++ ++ if (output_event) { ++ /* get the rb we want to redirect to */ ++ rb = ring_buffer_get(output_event); ++ if (!rb) ++ goto unlock; ++ } ++ ++ ring_buffer_attach(event, rb); ++ ++ ret = 0; ++unlock: ++ mutex_unlock(&event->mmap_mutex); ++ ++out: ++ return ret; ++} ++ ++/** ++ * sys_perf_event_open - open a performance event, associate it to a task/cpu ++ * ++ * @attr_uptr: event_id type attributes for monitoring/sampling ++ * @pid: target pid ++ * @cpu: target cpu ++ * @group_fd: group leader event fd ++ */ ++SYSCALL_DEFINE5(perf_event_open, ++ struct perf_event_attr __user *, attr_uptr, ++ pid_t, pid, int, cpu, int, group_fd, unsigned long, flags) ++{ ++ struct perf_event *group_leader = NULL, *output_event = NULL; ++ struct perf_event *event, *sibling; ++ struct perf_event_attr attr; ++ struct perf_event_context *ctx; ++ struct file *event_file = NULL; ++ struct fd group = {NULL, 0}; ++ struct task_struct *task = NULL; ++ struct pmu *pmu; ++ int event_fd; ++ int move_group = 0; ++ int err; ++ int f_flags = O_RDWR; ++ ++ /* for future expandability... */ ++ if (flags & ~PERF_FLAG_ALL) ++ return -EINVAL; ++ ++ err = perf_copy_attr(attr_uptr, &attr); ++ if (err) ++ return err; ++ ++ if (!attr.exclude_kernel) { ++ if (perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN)) ++ return -EACCES; ++ } ++ ++ if (attr.freq) { ++ if (attr.sample_freq > sysctl_perf_event_sample_rate) ++ return -EINVAL; ++ } else { ++ if (attr.sample_period & (1ULL << 63)) ++ return -EINVAL; ++ } ++ ++ /* ++ * In cgroup mode, the pid argument is used to pass the fd ++ * opened to the cgroup directory in cgroupfs. The cpu argument ++ * designates the cpu on which to monitor threads from that ++ * cgroup. ++ */ ++ if ((flags & PERF_FLAG_PID_CGROUP) && (pid == -1 || cpu == -1)) ++ return -EINVAL; ++ ++ if (flags & PERF_FLAG_FD_CLOEXEC) ++ f_flags |= O_CLOEXEC; ++ ++ event_fd = get_unused_fd_flags(f_flags); ++ if (event_fd < 0) ++ return event_fd; ++ ++ if (group_fd != -1) { ++ err = perf_fget_light(group_fd, &group); ++ if (err) ++ goto err_fd; ++ group_leader = group.file->private_data; ++ if (flags & PERF_FLAG_FD_OUTPUT) ++ output_event = group_leader; ++ if (flags & PERF_FLAG_FD_NO_GROUP) ++ group_leader = NULL; ++ } ++ ++ if (pid != -1 && !(flags & PERF_FLAG_PID_CGROUP)) { ++ task = find_lively_task_by_vpid(pid); ++ if (IS_ERR(task)) { ++ err = PTR_ERR(task); ++ goto err_group_fd; ++ } ++ } ++ ++ if (task && group_leader && ++ group_leader->attr.inherit != attr.inherit) { ++ err = -EINVAL; ++ goto err_task; ++ } ++ ++ get_online_cpus(); ++ ++ event = perf_event_alloc(&attr, cpu, task, group_leader, NULL, ++ NULL, NULL); ++ if (IS_ERR(event)) { ++ err = PTR_ERR(event); ++ goto err_cpus; ++ } ++ ++ if (flags & PERF_FLAG_PID_CGROUP) { ++ err = perf_cgroup_connect(pid, event, &attr, group_leader); ++ if (err) { ++ __free_event(event); ++ goto err_cpus; ++ } ++ } ++ ++ if (is_sampling_event(event)) { ++ if (event->pmu->capabilities & PERF_PMU_CAP_NO_INTERRUPT) { ++ err = -ENOTSUPP; ++ goto err_alloc; ++ } ++ } ++ ++ account_event(event); ++ ++ /* ++ * Special case software events and allow them to be part of ++ * any hardware group. ++ */ ++ pmu = event->pmu; ++ ++ if (group_leader && ++ (is_software_event(event) != is_software_event(group_leader))) { ++ if (is_software_event(event)) { ++ /* ++ * If event and group_leader are not both a software ++ * event, and event is, then group leader is not. ++ * ++ * Allow the addition of software events to !software ++ * groups, this is safe because software events never ++ * fail to schedule. ++ */ ++ pmu = group_leader->pmu; ++ } else if (is_software_event(group_leader) && ++ (group_leader->group_flags & PERF_GROUP_SOFTWARE)) { ++ /* ++ * In case the group is a pure software group, and we ++ * try to add a hardware event, move the whole group to ++ * the hardware context. ++ */ ++ move_group = 1; ++ } ++ } ++ ++ /* ++ * Get the target context (task or percpu): ++ */ ++ ctx = find_get_context(pmu, task, event->cpu); ++ if (IS_ERR(ctx)) { ++ err = PTR_ERR(ctx); ++ goto err_alloc; ++ } ++ ++ if (task) { ++ put_task_struct(task); ++ task = NULL; ++ } ++ ++ /* ++ * Look up the group leader (we will attach this event to it): ++ */ ++ if (group_leader) { ++ err = -EINVAL; ++ ++ /* ++ * Do not allow a recursive hierarchy (this new sibling ++ * becoming part of another group-sibling): ++ */ ++ if (group_leader->group_leader != group_leader) ++ goto err_context; ++ /* ++ * Do not allow to attach to a group in a different ++ * task or CPU context: ++ */ ++ if (move_group) { ++ if (group_leader->ctx->type != ctx->type) ++ goto err_context; ++ } else { ++ if (group_leader->ctx != ctx) ++ goto err_context; ++ } ++ ++ /* ++ * Only a group leader can be exclusive or pinned ++ */ ++ if (attr.exclusive || attr.pinned) ++ goto err_context; ++ } ++ ++ if (output_event) { ++ err = perf_event_set_output(event, output_event); ++ if (err) ++ goto err_context; ++ } ++ ++ event_file = anon_inode_getfile("[perf_event]", &perf_fops, event, ++ f_flags); ++ if (IS_ERR(event_file)) { ++ err = PTR_ERR(event_file); ++ goto err_context; ++ } ++ ++ if (move_group) { ++ struct perf_event_context *gctx = group_leader->ctx; ++ ++ mutex_lock(&gctx->mutex); ++ perf_remove_from_context(group_leader, false); ++ ++ /* ++ * Removing from the context ends up with disabled ++ * event. What we want here is event in the initial ++ * startup state, ready to be add into new context. ++ */ ++ perf_event__state_init(group_leader); ++ list_for_each_entry(sibling, &group_leader->sibling_list, ++ group_entry) { ++ perf_remove_from_context(sibling, false); ++ perf_event__state_init(sibling); ++ put_ctx(gctx); ++ } ++ mutex_unlock(&gctx->mutex); ++ put_ctx(gctx); ++ } ++ ++ WARN_ON_ONCE(ctx->parent_ctx); ++ mutex_lock(&ctx->mutex); ++ ++ if (move_group) { ++ synchronize_rcu(); ++ perf_install_in_context(ctx, group_leader, group_leader->cpu); ++ get_ctx(ctx); ++ list_for_each_entry(sibling, &group_leader->sibling_list, ++ group_entry) { ++ perf_install_in_context(ctx, sibling, sibling->cpu); ++ get_ctx(ctx); ++ } ++ } ++ ++ perf_install_in_context(ctx, event, event->cpu); ++ perf_unpin_context(ctx); ++ mutex_unlock(&ctx->mutex); ++ ++ put_online_cpus(); ++ ++ event->owner = current; ++ ++ mutex_lock(¤t->perf_event_mutex); ++ list_add_tail(&event->owner_entry, ¤t->perf_event_list); ++ mutex_unlock(¤t->perf_event_mutex); ++ ++ /* ++ * Precalculate sample_data sizes ++ */ ++ perf_event__header_size(event); ++ perf_event__id_header_size(event); ++ ++ /* ++ * Drop the reference on the group_event after placing the ++ * new event on the sibling_list. This ensures destruction ++ * of the group leader will find the pointer to itself in ++ * perf_group_detach(). ++ */ ++ fdput(group); ++ fd_install(event_fd, event_file); ++ return event_fd; ++ ++err_context: ++ perf_unpin_context(ctx); ++ put_ctx(ctx); ++err_alloc: ++ free_event(event); ++err_cpus: ++ put_online_cpus(); ++err_task: ++ if (task) ++ put_task_struct(task); ++err_group_fd: ++ fdput(group); ++err_fd: ++ put_unused_fd(event_fd); ++ return err; ++} ++ ++/** ++ * perf_event_create_kernel_counter ++ * ++ * @attr: attributes of the counter to create ++ * @cpu: cpu in which the counter is bound ++ * @task: task to profile (NULL for percpu) ++ */ ++struct perf_event * ++perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu, ++ struct task_struct *task, ++ perf_overflow_handler_t overflow_handler, ++ void *context) ++{ ++ struct perf_event_context *ctx; ++ struct perf_event *event; ++ int err; ++ ++ /* ++ * Get the target context (task or percpu): ++ */ ++ ++ event = perf_event_alloc(attr, cpu, task, NULL, NULL, ++ overflow_handler, context); ++ if (IS_ERR(event)) { ++ err = PTR_ERR(event); ++ goto err; ++ } ++ ++ /* Mark owner so we could distinguish it from user events. */ ++ event->owner = EVENT_OWNER_KERNEL; ++ ++ account_event(event); ++ ++ ctx = find_get_context(event->pmu, task, cpu); ++ if (IS_ERR(ctx)) { ++ err = PTR_ERR(ctx); ++ goto err_free; ++ } ++ ++ WARN_ON_ONCE(ctx->parent_ctx); ++ mutex_lock(&ctx->mutex); ++ perf_install_in_context(ctx, event, cpu); ++ perf_unpin_context(ctx); ++ mutex_unlock(&ctx->mutex); ++ ++ return event; ++ ++err_free: ++ free_event(event); ++err: ++ return ERR_PTR(err); ++} ++EXPORT_SYMBOL_GPL(perf_event_create_kernel_counter); ++ ++void perf_pmu_migrate_context(struct pmu *pmu, int src_cpu, int dst_cpu) ++{ ++ struct perf_event_context *src_ctx; ++ struct perf_event_context *dst_ctx; ++ struct perf_event *event, *tmp; ++ LIST_HEAD(events); ++ ++ src_ctx = &per_cpu_ptr(pmu->pmu_cpu_context, src_cpu)->ctx; ++ dst_ctx = &per_cpu_ptr(pmu->pmu_cpu_context, dst_cpu)->ctx; ++ ++ mutex_lock(&src_ctx->mutex); ++ list_for_each_entry_safe(event, tmp, &src_ctx->event_list, ++ event_entry) { ++ perf_remove_from_context(event, false); ++ unaccount_event_cpu(event, src_cpu); ++ put_ctx(src_ctx); ++ list_add(&event->migrate_entry, &events); ++ } ++ mutex_unlock(&src_ctx->mutex); ++ ++ synchronize_rcu(); ++ ++ mutex_lock(&dst_ctx->mutex); ++ list_for_each_entry_safe(event, tmp, &events, migrate_entry) { ++ list_del(&event->migrate_entry); ++ if (event->state >= PERF_EVENT_STATE_OFF) ++ event->state = PERF_EVENT_STATE_INACTIVE; ++ account_event_cpu(event, dst_cpu); ++ perf_install_in_context(dst_ctx, event, dst_cpu); ++ get_ctx(dst_ctx); ++ } ++ mutex_unlock(&dst_ctx->mutex); ++} ++EXPORT_SYMBOL_GPL(perf_pmu_migrate_context); ++ ++static void sync_child_event(struct perf_event *child_event, ++ struct task_struct *child) ++{ ++ struct perf_event *parent_event = child_event->parent; ++ u64 child_val; ++ ++ if (child_event->attr.inherit_stat) ++ perf_event_read_event(child_event, child); ++ ++ child_val = perf_event_count(child_event); ++ ++ /* ++ * Add back the child's count to the parent's count: ++ */ ++ atomic64_add(child_val, &parent_event->child_count); ++ atomic64_add(child_event->total_time_enabled, ++ &parent_event->child_total_time_enabled); ++ atomic64_add(child_event->total_time_running, ++ &parent_event->child_total_time_running); ++ ++ /* ++ * Remove this event from the parent's list ++ */ ++ WARN_ON_ONCE(parent_event->ctx->parent_ctx); ++ mutex_lock(&parent_event->child_mutex); ++ list_del_init(&child_event->child_list); ++ mutex_unlock(&parent_event->child_mutex); ++ ++ /* ++ * Make sure user/parent get notified, that we just ++ * lost one event. ++ */ ++ perf_event_wakeup(parent_event); ++ ++ /* ++ * Release the parent event, if this was the last ++ * reference to it. ++ */ ++ put_event(parent_event); ++} ++ ++static void ++__perf_event_exit_task(struct perf_event *child_event, ++ struct perf_event_context *child_ctx, ++ struct task_struct *child) ++{ ++ /* ++ * Do not destroy the 'original' grouping; because of the context ++ * switch optimization the original events could've ended up in a ++ * random child task. ++ * ++ * If we were to destroy the original group, all group related ++ * operations would cease to function properly after this random ++ * child dies. ++ * ++ * Do destroy all inherited groups, we don't care about those ++ * and being thorough is better. ++ */ ++ perf_remove_from_context(child_event, !!child_event->parent); ++ ++ /* ++ * It can happen that the parent exits first, and has events ++ * that are still around due to the child reference. These ++ * events need to be zapped. ++ */ ++ if (child_event->parent) { ++ sync_child_event(child_event, child); ++ free_event(child_event); ++ } else { ++ child_event->state = PERF_EVENT_STATE_EXIT; ++ perf_event_wakeup(child_event); ++ } ++} ++ ++static void perf_event_exit_task_context(struct task_struct *child, int ctxn) ++{ ++ struct perf_event *child_event, *next; ++ struct perf_event_context *child_ctx, *clone_ctx = NULL; ++ unsigned long flags; ++ ++ if (likely(!child->perf_event_ctxp[ctxn])) { ++ perf_event_task(child, NULL, 0); ++ return; ++ } ++ ++ local_irq_save(flags); ++ /* ++ * We can't reschedule here because interrupts are disabled, ++ * and either child is current or it is a task that can't be ++ * scheduled, so we are now safe from rescheduling changing ++ * our context. ++ */ ++ child_ctx = rcu_dereference_raw(child->perf_event_ctxp[ctxn]); ++ ++ /* ++ * Take the context lock here so that if find_get_context is ++ * reading child->perf_event_ctxp, we wait until it has ++ * incremented the context's refcount before we do put_ctx below. ++ */ ++ raw_spin_lock(&child_ctx->lock); ++ task_ctx_sched_out(child_ctx); ++ child->perf_event_ctxp[ctxn] = NULL; ++ ++ /* ++ * If this context is a clone; unclone it so it can't get ++ * swapped to another process while we're removing all ++ * the events from it. ++ */ ++ clone_ctx = unclone_ctx(child_ctx); ++ update_context_time(child_ctx); ++ raw_spin_unlock_irqrestore(&child_ctx->lock, flags); ++ ++ if (clone_ctx) ++ put_ctx(clone_ctx); ++ ++ /* ++ * Report the task dead after unscheduling the events so that we ++ * won't get any samples after PERF_RECORD_EXIT. We can however still ++ * get a few PERF_RECORD_READ events. ++ */ ++ perf_event_task(child, child_ctx, 0); ++ ++ /* ++ * We can recurse on the same lock type through: ++ * ++ * __perf_event_exit_task() ++ * sync_child_event() ++ * put_event() ++ * mutex_lock(&ctx->mutex) ++ * ++ * But since its the parent context it won't be the same instance. ++ */ ++ mutex_lock(&child_ctx->mutex); ++ ++ list_for_each_entry_safe(child_event, next, &child_ctx->event_list, event_entry) ++ __perf_event_exit_task(child_event, child_ctx, child); ++ ++ mutex_unlock(&child_ctx->mutex); ++ ++ put_ctx(child_ctx); ++} ++ ++/* ++ * When a child task exits, feed back event values to parent events. ++ */ ++void perf_event_exit_task(struct task_struct *child) ++{ ++ struct perf_event *event, *tmp; ++ int ctxn; ++ ++ mutex_lock(&child->perf_event_mutex); ++ list_for_each_entry_safe(event, tmp, &child->perf_event_list, ++ owner_entry) { ++ list_del_init(&event->owner_entry); ++ ++ /* ++ * Ensure the list deletion is visible before we clear ++ * the owner, closes a race against perf_release() where ++ * we need to serialize on the owner->perf_event_mutex. ++ */ ++ smp_wmb(); ++ event->owner = NULL; ++ } ++ mutex_unlock(&child->perf_event_mutex); ++ ++ for_each_task_context_nr(ctxn) ++ perf_event_exit_task_context(child, ctxn); ++} ++ ++static void perf_free_event(struct perf_event *event, ++ struct perf_event_context *ctx) ++{ ++ struct perf_event *parent = event->parent; ++ ++ if (WARN_ON_ONCE(!parent)) ++ return; ++ ++ mutex_lock(&parent->child_mutex); ++ list_del_init(&event->child_list); ++ mutex_unlock(&parent->child_mutex); ++ ++ put_event(parent); ++ ++ perf_group_detach(event); ++ list_del_event(event, ctx); ++ free_event(event); ++} ++ ++/* ++ * free an unexposed, unused context as created by inheritance by ++ * perf_event_init_task below, used by fork() in case of fail. ++ */ ++void perf_event_free_task(struct task_struct *task) ++{ ++ struct perf_event_context *ctx; ++ struct perf_event *event, *tmp; ++ int ctxn; ++ ++ for_each_task_context_nr(ctxn) { ++ ctx = task->perf_event_ctxp[ctxn]; ++ if (!ctx) ++ continue; ++ ++ mutex_lock(&ctx->mutex); ++again: ++ list_for_each_entry_safe(event, tmp, &ctx->pinned_groups, ++ group_entry) ++ perf_free_event(event, ctx); ++ ++ list_for_each_entry_safe(event, tmp, &ctx->flexible_groups, ++ group_entry) ++ perf_free_event(event, ctx); ++ ++ if (!list_empty(&ctx->pinned_groups) || ++ !list_empty(&ctx->flexible_groups)) ++ goto again; ++ ++ mutex_unlock(&ctx->mutex); ++ ++ put_ctx(ctx); ++ } ++} ++ ++void perf_event_delayed_put(struct task_struct *task) ++{ ++ int ctxn; ++ ++ for_each_task_context_nr(ctxn) ++ WARN_ON_ONCE(task->perf_event_ctxp[ctxn]); ++} ++ ++/* ++ * inherit a event from parent task to child task: ++ */ ++static struct perf_event * ++inherit_event(struct perf_event *parent_event, ++ struct task_struct *parent, ++ struct perf_event_context *parent_ctx, ++ struct task_struct *child, ++ struct perf_event *group_leader, ++ struct perf_event_context *child_ctx) ++{ ++ enum perf_event_active_state parent_state = parent_event->state; ++ struct perf_event *child_event; ++ unsigned long flags; ++ ++ /* ++ * Instead of creating recursive hierarchies of events, ++ * we link inherited events back to the original parent, ++ * which has a filp for sure, which we use as the reference ++ * count: ++ */ ++ if (parent_event->parent) ++ parent_event = parent_event->parent; ++ ++ child_event = perf_event_alloc(&parent_event->attr, ++ parent_event->cpu, ++ child, ++ group_leader, parent_event, ++ NULL, NULL); ++ if (IS_ERR(child_event)) ++ return child_event; ++ ++ if (is_orphaned_event(parent_event) || ++ !atomic_long_inc_not_zero(&parent_event->refcount)) { ++ free_event(child_event); ++ return NULL; ++ } ++ ++ get_ctx(child_ctx); ++ ++ /* ++ * Make the child state follow the state of the parent event, ++ * not its attr.disabled bit. We hold the parent's mutex, ++ * so we won't race with perf_event_{en, dis}able_family. ++ */ ++ if (parent_state >= PERF_EVENT_STATE_INACTIVE) ++ child_event->state = PERF_EVENT_STATE_INACTIVE; ++ else ++ child_event->state = PERF_EVENT_STATE_OFF; ++ ++ if (parent_event->attr.freq) { ++ u64 sample_period = parent_event->hw.sample_period; ++ struct hw_perf_event *hwc = &child_event->hw; ++ ++ hwc->sample_period = sample_period; ++ hwc->last_period = sample_period; ++ ++ local64_set(&hwc->period_left, sample_period); ++ } ++ ++ child_event->ctx = child_ctx; ++ child_event->overflow_handler = parent_event->overflow_handler; ++ child_event->overflow_handler_context ++ = parent_event->overflow_handler_context; ++ ++ /* ++ * Precalculate sample_data sizes ++ */ ++ perf_event__header_size(child_event); ++ perf_event__id_header_size(child_event); ++ ++ /* ++ * Link it up in the child's context: ++ */ ++ raw_spin_lock_irqsave(&child_ctx->lock, flags); ++ add_event_to_ctx(child_event, child_ctx); ++ raw_spin_unlock_irqrestore(&child_ctx->lock, flags); ++ ++ /* ++ * Link this into the parent event's child list ++ */ ++ WARN_ON_ONCE(parent_event->ctx->parent_ctx); ++ mutex_lock(&parent_event->child_mutex); ++ list_add_tail(&child_event->child_list, &parent_event->child_list); ++ mutex_unlock(&parent_event->child_mutex); ++ ++ return child_event; ++} ++ ++static int inherit_group(struct perf_event *parent_event, ++ struct task_struct *parent, ++ struct perf_event_context *parent_ctx, ++ struct task_struct *child, ++ struct perf_event_context *child_ctx) ++{ ++ struct perf_event *leader; ++ struct perf_event *sub; ++ struct perf_event *child_ctr; ++ ++ leader = inherit_event(parent_event, parent, parent_ctx, ++ child, NULL, child_ctx); ++ if (IS_ERR(leader)) ++ return PTR_ERR(leader); ++ list_for_each_entry(sub, &parent_event->sibling_list, group_entry) { ++ child_ctr = inherit_event(sub, parent, parent_ctx, ++ child, leader, child_ctx); ++ if (IS_ERR(child_ctr)) ++ return PTR_ERR(child_ctr); ++ } ++ return 0; ++} ++ ++static int ++inherit_task_group(struct perf_event *event, struct task_struct *parent, ++ struct perf_event_context *parent_ctx, ++ struct task_struct *child, int ctxn, ++ int *inherited_all) ++{ ++ int ret; ++ struct perf_event_context *child_ctx; ++ ++ if (!event->attr.inherit) { ++ *inherited_all = 0; ++ return 0; ++ } ++ ++ child_ctx = child->perf_event_ctxp[ctxn]; ++ if (!child_ctx) { ++ /* ++ * This is executed from the parent task context, so ++ * inherit events that have been marked for cloning. ++ * First allocate and initialize a context for the ++ * child. ++ */ ++ ++ child_ctx = alloc_perf_context(parent_ctx->pmu, child); ++ if (!child_ctx) ++ return -ENOMEM; ++ ++ child->perf_event_ctxp[ctxn] = child_ctx; ++ } ++ ++ ret = inherit_group(event, parent, parent_ctx, ++ child, child_ctx); ++ ++ if (ret) ++ *inherited_all = 0; ++ ++ return ret; ++} ++ ++/* ++ * Initialize the perf_event context in task_struct ++ */ ++static int perf_event_init_context(struct task_struct *child, int ctxn) ++{ ++ struct perf_event_context *child_ctx, *parent_ctx; ++ struct perf_event_context *cloned_ctx; ++ struct perf_event *event; ++ struct task_struct *parent = current; ++ int inherited_all = 1; ++ unsigned long flags; ++ int ret = 0; ++ ++ if (likely(!parent->perf_event_ctxp[ctxn])) ++ return 0; ++ ++ /* ++ * If the parent's context is a clone, pin it so it won't get ++ * swapped under us. ++ */ ++ parent_ctx = perf_pin_task_context(parent, ctxn); ++ if (!parent_ctx) ++ return 0; ++ ++ /* ++ * No need to check if parent_ctx != NULL here; since we saw ++ * it non-NULL earlier, the only reason for it to become NULL ++ * is if we exit, and since we're currently in the middle of ++ * a fork we can't be exiting at the same time. ++ */ ++ ++ /* ++ * Lock the parent list. No need to lock the child - not PID ++ * hashed yet and not running, so nobody can access it. ++ */ ++ mutex_lock(&parent_ctx->mutex); ++ ++ /* ++ * We dont have to disable NMIs - we are only looking at ++ * the list, not manipulating it: ++ */ ++ list_for_each_entry(event, &parent_ctx->pinned_groups, group_entry) { ++ ret = inherit_task_group(event, parent, parent_ctx, ++ child, ctxn, &inherited_all); ++ if (ret) ++ break; ++ } ++ ++ /* ++ * We can't hold ctx->lock when iterating the ->flexible_group list due ++ * to allocations, but we need to prevent rotation because ++ * rotate_ctx() will change the list from interrupt context. ++ */ ++ raw_spin_lock_irqsave(&parent_ctx->lock, flags); ++ parent_ctx->rotate_disable = 1; ++ raw_spin_unlock_irqrestore(&parent_ctx->lock, flags); ++ ++ list_for_each_entry(event, &parent_ctx->flexible_groups, group_entry) { ++ ret = inherit_task_group(event, parent, parent_ctx, ++ child, ctxn, &inherited_all); ++ if (ret) ++ break; ++ } ++ ++ raw_spin_lock_irqsave(&parent_ctx->lock, flags); ++ parent_ctx->rotate_disable = 0; ++ ++ child_ctx = child->perf_event_ctxp[ctxn]; ++ ++ if (child_ctx && inherited_all) { ++ /* ++ * Mark the child context as a clone of the parent ++ * context, or of whatever the parent is a clone of. ++ * ++ * Note that if the parent is a clone, the holding of ++ * parent_ctx->lock avoids it from being uncloned. ++ */ ++ cloned_ctx = parent_ctx->parent_ctx; ++ if (cloned_ctx) { ++ child_ctx->parent_ctx = cloned_ctx; ++ child_ctx->parent_gen = parent_ctx->parent_gen; ++ } else { ++ child_ctx->parent_ctx = parent_ctx; ++ child_ctx->parent_gen = parent_ctx->generation; ++ } ++ get_ctx(child_ctx->parent_ctx); ++ } ++ ++ raw_spin_unlock_irqrestore(&parent_ctx->lock, flags); ++ mutex_unlock(&parent_ctx->mutex); ++ ++ perf_unpin_context(parent_ctx); ++ put_ctx(parent_ctx); ++ ++ return ret; ++} ++ ++/* ++ * Initialize the perf_event context in task_struct ++ */ ++int perf_event_init_task(struct task_struct *child) ++{ ++ int ctxn, ret; ++ ++ memset(child->perf_event_ctxp, 0, sizeof(child->perf_event_ctxp)); ++ mutex_init(&child->perf_event_mutex); ++ INIT_LIST_HEAD(&child->perf_event_list); ++ ++ for_each_task_context_nr(ctxn) { ++ ret = perf_event_init_context(child, ctxn); ++ if (ret) { ++ perf_event_free_task(child); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static void __init perf_event_init_all_cpus(void) ++{ ++ struct swevent_htable *swhash; ++ int cpu; ++ ++ for_each_possible_cpu(cpu) { ++ swhash = &per_cpu(swevent_htable, cpu); ++ mutex_init(&swhash->hlist_mutex); ++ INIT_LIST_HEAD(&per_cpu(rotation_list, cpu)); ++ } ++} ++ ++static void perf_event_init_cpu(int cpu) ++{ ++ struct swevent_htable *swhash = &per_cpu(swevent_htable, cpu); ++ ++ mutex_lock(&swhash->hlist_mutex); ++ swhash->online = true; ++ if (swhash->hlist_refcount > 0) { ++ struct swevent_hlist *hlist; ++ ++ hlist = kzalloc_node(sizeof(*hlist), GFP_KERNEL, cpu_to_node(cpu)); ++ WARN_ON(!hlist); ++ rcu_assign_pointer(swhash->swevent_hlist, hlist); ++ } ++ mutex_unlock(&swhash->hlist_mutex); ++} ++ ++#if defined CONFIG_HOTPLUG_CPU || defined CONFIG_KEXEC ++static void perf_pmu_rotate_stop(struct pmu *pmu) ++{ ++ struct perf_cpu_context *cpuctx = this_cpu_ptr(pmu->pmu_cpu_context); ++ ++ WARN_ON(!irqs_disabled()); ++ ++ list_del_init(&cpuctx->rotation_list); ++} ++ ++static void __perf_event_exit_context(void *__info) ++{ ++ struct remove_event re = { .detach_group = true }; ++ struct perf_event_context *ctx = __info; ++ ++ perf_pmu_rotate_stop(ctx->pmu); ++ ++ rcu_read_lock(); ++ list_for_each_entry_rcu(re.event, &ctx->event_list, event_entry) ++ __perf_remove_from_context(&re); ++ rcu_read_unlock(); ++} ++ ++static void perf_event_exit_cpu_context(int cpu) ++{ ++ struct perf_event_context *ctx; ++ struct pmu *pmu; ++ int idx; ++ ++ idx = srcu_read_lock(&pmus_srcu); ++ list_for_each_entry_rcu(pmu, &pmus, entry) { ++ ctx = &per_cpu_ptr(pmu->pmu_cpu_context, cpu)->ctx; ++ ++ mutex_lock(&ctx->mutex); ++ smp_call_function_single(cpu, __perf_event_exit_context, ctx, 1); ++ mutex_unlock(&ctx->mutex); ++ } ++ srcu_read_unlock(&pmus_srcu, idx); ++} ++ ++static void perf_event_exit_cpu(int cpu) ++{ ++ struct swevent_htable *swhash = &per_cpu(swevent_htable, cpu); ++ ++ perf_event_exit_cpu_context(cpu); ++ ++ mutex_lock(&swhash->hlist_mutex); ++ swhash->online = false; ++ swevent_hlist_release(swhash); ++ mutex_unlock(&swhash->hlist_mutex); ++} ++#else ++static inline void perf_event_exit_cpu(int cpu) { } ++#endif ++ ++static int ++perf_reboot(struct notifier_block *notifier, unsigned long val, void *v) ++{ ++ int cpu; ++ ++ for_each_online_cpu(cpu) ++ perf_event_exit_cpu(cpu); ++ ++ return NOTIFY_OK; ++} ++ ++/* ++ * Run the perf reboot notifier at the very last possible moment so that ++ * the generic watchdog code runs as long as possible. ++ */ ++static struct notifier_block perf_reboot_notifier = { ++ .notifier_call = perf_reboot, ++ .priority = INT_MIN, ++}; ++ ++static int ++perf_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) ++{ ++ unsigned int cpu = (long)hcpu; ++ ++ switch (action & ~CPU_TASKS_FROZEN) { ++ ++ case CPU_UP_PREPARE: ++ case CPU_DOWN_FAILED: ++ perf_event_init_cpu(cpu); ++ break; ++ ++ case CPU_UP_CANCELED: ++ case CPU_DOWN_PREPARE: ++ perf_event_exit_cpu(cpu); ++ break; ++ default: ++ break; ++ } ++ ++ return NOTIFY_OK; ++} ++ ++void __init perf_event_init(void) ++{ ++ int ret; ++ ++ idr_init(&pmu_idr); ++ ++ perf_event_init_all_cpus(); ++ init_srcu_struct(&pmus_srcu); ++ perf_pmu_register(&perf_swevent, "software", PERF_TYPE_SOFTWARE); ++ perf_pmu_register(&perf_cpu_clock, NULL, -1); ++ perf_pmu_register(&perf_task_clock, NULL, -1); ++ perf_tp_register(); ++ perf_cpu_notifier(perf_cpu_notify); ++ register_reboot_notifier(&perf_reboot_notifier); ++ ++ ret = init_hw_breakpoint(); ++ WARN(ret, "hw_breakpoint initialization failed with: %d", ret); ++ ++ /* do not patch jump label more than once per second */ ++ jump_label_rate_limit(&perf_sched_events, HZ); ++ ++ /* ++ * Build time assertion that we keep the data_head at the intended ++ * location. IOW, validation we got the __reserved[] size right. ++ */ ++ BUILD_BUG_ON((offsetof(struct perf_event_mmap_page, data_head)) ++ != 1024); ++} ++ ++static int __init perf_event_sysfs_init(void) ++{ ++ struct pmu *pmu; ++ int ret; ++ ++ mutex_lock(&pmus_lock); ++ ++ ret = bus_register(&pmu_bus); ++ if (ret) ++ goto unlock; ++ ++ list_for_each_entry(pmu, &pmus, entry) { ++ if (!pmu->name || pmu->type < 0) ++ continue; ++ ++ ret = pmu_dev_alloc(pmu); ++ WARN(ret, "Failed to register pmu: %s, reason %d\n", pmu->name, ret); ++ } ++ pmu_bus_running = 1; ++ ret = 0; ++ ++unlock: ++ mutex_unlock(&pmus_lock); ++ ++ return ret; ++} ++device_initcall(perf_event_sysfs_init); ++ ++#ifdef CONFIG_CGROUP_PERF ++static struct cgroup_subsys_state * ++perf_cgroup_css_alloc(struct cgroup_subsys_state *parent_css) ++{ ++ struct perf_cgroup *jc; ++ ++ jc = kzalloc(sizeof(*jc), GFP_KERNEL); ++ if (!jc) ++ return ERR_PTR(-ENOMEM); ++ ++ jc->info = alloc_percpu(struct perf_cgroup_info); ++ if (!jc->info) { ++ kfree(jc); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ return &jc->css; ++} ++ ++static void perf_cgroup_css_free(struct cgroup_subsys_state *css) ++{ ++ struct perf_cgroup *jc = container_of(css, struct perf_cgroup, css); ++ ++ free_percpu(jc->info); ++ kfree(jc); ++} ++ ++static int __perf_cgroup_move(void *info) ++{ ++ struct task_struct *task = info; ++ perf_cgroup_switch(task, PERF_CGROUP_SWOUT | PERF_CGROUP_SWIN); ++ return 0; ++} ++ ++static void perf_cgroup_attach(struct cgroup_subsys_state *css, ++ struct cgroup_taskset *tset) ++{ ++ struct task_struct *task; ++ ++ cgroup_taskset_for_each(task, tset) ++ task_function_call(task, __perf_cgroup_move, task); ++} ++ ++static void perf_cgroup_exit(struct cgroup_subsys_state *css, ++ struct cgroup_subsys_state *old_css, ++ struct task_struct *task) ++{ ++ /* ++ * cgroup_exit() is called in the copy_process() failure path. ++ * Ignore this case since the task hasn't ran yet, this avoids ++ * trying to poke a half freed task state from generic code. ++ */ ++ if (!(task->flags & PF_EXITING)) ++ return; ++ ++ task_function_call(task, __perf_cgroup_move, task); ++} ++ ++struct cgroup_subsys perf_event_cgrp_subsys = { ++ .css_alloc = perf_cgroup_css_alloc, ++ .css_free = perf_cgroup_css_free, ++ .exit = perf_cgroup_exit, ++ .attach = perf_cgroup_attach, ++}; ++#endif /* CONFIG_CGROUP_PERF */ +diff -Nur linux-3.18.12.orig/kernel/exit.c linux-3.18.12/kernel/exit.c +--- linux-3.18.12.orig/kernel/exit.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/exit.c 2015-04-26 13:32:22.431684003 -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.12.orig/kernel/fork.c linux-3.18.12/kernel/fork.c +--- linux-3.18.12.orig/kernel/fork.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/fork.c 2015-04-26 13:32:22.435684003 -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.12.orig/kernel/futex.c linux-3.18.12/kernel/futex.c +--- linux-3.18.12.orig/kernel/futex.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/futex.c 2015-04-26 13:32:22.435684003 -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.12.orig/kernel/irq/handle.c linux-3.18.12/kernel/irq/handle.c +--- linux-3.18.12.orig/kernel/irq/handle.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/irq/handle.c 2015-04-26 13:32:22.435684003 -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.12.orig/kernel/irq/manage.c linux-3.18.12/kernel/irq/manage.c +--- linux-3.18.12.orig/kernel/irq/manage.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/irq/manage.c 2015-04-26 13:32:22.435684003 -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.12.orig/kernel/irq/settings.h linux-3.18.12/kernel/irq/settings.h +--- linux-3.18.12.orig/kernel/irq/settings.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/irq/settings.h 2015-04-26 13:32:22.435684003 -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.12.orig/kernel/irq/spurious.c linux-3.18.12/kernel/irq/spurious.c +--- linux-3.18.12.orig/kernel/irq/spurious.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/irq/spurious.c 2015-04-26 13:32:22.435684003 -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.12.orig/kernel/irq_work.c linux-3.18.12/kernel/irq_work.c +--- linux-3.18.12.orig/kernel/irq_work.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/irq_work.c 2015-04-26 13:32:22.435684003 -0500 +@@ -17,12 +17,15 @@ + #include + #include + #include ++#include + #include + + + static DEFINE_PER_CPU(struct llist_head, raised_list); + static DEFINE_PER_CPU(struct llist_head, lazy_list); +- ++#ifdef CONFIG_PREEMPT_RT_FULL ++static DEFINE_PER_CPU(struct llist_head, hirq_work_list); ++#endif + /* + * Claim the entry so that no one else will poke at it. + */ +@@ -65,6 +68,8 @@ + */ + bool irq_work_queue_on(struct irq_work *work, int cpu) + { ++ bool raise_irqwork; ++ + /* All work should have been flushed before going offline */ + WARN_ON_ONCE(cpu_is_offline(cpu)); + +@@ -75,7 +80,19 @@ + if (!irq_work_claim(work)) + return false; + +- if (llist_add(&work->llnode, &per_cpu(raised_list, cpu))) ++#ifdef CONFIG_PREEMPT_RT_FULL ++ if (work->flags & IRQ_WORK_HARD_IRQ) ++ raise_irqwork = llist_add(&work->llnode, ++ &per_cpu(hirq_work_list, cpu)); ++ else ++ raise_irqwork = llist_add(&work->llnode, ++ &per_cpu(lazy_list, cpu)); ++#else ++ raise_irqwork = llist_add(&work->llnode, ++ &per_cpu(raised_list, cpu)); ++#endif ++ ++ if (raise_irqwork) + arch_send_call_function_single_ipi(cpu); + + return true; +@@ -93,7 +110,16 @@ + /* Queue the entry and raise the IPI if needed. */ + preempt_disable(); + +- /* If the work is "lazy", handle it from next tick if any */ ++#ifdef CONFIG_PREEMPT_RT_FULL ++ if (work->flags & IRQ_WORK_HARD_IRQ) { ++ if (llist_add(&work->llnode, this_cpu_ptr(&hirq_work_list))) ++ arch_irq_work_raise(); ++ } else { ++ if (llist_add(&work->llnode, this_cpu_ptr(&lazy_list)) && ++ tick_nohz_tick_stopped()) ++ raise_softirq(TIMER_SOFTIRQ); ++ } ++#else + if (work->flags & IRQ_WORK_LAZY) { + if (llist_add(&work->llnode, this_cpu_ptr(&lazy_list)) && + tick_nohz_tick_stopped()) +@@ -102,6 +128,7 @@ + if (llist_add(&work->llnode, this_cpu_ptr(&raised_list))) + arch_irq_work_raise(); + } ++#endif + + preempt_enable(); + +@@ -116,9 +143,12 @@ + raised = this_cpu_ptr(&raised_list); + lazy = this_cpu_ptr(&lazy_list); + +- if (llist_empty(raised) || arch_irq_work_has_interrupt()) ++ if (llist_empty(raised)) + if (llist_empty(lazy)) +- return false; ++#ifdef CONFIG_PREEMPT_RT_FULL ++ if (llist_empty(this_cpu_ptr(&hirq_work_list))) ++#endif ++ return false; + + /* All work should have been flushed before going offline */ + WARN_ON_ONCE(cpu_is_offline(smp_processor_id())); +@@ -132,7 +162,9 @@ + struct irq_work *work; + struct llist_node *llnode; + ++#ifndef CONFIG_PREEMPT_RT_FULL + BUG_ON(!irqs_disabled()); ++#endif + + if (llist_empty(list)) + return; +@@ -168,18 +200,26 @@ + */ + void irq_work_run(void) + { ++#ifdef CONFIG_PREEMPT_RT_FULL ++ irq_work_run_list(this_cpu_ptr(&hirq_work_list)); ++#else + irq_work_run_list(this_cpu_ptr(&raised_list)); + irq_work_run_list(this_cpu_ptr(&lazy_list)); ++#endif + } + EXPORT_SYMBOL_GPL(irq_work_run); + + void irq_work_tick(void) + { ++#ifdef CONFIG_PREEMPT_RT_FULL ++ irq_work_run_list(this_cpu_ptr(&lazy_list)); ++#else + struct llist_head *raised = &__get_cpu_var(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)); ++#endif + } + + /* +diff -Nur linux-3.18.12.orig/kernel/Kconfig.locks linux-3.18.12/kernel/Kconfig.locks +--- linux-3.18.12.orig/kernel/Kconfig.locks 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/Kconfig.locks 2015-04-26 13:32:22.431684003 -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.12.orig/kernel/Kconfig.preempt linux-3.18.12/kernel/Kconfig.preempt +--- linux-3.18.12.orig/kernel/Kconfig.preempt 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/Kconfig.preempt 2015-04-26 13:32:22.431684003 -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.12.orig/kernel/ksysfs.c linux-3.18.12/kernel/ksysfs.c +--- linux-3.18.12.orig/kernel/ksysfs.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/ksysfs.c 2015-04-26 13:32:22.435684003 -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.12.orig/kernel/locking/lglock.c linux-3.18.12/kernel/locking/lglock.c +--- linux-3.18.12.orig/kernel/locking/lglock.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/locking/lglock.c 2015-04-26 13:32:22.435684003 -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.12.orig/kernel/locking/lockdep.c linux-3.18.12/kernel/locking/lockdep.c +--- linux-3.18.12.orig/kernel/locking/lockdep.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/locking/lockdep.c 2015-04-26 13:32:22.435684003 -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.12.orig/kernel/locking/Makefile linux-3.18.12/kernel/locking/Makefile +--- linux-3.18.12.orig/kernel/locking/Makefile 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/locking/Makefile 2015-04-26 13:32:22.435684003 -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.12.orig/kernel/locking/percpu-rwsem.c linux-3.18.12/kernel/locking/percpu-rwsem.c +--- linux-3.18.12.orig/kernel/locking/percpu-rwsem.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/locking/percpu-rwsem.c 2015-04-26 13:32:22.435684003 -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.12.orig/kernel/locking/rt.c linux-3.18.12/kernel/locking/rt.c +--- linux-3.18.12.orig/kernel/locking/rt.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.12/kernel/locking/rt.c 2015-04-26 13:32:22.435684003 -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.12.orig/kernel/locking/rtmutex.c linux-3.18.12/kernel/locking/rtmutex.c +--- linux-3.18.12.orig/kernel/locking/rtmutex.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/locking/rtmutex.c 2015-04-26 13:32:22.439684003 -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.12.orig/kernel/locking/rtmutex_common.h linux-3.18.12/kernel/locking/rtmutex_common.h +--- linux-3.18.12.orig/kernel/locking/rtmutex_common.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/locking/rtmutex_common.h 2015-04-26 13:32:22.439684003 -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.12.orig/kernel/locking/spinlock.c linux-3.18.12/kernel/locking/spinlock.c +--- linux-3.18.12.orig/kernel/locking/spinlock.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/locking/spinlock.c 2015-04-26 13:32:22.439684003 -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.12.orig/kernel/locking/spinlock_debug.c linux-3.18.12/kernel/locking/spinlock_debug.c +--- linux-3.18.12.orig/kernel/locking/spinlock_debug.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/locking/spinlock_debug.c 2015-04-26 13:32:22.439684003 -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.12.orig/kernel/panic.c linux-3.18.12/kernel/panic.c +--- linux-3.18.12.orig/kernel/panic.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/panic.c 2015-04-26 13:32:22.439684003 -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.12.orig/kernel/power/hibernate.c linux-3.18.12/kernel/power/hibernate.c +--- linux-3.18.12.orig/kernel/power/hibernate.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/power/hibernate.c 2015-04-26 13:32:22.439684003 -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.12.orig/kernel/power/suspend.c linux-3.18.12/kernel/power/suspend.c +--- linux-3.18.12.orig/kernel/power/suspend.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/power/suspend.c 2015-04-26 13:32:22.439684003 -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.12.orig/kernel/printk/printk.c linux-3.18.12/kernel/printk/printk.c +--- linux-3.18.12.orig/kernel/printk/printk.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/printk/printk.c 2015-04-26 13:32:22.439684003 -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.12.orig/kernel/ptrace.c linux-3.18.12/kernel/ptrace.c +--- linux-3.18.12.orig/kernel/ptrace.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/ptrace.c 2015-04-26 13:32:22.439684003 -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.12.orig/kernel/rcu/tiny.c linux-3.18.12/kernel/rcu/tiny.c +--- linux-3.18.12.orig/kernel/rcu/tiny.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/rcu/tiny.c 2015-04-26 13:32:22.439684003 -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.12.orig/kernel/rcu/tree.c linux-3.18.12/kernel/rcu/tree.c +--- linux-3.18.12.orig/kernel/rcu/tree.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/rcu/tree.c 2015-04-26 13:32:22.439684003 -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.12.orig/kernel/rcu/tree.h linux-3.18.12/kernel/rcu/tree.h +--- linux-3.18.12.orig/kernel/rcu/tree.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/rcu/tree.h 2015-04-26 13:32:22.443684003 -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.12.orig/kernel/rcu/tree_plugin.h linux-3.18.12/kernel/rcu/tree_plugin.h +--- linux-3.18.12.orig/kernel/rcu/tree_plugin.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/rcu/tree_plugin.h 2015-04-26 13:32:22.443684003 -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.12.orig/kernel/rcu/update.c linux-3.18.12/kernel/rcu/update.c +--- linux-3.18.12.orig/kernel/rcu/update.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/rcu/update.c 2015-04-26 13:32:22.443684003 -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.12.orig/kernel/relay.c linux-3.18.12/kernel/relay.c +--- linux-3.18.12.orig/kernel/relay.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/relay.c 2015-04-26 13:32:22.443684003 -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.12.orig/kernel/res_counter.c linux-3.18.12/kernel/res_counter.c +--- linux-3.18.12.orig/kernel/res_counter.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/res_counter.c 2015-04-26 13:32:22.443684003 -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.12.orig/kernel/sched/completion.c linux-3.18.12/kernel/sched/completion.c +--- linux-3.18.12.orig/kernel/sched/completion.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/sched/completion.c 2015-04-26 13:32:22.443684003 -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.12.orig/kernel/sched/core.c linux-3.18.12/kernel/sched/core.c +--- linux-3.18.12.orig/kernel/sched/core.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/sched/core.c 2015-04-26 13:32:22.443684003 -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); + + /* +@@ -3097,6 +3316,8 @@ + } else { + if (dl_prio(oldprio)) + p->dl.dl_boosted = 0; ++ if (rt_prio(oldprio)) ++ p->rt.timeout = 0; + p->sched_class = &fair_sched_class; + } + +@@ -4234,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) +@@ -4277,6 +4505,7 @@ + } + EXPORT_SYMBOL(__cond_resched_lock); + ++#ifndef CONFIG_PREEMPT_RT_FULL + int __sched __cond_resched_softirq(void) + { + BUG_ON(!in_softirq()); +@@ -4290,6 +4519,7 @@ + return 0; + } + EXPORT_SYMBOL(__cond_resched_softirq); ++#endif + + /** + * yield - yield the current processor to other threads. +@@ -4651,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: + */ +@@ -4693,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; + } + + /* +@@ -4743,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); +@@ -4883,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. +@@ -4897,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; + } + + /* +@@ -5240,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 + } +@@ -7181,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.12.orig/kernel/sched/cputime.c linux-3.18.12/kernel/sched/cputime.c +--- linux-3.18.12.orig/kernel/sched/cputime.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/sched/cputime.c 2015-04-26 13:32:22.443684003 -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.12.orig/kernel/sched/deadline.c linux-3.18.12/kernel/sched/deadline.c +--- linux-3.18.12.orig/kernel/sched/deadline.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/sched/deadline.c 2015-04-26 13:32:22.447684003 -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.12.orig/kernel/sched/debug.c linux-3.18.12/kernel/sched/debug.c +--- linux-3.18.12.orig/kernel/sched/debug.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/sched/debug.c 2015-04-26 13:32:22.447684003 -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.12.orig/kernel/sched/fair.c linux-3.18.12/kernel/sched/fair.c +--- linux-3.18.12.orig/kernel/sched/fair.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/sched/fair.c 2015-04-26 13:32:22.447684003 -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.12.orig/kernel/sched/features.h linux-3.18.12/kernel/sched/features.h +--- linux-3.18.12.orig/kernel/sched/features.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/sched/features.h 2015-04-26 13:32:22.447684003 -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.12.orig/kernel/sched/Makefile linux-3.18.12/kernel/sched/Makefile +--- linux-3.18.12.orig/kernel/sched/Makefile 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/sched/Makefile 2015-04-26 13:32:22.443684003 -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.12.orig/kernel/sched/rt.c linux-3.18.12/kernel/sched/rt.c +--- linux-3.18.12.orig/kernel/sched/rt.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/sched/rt.c 2015-04-26 13:32:22.447684003 -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.12.orig/kernel/sched/sched.h linux-3.18.12/kernel/sched/sched.h +--- linux-3.18.12.orig/kernel/sched/sched.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/sched/sched.h 2015-04-26 13:32:22.447684003 -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.12.orig/kernel/sched/wait-simple.c linux-3.18.12/kernel/sched/wait-simple.c +--- linux-3.18.12.orig/kernel/sched/wait-simple.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.12/kernel/sched/wait-simple.c 2015-04-26 13:32:22.447684003 -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.12.orig/kernel/sched/work-simple.c linux-3.18.12/kernel/sched/work-simple.c +--- linux-3.18.12.orig/kernel/sched/work-simple.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.12/kernel/sched/work-simple.c 2015-04-26 13:32:22.447684003 -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.12.orig/kernel/signal.c linux-3.18.12/kernel/signal.c +--- linux-3.18.12.orig/kernel/signal.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/signal.c 2015-04-26 13:32:22.447684003 -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.12.orig/kernel/softirq.c linux-3.18.12/kernel/softirq.c +--- linux-3.18.12.orig/kernel/softirq.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/softirq.c 2015-04-26 13:32:22.451684003 -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.12.orig/kernel/stop_machine.c linux-3.18.12/kernel/stop_machine.c +--- linux-3.18.12.orig/kernel/stop_machine.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/stop_machine.c 2015-04-26 13:32:22.451684003 -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.12.orig/kernel/time/hrtimer.c linux-3.18.12/kernel/time/hrtimer.c +--- linux-3.18.12.orig/kernel/time/hrtimer.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/time/hrtimer.c 2015-04-26 13:32:22.451684003 -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.12.orig/kernel/time/itimer.c linux-3.18.12/kernel/time/itimer.c +--- linux-3.18.12.orig/kernel/time/itimer.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/time/itimer.c 2015-04-26 13:32:22.451684003 -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.12.orig/kernel/time/jiffies.c linux-3.18.12/kernel/time/jiffies.c +--- linux-3.18.12.orig/kernel/time/jiffies.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/time/jiffies.c 2015-04-26 13:32:22.451684003 -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.12.orig/kernel/time/ntp.c linux-3.18.12/kernel/time/ntp.c +--- linux-3.18.12.orig/kernel/time/ntp.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/time/ntp.c 2015-04-26 13:32:22.451684003 -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.12.orig/kernel/time/posix-cpu-timers.c linux-3.18.12/kernel/time/posix-cpu-timers.c +--- linux-3.18.12.orig/kernel/time/posix-cpu-timers.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/time/posix-cpu-timers.c 2015-04-26 13:32:22.451684003 -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.12.orig/kernel/time/posix-timers.c linux-3.18.12/kernel/time/posix-timers.c +--- linux-3.18.12.orig/kernel/time/posix-timers.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/time/posix-timers.c 2015-04-26 13:32:22.451684003 -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.12.orig/kernel/time/tick-common.c linux-3.18.12/kernel/time/tick-common.c +--- linux-3.18.12.orig/kernel/time/tick-common.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/time/tick-common.c 2015-04-26 13:32:22.451684003 -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.12.orig/kernel/time/tick-internal.h linux-3.18.12/kernel/time/tick-internal.h +--- linux-3.18.12.orig/kernel/time/tick-internal.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/time/tick-internal.h 2015-04-26 13:32:22.451684003 -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.12.orig/kernel/time/tick-sched.c linux-3.18.12/kernel/time/tick-sched.c +--- linux-3.18.12.orig/kernel/time/tick-sched.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/time/tick-sched.c 2015-04-26 13:32:22.451684003 -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.12.orig/kernel/time/timekeeping.c linux-3.18.12/kernel/time/timekeeping.c +--- linux-3.18.12.orig/kernel/time/timekeeping.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/time/timekeeping.c 2015-04-26 13:32:22.451684003 -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.12.orig/kernel/time/timer.c linux-3.18.12/kernel/time/timer.c +--- linux-3.18.12.orig/kernel/time/timer.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/time/timer.c 2015-04-26 13:32:22.455684003 -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.12.orig/kernel/trace/Kconfig linux-3.18.12/kernel/trace/Kconfig +--- linux-3.18.12.orig/kernel/trace/Kconfig 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/trace/Kconfig 2015-04-26 13:32:22.455684003 -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.12.orig/kernel/trace/latency_hist.c linux-3.18.12/kernel/trace/latency_hist.c +--- linux-3.18.12.orig/kernel/trace/latency_hist.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.12/kernel/trace/latency_hist.c 2015-04-26 13:32:22.455684003 -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.12.orig/kernel/trace/Makefile linux-3.18.12/kernel/trace/Makefile +--- linux-3.18.12.orig/kernel/trace/Makefile 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/trace/Makefile 2015-04-26 13:32:22.455684003 -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.12.orig/kernel/trace/trace.c linux-3.18.12/kernel/trace/trace.c +--- linux-3.18.12.orig/kernel/trace/trace.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/trace/trace.c 2015-04-26 13:32:22.455684003 -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.12.orig/kernel/trace/trace_events.c linux-3.18.12/kernel/trace/trace_events.c +--- linux-3.18.12.orig/kernel/trace/trace_events.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/trace/trace_events.c 2015-04-26 13:32:22.455684003 -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.12.orig/kernel/trace/trace.h linux-3.18.12/kernel/trace/trace.h +--- linux-3.18.12.orig/kernel/trace/trace.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/trace/trace.h 2015-04-26 13:32:22.455684003 -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.12.orig/kernel/trace/trace_irqsoff.c linux-3.18.12/kernel/trace/trace_irqsoff.c +--- linux-3.18.12.orig/kernel/trace/trace_irqsoff.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/trace/trace_irqsoff.c 2015-04-26 13:32:22.455684003 -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.12.orig/kernel/trace/trace_output.c linux-3.18.12/kernel/trace/trace_output.c +--- linux-3.18.12.orig/kernel/trace/trace_output.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/trace/trace_output.c 2015-04-26 13:32:22.455684003 -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.12.orig/kernel/user.c linux-3.18.12/kernel/user.c +--- linux-3.18.12.orig/kernel/user.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/user.c 2015-04-26 13:32:22.455684003 -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.12.orig/kernel/watchdog.c linux-3.18.12/kernel/watchdog.c +--- linux-3.18.12.orig/kernel/watchdog.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/watchdog.c 2015-04-26 13:32:22.459684003 -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.12.orig/kernel/workqueue.c linux-3.18.12/kernel/workqueue.c +--- linux-3.18.12.orig/kernel/workqueue.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/workqueue.c 2015-04-26 13:32:22.459684003 -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.12.orig/kernel/workqueue_internal.h linux-3.18.12/kernel/workqueue_internal.h +--- linux-3.18.12.orig/kernel/workqueue_internal.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/kernel/workqueue_internal.h 2015-04-26 13:32:22.459684003 -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.12.orig/lib/debugobjects.c linux-3.18.12/lib/debugobjects.c +--- linux-3.18.12.orig/lib/debugobjects.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/lib/debugobjects.c 2015-04-26 13:32:22.459684003 -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.12.orig/lib/idr.c linux-3.18.12/lib/idr.c +--- linux-3.18.12.orig/lib/idr.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/lib/idr.c 2015-04-26 13:32:22.459684003 -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.12.orig/lib/Kconfig linux-3.18.12/lib/Kconfig +--- linux-3.18.12.orig/lib/Kconfig 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/lib/Kconfig 2015-04-26 13:32:22.459684003 -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.12.orig/lib/Kconfig.debug linux-3.18.12/lib/Kconfig.debug +--- linux-3.18.12.orig/lib/Kconfig.debug 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/lib/Kconfig.debug 2015-04-26 13:32:22.459684003 -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.12.orig/lib/locking-selftest.c linux-3.18.12/lib/locking-selftest.c +--- linux-3.18.12.orig/lib/locking-selftest.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/lib/locking-selftest.c 2015-04-26 13:32:22.459684003 -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.12.orig/lib/percpu_ida.c linux-3.18.12/lib/percpu_ida.c +--- linux-3.18.12.orig/lib/percpu_ida.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/lib/percpu_ida.c 2015-04-26 13:32:22.459684003 -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.12.orig/lib/radix-tree.c linux-3.18.12/lib/radix-tree.c +--- linux-3.18.12.orig/lib/radix-tree.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/lib/radix-tree.c 2015-04-26 13:32:22.459684003 -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.12.orig/lib/scatterlist.c linux-3.18.12/lib/scatterlist.c +--- linux-3.18.12.orig/lib/scatterlist.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/lib/scatterlist.c 2015-04-26 13:32:22.459684003 -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.12.orig/lib/smp_processor_id.c linux-3.18.12/lib/smp_processor_id.c +--- linux-3.18.12.orig/lib/smp_processor_id.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/lib/smp_processor_id.c 2015-04-26 13:32:22.459684003 -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.12.orig/mm/filemap.c linux-3.18.12/mm/filemap.c +--- linux-3.18.12.orig/mm/filemap.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/mm/filemap.c 2015-04-26 13:32:22.463684003 -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.12.orig/mm/highmem.c linux-3.18.12/mm/highmem.c +--- linux-3.18.12.orig/mm/highmem.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/mm/highmem.c 2015-04-26 13:32:22.463684003 -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.12.orig/mm/Kconfig linux-3.18.12/mm/Kconfig +--- linux-3.18.12.orig/mm/Kconfig 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/mm/Kconfig 2015-04-26 13:32:22.463684003 -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.12.orig/mm/memcontrol.c linux-3.18.12/mm/memcontrol.c +--- linux-3.18.12.orig/mm/memcontrol.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/mm/memcontrol.c 2015-04-26 13:32:22.463684003 -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.12.orig/mm/memory.c linux-3.18.12/mm/memory.c +--- linux-3.18.12.orig/mm/memory.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/mm/memory.c 2015-04-26 13:32:22.463684003 -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.12.orig/mm/mmu_context.c linux-3.18.12/mm/mmu_context.c +--- linux-3.18.12.orig/mm/mmu_context.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/mm/mmu_context.c 2015-04-26 13:32:22.463684003 -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.12.orig/mm/page_alloc.c linux-3.18.12/mm/page_alloc.c +--- linux-3.18.12.orig/mm/page_alloc.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/mm/page_alloc.c 2015-04-26 13:32:22.463684003 -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.12.orig/mm/slab.h linux-3.18.12/mm/slab.h +--- linux-3.18.12.orig/mm/slab.h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/mm/slab.h 2015-04-26 13:32:22.467684003 -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.12.orig/mm/slub.c linux-3.18.12/mm/slub.c +--- linux-3.18.12.orig/mm/slub.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/mm/slub.c 2015-04-26 13:32:22.467684003 -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.12.orig/mm/swap.c linux-3.18.12/mm/swap.c +--- linux-3.18.12.orig/mm/swap.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/mm/swap.c 2015-04-26 13:32:22.467684003 -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.12.orig/mm/truncate.c linux-3.18.12/mm/truncate.c +--- linux-3.18.12.orig/mm/truncate.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/mm/truncate.c 2015-04-26 13:32:22.467684003 -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.12.orig/mm/vmalloc.c linux-3.18.12/mm/vmalloc.c +--- linux-3.18.12.orig/mm/vmalloc.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/mm/vmalloc.c 2015-04-26 13:32:22.467684003 -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.12.orig/mm/vmstat.c linux-3.18.12/mm/vmstat.c +--- linux-3.18.12.orig/mm/vmstat.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/mm/vmstat.c 2015-04-26 13:32:22.467684003 -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.12.orig/mm/workingset.c linux-3.18.12/mm/workingset.c +--- linux-3.18.12.orig/mm/workingset.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/mm/workingset.c 2015-04-26 13:32:22.467684003 -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.12.orig/net/core/dev.c linux-3.18.12/net/core/dev.c +--- linux-3.18.12.orig/net/core/dev.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/net/core/dev.c 2015-04-26 13:32:22.471684003 -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); + +@@ -3336,6 +3344,7 @@ + rps_unlock(sd); + + local_irq_restore(flags); ++ preempt_check_resched_rt(); + + atomic_long_inc(&skb->dev->rx_dropped); + kfree_skb(skb); +@@ -3354,7 +3363,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); +@@ -3364,13 +3373,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; + } +@@ -3404,16 +3413,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); +@@ -3455,7 +3492,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); +@@ -3848,7 +3885,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); + } + } +@@ -3857,10 +3894,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) +@@ -4323,6 +4363,7 @@ + } else + #endif + local_irq_enable(); ++ preempt_check_resched_rt(); + } + + static int process_backlog(struct napi_struct *napi, int quota) +@@ -4394,6 +4435,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); + +@@ -4516,10 +4558,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; +@@ -7008,6 +7057,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))) { +@@ -7018,6 +7068,9 @@ + netif_rx_internal(skb); + input_queue_head_incr(oldsd); + } ++ while ((skb = __skb_dequeue(&oldsd->tofree_queue))) { ++ kfree_skb(skb); ++ } + + return NOTIFY_OK; + } +@@ -7319,8 +7372,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.12.orig/net/core/skbuff.c linux-3.18.12/net/core/skbuff.c +--- linux-3.18.12.orig/net/core/skbuff.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/net/core/skbuff.c 2015-04-26 13:32:22.471684003 -0500 +@@ -63,6 +63,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -336,6 +337,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) + { +@@ -344,7 +346,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: +@@ -389,7 +391,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.12.orig/net/core/sock.c linux-3.18.12/net/core/sock.c +--- linux-3.18.12.orig/net/core/sock.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/net/core/sock.c 2015-04-26 13:32:22.471684003 -0500 +@@ -2326,12 +2326,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.12.orig/net/ipv4/icmp.c linux-3.18.12/net/ipv4/icmp.c +--- linux-3.18.12.orig/net/ipv4/icmp.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/net/ipv4/icmp.c 2015-04-26 13:32:22.471684003 -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.12.orig/net/ipv4/sysctl_net_ipv4.c linux-3.18.12/net/ipv4/sysctl_net_ipv4.c +--- linux-3.18.12.orig/net/ipv4/sysctl_net_ipv4.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/net/ipv4/sysctl_net_ipv4.c 2015-04-26 13:32:22.471684003 -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.12.orig/net/mac80211/rx.c linux-3.18.12/net/mac80211/rx.c +--- linux-3.18.12.orig/net/mac80211/rx.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/net/mac80211/rx.c 2015-04-26 13:32:22.471684003 -0500 +@@ -3359,7 +3359,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.12.orig/net/mac80211/rx.c.orig linux-3.18.12/net/mac80211/rx.c.orig +--- linux-3.18.12.orig/net/mac80211/rx.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.12/net/mac80211/rx.c.orig 2015-04-20 14:48:02.000000000 -0500 +@@ -0,0 +1,3476 @@ ++/* ++ * Copyright 2002-2005, Instant802 Networks, Inc. ++ * Copyright 2005-2006, Devicescape Software, Inc. ++ * Copyright 2006-2007 Jiri Benc ++ * Copyright 2007-2010 Johannes Berg ++ * Copyright 2013-2014 Intel Mobile Communications GmbH ++ * ++ * 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 "ieee80211_i.h" ++#include "driver-ops.h" ++#include "led.h" ++#include "mesh.h" ++#include "wep.h" ++#include "wpa.h" ++#include "tkip.h" ++#include "wme.h" ++#include "rate.h" ++ ++/* ++ * monitor mode reception ++ * ++ * This function cleans up the SKB, i.e. it removes all the stuff ++ * only useful for monitoring. ++ */ ++static struct sk_buff *remove_monitor_info(struct ieee80211_local *local, ++ struct sk_buff *skb) ++{ ++ if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) { ++ if (likely(skb->len > FCS_LEN)) ++ __pskb_trim(skb, skb->len - FCS_LEN); ++ else { ++ /* driver bug */ ++ WARN_ON(1); ++ dev_kfree_skb(skb); ++ return NULL; ++ } ++ } ++ ++ return skb; ++} ++ ++static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len) ++{ ++ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); ++ struct ieee80211_hdr *hdr = (void *)skb->data; ++ ++ if (status->flag & (RX_FLAG_FAILED_FCS_CRC | ++ RX_FLAG_FAILED_PLCP_CRC | ++ RX_FLAG_AMPDU_IS_ZEROLEN)) ++ return true; ++ ++ if (unlikely(skb->len < 16 + present_fcs_len)) ++ return true; ++ ++ if (ieee80211_is_ctl(hdr->frame_control) && ++ !ieee80211_is_pspoll(hdr->frame_control) && ++ !ieee80211_is_back_req(hdr->frame_control)) ++ return true; ++ ++ return false; ++} ++ ++static int ++ieee80211_rx_radiotap_space(struct ieee80211_local *local, ++ struct ieee80211_rx_status *status) ++{ ++ int len; ++ ++ /* always present fields */ ++ len = sizeof(struct ieee80211_radiotap_header) + 8; ++ ++ /* allocate extra bitmaps */ ++ if (status->chains) ++ len += 4 * hweight8(status->chains); ++ ++ if (ieee80211_have_rx_timestamp(status)) { ++ len = ALIGN(len, 8); ++ len += 8; ++ } ++ if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) ++ len += 1; ++ ++ /* antenna field, if we don't have per-chain info */ ++ if (!status->chains) ++ len += 1; ++ ++ /* padding for RX_FLAGS if necessary */ ++ len = ALIGN(len, 2); ++ ++ if (status->flag & RX_FLAG_HT) /* HT info */ ++ len += 3; ++ ++ if (status->flag & RX_FLAG_AMPDU_DETAILS) { ++ len = ALIGN(len, 4); ++ len += 8; ++ } ++ ++ if (status->flag & RX_FLAG_VHT) { ++ len = ALIGN(len, 2); ++ len += 12; ++ } ++ ++ if (status->chains) { ++ /* antenna and antenna signal fields */ ++ len += 2 * hweight8(status->chains); ++ } ++ ++ return len; ++} ++ ++/* ++ * ieee80211_add_rx_radiotap_header - add radiotap header ++ * ++ * add a radiotap header containing all the fields which the hardware provided. ++ */ ++static void ++ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, ++ struct sk_buff *skb, ++ struct ieee80211_rate *rate, ++ int rtap_len, bool has_fcs) ++{ ++ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); ++ struct ieee80211_radiotap_header *rthdr; ++ unsigned char *pos; ++ __le32 *it_present; ++ u32 it_present_val; ++ u16 rx_flags = 0; ++ u16 channel_flags = 0; ++ int mpdulen, chain; ++ unsigned long chains = status->chains; ++ ++ mpdulen = skb->len; ++ if (!(has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS))) ++ mpdulen += FCS_LEN; ++ ++ rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len); ++ memset(rthdr, 0, rtap_len); ++ it_present = &rthdr->it_present; ++ ++ /* radiotap header, set always present flags */ ++ rthdr->it_len = cpu_to_le16(rtap_len); ++ it_present_val = BIT(IEEE80211_RADIOTAP_FLAGS) | ++ BIT(IEEE80211_RADIOTAP_CHANNEL) | ++ BIT(IEEE80211_RADIOTAP_RX_FLAGS); ++ ++ if (!status->chains) ++ it_present_val |= BIT(IEEE80211_RADIOTAP_ANTENNA); ++ ++ for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) { ++ it_present_val |= ++ BIT(IEEE80211_RADIOTAP_EXT) | ++ BIT(IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE); ++ put_unaligned_le32(it_present_val, it_present); ++ it_present++; ++ it_present_val = BIT(IEEE80211_RADIOTAP_ANTENNA) | ++ BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL); ++ } ++ ++ put_unaligned_le32(it_present_val, it_present); ++ ++ pos = (void *)(it_present + 1); ++ ++ /* the order of the following fields is important */ ++ ++ /* IEEE80211_RADIOTAP_TSFT */ ++ if (ieee80211_have_rx_timestamp(status)) { ++ /* padding */ ++ while ((pos - (u8 *)rthdr) & 7) ++ *pos++ = 0; ++ put_unaligned_le64( ++ ieee80211_calculate_rx_timestamp(local, status, ++ mpdulen, 0), ++ pos); ++ rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT); ++ pos += 8; ++ } ++ ++ /* IEEE80211_RADIOTAP_FLAGS */ ++ if (has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)) ++ *pos |= IEEE80211_RADIOTAP_F_FCS; ++ if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC)) ++ *pos |= IEEE80211_RADIOTAP_F_BADFCS; ++ if (status->flag & RX_FLAG_SHORTPRE) ++ *pos |= IEEE80211_RADIOTAP_F_SHORTPRE; ++ pos++; ++ ++ /* IEEE80211_RADIOTAP_RATE */ ++ if (!rate || status->flag & (RX_FLAG_HT | RX_FLAG_VHT)) { ++ /* ++ * Without rate information don't add it. If we have, ++ * MCS information is a separate field in radiotap, ++ * added below. The byte here is needed as padding ++ * for the channel though, so initialise it to 0. ++ */ ++ *pos = 0; ++ } else { ++ int shift = 0; ++ rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE); ++ if (status->flag & RX_FLAG_10MHZ) ++ shift = 1; ++ else if (status->flag & RX_FLAG_5MHZ) ++ shift = 2; ++ *pos = DIV_ROUND_UP(rate->bitrate, 5 * (1 << shift)); ++ } ++ pos++; ++ ++ /* IEEE80211_RADIOTAP_CHANNEL */ ++ put_unaligned_le16(status->freq, pos); ++ pos += 2; ++ if (status->flag & RX_FLAG_10MHZ) ++ channel_flags |= IEEE80211_CHAN_HALF; ++ else if (status->flag & RX_FLAG_5MHZ) ++ channel_flags |= IEEE80211_CHAN_QUARTER; ++ ++ if (status->band == IEEE80211_BAND_5GHZ) ++ channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ; ++ else if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT)) ++ channel_flags |= IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; ++ else if (rate && rate->flags & IEEE80211_RATE_ERP_G) ++ channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ; ++ else if (rate) ++ channel_flags |= IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ; ++ else ++ channel_flags |= IEEE80211_CHAN_2GHZ; ++ put_unaligned_le16(channel_flags, pos); ++ pos += 2; ++ ++ /* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */ ++ if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM && ++ !(status->flag & RX_FLAG_NO_SIGNAL_VAL)) { ++ *pos = status->signal; ++ rthdr->it_present |= ++ cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL); ++ pos++; ++ } ++ ++ /* IEEE80211_RADIOTAP_LOCK_QUALITY is missing */ ++ ++ if (!status->chains) { ++ /* IEEE80211_RADIOTAP_ANTENNA */ ++ *pos = status->antenna; ++ pos++; ++ } ++ ++ /* IEEE80211_RADIOTAP_DB_ANTNOISE is not used */ ++ ++ /* IEEE80211_RADIOTAP_RX_FLAGS */ ++ /* ensure 2 byte alignment for the 2 byte field as required */ ++ if ((pos - (u8 *)rthdr) & 1) ++ *pos++ = 0; ++ if (status->flag & RX_FLAG_FAILED_PLCP_CRC) ++ rx_flags |= IEEE80211_RADIOTAP_F_RX_BADPLCP; ++ put_unaligned_le16(rx_flags, pos); ++ pos += 2; ++ ++ if (status->flag & RX_FLAG_HT) { ++ unsigned int stbc; ++ ++ rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS); ++ *pos++ = local->hw.radiotap_mcs_details; ++ *pos = 0; ++ if (status->flag & RX_FLAG_SHORT_GI) ++ *pos |= IEEE80211_RADIOTAP_MCS_SGI; ++ if (status->flag & RX_FLAG_40MHZ) ++ *pos |= IEEE80211_RADIOTAP_MCS_BW_40; ++ if (status->flag & RX_FLAG_HT_GF) ++ *pos |= IEEE80211_RADIOTAP_MCS_FMT_GF; ++ if (status->flag & RX_FLAG_LDPC) ++ *pos |= IEEE80211_RADIOTAP_MCS_FEC_LDPC; ++ stbc = (status->flag & RX_FLAG_STBC_MASK) >> RX_FLAG_STBC_SHIFT; ++ *pos |= stbc << IEEE80211_RADIOTAP_MCS_STBC_SHIFT; ++ pos++; ++ *pos++ = status->rate_idx; ++ } ++ ++ if (status->flag & RX_FLAG_AMPDU_DETAILS) { ++ u16 flags = 0; ++ ++ /* ensure 4 byte alignment */ ++ while ((pos - (u8 *)rthdr) & 3) ++ pos++; ++ rthdr->it_present |= ++ cpu_to_le32(1 << IEEE80211_RADIOTAP_AMPDU_STATUS); ++ put_unaligned_le32(status->ampdu_reference, pos); ++ pos += 4; ++ if (status->flag & RX_FLAG_AMPDU_REPORT_ZEROLEN) ++ flags |= IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN; ++ if (status->flag & RX_FLAG_AMPDU_IS_ZEROLEN) ++ flags |= IEEE80211_RADIOTAP_AMPDU_IS_ZEROLEN; ++ if (status->flag & RX_FLAG_AMPDU_LAST_KNOWN) ++ flags |= IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN; ++ if (status->flag & RX_FLAG_AMPDU_IS_LAST) ++ flags |= IEEE80211_RADIOTAP_AMPDU_IS_LAST; ++ if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_ERROR) ++ flags |= IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR; ++ if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_KNOWN) ++ flags |= IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN; ++ put_unaligned_le16(flags, pos); ++ pos += 2; ++ if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_KNOWN) ++ *pos++ = status->ampdu_delimiter_crc; ++ else ++ *pos++ = 0; ++ *pos++ = 0; ++ } ++ ++ if (status->flag & RX_FLAG_VHT) { ++ u16 known = local->hw.radiotap_vht_details; ++ ++ rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT); ++ /* known field - how to handle 80+80? */ ++ if (status->vht_flag & RX_VHT_FLAG_80P80MHZ) ++ known &= ~IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH; ++ put_unaligned_le16(known, pos); ++ pos += 2; ++ /* flags */ ++ if (status->flag & RX_FLAG_SHORT_GI) ++ *pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI; ++ /* in VHT, STBC is binary */ ++ if (status->flag & RX_FLAG_STBC_MASK) ++ *pos |= IEEE80211_RADIOTAP_VHT_FLAG_STBC; ++ if (status->vht_flag & RX_VHT_FLAG_BF) ++ *pos |= IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED; ++ pos++; ++ /* bandwidth */ ++ if (status->vht_flag & RX_VHT_FLAG_80MHZ) ++ *pos++ = 4; ++ else if (status->vht_flag & RX_VHT_FLAG_80P80MHZ) ++ *pos++ = 0; /* marked not known above */ ++ else if (status->vht_flag & RX_VHT_FLAG_160MHZ) ++ *pos++ = 11; ++ else if (status->flag & RX_FLAG_40MHZ) ++ *pos++ = 1; ++ else /* 20 MHz */ ++ *pos++ = 0; ++ /* MCS/NSS */ ++ *pos = (status->rate_idx << 4) | status->vht_nss; ++ pos += 4; ++ /* coding field */ ++ if (status->flag & RX_FLAG_LDPC) ++ *pos |= IEEE80211_RADIOTAP_CODING_LDPC_USER0; ++ pos++; ++ /* group ID */ ++ pos++; ++ /* partial_aid */ ++ pos += 2; ++ } ++ ++ for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) { ++ *pos++ = status->chain_signal[chain]; ++ *pos++ = chain; ++ } ++} ++ ++/* ++ * This function copies a received frame to all monitor interfaces and ++ * returns a cleaned-up SKB that no longer includes the FCS nor the ++ * radiotap header the driver might have added. ++ */ ++static struct sk_buff * ++ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, ++ struct ieee80211_rate *rate) ++{ ++ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb); ++ struct ieee80211_sub_if_data *sdata; ++ int needed_headroom; ++ struct sk_buff *skb, *skb2; ++ struct net_device *prev_dev = NULL; ++ int present_fcs_len = 0; ++ ++ /* ++ * First, we may need to make a copy of the skb because ++ * (1) we need to modify it for radiotap (if not present), and ++ * (2) the other RX handlers will modify the skb we got. ++ * ++ * We don't need to, of course, if we aren't going to return ++ * the SKB because it has a bad FCS/PLCP checksum. ++ */ ++ ++ if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) ++ present_fcs_len = FCS_LEN; ++ ++ /* ensure hdr->frame_control is in skb head */ ++ if (!pskb_may_pull(origskb, 2)) { ++ dev_kfree_skb(origskb); ++ return NULL; ++ } ++ ++ if (!local->monitors) { ++ if (should_drop_frame(origskb, present_fcs_len)) { ++ dev_kfree_skb(origskb); ++ return NULL; ++ } ++ ++ return remove_monitor_info(local, origskb); ++ } ++ ++ /* room for the radiotap header based on driver features */ ++ needed_headroom = ieee80211_rx_radiotap_space(local, status); ++ ++ if (should_drop_frame(origskb, present_fcs_len)) { ++ /* only need to expand headroom if necessary */ ++ skb = origskb; ++ origskb = NULL; ++ ++ /* ++ * This shouldn't trigger often because most devices have an ++ * RX header they pull before we get here, and that should ++ * be big enough for our radiotap information. We should ++ * probably export the length to drivers so that we can have ++ * them allocate enough headroom to start with. ++ */ ++ if (skb_headroom(skb) < needed_headroom && ++ pskb_expand_head(skb, needed_headroom, 0, GFP_ATOMIC)) { ++ dev_kfree_skb(skb); ++ return NULL; ++ } ++ } else { ++ /* ++ * Need to make a copy and possibly remove radiotap header ++ * and FCS from the original. ++ */ ++ skb = skb_copy_expand(origskb, needed_headroom, 0, GFP_ATOMIC); ++ ++ origskb = remove_monitor_info(local, origskb); ++ ++ if (!skb) ++ return origskb; ++ } ++ ++ /* prepend radiotap information */ ++ ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom, ++ true); ++ ++ skb_reset_mac_header(skb); ++ skb->ip_summed = CHECKSUM_UNNECESSARY; ++ skb->pkt_type = PACKET_OTHERHOST; ++ skb->protocol = htons(ETH_P_802_2); ++ ++ list_for_each_entry_rcu(sdata, &local->interfaces, list) { ++ if (sdata->vif.type != NL80211_IFTYPE_MONITOR) ++ continue; ++ ++ if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) ++ continue; ++ ++ if (!ieee80211_sdata_running(sdata)) ++ continue; ++ ++ if (prev_dev) { ++ skb2 = skb_clone(skb, GFP_ATOMIC); ++ if (skb2) { ++ skb2->dev = prev_dev; ++ netif_receive_skb(skb2); ++ } ++ } ++ ++ prev_dev = sdata->dev; ++ sdata->dev->stats.rx_packets++; ++ sdata->dev->stats.rx_bytes += skb->len; ++ } ++ ++ if (prev_dev) { ++ skb->dev = prev_dev; ++ netif_receive_skb(skb); ++ } else ++ dev_kfree_skb(skb); ++ ++ return origskb; ++} ++ ++static void ieee80211_parse_qos(struct ieee80211_rx_data *rx) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; ++ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); ++ int tid, seqno_idx, security_idx; ++ ++ /* does the frame have a qos control field? */ ++ if (ieee80211_is_data_qos(hdr->frame_control)) { ++ u8 *qc = ieee80211_get_qos_ctl(hdr); ++ /* frame has qos control */ ++ tid = *qc & IEEE80211_QOS_CTL_TID_MASK; ++ if (*qc & IEEE80211_QOS_CTL_A_MSDU_PRESENT) ++ status->rx_flags |= IEEE80211_RX_AMSDU; ++ ++ seqno_idx = tid; ++ security_idx = tid; ++ } else { ++ /* ++ * IEEE 802.11-2007, 7.1.3.4.1 ("Sequence Number field"): ++ * ++ * Sequence numbers for management frames, QoS data ++ * frames with a broadcast/multicast address in the ++ * Address 1 field, and all non-QoS data frames sent ++ * by QoS STAs are assigned using an additional single ++ * modulo-4096 counter, [...] ++ * ++ * We also use that counter for non-QoS STAs. ++ */ ++ seqno_idx = IEEE80211_NUM_TIDS; ++ security_idx = 0; ++ if (ieee80211_is_mgmt(hdr->frame_control)) ++ security_idx = IEEE80211_NUM_TIDS; ++ tid = 0; ++ } ++ ++ rx->seqno_idx = seqno_idx; ++ rx->security_idx = security_idx; ++ /* Set skb->priority to 1d tag if highest order bit of TID is not set. ++ * For now, set skb->priority to 0 for other cases. */ ++ rx->skb->priority = (tid > 7) ? 0 : tid; ++} ++ ++/** ++ * DOC: Packet alignment ++ * ++ * Drivers always need to pass packets that are aligned to two-byte boundaries ++ * to the stack. ++ * ++ * Additionally, should, if possible, align the payload data in a way that ++ * guarantees that the contained IP header is aligned to a four-byte ++ * boundary. In the case of regular frames, this simply means aligning the ++ * payload to a four-byte boundary (because either the IP header is directly ++ * contained, or IV/RFC1042 headers that have a length divisible by four are ++ * in front of it). If the payload data is not properly aligned and the ++ * architecture doesn't support efficient unaligned operations, mac80211 ++ * will align the data. ++ * ++ * With A-MSDU frames, however, the payload data address must yield two modulo ++ * four because there are 14-byte 802.3 headers within the A-MSDU frames that ++ * push the IP header further back to a multiple of four again. Thankfully, the ++ * specs were sane enough this time around to require padding each A-MSDU ++ * subframe to a length that is a multiple of four. ++ * ++ * Padding like Atheros hardware adds which is between the 802.11 header and ++ * the payload is not supported, the driver is required to move the 802.11 ++ * header to be directly in front of the payload in that case. ++ */ ++static void ieee80211_verify_alignment(struct ieee80211_rx_data *rx) ++{ ++#ifdef CONFIG_MAC80211_VERBOSE_DEBUG ++ WARN_ONCE((unsigned long)rx->skb->data & 1, ++ "unaligned packet at 0x%p\n", rx->skb->data); ++#endif ++} ++ ++ ++/* rx handlers */ ++ ++static int ieee80211_is_unicast_robust_mgmt_frame(struct sk_buff *skb) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ ++ if (is_multicast_ether_addr(hdr->addr1)) ++ return 0; ++ ++ return ieee80211_is_robust_mgmt_frame(skb); ++} ++ ++ ++static int ieee80211_is_multicast_robust_mgmt_frame(struct sk_buff *skb) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ ++ if (!is_multicast_ether_addr(hdr->addr1)) ++ return 0; ++ ++ return ieee80211_is_robust_mgmt_frame(skb); ++} ++ ++ ++/* Get the BIP key index from MMIE; return -1 if this is not a BIP frame */ ++static int ieee80211_get_mmie_keyidx(struct sk_buff *skb) ++{ ++ struct ieee80211_mgmt *hdr = (struct ieee80211_mgmt *) skb->data; ++ struct ieee80211_mmie *mmie; ++ ++ if (skb->len < 24 + sizeof(*mmie) || !is_multicast_ether_addr(hdr->da)) ++ return -1; ++ ++ if (!ieee80211_is_robust_mgmt_frame(skb)) ++ return -1; /* not a robust management frame */ ++ ++ mmie = (struct ieee80211_mmie *) ++ (skb->data + skb->len - sizeof(*mmie)); ++ if (mmie->element_id != WLAN_EID_MMIE || ++ mmie->length != sizeof(*mmie) - 2) ++ return -1; ++ ++ return le16_to_cpu(mmie->key_id); ++} ++ ++static int iwl80211_get_cs_keyid(const struct ieee80211_cipher_scheme *cs, ++ struct sk_buff *skb) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; ++ __le16 fc; ++ int hdrlen; ++ u8 keyid; ++ ++ fc = hdr->frame_control; ++ hdrlen = ieee80211_hdrlen(fc); ++ ++ if (skb->len < hdrlen + cs->hdr_len) ++ return -EINVAL; ++ ++ skb_copy_bits(skb, hdrlen + cs->key_idx_off, &keyid, 1); ++ keyid &= cs->key_idx_mask; ++ keyid >>= cs->key_idx_shift; ++ ++ return keyid; ++} ++ ++static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; ++ char *dev_addr = rx->sdata->vif.addr; ++ ++ if (ieee80211_is_data(hdr->frame_control)) { ++ if (is_multicast_ether_addr(hdr->addr1)) { ++ if (ieee80211_has_tods(hdr->frame_control) || ++ !ieee80211_has_fromds(hdr->frame_control)) ++ return RX_DROP_MONITOR; ++ if (ether_addr_equal(hdr->addr3, dev_addr)) ++ return RX_DROP_MONITOR; ++ } else { ++ if (!ieee80211_has_a4(hdr->frame_control)) ++ return RX_DROP_MONITOR; ++ if (ether_addr_equal(hdr->addr4, dev_addr)) ++ return RX_DROP_MONITOR; ++ } ++ } ++ ++ /* If there is not an established peer link and this is not a peer link ++ * establisment frame, beacon or probe, drop the frame. ++ */ ++ ++ if (!rx->sta || sta_plink_state(rx->sta) != NL80211_PLINK_ESTAB) { ++ struct ieee80211_mgmt *mgmt; ++ ++ if (!ieee80211_is_mgmt(hdr->frame_control)) ++ return RX_DROP_MONITOR; ++ ++ if (ieee80211_is_action(hdr->frame_control)) { ++ u8 category; ++ ++ /* make sure category field is present */ ++ if (rx->skb->len < IEEE80211_MIN_ACTION_SIZE) ++ return RX_DROP_MONITOR; ++ ++ mgmt = (struct ieee80211_mgmt *)hdr; ++ category = mgmt->u.action.category; ++ if (category != WLAN_CATEGORY_MESH_ACTION && ++ category != WLAN_CATEGORY_SELF_PROTECTED) ++ return RX_DROP_MONITOR; ++ return RX_CONTINUE; ++ } ++ ++ if (ieee80211_is_probe_req(hdr->frame_control) || ++ ieee80211_is_probe_resp(hdr->frame_control) || ++ ieee80211_is_beacon(hdr->frame_control) || ++ ieee80211_is_auth(hdr->frame_control)) ++ return RX_CONTINUE; ++ ++ return RX_DROP_MONITOR; ++ } ++ ++ return RX_CONTINUE; ++} ++ ++static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata, ++ struct tid_ampdu_rx *tid_agg_rx, ++ int index, ++ struct sk_buff_head *frames) ++{ ++ struct sk_buff_head *skb_list = &tid_agg_rx->reorder_buf[index]; ++ struct sk_buff *skb; ++ struct ieee80211_rx_status *status; ++ ++ lockdep_assert_held(&tid_agg_rx->reorder_lock); ++ ++ if (skb_queue_empty(skb_list)) ++ goto no_frame; ++ ++ if (!ieee80211_rx_reorder_ready(skb_list)) { ++ __skb_queue_purge(skb_list); ++ goto no_frame; ++ } ++ ++ /* release frames from the reorder ring buffer */ ++ tid_agg_rx->stored_mpdu_num--; ++ while ((skb = __skb_dequeue(skb_list))) { ++ status = IEEE80211_SKB_RXCB(skb); ++ status->rx_flags |= IEEE80211_RX_DEFERRED_RELEASE; ++ __skb_queue_tail(frames, skb); ++ } ++ ++no_frame: ++ tid_agg_rx->head_seq_num = ieee80211_sn_inc(tid_agg_rx->head_seq_num); ++} ++ ++static void ieee80211_release_reorder_frames(struct ieee80211_sub_if_data *sdata, ++ struct tid_ampdu_rx *tid_agg_rx, ++ u16 head_seq_num, ++ struct sk_buff_head *frames) ++{ ++ int index; ++ ++ lockdep_assert_held(&tid_agg_rx->reorder_lock); ++ ++ while (ieee80211_sn_less(tid_agg_rx->head_seq_num, head_seq_num)) { ++ index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size; ++ ieee80211_release_reorder_frame(sdata, tid_agg_rx, index, ++ frames); ++ } ++} ++ ++/* ++ * Timeout (in jiffies) for skb's that are waiting in the RX reorder buffer. If ++ * the skb was added to the buffer longer than this time ago, the earlier ++ * frames that have not yet been received are assumed to be lost and the skb ++ * can be released for processing. This may also release other skb's from the ++ * reorder buffer if there are no additional gaps between the frames. ++ * ++ * Callers must hold tid_agg_rx->reorder_lock. ++ */ ++#define HT_RX_REORDER_BUF_TIMEOUT (HZ / 10) ++ ++static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata, ++ struct tid_ampdu_rx *tid_agg_rx, ++ struct sk_buff_head *frames) ++{ ++ int index, i, j; ++ ++ lockdep_assert_held(&tid_agg_rx->reorder_lock); ++ ++ /* release the buffer until next missing frame */ ++ index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size; ++ if (!ieee80211_rx_reorder_ready(&tid_agg_rx->reorder_buf[index]) && ++ tid_agg_rx->stored_mpdu_num) { ++ /* ++ * No buffers ready to be released, but check whether any ++ * frames in the reorder buffer have timed out. ++ */ ++ int skipped = 1; ++ for (j = (index + 1) % tid_agg_rx->buf_size; j != index; ++ j = (j + 1) % tid_agg_rx->buf_size) { ++ if (!ieee80211_rx_reorder_ready( ++ &tid_agg_rx->reorder_buf[j])) { ++ skipped++; ++ continue; ++ } ++ if (skipped && ++ !time_after(jiffies, tid_agg_rx->reorder_time[j] + ++ HT_RX_REORDER_BUF_TIMEOUT)) ++ goto set_release_timer; ++ ++ /* don't leave incomplete A-MSDUs around */ ++ for (i = (index + 1) % tid_agg_rx->buf_size; i != j; ++ i = (i + 1) % tid_agg_rx->buf_size) ++ __skb_queue_purge(&tid_agg_rx->reorder_buf[i]); ++ ++ ht_dbg_ratelimited(sdata, ++ "release an RX reorder frame due to timeout on earlier frames\n"); ++ ieee80211_release_reorder_frame(sdata, tid_agg_rx, j, ++ frames); ++ ++ /* ++ * Increment the head seq# also for the skipped slots. ++ */ ++ tid_agg_rx->head_seq_num = ++ (tid_agg_rx->head_seq_num + ++ skipped) & IEEE80211_SN_MASK; ++ skipped = 0; ++ } ++ } else while (ieee80211_rx_reorder_ready( ++ &tid_agg_rx->reorder_buf[index])) { ++ ieee80211_release_reorder_frame(sdata, tid_agg_rx, index, ++ frames); ++ index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size; ++ } ++ ++ if (tid_agg_rx->stored_mpdu_num) { ++ j = index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size; ++ ++ for (; j != (index - 1) % tid_agg_rx->buf_size; ++ j = (j + 1) % tid_agg_rx->buf_size) { ++ if (ieee80211_rx_reorder_ready( ++ &tid_agg_rx->reorder_buf[j])) ++ break; ++ } ++ ++ set_release_timer: ++ ++ mod_timer(&tid_agg_rx->reorder_timer, ++ tid_agg_rx->reorder_time[j] + 1 + ++ HT_RX_REORDER_BUF_TIMEOUT); ++ } else { ++ del_timer(&tid_agg_rx->reorder_timer); ++ } ++} ++ ++/* ++ * As this function belongs to the RX path it must be under ++ * rcu_read_lock protection. It returns false if the frame ++ * can be processed immediately, true if it was consumed. ++ */ ++static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata, ++ struct tid_ampdu_rx *tid_agg_rx, ++ struct sk_buff *skb, ++ struct sk_buff_head *frames) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); ++ u16 sc = le16_to_cpu(hdr->seq_ctrl); ++ u16 mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4; ++ u16 head_seq_num, buf_size; ++ int index; ++ bool ret = true; ++ ++ spin_lock(&tid_agg_rx->reorder_lock); ++ ++ /* ++ * Offloaded BA sessions have no known starting sequence number so pick ++ * one from first Rxed frame for this tid after BA was started. ++ */ ++ if (unlikely(tid_agg_rx->auto_seq)) { ++ tid_agg_rx->auto_seq = false; ++ tid_agg_rx->ssn = mpdu_seq_num; ++ tid_agg_rx->head_seq_num = mpdu_seq_num; ++ } ++ ++ buf_size = tid_agg_rx->buf_size; ++ head_seq_num = tid_agg_rx->head_seq_num; ++ ++ /* frame with out of date sequence number */ ++ if (ieee80211_sn_less(mpdu_seq_num, head_seq_num)) { ++ dev_kfree_skb(skb); ++ goto out; ++ } ++ ++ /* ++ * If frame the sequence number exceeds our buffering window ++ * size release some previous frames to make room for this one. ++ */ ++ if (!ieee80211_sn_less(mpdu_seq_num, head_seq_num + buf_size)) { ++ head_seq_num = ieee80211_sn_inc( ++ ieee80211_sn_sub(mpdu_seq_num, buf_size)); ++ /* release stored frames up to new head to stack */ ++ ieee80211_release_reorder_frames(sdata, tid_agg_rx, ++ head_seq_num, frames); ++ } ++ ++ /* Now the new frame is always in the range of the reordering buffer */ ++ ++ index = mpdu_seq_num % tid_agg_rx->buf_size; ++ ++ /* check if we already stored this frame */ ++ if (ieee80211_rx_reorder_ready(&tid_agg_rx->reorder_buf[index])) { ++ dev_kfree_skb(skb); ++ goto out; ++ } ++ ++ /* ++ * If the current MPDU is in the right order and nothing else ++ * is stored we can process it directly, no need to buffer it. ++ * If it is first but there's something stored, we may be able ++ * to release frames after this one. ++ */ ++ if (mpdu_seq_num == tid_agg_rx->head_seq_num && ++ tid_agg_rx->stored_mpdu_num == 0) { ++ if (!(status->flag & RX_FLAG_AMSDU_MORE)) ++ tid_agg_rx->head_seq_num = ++ ieee80211_sn_inc(tid_agg_rx->head_seq_num); ++ ret = false; ++ goto out; ++ } ++ ++ /* put the frame in the reordering buffer */ ++ __skb_queue_tail(&tid_agg_rx->reorder_buf[index], skb); ++ if (!(status->flag & RX_FLAG_AMSDU_MORE)) { ++ tid_agg_rx->reorder_time[index] = jiffies; ++ tid_agg_rx->stored_mpdu_num++; ++ ieee80211_sta_reorder_release(sdata, tid_agg_rx, frames); ++ } ++ ++ out: ++ spin_unlock(&tid_agg_rx->reorder_lock); ++ return ret; ++} ++ ++/* ++ * Reorder MPDUs from A-MPDUs, keeping them on a buffer. Returns ++ * true if the MPDU was buffered, false if it should be processed. ++ */ ++static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx, ++ struct sk_buff_head *frames) ++{ ++ struct sk_buff *skb = rx->skb; ++ struct ieee80211_local *local = rx->local; ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); ++ struct sta_info *sta = rx->sta; ++ struct tid_ampdu_rx *tid_agg_rx; ++ u16 sc; ++ u8 tid, ack_policy; ++ ++ if (!ieee80211_is_data_qos(hdr->frame_control) || ++ is_multicast_ether_addr(hdr->addr1)) ++ goto dont_reorder; ++ ++ /* ++ * filter the QoS data rx stream according to ++ * STA/TID and check if this STA/TID is on aggregation ++ */ ++ ++ if (!sta) ++ goto dont_reorder; ++ ++ ack_policy = *ieee80211_get_qos_ctl(hdr) & ++ IEEE80211_QOS_CTL_ACK_POLICY_MASK; ++ tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; ++ ++ tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]); ++ if (!tid_agg_rx) ++ goto dont_reorder; ++ ++ /* qos null data frames are excluded */ ++ if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC))) ++ goto dont_reorder; ++ ++ /* not part of a BA session */ ++ if (ack_policy != IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK && ++ ack_policy != IEEE80211_QOS_CTL_ACK_POLICY_NORMAL) ++ goto dont_reorder; ++ ++ /* not actually part of this BA session */ ++ if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) ++ goto dont_reorder; ++ ++ /* new, potentially un-ordered, ampdu frame - process it */ ++ ++ /* reset session timer */ ++ if (tid_agg_rx->timeout) ++ tid_agg_rx->last_rx = jiffies; ++ ++ /* if this mpdu is fragmented - terminate rx aggregation session */ ++ sc = le16_to_cpu(hdr->seq_ctrl); ++ if (sc & IEEE80211_SCTL_FRAG) { ++ skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME; ++ skb_queue_tail(&rx->sdata->skb_queue, skb); ++ ieee80211_queue_work(&local->hw, &rx->sdata->work); ++ return; ++ } ++ ++ /* ++ * No locking needed -- we will only ever process one ++ * RX packet at a time, and thus own tid_agg_rx. All ++ * other code manipulating it needs to (and does) make ++ * sure that we cannot get to it any more before doing ++ * anything with it. ++ */ ++ if (ieee80211_sta_manage_reorder_buf(rx->sdata, tid_agg_rx, skb, ++ frames)) ++ return; ++ ++ dont_reorder: ++ __skb_queue_tail(frames, skb); ++} ++ ++static ieee80211_rx_result debug_noinline ++ieee80211_rx_h_check(struct ieee80211_rx_data *rx) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; ++ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); ++ ++ /* ++ * Drop duplicate 802.11 retransmissions ++ * (IEEE 802.11-2012: 9.3.2.10 "Duplicate detection and recovery") ++ */ ++ if (rx->skb->len >= 24 && rx->sta && ++ !ieee80211_is_ctl(hdr->frame_control) && ++ !ieee80211_is_qos_nullfunc(hdr->frame_control) && ++ !is_multicast_ether_addr(hdr->addr1)) { ++ if (unlikely(ieee80211_has_retry(hdr->frame_control) && ++ rx->sta->last_seq_ctrl[rx->seqno_idx] == ++ hdr->seq_ctrl)) { ++ if (status->rx_flags & IEEE80211_RX_RA_MATCH) { ++ rx->local->dot11FrameDuplicateCount++; ++ rx->sta->num_duplicates++; ++ } ++ return RX_DROP_UNUSABLE; ++ } else if (!(status->flag & RX_FLAG_AMSDU_MORE)) { ++ rx->sta->last_seq_ctrl[rx->seqno_idx] = hdr->seq_ctrl; ++ } ++ } ++ ++ if (unlikely(rx->skb->len < 16)) { ++ I802_DEBUG_INC(rx->local->rx_handlers_drop_short); ++ return RX_DROP_MONITOR; ++ } ++ ++ /* Drop disallowed frame classes based on STA auth/assoc state; ++ * IEEE 802.11, Chap 5.5. ++ * ++ * mac80211 filters only based on association state, i.e. it drops ++ * Class 3 frames from not associated stations. hostapd sends ++ * deauth/disassoc frames when needed. In addition, hostapd is ++ * responsible for filtering on both auth and assoc states. ++ */ ++ ++ if (ieee80211_vif_is_mesh(&rx->sdata->vif)) ++ return ieee80211_rx_mesh_check(rx); ++ ++ if (unlikely((ieee80211_is_data(hdr->frame_control) || ++ ieee80211_is_pspoll(hdr->frame_control)) && ++ rx->sdata->vif.type != NL80211_IFTYPE_ADHOC && ++ rx->sdata->vif.type != NL80211_IFTYPE_WDS && ++ (!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_ASSOC)))) { ++ /* ++ * accept port control frames from the AP even when it's not ++ * yet marked ASSOC to prevent a race where we don't set the ++ * assoc bit quickly enough before it sends the first frame ++ */ ++ if (rx->sta && rx->sdata->vif.type == NL80211_IFTYPE_STATION && ++ ieee80211_is_data_present(hdr->frame_control)) { ++ unsigned int hdrlen; ++ __be16 ethertype; ++ ++ hdrlen = ieee80211_hdrlen(hdr->frame_control); ++ ++ if (rx->skb->len < hdrlen + 8) ++ return RX_DROP_MONITOR; ++ ++ skb_copy_bits(rx->skb, hdrlen + 6, ðertype, 2); ++ if (ethertype == rx->sdata->control_port_protocol) ++ return RX_CONTINUE; ++ } ++ ++ if (rx->sdata->vif.type == NL80211_IFTYPE_AP && ++ cfg80211_rx_spurious_frame(rx->sdata->dev, ++ hdr->addr2, ++ GFP_ATOMIC)) ++ return RX_DROP_UNUSABLE; ++ ++ return RX_DROP_MONITOR; ++ } ++ ++ return RX_CONTINUE; ++} ++ ++ ++static ieee80211_rx_result debug_noinline ++ieee80211_rx_h_check_more_data(struct ieee80211_rx_data *rx) ++{ ++ struct ieee80211_local *local; ++ struct ieee80211_hdr *hdr; ++ struct sk_buff *skb; ++ ++ local = rx->local; ++ skb = rx->skb; ++ hdr = (struct ieee80211_hdr *) skb->data; ++ ++ if (!local->pspolling) ++ return RX_CONTINUE; ++ ++ if (!ieee80211_has_fromds(hdr->frame_control)) ++ /* this is not from AP */ ++ return RX_CONTINUE; ++ ++ if (!ieee80211_is_data(hdr->frame_control)) ++ return RX_CONTINUE; ++ ++ if (!ieee80211_has_moredata(hdr->frame_control)) { ++ /* AP has no more frames buffered for us */ ++ local->pspolling = false; ++ return RX_CONTINUE; ++ } ++ ++ /* more data bit is set, let's request a new frame from the AP */ ++ ieee80211_send_pspoll(local, rx->sdata); ++ ++ return RX_CONTINUE; ++} ++ ++static void sta_ps_start(struct sta_info *sta) ++{ ++ struct ieee80211_sub_if_data *sdata = sta->sdata; ++ struct ieee80211_local *local = sdata->local; ++ struct ps_data *ps; ++ ++ if (sta->sdata->vif.type == NL80211_IFTYPE_AP || ++ sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) ++ ps = &sdata->bss->ps; ++ else ++ return; ++ ++ atomic_inc(&ps->num_sta_ps); ++ set_sta_flag(sta, WLAN_STA_PS_STA); ++ if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) ++ drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta); ++ ps_dbg(sdata, "STA %pM aid %d enters power save mode\n", ++ sta->sta.addr, sta->sta.aid); ++} ++ ++static void sta_ps_end(struct sta_info *sta) ++{ ++ ps_dbg(sta->sdata, "STA %pM aid %d exits power save mode\n", ++ sta->sta.addr, sta->sta.aid); ++ ++ if (test_sta_flag(sta, WLAN_STA_PS_DRIVER)) { ++ /* ++ * Clear the flag only if the other one is still set ++ * so that the TX path won't start TX'ing new frames ++ * directly ... In the case that the driver flag isn't ++ * set ieee80211_sta_ps_deliver_wakeup() will clear it. ++ */ ++ clear_sta_flag(sta, WLAN_STA_PS_STA); ++ ps_dbg(sta->sdata, "STA %pM aid %d driver-ps-blocked\n", ++ sta->sta.addr, sta->sta.aid); ++ return; ++ } ++ ++ set_sta_flag(sta, WLAN_STA_PS_DELIVER); ++ clear_sta_flag(sta, WLAN_STA_PS_STA); ++ ieee80211_sta_ps_deliver_wakeup(sta); ++} ++ ++int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start) ++{ ++ struct sta_info *sta_inf = container_of(sta, struct sta_info, sta); ++ bool in_ps; ++ ++ WARN_ON(!(sta_inf->local->hw.flags & IEEE80211_HW_AP_LINK_PS)); ++ ++ /* Don't let the same PS state be set twice */ ++ in_ps = test_sta_flag(sta_inf, WLAN_STA_PS_STA); ++ if ((start && in_ps) || (!start && !in_ps)) ++ return -EINVAL; ++ ++ if (start) ++ sta_ps_start(sta_inf); ++ else ++ sta_ps_end(sta_inf); ++ ++ return 0; ++} ++EXPORT_SYMBOL(ieee80211_sta_ps_transition); ++ ++static ieee80211_rx_result debug_noinline ++ieee80211_rx_h_uapsd_and_pspoll(struct ieee80211_rx_data *rx) ++{ ++ struct ieee80211_sub_if_data *sdata = rx->sdata; ++ struct ieee80211_hdr *hdr = (void *)rx->skb->data; ++ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); ++ int tid, ac; ++ ++ if (!rx->sta || !(status->rx_flags & IEEE80211_RX_RA_MATCH)) ++ return RX_CONTINUE; ++ ++ if (sdata->vif.type != NL80211_IFTYPE_AP && ++ sdata->vif.type != NL80211_IFTYPE_AP_VLAN) ++ return RX_CONTINUE; ++ ++ /* ++ * The device handles station powersave, so don't do anything about ++ * uAPSD and PS-Poll frames (the latter shouldn't even come up from ++ * it to mac80211 since they're handled.) ++ */ ++ if (sdata->local->hw.flags & IEEE80211_HW_AP_LINK_PS) ++ return RX_CONTINUE; ++ ++ /* ++ * Don't do anything if the station isn't already asleep. In ++ * the uAPSD case, the station will probably be marked asleep, ++ * in the PS-Poll case the station must be confused ... ++ */ ++ if (!test_sta_flag(rx->sta, WLAN_STA_PS_STA)) ++ return RX_CONTINUE; ++ ++ if (unlikely(ieee80211_is_pspoll(hdr->frame_control))) { ++ if (!test_sta_flag(rx->sta, WLAN_STA_SP)) { ++ if (!test_sta_flag(rx->sta, WLAN_STA_PS_DRIVER)) ++ ieee80211_sta_ps_deliver_poll_response(rx->sta); ++ else ++ set_sta_flag(rx->sta, WLAN_STA_PSPOLL); ++ } ++ ++ /* Free PS Poll skb here instead of returning RX_DROP that would ++ * count as an dropped frame. */ ++ dev_kfree_skb(rx->skb); ++ ++ return RX_QUEUED; ++ } else if (!ieee80211_has_morefrags(hdr->frame_control) && ++ !(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) && ++ ieee80211_has_pm(hdr->frame_control) && ++ (ieee80211_is_data_qos(hdr->frame_control) || ++ ieee80211_is_qos_nullfunc(hdr->frame_control))) { ++ tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; ++ ac = ieee802_1d_to_ac[tid & 7]; ++ ++ /* ++ * If this AC is not trigger-enabled do nothing. ++ * ++ * NB: This could/should check a separate bitmap of trigger- ++ * enabled queues, but for now we only implement uAPSD w/o ++ * TSPEC changes to the ACs, so they're always the same. ++ */ ++ if (!(rx->sta->sta.uapsd_queues & BIT(ac))) ++ return RX_CONTINUE; ++ ++ /* if we are in a service period, do nothing */ ++ if (test_sta_flag(rx->sta, WLAN_STA_SP)) ++ return RX_CONTINUE; ++ ++ if (!test_sta_flag(rx->sta, WLAN_STA_PS_DRIVER)) ++ ieee80211_sta_ps_deliver_uapsd(rx->sta); ++ else ++ set_sta_flag(rx->sta, WLAN_STA_UAPSD); ++ } ++ ++ return RX_CONTINUE; ++} ++ ++static ieee80211_rx_result debug_noinline ++ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) ++{ ++ struct sta_info *sta = rx->sta; ++ struct sk_buff *skb = rx->skb; ++ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; ++ int i; ++ ++ if (!sta) ++ return RX_CONTINUE; ++ ++ /* ++ * Update last_rx only for IBSS packets which are for the current ++ * BSSID and for station already AUTHORIZED to avoid keeping the ++ * current IBSS network alive in cases where other STAs start ++ * using different BSSID. This will also give the station another ++ * chance to restart the authentication/authorization in case ++ * something went wrong the first time. ++ */ ++ if (rx->sdata->vif.type == NL80211_IFTYPE_ADHOC) { ++ u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len, ++ NL80211_IFTYPE_ADHOC); ++ if (ether_addr_equal(bssid, rx->sdata->u.ibss.bssid) && ++ test_sta_flag(sta, WLAN_STA_AUTHORIZED)) { ++ sta->last_rx = jiffies; ++ if (ieee80211_is_data(hdr->frame_control) && ++ !is_multicast_ether_addr(hdr->addr1)) { ++ sta->last_rx_rate_idx = status->rate_idx; ++ sta->last_rx_rate_flag = status->flag; ++ sta->last_rx_rate_vht_flag = status->vht_flag; ++ sta->last_rx_rate_vht_nss = status->vht_nss; ++ } ++ } ++ } else if (!is_multicast_ether_addr(hdr->addr1)) { ++ /* ++ * Mesh beacons will update last_rx when if they are found to ++ * match the current local configuration when processed. ++ */ ++ sta->last_rx = jiffies; ++ if (ieee80211_is_data(hdr->frame_control)) { ++ sta->last_rx_rate_idx = status->rate_idx; ++ sta->last_rx_rate_flag = status->flag; ++ sta->last_rx_rate_vht_flag = status->vht_flag; ++ sta->last_rx_rate_vht_nss = status->vht_nss; ++ } ++ } ++ ++ if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) ++ return RX_CONTINUE; ++ ++ if (rx->sdata->vif.type == NL80211_IFTYPE_STATION) ++ ieee80211_sta_rx_notify(rx->sdata, hdr); ++ ++ sta->rx_fragments++; ++ sta->rx_bytes += rx->skb->len; ++ if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) { ++ sta->last_signal = status->signal; ++ ewma_add(&sta->avg_signal, -status->signal); ++ } ++ ++ if (status->chains) { ++ sta->chains = status->chains; ++ for (i = 0; i < ARRAY_SIZE(status->chain_signal); i++) { ++ int signal = status->chain_signal[i]; ++ ++ if (!(status->chains & BIT(i))) ++ continue; ++ ++ sta->chain_signal_last[i] = signal; ++ ewma_add(&sta->chain_signal_avg[i], -signal); ++ } ++ } ++ ++ /* ++ * Change STA power saving mode only at the end of a frame ++ * exchange sequence. ++ */ ++ if (!(sta->local->hw.flags & IEEE80211_HW_AP_LINK_PS) && ++ !ieee80211_has_morefrags(hdr->frame_control) && ++ !(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) && ++ (rx->sdata->vif.type == NL80211_IFTYPE_AP || ++ rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) && ++ /* PM bit is only checked in frames where it isn't reserved, ++ * in AP mode it's reserved in non-bufferable management frames ++ * (cf. IEEE 802.11-2012 8.2.4.1.7 Power Management field) ++ */ ++ (!ieee80211_is_mgmt(hdr->frame_control) || ++ ieee80211_is_bufferable_mmpdu(hdr->frame_control))) { ++ if (test_sta_flag(sta, WLAN_STA_PS_STA)) { ++ if (!ieee80211_has_pm(hdr->frame_control)) ++ sta_ps_end(sta); ++ } else { ++ if (ieee80211_has_pm(hdr->frame_control)) ++ sta_ps_start(sta); ++ } ++ } ++ ++ /* mesh power save support */ ++ if (ieee80211_vif_is_mesh(&rx->sdata->vif)) ++ ieee80211_mps_rx_h_sta_process(sta, hdr); ++ ++ /* ++ * Drop (qos-)data::nullfunc frames silently, since they ++ * are used only to control station power saving mode. ++ */ ++ if (ieee80211_is_nullfunc(hdr->frame_control) || ++ ieee80211_is_qos_nullfunc(hdr->frame_control)) { ++ I802_DEBUG_INC(rx->local->rx_handlers_drop_nullfunc); ++ ++ /* ++ * If we receive a 4-addr nullfunc frame from a STA ++ * that was not moved to a 4-addr STA vlan yet send ++ * the event to userspace and for older hostapd drop ++ * the frame to the monitor interface. ++ */ ++ if (ieee80211_has_a4(hdr->frame_control) && ++ (rx->sdata->vif.type == NL80211_IFTYPE_AP || ++ (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && ++ !rx->sdata->u.vlan.sta))) { ++ if (!test_and_set_sta_flag(sta, WLAN_STA_4ADDR_EVENT)) ++ cfg80211_rx_unexpected_4addr_frame( ++ rx->sdata->dev, sta->sta.addr, ++ GFP_ATOMIC); ++ return RX_DROP_MONITOR; ++ } ++ /* ++ * Update counter and free packet here to avoid ++ * counting this as a dropped packed. ++ */ ++ sta->rx_packets++; ++ dev_kfree_skb(rx->skb); ++ return RX_QUEUED; ++ } ++ ++ return RX_CONTINUE; ++} /* ieee80211_rx_h_sta_process */ ++ ++static ieee80211_rx_result debug_noinline ++ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) ++{ ++ struct sk_buff *skb = rx->skb; ++ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; ++ int keyidx; ++ int hdrlen; ++ ieee80211_rx_result result = RX_DROP_UNUSABLE; ++ struct ieee80211_key *sta_ptk = NULL; ++ int mmie_keyidx = -1; ++ __le16 fc; ++ const struct ieee80211_cipher_scheme *cs = NULL; ++ ++ /* ++ * Key selection 101 ++ * ++ * There are four types of keys: ++ * - GTK (group keys) ++ * - IGTK (group keys for management frames) ++ * - PTK (pairwise keys) ++ * - STK (station-to-station pairwise keys) ++ * ++ * When selecting a key, we have to distinguish between multicast ++ * (including broadcast) and unicast frames, the latter can only ++ * use PTKs and STKs while the former always use GTKs and IGTKs. ++ * Unless, of course, actual WEP keys ("pre-RSNA") are used, then ++ * unicast frames can also use key indices like GTKs. Hence, if we ++ * don't have a PTK/STK we check the key index for a WEP key. ++ * ++ * Note that in a regular BSS, multicast frames are sent by the ++ * AP only, associated stations unicast the frame to the AP first ++ * which then multicasts it on their behalf. ++ * ++ * There is also a slight problem in IBSS mode: GTKs are negotiated ++ * with each station, that is something we don't currently handle. ++ * The spec seems to expect that one negotiates the same key with ++ * every station but there's no such requirement; VLANs could be ++ * possible. ++ */ ++ ++ /* ++ * No point in finding a key and decrypting if the frame is neither ++ * addressed to us nor a multicast frame. ++ */ ++ if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) ++ return RX_CONTINUE; ++ ++ /* start without a key */ ++ rx->key = NULL; ++ fc = hdr->frame_control; ++ ++ if (rx->sta) { ++ int keyid = rx->sta->ptk_idx; ++ ++ if (ieee80211_has_protected(fc) && rx->sta->cipher_scheme) { ++ cs = rx->sta->cipher_scheme; ++ keyid = iwl80211_get_cs_keyid(cs, rx->skb); ++ if (unlikely(keyid < 0)) ++ return RX_DROP_UNUSABLE; ++ } ++ sta_ptk = rcu_dereference(rx->sta->ptk[keyid]); ++ } ++ ++ if (!ieee80211_has_protected(fc)) ++ mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb); ++ ++ if (!is_multicast_ether_addr(hdr->addr1) && sta_ptk) { ++ rx->key = sta_ptk; ++ if ((status->flag & RX_FLAG_DECRYPTED) && ++ (status->flag & RX_FLAG_IV_STRIPPED)) ++ return RX_CONTINUE; ++ /* Skip decryption if the frame is not protected. */ ++ if (!ieee80211_has_protected(fc)) ++ return RX_CONTINUE; ++ } else if (mmie_keyidx >= 0) { ++ /* Broadcast/multicast robust management frame / BIP */ ++ if ((status->flag & RX_FLAG_DECRYPTED) && ++ (status->flag & RX_FLAG_IV_STRIPPED)) ++ return RX_CONTINUE; ++ ++ if (mmie_keyidx < NUM_DEFAULT_KEYS || ++ mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) ++ return RX_DROP_MONITOR; /* unexpected BIP keyidx */ ++ if (rx->sta) ++ rx->key = rcu_dereference(rx->sta->gtk[mmie_keyidx]); ++ if (!rx->key) ++ rx->key = rcu_dereference(rx->sdata->keys[mmie_keyidx]); ++ } else if (!ieee80211_has_protected(fc)) { ++ /* ++ * The frame was not protected, so skip decryption. However, we ++ * need to set rx->key if there is a key that could have been ++ * used so that the frame may be dropped if encryption would ++ * have been expected. ++ */ ++ struct ieee80211_key *key = NULL; ++ struct ieee80211_sub_if_data *sdata = rx->sdata; ++ int i; ++ ++ if (ieee80211_is_mgmt(fc) && ++ is_multicast_ether_addr(hdr->addr1) && ++ (key = rcu_dereference(rx->sdata->default_mgmt_key))) ++ rx->key = key; ++ else { ++ if (rx->sta) { ++ for (i = 0; i < NUM_DEFAULT_KEYS; i++) { ++ key = rcu_dereference(rx->sta->gtk[i]); ++ if (key) ++ break; ++ } ++ } ++ if (!key) { ++ for (i = 0; i < NUM_DEFAULT_KEYS; i++) { ++ key = rcu_dereference(sdata->keys[i]); ++ if (key) ++ break; ++ } ++ } ++ if (key) ++ rx->key = key; ++ } ++ return RX_CONTINUE; ++ } else { ++ u8 keyid; ++ ++ /* ++ * The device doesn't give us the IV so we won't be ++ * able to look up the key. That's ok though, we ++ * don't need to decrypt the frame, we just won't ++ * be able to keep statistics accurate. ++ * Except for key threshold notifications, should ++ * we somehow allow the driver to tell us which key ++ * the hardware used if this flag is set? ++ */ ++ if ((status->flag & RX_FLAG_DECRYPTED) && ++ (status->flag & RX_FLAG_IV_STRIPPED)) ++ return RX_CONTINUE; ++ ++ hdrlen = ieee80211_hdrlen(fc); ++ ++ if (cs) { ++ keyidx = iwl80211_get_cs_keyid(cs, rx->skb); ++ ++ if (unlikely(keyidx < 0)) ++ return RX_DROP_UNUSABLE; ++ } else { ++ if (rx->skb->len < 8 + hdrlen) ++ return RX_DROP_UNUSABLE; /* TODO: count this? */ ++ /* ++ * no need to call ieee80211_wep_get_keyidx, ++ * it verifies a bunch of things we've done already ++ */ ++ skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1); ++ keyidx = keyid >> 6; ++ } ++ ++ /* check per-station GTK first, if multicast packet */ ++ if (is_multicast_ether_addr(hdr->addr1) && rx->sta) ++ rx->key = rcu_dereference(rx->sta->gtk[keyidx]); ++ ++ /* if not found, try default key */ ++ if (!rx->key) { ++ rx->key = rcu_dereference(rx->sdata->keys[keyidx]); ++ ++ /* ++ * RSNA-protected unicast frames should always be ++ * sent with pairwise or station-to-station keys, ++ * but for WEP we allow using a key index as well. ++ */ ++ if (rx->key && ++ rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP40 && ++ rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP104 && ++ !is_multicast_ether_addr(hdr->addr1)) ++ rx->key = NULL; ++ } ++ } ++ ++ if (rx->key) { ++ if (unlikely(rx->key->flags & KEY_FLAG_TAINTED)) ++ return RX_DROP_MONITOR; ++ ++ rx->key->tx_rx_count++; ++ /* TODO: add threshold stuff again */ ++ } else { ++ return RX_DROP_MONITOR; ++ } ++ ++ switch (rx->key->conf.cipher) { ++ case WLAN_CIPHER_SUITE_WEP40: ++ case WLAN_CIPHER_SUITE_WEP104: ++ result = ieee80211_crypto_wep_decrypt(rx); ++ break; ++ case WLAN_CIPHER_SUITE_TKIP: ++ result = ieee80211_crypto_tkip_decrypt(rx); ++ break; ++ case WLAN_CIPHER_SUITE_CCMP: ++ result = ieee80211_crypto_ccmp_decrypt(rx); ++ break; ++ case WLAN_CIPHER_SUITE_AES_CMAC: ++ result = ieee80211_crypto_aes_cmac_decrypt(rx); ++ break; ++ default: ++ result = ieee80211_crypto_hw_decrypt(rx); ++ } ++ ++ /* the hdr variable is invalid after the decrypt handlers */ ++ ++ /* either the frame has been decrypted or will be dropped */ ++ status->flag |= RX_FLAG_DECRYPTED; ++ ++ return result; ++} ++ ++static inline struct ieee80211_fragment_entry * ++ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata, ++ unsigned int frag, unsigned int seq, int rx_queue, ++ struct sk_buff **skb) ++{ ++ struct ieee80211_fragment_entry *entry; ++ ++ entry = &sdata->fragments[sdata->fragment_next++]; ++ if (sdata->fragment_next >= IEEE80211_FRAGMENT_MAX) ++ sdata->fragment_next = 0; ++ ++ if (!skb_queue_empty(&entry->skb_list)) ++ __skb_queue_purge(&entry->skb_list); ++ ++ __skb_queue_tail(&entry->skb_list, *skb); /* no need for locking */ ++ *skb = NULL; ++ entry->first_frag_time = jiffies; ++ entry->seq = seq; ++ entry->rx_queue = rx_queue; ++ entry->last_frag = frag; ++ entry->ccmp = 0; ++ entry->extra_len = 0; ++ ++ return entry; ++} ++ ++static inline struct ieee80211_fragment_entry * ++ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata, ++ unsigned int frag, unsigned int seq, ++ int rx_queue, struct ieee80211_hdr *hdr) ++{ ++ struct ieee80211_fragment_entry *entry; ++ int i, idx; ++ ++ idx = sdata->fragment_next; ++ for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) { ++ struct ieee80211_hdr *f_hdr; ++ ++ idx--; ++ if (idx < 0) ++ idx = IEEE80211_FRAGMENT_MAX - 1; ++ ++ entry = &sdata->fragments[idx]; ++ if (skb_queue_empty(&entry->skb_list) || entry->seq != seq || ++ entry->rx_queue != rx_queue || ++ entry->last_frag + 1 != frag) ++ continue; ++ ++ f_hdr = (struct ieee80211_hdr *)entry->skb_list.next->data; ++ ++ /* ++ * Check ftype and addresses are equal, else check next fragment ++ */ ++ if (((hdr->frame_control ^ f_hdr->frame_control) & ++ cpu_to_le16(IEEE80211_FCTL_FTYPE)) || ++ !ether_addr_equal(hdr->addr1, f_hdr->addr1) || ++ !ether_addr_equal(hdr->addr2, f_hdr->addr2)) ++ continue; ++ ++ if (time_after(jiffies, entry->first_frag_time + 2 * HZ)) { ++ __skb_queue_purge(&entry->skb_list); ++ continue; ++ } ++ return entry; ++ } ++ ++ return NULL; ++} ++ ++static ieee80211_rx_result debug_noinline ++ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) ++{ ++ struct ieee80211_hdr *hdr; ++ u16 sc; ++ __le16 fc; ++ unsigned int frag, seq; ++ struct ieee80211_fragment_entry *entry; ++ struct sk_buff *skb; ++ struct ieee80211_rx_status *status; ++ ++ hdr = (struct ieee80211_hdr *)rx->skb->data; ++ fc = hdr->frame_control; ++ ++ if (ieee80211_is_ctl(fc)) ++ return RX_CONTINUE; ++ ++ sc = le16_to_cpu(hdr->seq_ctrl); ++ frag = sc & IEEE80211_SCTL_FRAG; ++ ++ if (is_multicast_ether_addr(hdr->addr1)) { ++ rx->local->dot11MulticastReceivedFrameCount++; ++ goto out_no_led; ++ } ++ ++ if (likely(!ieee80211_has_morefrags(fc) && frag == 0)) ++ goto out; ++ ++ I802_DEBUG_INC(rx->local->rx_handlers_fragments); ++ ++ if (skb_linearize(rx->skb)) ++ return RX_DROP_UNUSABLE; ++ ++ /* ++ * skb_linearize() might change the skb->data and ++ * previously cached variables (in this case, hdr) need to ++ * be refreshed with the new data. ++ */ ++ hdr = (struct ieee80211_hdr *)rx->skb->data; ++ seq = (sc & IEEE80211_SCTL_SEQ) >> 4; ++ ++ if (frag == 0) { ++ /* This is the first fragment of a new frame. */ ++ entry = ieee80211_reassemble_add(rx->sdata, frag, seq, ++ rx->seqno_idx, &(rx->skb)); ++ if (rx->key && rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP && ++ ieee80211_has_protected(fc)) { ++ int queue = rx->security_idx; ++ /* Store CCMP PN so that we can verify that the next ++ * fragment has a sequential PN value. */ ++ entry->ccmp = 1; ++ memcpy(entry->last_pn, ++ rx->key->u.ccmp.rx_pn[queue], ++ IEEE80211_CCMP_PN_LEN); ++ } ++ return RX_QUEUED; ++ } ++ ++ /* This is a fragment for a frame that should already be pending in ++ * fragment cache. Add this fragment to the end of the pending entry. ++ */ ++ entry = ieee80211_reassemble_find(rx->sdata, frag, seq, ++ rx->seqno_idx, hdr); ++ if (!entry) { ++ I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag); ++ return RX_DROP_MONITOR; ++ } ++ ++ /* Verify that MPDUs within one MSDU have sequential PN values. ++ * (IEEE 802.11i, 8.3.3.4.5) */ ++ if (entry->ccmp) { ++ int i; ++ u8 pn[IEEE80211_CCMP_PN_LEN], *rpn; ++ int queue; ++ if (!rx->key || rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP) ++ return RX_DROP_UNUSABLE; ++ memcpy(pn, entry->last_pn, IEEE80211_CCMP_PN_LEN); ++ for (i = IEEE80211_CCMP_PN_LEN - 1; i >= 0; i--) { ++ pn[i]++; ++ if (pn[i]) ++ break; ++ } ++ queue = rx->security_idx; ++ rpn = rx->key->u.ccmp.rx_pn[queue]; ++ if (memcmp(pn, rpn, IEEE80211_CCMP_PN_LEN)) ++ return RX_DROP_UNUSABLE; ++ memcpy(entry->last_pn, pn, IEEE80211_CCMP_PN_LEN); ++ } ++ ++ skb_pull(rx->skb, ieee80211_hdrlen(fc)); ++ __skb_queue_tail(&entry->skb_list, rx->skb); ++ entry->last_frag = frag; ++ entry->extra_len += rx->skb->len; ++ if (ieee80211_has_morefrags(fc)) { ++ rx->skb = NULL; ++ return RX_QUEUED; ++ } ++ ++ rx->skb = __skb_dequeue(&entry->skb_list); ++ if (skb_tailroom(rx->skb) < entry->extra_len) { ++ I802_DEBUG_INC(rx->local->rx_expand_skb_head2); ++ if (unlikely(pskb_expand_head(rx->skb, 0, entry->extra_len, ++ GFP_ATOMIC))) { ++ I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag); ++ __skb_queue_purge(&entry->skb_list); ++ return RX_DROP_UNUSABLE; ++ } ++ } ++ while ((skb = __skb_dequeue(&entry->skb_list))) { ++ memcpy(skb_put(rx->skb, skb->len), skb->data, skb->len); ++ dev_kfree_skb(skb); ++ } ++ ++ /* Complete frame has been reassembled - process it now */ ++ status = IEEE80211_SKB_RXCB(rx->skb); ++ status->rx_flags |= IEEE80211_RX_FRAGMENTED; ++ ++ out: ++ ieee80211_led_rx(rx->local); ++ out_no_led: ++ if (rx->sta) ++ rx->sta->rx_packets++; ++ return RX_CONTINUE; ++} ++ ++static int ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx) ++{ ++ if (unlikely(!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_AUTHORIZED))) ++ return -EACCES; ++ ++ return 0; ++} ++ ++static int ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc) ++{ ++ struct sk_buff *skb = rx->skb; ++ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); ++ ++ /* ++ * Pass through unencrypted frames if the hardware has ++ * decrypted them already. ++ */ ++ if (status->flag & RX_FLAG_DECRYPTED) ++ return 0; ++ ++ /* Drop unencrypted frames if key is set. */ ++ if (unlikely(!ieee80211_has_protected(fc) && ++ !ieee80211_is_nullfunc(fc) && ++ ieee80211_is_data(fc) && ++ (rx->key || rx->sdata->drop_unencrypted))) ++ return -EACCES; ++ ++ return 0; ++} ++ ++static int ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; ++ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); ++ __le16 fc = hdr->frame_control; ++ ++ /* ++ * Pass through unencrypted frames if the hardware has ++ * decrypted them already. ++ */ ++ if (status->flag & RX_FLAG_DECRYPTED) ++ return 0; ++ ++ if (rx->sta && test_sta_flag(rx->sta, WLAN_STA_MFP)) { ++ if (unlikely(!ieee80211_has_protected(fc) && ++ ieee80211_is_unicast_robust_mgmt_frame(rx->skb) && ++ rx->key)) { ++ if (ieee80211_is_deauth(fc) || ++ ieee80211_is_disassoc(fc)) ++ cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev, ++ rx->skb->data, ++ rx->skb->len); ++ return -EACCES; ++ } ++ /* BIP does not use Protected field, so need to check MMIE */ ++ if (unlikely(ieee80211_is_multicast_robust_mgmt_frame(rx->skb) && ++ ieee80211_get_mmie_keyidx(rx->skb) < 0)) { ++ if (ieee80211_is_deauth(fc) || ++ ieee80211_is_disassoc(fc)) ++ cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev, ++ rx->skb->data, ++ rx->skb->len); ++ return -EACCES; ++ } ++ /* ++ * When using MFP, Action frames are not allowed prior to ++ * having configured keys. ++ */ ++ if (unlikely(ieee80211_is_action(fc) && !rx->key && ++ ieee80211_is_robust_mgmt_frame(rx->skb))) ++ return -EACCES; ++ } ++ ++ return 0; ++} ++ ++static int ++__ieee80211_data_to_8023(struct ieee80211_rx_data *rx, bool *port_control) ++{ ++ struct ieee80211_sub_if_data *sdata = rx->sdata; ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; ++ bool check_port_control = false; ++ struct ethhdr *ehdr; ++ int ret; ++ ++ *port_control = false; ++ if (ieee80211_has_a4(hdr->frame_control) && ++ sdata->vif.type == NL80211_IFTYPE_AP_VLAN && !sdata->u.vlan.sta) ++ return -1; ++ ++ if (sdata->vif.type == NL80211_IFTYPE_STATION && ++ !!sdata->u.mgd.use_4addr != !!ieee80211_has_a4(hdr->frame_control)) { ++ ++ if (!sdata->u.mgd.use_4addr) ++ return -1; ++ else ++ check_port_control = true; ++ } ++ ++ if (is_multicast_ether_addr(hdr->addr1) && ++ sdata->vif.type == NL80211_IFTYPE_AP_VLAN && sdata->u.vlan.sta) ++ return -1; ++ ++ ret = ieee80211_data_to_8023(rx->skb, sdata->vif.addr, sdata->vif.type); ++ if (ret < 0) ++ return ret; ++ ++ ehdr = (struct ethhdr *) rx->skb->data; ++ if (ehdr->h_proto == rx->sdata->control_port_protocol) ++ *port_control = true; ++ else if (check_port_control) ++ return -1; ++ ++ return 0; ++} ++ ++/* ++ * requires that rx->skb is a frame with ethernet header ++ */ ++static bool ieee80211_frame_allowed(struct ieee80211_rx_data *rx, __le16 fc) ++{ ++ static const u8 pae_group_addr[ETH_ALEN] __aligned(2) ++ = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x03 }; ++ struct ethhdr *ehdr = (struct ethhdr *) rx->skb->data; ++ ++ /* ++ * Allow EAPOL frames to us/the PAE group address regardless ++ * of whether the frame was encrypted or not. ++ */ ++ if (ehdr->h_proto == rx->sdata->control_port_protocol && ++ (ether_addr_equal(ehdr->h_dest, rx->sdata->vif.addr) || ++ ether_addr_equal(ehdr->h_dest, pae_group_addr))) ++ return true; ++ ++ if (ieee80211_802_1x_port_control(rx) || ++ ieee80211_drop_unencrypted(rx, fc)) ++ return false; ++ ++ return true; ++} ++ ++/* ++ * requires that rx->skb is a frame with ethernet header ++ */ ++static void ++ieee80211_deliver_skb(struct ieee80211_rx_data *rx) ++{ ++ struct ieee80211_sub_if_data *sdata = rx->sdata; ++ struct net_device *dev = sdata->dev; ++ struct sk_buff *skb, *xmit_skb; ++ struct ethhdr *ehdr = (struct ethhdr *) rx->skb->data; ++ struct sta_info *dsta; ++ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); ++ ++ skb = rx->skb; ++ xmit_skb = NULL; ++ ++ if ((sdata->vif.type == NL80211_IFTYPE_AP || ++ sdata->vif.type == NL80211_IFTYPE_AP_VLAN) && ++ !(sdata->flags & IEEE80211_SDATA_DONT_BRIDGE_PACKETS) && ++ (status->rx_flags & IEEE80211_RX_RA_MATCH) && ++ (sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->u.vlan.sta)) { ++ if (is_multicast_ether_addr(ehdr->h_dest)) { ++ /* ++ * send multicast frames both to higher layers in ++ * local net stack and back to the wireless medium ++ */ ++ xmit_skb = skb_copy(skb, GFP_ATOMIC); ++ if (!xmit_skb) ++ net_info_ratelimited("%s: failed to clone multicast frame\n", ++ dev->name); ++ } else { ++ dsta = sta_info_get(sdata, skb->data); ++ if (dsta) { ++ /* ++ * The destination station is associated to ++ * this AP (in this VLAN), so send the frame ++ * directly to it and do not pass it to local ++ * net stack. ++ */ ++ xmit_skb = skb; ++ skb = NULL; ++ } ++ } ++ } ++ ++#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS ++ if (skb) { ++ /* 'align' will only take the values 0 or 2 here since all ++ * frames are required to be aligned to 2-byte boundaries ++ * when being passed to mac80211; the code here works just ++ * as well if that isn't true, but mac80211 assumes it can ++ * access fields as 2-byte aligned (e.g. for ether_addr_equal) ++ */ ++ int align; ++ ++ align = (unsigned long)(skb->data + sizeof(struct ethhdr)) & 3; ++ if (align) { ++ if (WARN_ON(skb_headroom(skb) < 3)) { ++ dev_kfree_skb(skb); ++ skb = NULL; ++ } else { ++ u8 *data = skb->data; ++ size_t len = skb_headlen(skb); ++ skb->data -= align; ++ memmove(skb->data, data, len); ++ skb_set_tail_pointer(skb, len); ++ } ++ } ++ } ++#endif ++ ++ if (skb) { ++ /* deliver to local stack */ ++ skb->protocol = eth_type_trans(skb, dev); ++ memset(skb->cb, 0, sizeof(skb->cb)); ++ if (rx->local->napi) ++ napi_gro_receive(rx->local->napi, skb); ++ else ++ netif_receive_skb(skb); ++ } ++ ++ if (xmit_skb) { ++ /* ++ * Send to wireless media and increase priority by 256 to ++ * keep the received priority instead of reclassifying ++ * the frame (see cfg80211_classify8021d). ++ */ ++ xmit_skb->priority += 256; ++ xmit_skb->protocol = htons(ETH_P_802_3); ++ skb_reset_network_header(xmit_skb); ++ skb_reset_mac_header(xmit_skb); ++ dev_queue_xmit(xmit_skb); ++ } ++} ++ ++static ieee80211_rx_result debug_noinline ++ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx) ++{ ++ struct net_device *dev = rx->sdata->dev; ++ struct sk_buff *skb = rx->skb; ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; ++ __le16 fc = hdr->frame_control; ++ struct sk_buff_head frame_list; ++ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); ++ ++ if (unlikely(!ieee80211_is_data(fc))) ++ return RX_CONTINUE; ++ ++ if (unlikely(!ieee80211_is_data_present(fc))) ++ return RX_DROP_MONITOR; ++ ++ if (!(status->rx_flags & IEEE80211_RX_AMSDU)) ++ return RX_CONTINUE; ++ ++ if (ieee80211_has_a4(hdr->frame_control) && ++ rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && ++ !rx->sdata->u.vlan.sta) ++ return RX_DROP_UNUSABLE; ++ ++ if (is_multicast_ether_addr(hdr->addr1) && ++ ((rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && ++ rx->sdata->u.vlan.sta) || ++ (rx->sdata->vif.type == NL80211_IFTYPE_STATION && ++ rx->sdata->u.mgd.use_4addr))) ++ return RX_DROP_UNUSABLE; ++ ++ skb->dev = dev; ++ __skb_queue_head_init(&frame_list); ++ ++ if (skb_linearize(skb)) ++ return RX_DROP_UNUSABLE; ++ ++ ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr, ++ rx->sdata->vif.type, ++ rx->local->hw.extra_tx_headroom, true); ++ ++ while (!skb_queue_empty(&frame_list)) { ++ rx->skb = __skb_dequeue(&frame_list); ++ ++ if (!ieee80211_frame_allowed(rx, fc)) { ++ dev_kfree_skb(rx->skb); ++ continue; ++ } ++ dev->stats.rx_packets++; ++ dev->stats.rx_bytes += rx->skb->len; ++ ++ ieee80211_deliver_skb(rx); ++ } ++ ++ return RX_QUEUED; ++} ++ ++#ifdef CONFIG_MAC80211_MESH ++static ieee80211_rx_result ++ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) ++{ ++ struct ieee80211_hdr *fwd_hdr, *hdr; ++ struct ieee80211_tx_info *info; ++ struct ieee80211s_hdr *mesh_hdr; ++ struct sk_buff *skb = rx->skb, *fwd_skb; ++ struct ieee80211_local *local = rx->local; ++ struct ieee80211_sub_if_data *sdata = rx->sdata; ++ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); ++ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; ++ u16 q, hdrlen; ++ ++ hdr = (struct ieee80211_hdr *) skb->data; ++ hdrlen = ieee80211_hdrlen(hdr->frame_control); ++ ++ /* make sure fixed part of mesh header is there, also checks skb len */ ++ if (!pskb_may_pull(rx->skb, hdrlen + 6)) ++ return RX_DROP_MONITOR; ++ ++ mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); ++ ++ /* make sure full mesh header is there, also checks skb len */ ++ if (!pskb_may_pull(rx->skb, ++ hdrlen + ieee80211_get_mesh_hdrlen(mesh_hdr))) ++ return RX_DROP_MONITOR; ++ ++ /* reload pointers */ ++ hdr = (struct ieee80211_hdr *) skb->data; ++ mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); ++ ++ if (ieee80211_drop_unencrypted(rx, hdr->frame_control)) ++ return RX_DROP_MONITOR; ++ ++ /* frame is in RMC, don't forward */ ++ if (ieee80211_is_data(hdr->frame_control) && ++ is_multicast_ether_addr(hdr->addr1) && ++ mesh_rmc_check(rx->sdata, hdr->addr3, mesh_hdr)) ++ return RX_DROP_MONITOR; ++ ++ if (!ieee80211_is_data(hdr->frame_control) || ++ !(status->rx_flags & IEEE80211_RX_RA_MATCH)) ++ return RX_CONTINUE; ++ ++ if (!mesh_hdr->ttl) ++ return RX_DROP_MONITOR; ++ ++ if (mesh_hdr->flags & MESH_FLAGS_AE) { ++ struct mesh_path *mppath; ++ char *proxied_addr; ++ char *mpp_addr; ++ ++ if (is_multicast_ether_addr(hdr->addr1)) { ++ mpp_addr = hdr->addr3; ++ proxied_addr = mesh_hdr->eaddr1; ++ } else if (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6) { ++ /* has_a4 already checked in ieee80211_rx_mesh_check */ ++ mpp_addr = hdr->addr4; ++ proxied_addr = mesh_hdr->eaddr2; ++ } else { ++ return RX_DROP_MONITOR; ++ } ++ ++ rcu_read_lock(); ++ mppath = mpp_path_lookup(sdata, proxied_addr); ++ if (!mppath) { ++ mpp_path_add(sdata, proxied_addr, mpp_addr); ++ } else { ++ spin_lock_bh(&mppath->state_lock); ++ if (!ether_addr_equal(mppath->mpp, mpp_addr)) ++ memcpy(mppath->mpp, mpp_addr, ETH_ALEN); ++ spin_unlock_bh(&mppath->state_lock); ++ } ++ rcu_read_unlock(); ++ } ++ ++ /* Frame has reached destination. Don't forward */ ++ if (!is_multicast_ether_addr(hdr->addr1) && ++ ether_addr_equal(sdata->vif.addr, hdr->addr3)) ++ return RX_CONTINUE; ++ ++ q = ieee80211_select_queue_80211(sdata, skb, hdr); ++ if (ieee80211_queue_stopped(&local->hw, q)) { ++ IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_congestion); ++ return RX_DROP_MONITOR; ++ } ++ skb_set_queue_mapping(skb, q); ++ ++ if (!--mesh_hdr->ttl) { ++ IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_ttl); ++ goto out; ++ } ++ ++ if (!ifmsh->mshcfg.dot11MeshForwarding) ++ goto out; ++ ++ fwd_skb = skb_copy(skb, GFP_ATOMIC); ++ if (!fwd_skb) { ++ net_info_ratelimited("%s: failed to clone mesh frame\n", ++ sdata->name); ++ goto out; ++ } ++ ++ fwd_hdr = (struct ieee80211_hdr *) fwd_skb->data; ++ fwd_hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_RETRY); ++ info = IEEE80211_SKB_CB(fwd_skb); ++ memset(info, 0, sizeof(*info)); ++ info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; ++ info->control.vif = &rx->sdata->vif; ++ info->control.jiffies = jiffies; ++ if (is_multicast_ether_addr(fwd_hdr->addr1)) { ++ IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_mcast); ++ memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN); ++ /* update power mode indication when forwarding */ ++ ieee80211_mps_set_frame_flags(sdata, NULL, fwd_hdr); ++ } else if (!mesh_nexthop_lookup(sdata, fwd_skb)) { ++ /* mesh power mode flags updated in mesh_nexthop_lookup */ ++ IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_unicast); ++ } else { ++ /* unable to resolve next hop */ ++ mesh_path_error_tx(sdata, ifmsh->mshcfg.element_ttl, ++ fwd_hdr->addr3, 0, ++ WLAN_REASON_MESH_PATH_NOFORWARD, ++ fwd_hdr->addr2); ++ IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route); ++ kfree_skb(fwd_skb); ++ return RX_DROP_MONITOR; ++ } ++ ++ IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_frames); ++ ieee80211_add_pending_skb(local, fwd_skb); ++ out: ++ if (is_multicast_ether_addr(hdr->addr1) || ++ sdata->dev->flags & IFF_PROMISC) ++ return RX_CONTINUE; ++ else ++ return RX_DROP_MONITOR; ++} ++#endif ++ ++static ieee80211_rx_result debug_noinline ++ieee80211_rx_h_data(struct ieee80211_rx_data *rx) ++{ ++ struct ieee80211_sub_if_data *sdata = rx->sdata; ++ struct ieee80211_local *local = rx->local; ++ struct net_device *dev = sdata->dev; ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; ++ __le16 fc = hdr->frame_control; ++ bool port_control; ++ int err; ++ ++ if (unlikely(!ieee80211_is_data(hdr->frame_control))) ++ return RX_CONTINUE; ++ ++ if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) ++ return RX_DROP_MONITOR; ++ ++ /* ++ * Send unexpected-4addr-frame event to hostapd. For older versions, ++ * also drop the frame to cooked monitor interfaces. ++ */ ++ if (ieee80211_has_a4(hdr->frame_control) && ++ sdata->vif.type == NL80211_IFTYPE_AP) { ++ if (rx->sta && ++ !test_and_set_sta_flag(rx->sta, WLAN_STA_4ADDR_EVENT)) ++ cfg80211_rx_unexpected_4addr_frame( ++ rx->sdata->dev, rx->sta->sta.addr, GFP_ATOMIC); ++ return RX_DROP_MONITOR; ++ } ++ ++ err = __ieee80211_data_to_8023(rx, &port_control); ++ if (unlikely(err)) ++ return RX_DROP_UNUSABLE; ++ ++ if (!ieee80211_frame_allowed(rx, fc)) ++ return RX_DROP_MONITOR; ++ ++ if (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && ++ unlikely(port_control) && sdata->bss) { ++ sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, ++ u.ap); ++ dev = sdata->dev; ++ rx->sdata = sdata; ++ } ++ ++ rx->skb->dev = dev; ++ ++ dev->stats.rx_packets++; ++ dev->stats.rx_bytes += rx->skb->len; ++ ++ if (local->ps_sdata && local->hw.conf.dynamic_ps_timeout > 0 && ++ !is_multicast_ether_addr( ++ ((struct ethhdr *)rx->skb->data)->h_dest) && ++ (!local->scanning && ++ !test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))) { ++ mod_timer(&local->dynamic_ps_timer, jiffies + ++ msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout)); ++ } ++ ++ ieee80211_deliver_skb(rx); ++ ++ return RX_QUEUED; ++} ++ ++static ieee80211_rx_result debug_noinline ++ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames) ++{ ++ struct sk_buff *skb = rx->skb; ++ struct ieee80211_bar *bar = (struct ieee80211_bar *)skb->data; ++ struct tid_ampdu_rx *tid_agg_rx; ++ u16 start_seq_num; ++ u16 tid; ++ ++ if (likely(!ieee80211_is_ctl(bar->frame_control))) ++ return RX_CONTINUE; ++ ++ if (ieee80211_is_back_req(bar->frame_control)) { ++ struct { ++ __le16 control, start_seq_num; ++ } __packed bar_data; ++ ++ if (!rx->sta) ++ return RX_DROP_MONITOR; ++ ++ if (skb_copy_bits(skb, offsetof(struct ieee80211_bar, control), ++ &bar_data, sizeof(bar_data))) ++ return RX_DROP_MONITOR; ++ ++ tid = le16_to_cpu(bar_data.control) >> 12; ++ ++ tid_agg_rx = rcu_dereference(rx->sta->ampdu_mlme.tid_rx[tid]); ++ if (!tid_agg_rx) ++ return RX_DROP_MONITOR; ++ ++ start_seq_num = le16_to_cpu(bar_data.start_seq_num) >> 4; ++ ++ /* reset session timer */ ++ if (tid_agg_rx->timeout) ++ mod_timer(&tid_agg_rx->session_timer, ++ TU_TO_EXP_TIME(tid_agg_rx->timeout)); ++ ++ spin_lock(&tid_agg_rx->reorder_lock); ++ /* release stored frames up to start of BAR */ ++ ieee80211_release_reorder_frames(rx->sdata, tid_agg_rx, ++ start_seq_num, frames); ++ spin_unlock(&tid_agg_rx->reorder_lock); ++ ++ kfree_skb(skb); ++ return RX_QUEUED; ++ } ++ ++ /* ++ * After this point, we only want management frames, ++ * so we can drop all remaining control frames to ++ * cooked monitor interfaces. ++ */ ++ return RX_DROP_MONITOR; ++} ++ ++static void ieee80211_process_sa_query_req(struct ieee80211_sub_if_data *sdata, ++ struct ieee80211_mgmt *mgmt, ++ size_t len) ++{ ++ struct ieee80211_local *local = sdata->local; ++ struct sk_buff *skb; ++ struct ieee80211_mgmt *resp; ++ ++ if (!ether_addr_equal(mgmt->da, sdata->vif.addr)) { ++ /* Not to own unicast address */ ++ return; ++ } ++ ++ if (!ether_addr_equal(mgmt->sa, sdata->u.mgd.bssid) || ++ !ether_addr_equal(mgmt->bssid, sdata->u.mgd.bssid)) { ++ /* Not from the current AP or not associated yet. */ ++ return; ++ } ++ ++ if (len < 24 + 1 + sizeof(resp->u.action.u.sa_query)) { ++ /* Too short SA Query request frame */ ++ return; ++ } ++ ++ skb = dev_alloc_skb(sizeof(*resp) + local->hw.extra_tx_headroom); ++ if (skb == NULL) ++ return; ++ ++ skb_reserve(skb, local->hw.extra_tx_headroom); ++ resp = (struct ieee80211_mgmt *) skb_put(skb, 24); ++ memset(resp, 0, 24); ++ memcpy(resp->da, mgmt->sa, ETH_ALEN); ++ memcpy(resp->sa, sdata->vif.addr, ETH_ALEN); ++ memcpy(resp->bssid, sdata->u.mgd.bssid, ETH_ALEN); ++ resp->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | ++ IEEE80211_STYPE_ACTION); ++ skb_put(skb, 1 + sizeof(resp->u.action.u.sa_query)); ++ resp->u.action.category = WLAN_CATEGORY_SA_QUERY; ++ resp->u.action.u.sa_query.action = WLAN_ACTION_SA_QUERY_RESPONSE; ++ memcpy(resp->u.action.u.sa_query.trans_id, ++ mgmt->u.action.u.sa_query.trans_id, ++ WLAN_SA_QUERY_TR_ID_LEN); ++ ++ ieee80211_tx_skb(sdata, skb); ++} ++ ++static ieee80211_rx_result debug_noinline ++ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx) ++{ ++ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data; ++ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); ++ ++ /* ++ * From here on, look only at management frames. ++ * Data and control frames are already handled, ++ * and unknown (reserved) frames are useless. ++ */ ++ if (rx->skb->len < 24) ++ return RX_DROP_MONITOR; ++ ++ if (!ieee80211_is_mgmt(mgmt->frame_control)) ++ return RX_DROP_MONITOR; ++ ++ if (rx->sdata->vif.type == NL80211_IFTYPE_AP && ++ ieee80211_is_beacon(mgmt->frame_control) && ++ !(rx->flags & IEEE80211_RX_BEACON_REPORTED)) { ++ int sig = 0; ++ ++ if (rx->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) ++ sig = status->signal; ++ ++ cfg80211_report_obss_beacon(rx->local->hw.wiphy, ++ rx->skb->data, rx->skb->len, ++ status->freq, sig); ++ rx->flags |= IEEE80211_RX_BEACON_REPORTED; ++ } ++ ++ if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) ++ return RX_DROP_MONITOR; ++ ++ if (ieee80211_drop_unencrypted_mgmt(rx)) ++ return RX_DROP_UNUSABLE; ++ ++ return RX_CONTINUE; ++} ++ ++static ieee80211_rx_result debug_noinline ++ieee80211_rx_h_action(struct ieee80211_rx_data *rx) ++{ ++ struct ieee80211_local *local = rx->local; ++ struct ieee80211_sub_if_data *sdata = rx->sdata; ++ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data; ++ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); ++ int len = rx->skb->len; ++ ++ if (!ieee80211_is_action(mgmt->frame_control)) ++ return RX_CONTINUE; ++ ++ /* drop too small frames */ ++ if (len < IEEE80211_MIN_ACTION_SIZE) ++ return RX_DROP_UNUSABLE; ++ ++ if (!rx->sta && mgmt->u.action.category != WLAN_CATEGORY_PUBLIC && ++ mgmt->u.action.category != WLAN_CATEGORY_SELF_PROTECTED && ++ mgmt->u.action.category != WLAN_CATEGORY_SPECTRUM_MGMT) ++ return RX_DROP_UNUSABLE; ++ ++ if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) ++ return RX_DROP_UNUSABLE; ++ ++ switch (mgmt->u.action.category) { ++ case WLAN_CATEGORY_HT: ++ /* reject HT action frames from stations not supporting HT */ ++ if (!rx->sta->sta.ht_cap.ht_supported) ++ goto invalid; ++ ++ if (sdata->vif.type != NL80211_IFTYPE_STATION && ++ sdata->vif.type != NL80211_IFTYPE_MESH_POINT && ++ sdata->vif.type != NL80211_IFTYPE_AP_VLAN && ++ sdata->vif.type != NL80211_IFTYPE_AP && ++ sdata->vif.type != NL80211_IFTYPE_ADHOC) ++ break; ++ ++ /* verify action & smps_control/chanwidth are present */ ++ if (len < IEEE80211_MIN_ACTION_SIZE + 2) ++ goto invalid; ++ ++ switch (mgmt->u.action.u.ht_smps.action) { ++ case WLAN_HT_ACTION_SMPS: { ++ struct ieee80211_supported_band *sband; ++ enum ieee80211_smps_mode smps_mode; ++ ++ /* convert to HT capability */ ++ switch (mgmt->u.action.u.ht_smps.smps_control) { ++ case WLAN_HT_SMPS_CONTROL_DISABLED: ++ smps_mode = IEEE80211_SMPS_OFF; ++ break; ++ case WLAN_HT_SMPS_CONTROL_STATIC: ++ smps_mode = IEEE80211_SMPS_STATIC; ++ break; ++ case WLAN_HT_SMPS_CONTROL_DYNAMIC: ++ smps_mode = IEEE80211_SMPS_DYNAMIC; ++ break; ++ default: ++ goto invalid; ++ } ++ ++ /* if no change do nothing */ ++ if (rx->sta->sta.smps_mode == smps_mode) ++ goto handled; ++ rx->sta->sta.smps_mode = smps_mode; ++ ++ sband = rx->local->hw.wiphy->bands[status->band]; ++ ++ rate_control_rate_update(local, sband, rx->sta, ++ IEEE80211_RC_SMPS_CHANGED); ++ goto handled; ++ } ++ case WLAN_HT_ACTION_NOTIFY_CHANWIDTH: { ++ struct ieee80211_supported_band *sband; ++ u8 chanwidth = mgmt->u.action.u.ht_notify_cw.chanwidth; ++ enum ieee80211_sta_rx_bandwidth new_bw; ++ ++ /* If it doesn't support 40 MHz it can't change ... */ ++ if (!(rx->sta->sta.ht_cap.cap & ++ IEEE80211_HT_CAP_SUP_WIDTH_20_40)) ++ goto handled; ++ ++ if (chanwidth == IEEE80211_HT_CHANWIDTH_20MHZ) ++ new_bw = IEEE80211_STA_RX_BW_20; ++ else ++ new_bw = ieee80211_sta_cur_vht_bw(rx->sta); ++ ++ if (rx->sta->sta.bandwidth == new_bw) ++ goto handled; ++ ++ sband = rx->local->hw.wiphy->bands[status->band]; ++ ++ rate_control_rate_update(local, sband, rx->sta, ++ IEEE80211_RC_BW_CHANGED); ++ goto handled; ++ } ++ default: ++ goto invalid; ++ } ++ ++ break; ++ case WLAN_CATEGORY_PUBLIC: ++ if (len < IEEE80211_MIN_ACTION_SIZE + 1) ++ goto invalid; ++ if (sdata->vif.type != NL80211_IFTYPE_STATION) ++ break; ++ if (!rx->sta) ++ break; ++ if (!ether_addr_equal(mgmt->bssid, sdata->u.mgd.bssid)) ++ break; ++ if (mgmt->u.action.u.ext_chan_switch.action_code != ++ WLAN_PUB_ACTION_EXT_CHANSW_ANN) ++ break; ++ if (len < offsetof(struct ieee80211_mgmt, ++ u.action.u.ext_chan_switch.variable)) ++ goto invalid; ++ goto queue; ++ case WLAN_CATEGORY_VHT: ++ if (sdata->vif.type != NL80211_IFTYPE_STATION && ++ sdata->vif.type != NL80211_IFTYPE_MESH_POINT && ++ sdata->vif.type != NL80211_IFTYPE_AP_VLAN && ++ sdata->vif.type != NL80211_IFTYPE_AP && ++ sdata->vif.type != NL80211_IFTYPE_ADHOC) ++ break; ++ ++ /* verify action code is present */ ++ if (len < IEEE80211_MIN_ACTION_SIZE + 1) ++ goto invalid; ++ ++ switch (mgmt->u.action.u.vht_opmode_notif.action_code) { ++ case WLAN_VHT_ACTION_OPMODE_NOTIF: { ++ u8 opmode; ++ ++ /* verify opmode is present */ ++ if (len < IEEE80211_MIN_ACTION_SIZE + 2) ++ goto invalid; ++ ++ opmode = mgmt->u.action.u.vht_opmode_notif.operating_mode; ++ ++ ieee80211_vht_handle_opmode(rx->sdata, rx->sta, ++ opmode, status->band, ++ false); ++ goto handled; ++ } ++ default: ++ break; ++ } ++ break; ++ case WLAN_CATEGORY_BACK: ++ if (sdata->vif.type != NL80211_IFTYPE_STATION && ++ sdata->vif.type != NL80211_IFTYPE_MESH_POINT && ++ sdata->vif.type != NL80211_IFTYPE_AP_VLAN && ++ sdata->vif.type != NL80211_IFTYPE_AP && ++ sdata->vif.type != NL80211_IFTYPE_ADHOC) ++ break; ++ ++ /* verify action_code is present */ ++ if (len < IEEE80211_MIN_ACTION_SIZE + 1) ++ break; ++ ++ switch (mgmt->u.action.u.addba_req.action_code) { ++ case WLAN_ACTION_ADDBA_REQ: ++ if (len < (IEEE80211_MIN_ACTION_SIZE + ++ sizeof(mgmt->u.action.u.addba_req))) ++ goto invalid; ++ break; ++ case WLAN_ACTION_ADDBA_RESP: ++ if (len < (IEEE80211_MIN_ACTION_SIZE + ++ sizeof(mgmt->u.action.u.addba_resp))) ++ goto invalid; ++ break; ++ case WLAN_ACTION_DELBA: ++ if (len < (IEEE80211_MIN_ACTION_SIZE + ++ sizeof(mgmt->u.action.u.delba))) ++ goto invalid; ++ break; ++ default: ++ goto invalid; ++ } ++ ++ goto queue; ++ case WLAN_CATEGORY_SPECTRUM_MGMT: ++ /* verify action_code is present */ ++ if (len < IEEE80211_MIN_ACTION_SIZE + 1) ++ break; ++ ++ switch (mgmt->u.action.u.measurement.action_code) { ++ case WLAN_ACTION_SPCT_MSR_REQ: ++ if (status->band != IEEE80211_BAND_5GHZ) ++ break; ++ ++ if (len < (IEEE80211_MIN_ACTION_SIZE + ++ sizeof(mgmt->u.action.u.measurement))) ++ break; ++ ++ if (sdata->vif.type != NL80211_IFTYPE_STATION) ++ break; ++ ++ ieee80211_process_measurement_req(sdata, mgmt, len); ++ goto handled; ++ case WLAN_ACTION_SPCT_CHL_SWITCH: { ++ u8 *bssid; ++ if (len < (IEEE80211_MIN_ACTION_SIZE + ++ sizeof(mgmt->u.action.u.chan_switch))) ++ break; ++ ++ if (sdata->vif.type != NL80211_IFTYPE_STATION && ++ sdata->vif.type != NL80211_IFTYPE_ADHOC && ++ sdata->vif.type != NL80211_IFTYPE_MESH_POINT) ++ break; ++ ++ if (sdata->vif.type == NL80211_IFTYPE_STATION) ++ bssid = sdata->u.mgd.bssid; ++ else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) ++ bssid = sdata->u.ibss.bssid; ++ else if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT) ++ bssid = mgmt->sa; ++ else ++ break; ++ ++ if (!ether_addr_equal(mgmt->bssid, bssid)) ++ break; ++ ++ goto queue; ++ } ++ } ++ break; ++ case WLAN_CATEGORY_SA_QUERY: ++ if (len < (IEEE80211_MIN_ACTION_SIZE + ++ sizeof(mgmt->u.action.u.sa_query))) ++ break; ++ ++ switch (mgmt->u.action.u.sa_query.action) { ++ case WLAN_ACTION_SA_QUERY_REQUEST: ++ if (sdata->vif.type != NL80211_IFTYPE_STATION) ++ break; ++ ieee80211_process_sa_query_req(sdata, mgmt, len); ++ goto handled; ++ } ++ break; ++ case WLAN_CATEGORY_SELF_PROTECTED: ++ if (len < (IEEE80211_MIN_ACTION_SIZE + ++ sizeof(mgmt->u.action.u.self_prot.action_code))) ++ break; ++ ++ switch (mgmt->u.action.u.self_prot.action_code) { ++ case WLAN_SP_MESH_PEERING_OPEN: ++ case WLAN_SP_MESH_PEERING_CLOSE: ++ case WLAN_SP_MESH_PEERING_CONFIRM: ++ if (!ieee80211_vif_is_mesh(&sdata->vif)) ++ goto invalid; ++ if (sdata->u.mesh.user_mpm) ++ /* userspace handles this frame */ ++ break; ++ goto queue; ++ case WLAN_SP_MGK_INFORM: ++ case WLAN_SP_MGK_ACK: ++ if (!ieee80211_vif_is_mesh(&sdata->vif)) ++ goto invalid; ++ break; ++ } ++ break; ++ case WLAN_CATEGORY_MESH_ACTION: ++ if (len < (IEEE80211_MIN_ACTION_SIZE + ++ sizeof(mgmt->u.action.u.mesh_action.action_code))) ++ break; ++ ++ if (!ieee80211_vif_is_mesh(&sdata->vif)) ++ break; ++ if (mesh_action_is_path_sel(mgmt) && ++ !mesh_path_sel_is_hwmp(sdata)) ++ break; ++ goto queue; ++ } ++ ++ return RX_CONTINUE; ++ ++ invalid: ++ status->rx_flags |= IEEE80211_RX_MALFORMED_ACTION_FRM; ++ /* will return in the next handlers */ ++ return RX_CONTINUE; ++ ++ handled: ++ if (rx->sta) ++ rx->sta->rx_packets++; ++ dev_kfree_skb(rx->skb); ++ return RX_QUEUED; ++ ++ queue: ++ rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME; ++ skb_queue_tail(&sdata->skb_queue, rx->skb); ++ ieee80211_queue_work(&local->hw, &sdata->work); ++ if (rx->sta) ++ rx->sta->rx_packets++; ++ return RX_QUEUED; ++} ++ ++static ieee80211_rx_result debug_noinline ++ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx) ++{ ++ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); ++ int sig = 0; ++ ++ /* skip known-bad action frames and return them in the next handler */ ++ if (status->rx_flags & IEEE80211_RX_MALFORMED_ACTION_FRM) ++ return RX_CONTINUE; ++ ++ /* ++ * Getting here means the kernel doesn't know how to handle ++ * it, but maybe userspace does ... include returned frames ++ * so userspace can register for those to know whether ones ++ * it transmitted were processed or returned. ++ */ ++ ++ if (rx->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) ++ sig = status->signal; ++ ++ if (cfg80211_rx_mgmt(&rx->sdata->wdev, status->freq, sig, ++ rx->skb->data, rx->skb->len, 0)) { ++ if (rx->sta) ++ rx->sta->rx_packets++; ++ dev_kfree_skb(rx->skb); ++ return RX_QUEUED; ++ } ++ ++ return RX_CONTINUE; ++} ++ ++static ieee80211_rx_result debug_noinline ++ieee80211_rx_h_action_return(struct ieee80211_rx_data *rx) ++{ ++ struct ieee80211_local *local = rx->local; ++ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data; ++ struct sk_buff *nskb; ++ struct ieee80211_sub_if_data *sdata = rx->sdata; ++ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); ++ ++ if (!ieee80211_is_action(mgmt->frame_control)) ++ return RX_CONTINUE; ++ ++ /* ++ * For AP mode, hostapd is responsible for handling any action ++ * frames that we didn't handle, including returning unknown ++ * ones. For all other modes we will return them to the sender, ++ * setting the 0x80 bit in the action category, as required by ++ * 802.11-2012 9.24.4. ++ * Newer versions of hostapd shall also use the management frame ++ * registration mechanisms, but older ones still use cooked ++ * monitor interfaces so push all frames there. ++ */ ++ if (!(status->rx_flags & IEEE80211_RX_MALFORMED_ACTION_FRM) && ++ (sdata->vif.type == NL80211_IFTYPE_AP || ++ sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) ++ return RX_DROP_MONITOR; ++ ++ if (is_multicast_ether_addr(mgmt->da)) ++ return RX_DROP_MONITOR; ++ ++ /* do not return rejected action frames */ ++ if (mgmt->u.action.category & 0x80) ++ return RX_DROP_UNUSABLE; ++ ++ nskb = skb_copy_expand(rx->skb, local->hw.extra_tx_headroom, 0, ++ GFP_ATOMIC); ++ if (nskb) { ++ struct ieee80211_mgmt *nmgmt = (void *)nskb->data; ++ ++ nmgmt->u.action.category |= 0x80; ++ memcpy(nmgmt->da, nmgmt->sa, ETH_ALEN); ++ memcpy(nmgmt->sa, rx->sdata->vif.addr, ETH_ALEN); ++ ++ memset(nskb->cb, 0, sizeof(nskb->cb)); ++ ++ if (rx->sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) { ++ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(nskb); ++ ++ info->flags = IEEE80211_TX_CTL_TX_OFFCHAN | ++ IEEE80211_TX_INTFL_OFFCHAN_TX_OK | ++ IEEE80211_TX_CTL_NO_CCK_RATE; ++ if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) ++ info->hw_queue = ++ local->hw.offchannel_tx_hw_queue; ++ } ++ ++ __ieee80211_tx_skb_tid_band(rx->sdata, nskb, 7, ++ status->band); ++ } ++ dev_kfree_skb(rx->skb); ++ return RX_QUEUED; ++} ++ ++static ieee80211_rx_result debug_noinline ++ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) ++{ ++ struct ieee80211_sub_if_data *sdata = rx->sdata; ++ struct ieee80211_mgmt *mgmt = (void *)rx->skb->data; ++ __le16 stype; ++ ++ stype = mgmt->frame_control & cpu_to_le16(IEEE80211_FCTL_STYPE); ++ ++ if (!ieee80211_vif_is_mesh(&sdata->vif) && ++ sdata->vif.type != NL80211_IFTYPE_ADHOC && ++ sdata->vif.type != NL80211_IFTYPE_STATION) ++ return RX_DROP_MONITOR; ++ ++ switch (stype) { ++ case cpu_to_le16(IEEE80211_STYPE_AUTH): ++ case cpu_to_le16(IEEE80211_STYPE_BEACON): ++ case cpu_to_le16(IEEE80211_STYPE_PROBE_RESP): ++ /* process for all: mesh, mlme, ibss */ ++ break; ++ case cpu_to_le16(IEEE80211_STYPE_ASSOC_RESP): ++ case cpu_to_le16(IEEE80211_STYPE_REASSOC_RESP): ++ case cpu_to_le16(IEEE80211_STYPE_DEAUTH): ++ case cpu_to_le16(IEEE80211_STYPE_DISASSOC): ++ if (is_multicast_ether_addr(mgmt->da) && ++ !is_broadcast_ether_addr(mgmt->da)) ++ return RX_DROP_MONITOR; ++ ++ /* process only for station */ ++ if (sdata->vif.type != NL80211_IFTYPE_STATION) ++ return RX_DROP_MONITOR; ++ break; ++ case cpu_to_le16(IEEE80211_STYPE_PROBE_REQ): ++ /* process only for ibss and mesh */ ++ if (sdata->vif.type != NL80211_IFTYPE_ADHOC && ++ sdata->vif.type != NL80211_IFTYPE_MESH_POINT) ++ return RX_DROP_MONITOR; ++ break; ++ default: ++ return RX_DROP_MONITOR; ++ } ++ ++ /* queue up frame and kick off work to process it */ ++ rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME; ++ skb_queue_tail(&sdata->skb_queue, rx->skb); ++ ieee80211_queue_work(&rx->local->hw, &sdata->work); ++ if (rx->sta) ++ rx->sta->rx_packets++; ++ ++ return RX_QUEUED; ++} ++ ++/* TODO: use IEEE80211_RX_FRAGMENTED */ ++static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx, ++ struct ieee80211_rate *rate) ++{ ++ struct ieee80211_sub_if_data *sdata; ++ struct ieee80211_local *local = rx->local; ++ struct sk_buff *skb = rx->skb, *skb2; ++ struct net_device *prev_dev = NULL; ++ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); ++ int needed_headroom; ++ ++ /* ++ * If cooked monitor has been processed already, then ++ * don't do it again. If not, set the flag. ++ */ ++ if (rx->flags & IEEE80211_RX_CMNTR) ++ goto out_free_skb; ++ rx->flags |= IEEE80211_RX_CMNTR; ++ ++ /* If there are no cooked monitor interfaces, just free the SKB */ ++ if (!local->cooked_mntrs) ++ goto out_free_skb; ++ ++ /* room for the radiotap header based on driver features */ ++ needed_headroom = ieee80211_rx_radiotap_space(local, status); ++ ++ if (skb_headroom(skb) < needed_headroom && ++ pskb_expand_head(skb, needed_headroom, 0, GFP_ATOMIC)) ++ goto out_free_skb; ++ ++ /* prepend radiotap information */ ++ ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom, ++ false); ++ ++ skb_set_mac_header(skb, 0); ++ skb->ip_summed = CHECKSUM_UNNECESSARY; ++ skb->pkt_type = PACKET_OTHERHOST; ++ skb->protocol = htons(ETH_P_802_2); ++ ++ list_for_each_entry_rcu(sdata, &local->interfaces, list) { ++ if (!ieee80211_sdata_running(sdata)) ++ continue; ++ ++ if (sdata->vif.type != NL80211_IFTYPE_MONITOR || ++ !(sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES)) ++ continue; ++ ++ if (prev_dev) { ++ skb2 = skb_clone(skb, GFP_ATOMIC); ++ if (skb2) { ++ skb2->dev = prev_dev; ++ netif_receive_skb(skb2); ++ } ++ } ++ ++ prev_dev = sdata->dev; ++ sdata->dev->stats.rx_packets++; ++ sdata->dev->stats.rx_bytes += skb->len; ++ } ++ ++ if (prev_dev) { ++ skb->dev = prev_dev; ++ netif_receive_skb(skb); ++ return; ++ } ++ ++ out_free_skb: ++ dev_kfree_skb(skb); ++} ++ ++static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx, ++ ieee80211_rx_result res) ++{ ++ switch (res) { ++ case RX_DROP_MONITOR: ++ I802_DEBUG_INC(rx->sdata->local->rx_handlers_drop); ++ if (rx->sta) ++ rx->sta->rx_dropped++; ++ /* fall through */ ++ case RX_CONTINUE: { ++ struct ieee80211_rate *rate = NULL; ++ struct ieee80211_supported_band *sband; ++ struct ieee80211_rx_status *status; ++ ++ status = IEEE80211_SKB_RXCB((rx->skb)); ++ ++ sband = rx->local->hw.wiphy->bands[status->band]; ++ if (!(status->flag & RX_FLAG_HT) && ++ !(status->flag & RX_FLAG_VHT)) ++ rate = &sband->bitrates[status->rate_idx]; ++ ++ ieee80211_rx_cooked_monitor(rx, rate); ++ break; ++ } ++ case RX_DROP_UNUSABLE: ++ I802_DEBUG_INC(rx->sdata->local->rx_handlers_drop); ++ if (rx->sta) ++ rx->sta->rx_dropped++; ++ dev_kfree_skb(rx->skb); ++ break; ++ case RX_QUEUED: ++ I802_DEBUG_INC(rx->sdata->local->rx_handlers_queued); ++ break; ++ } ++} ++ ++static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx, ++ struct sk_buff_head *frames) ++{ ++ ieee80211_rx_result res = RX_DROP_MONITOR; ++ struct sk_buff *skb; ++ ++#define CALL_RXH(rxh) \ ++ do { \ ++ res = rxh(rx); \ ++ if (res != RX_CONTINUE) \ ++ goto rxh_next; \ ++ } while (0); ++ ++ spin_lock_bh(&rx->local->rx_path_lock); ++ ++ while ((skb = __skb_dequeue(frames))) { ++ /* ++ * all the other fields are valid across frames ++ * that belong to an aMPDU since they are on the ++ * same TID from the same station ++ */ ++ rx->skb = skb; ++ ++ CALL_RXH(ieee80211_rx_h_check_more_data) ++ CALL_RXH(ieee80211_rx_h_uapsd_and_pspoll) ++ CALL_RXH(ieee80211_rx_h_sta_process) ++ CALL_RXH(ieee80211_rx_h_decrypt) ++ CALL_RXH(ieee80211_rx_h_defragment) ++ CALL_RXH(ieee80211_rx_h_michael_mic_verify) ++ /* must be after MMIC verify so header is counted in MPDU mic */ ++#ifdef CONFIG_MAC80211_MESH ++ if (ieee80211_vif_is_mesh(&rx->sdata->vif)) ++ CALL_RXH(ieee80211_rx_h_mesh_fwding); ++#endif ++ CALL_RXH(ieee80211_rx_h_amsdu) ++ CALL_RXH(ieee80211_rx_h_data) ++ ++ /* special treatment -- needs the queue */ ++ res = ieee80211_rx_h_ctrl(rx, frames); ++ if (res != RX_CONTINUE) ++ goto rxh_next; ++ ++ CALL_RXH(ieee80211_rx_h_mgmt_check) ++ CALL_RXH(ieee80211_rx_h_action) ++ CALL_RXH(ieee80211_rx_h_userspace_mgmt) ++ CALL_RXH(ieee80211_rx_h_action_return) ++ CALL_RXH(ieee80211_rx_h_mgmt) ++ ++ rxh_next: ++ ieee80211_rx_handlers_result(rx, res); ++ ++#undef CALL_RXH ++ } ++ ++ spin_unlock_bh(&rx->local->rx_path_lock); ++} ++ ++static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx) ++{ ++ struct sk_buff_head reorder_release; ++ ieee80211_rx_result res = RX_DROP_MONITOR; ++ ++ __skb_queue_head_init(&reorder_release); ++ ++#define CALL_RXH(rxh) \ ++ do { \ ++ res = rxh(rx); \ ++ if (res != RX_CONTINUE) \ ++ goto rxh_next; \ ++ } while (0); ++ ++ CALL_RXH(ieee80211_rx_h_check) ++ ++ ieee80211_rx_reorder_ampdu(rx, &reorder_release); ++ ++ ieee80211_rx_handlers(rx, &reorder_release); ++ return; ++ ++ rxh_next: ++ ieee80211_rx_handlers_result(rx, res); ++ ++#undef CALL_RXH ++} ++ ++/* ++ * This function makes calls into the RX path, therefore ++ * it has to be invoked under RCU read lock. ++ */ ++void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid) ++{ ++ struct sk_buff_head frames; ++ struct ieee80211_rx_data rx = { ++ .sta = sta, ++ .sdata = sta->sdata, ++ .local = sta->local, ++ /* This is OK -- must be QoS data frame */ ++ .security_idx = tid, ++ .seqno_idx = tid, ++ .flags = 0, ++ }; ++ struct tid_ampdu_rx *tid_agg_rx; ++ ++ tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]); ++ if (!tid_agg_rx) ++ return; ++ ++ __skb_queue_head_init(&frames); ++ ++ spin_lock(&tid_agg_rx->reorder_lock); ++ ieee80211_sta_reorder_release(sta->sdata, tid_agg_rx, &frames); ++ spin_unlock(&tid_agg_rx->reorder_lock); ++ ++ ieee80211_rx_handlers(&rx, &frames); ++} ++ ++/* main receive path */ ++ ++static bool prepare_for_handlers(struct ieee80211_rx_data *rx, ++ struct ieee80211_hdr *hdr) ++{ ++ struct ieee80211_sub_if_data *sdata = rx->sdata; ++ struct sk_buff *skb = rx->skb; ++ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); ++ u8 *bssid = ieee80211_get_bssid(hdr, skb->len, sdata->vif.type); ++ int multicast = is_multicast_ether_addr(hdr->addr1); ++ ++ switch (sdata->vif.type) { ++ case NL80211_IFTYPE_STATION: ++ if (!bssid && !sdata->u.mgd.use_4addr) ++ return false; ++ if (!multicast && ++ !ether_addr_equal(sdata->vif.addr, hdr->addr1)) { ++ if (!(sdata->dev->flags & IFF_PROMISC) || ++ sdata->u.mgd.use_4addr) ++ return false; ++ status->rx_flags &= ~IEEE80211_RX_RA_MATCH; ++ } ++ break; ++ case NL80211_IFTYPE_ADHOC: ++ if (!bssid) ++ return false; ++ if (ether_addr_equal(sdata->vif.addr, hdr->addr2) || ++ ether_addr_equal(sdata->u.ibss.bssid, hdr->addr2)) ++ return false; ++ if (ieee80211_is_beacon(hdr->frame_control)) { ++ return true; ++ } else if (!ieee80211_bssid_match(bssid, sdata->u.ibss.bssid)) { ++ return false; ++ } else if (!multicast && ++ !ether_addr_equal(sdata->vif.addr, hdr->addr1)) { ++ if (!(sdata->dev->flags & IFF_PROMISC)) ++ return false; ++ status->rx_flags &= ~IEEE80211_RX_RA_MATCH; ++ } else if (!rx->sta) { ++ int rate_idx; ++ if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT)) ++ rate_idx = 0; /* TODO: HT/VHT rates */ ++ else ++ rate_idx = status->rate_idx; ++ ieee80211_ibss_rx_no_sta(sdata, bssid, hdr->addr2, ++ BIT(rate_idx)); ++ } ++ break; ++ case NL80211_IFTYPE_MESH_POINT: ++ if (!multicast && ++ !ether_addr_equal(sdata->vif.addr, hdr->addr1)) { ++ if (!(sdata->dev->flags & IFF_PROMISC)) ++ return false; ++ ++ status->rx_flags &= ~IEEE80211_RX_RA_MATCH; ++ } ++ break; ++ case NL80211_IFTYPE_AP_VLAN: ++ case NL80211_IFTYPE_AP: ++ if (!bssid) { ++ if (!ether_addr_equal(sdata->vif.addr, hdr->addr1)) ++ return false; ++ } else if (!ieee80211_bssid_match(bssid, sdata->vif.addr)) { ++ /* ++ * Accept public action frames even when the ++ * BSSID doesn't match, this is used for P2P ++ * and location updates. Note that mac80211 ++ * itself never looks at these frames. ++ */ ++ if (!multicast && ++ !ether_addr_equal(sdata->vif.addr, hdr->addr1)) ++ return false; ++ if (ieee80211_is_public_action(hdr, skb->len)) ++ return true; ++ if (!ieee80211_is_beacon(hdr->frame_control)) ++ return false; ++ status->rx_flags &= ~IEEE80211_RX_RA_MATCH; ++ } else if (!ieee80211_has_tods(hdr->frame_control)) { ++ /* ignore data frames to TDLS-peers */ ++ if (ieee80211_is_data(hdr->frame_control)) ++ return false; ++ /* ignore action frames to TDLS-peers */ ++ if (ieee80211_is_action(hdr->frame_control) && ++ !ether_addr_equal(bssid, hdr->addr1)) ++ return false; ++ } ++ break; ++ case NL80211_IFTYPE_WDS: ++ if (bssid || !ieee80211_is_data(hdr->frame_control)) ++ return false; ++ if (!ether_addr_equal(sdata->u.wds.remote_addr, hdr->addr2)) ++ return false; ++ break; ++ case NL80211_IFTYPE_P2P_DEVICE: ++ if (!ieee80211_is_public_action(hdr, skb->len) && ++ !ieee80211_is_probe_req(hdr->frame_control) && ++ !ieee80211_is_probe_resp(hdr->frame_control) && ++ !ieee80211_is_beacon(hdr->frame_control)) ++ return false; ++ if (!ether_addr_equal(sdata->vif.addr, hdr->addr1) && ++ !multicast) ++ status->rx_flags &= ~IEEE80211_RX_RA_MATCH; ++ break; ++ default: ++ /* should never get here */ ++ WARN_ON_ONCE(1); ++ break; ++ } ++ ++ return true; ++} ++ ++/* ++ * This function returns whether or not the SKB ++ * was destined for RX processing or not, which, ++ * if consume is true, is equivalent to whether ++ * or not the skb was consumed. ++ */ ++static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx, ++ struct sk_buff *skb, bool consume) ++{ ++ struct ieee80211_local *local = rx->local; ++ struct ieee80211_sub_if_data *sdata = rx->sdata; ++ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); ++ struct ieee80211_hdr *hdr = (void *)skb->data; ++ ++ rx->skb = skb; ++ status->rx_flags |= IEEE80211_RX_RA_MATCH; ++ ++ if (!prepare_for_handlers(rx, hdr)) ++ return false; ++ ++ if (!consume) { ++ skb = skb_copy(skb, GFP_ATOMIC); ++ if (!skb) { ++ if (net_ratelimit()) ++ wiphy_debug(local->hw.wiphy, ++ "failed to copy skb for %s\n", ++ sdata->name); ++ return true; ++ } ++ ++ rx->skb = skb; ++ } ++ ++ ieee80211_invoke_rx_handlers(rx); ++ return true; ++} ++ ++/* ++ * This is the actual Rx frames handler. as it belongs to Rx path it must ++ * be called with rcu_read_lock protection. ++ */ ++static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, ++ struct sk_buff *skb) ++{ ++ struct ieee80211_local *local = hw_to_local(hw); ++ struct ieee80211_sub_if_data *sdata; ++ struct ieee80211_hdr *hdr; ++ __le16 fc; ++ struct ieee80211_rx_data rx; ++ struct ieee80211_sub_if_data *prev; ++ struct sta_info *sta, *tmp, *prev_sta; ++ int err = 0; ++ ++ fc = ((struct ieee80211_hdr *)skb->data)->frame_control; ++ memset(&rx, 0, sizeof(rx)); ++ rx.skb = skb; ++ rx.local = local; ++ ++ if (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc)) ++ local->dot11ReceivedFragmentCount++; ++ ++ if (ieee80211_is_mgmt(fc)) { ++ /* drop frame if too short for header */ ++ if (skb->len < ieee80211_hdrlen(fc)) ++ err = -ENOBUFS; ++ else ++ err = skb_linearize(skb); ++ } else { ++ err = !pskb_may_pull(skb, ieee80211_hdrlen(fc)); ++ } ++ ++ if (err) { ++ dev_kfree_skb(skb); ++ return; ++ } ++ ++ hdr = (struct ieee80211_hdr *)skb->data; ++ ieee80211_parse_qos(&rx); ++ ieee80211_verify_alignment(&rx); ++ ++ if (unlikely(ieee80211_is_probe_resp(hdr->frame_control) || ++ ieee80211_is_beacon(hdr->frame_control))) ++ ieee80211_scan_rx(local, skb); ++ ++ if (ieee80211_is_data(fc)) { ++ prev_sta = NULL; ++ ++ for_each_sta_info(local, hdr->addr2, sta, tmp) { ++ if (!prev_sta) { ++ prev_sta = sta; ++ continue; ++ } ++ ++ rx.sta = prev_sta; ++ rx.sdata = prev_sta->sdata; ++ ieee80211_prepare_and_rx_handle(&rx, skb, false); ++ ++ prev_sta = sta; ++ } ++ ++ if (prev_sta) { ++ rx.sta = prev_sta; ++ rx.sdata = prev_sta->sdata; ++ ++ if (ieee80211_prepare_and_rx_handle(&rx, skb, true)) ++ return; ++ goto out; ++ } ++ } ++ ++ prev = NULL; ++ ++ list_for_each_entry_rcu(sdata, &local->interfaces, list) { ++ if (!ieee80211_sdata_running(sdata)) ++ continue; ++ ++ if (sdata->vif.type == NL80211_IFTYPE_MONITOR || ++ sdata->vif.type == NL80211_IFTYPE_AP_VLAN) ++ continue; ++ ++ /* ++ * frame is destined for this interface, but if it's ++ * not also for the previous one we handle that after ++ * the loop to avoid copying the SKB once too much ++ */ ++ ++ if (!prev) { ++ prev = sdata; ++ continue; ++ } ++ ++ rx.sta = sta_info_get_bss(prev, hdr->addr2); ++ rx.sdata = prev; ++ ieee80211_prepare_and_rx_handle(&rx, skb, false); ++ ++ prev = sdata; ++ } ++ ++ if (prev) { ++ rx.sta = sta_info_get_bss(prev, hdr->addr2); ++ rx.sdata = prev; ++ ++ if (ieee80211_prepare_and_rx_handle(&rx, skb, true)) ++ return; ++ } ++ ++ out: ++ dev_kfree_skb(skb); ++} ++ ++/* ++ * This is the receive path handler. It is called by a low level driver when an ++ * 802.11 MPDU is received from the hardware. ++ */ ++void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb) ++{ ++ struct ieee80211_local *local = hw_to_local(hw); ++ struct ieee80211_rate *rate = NULL; ++ struct ieee80211_supported_band *sband; ++ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); ++ ++ WARN_ON_ONCE(softirq_count() == 0); ++ ++ if (WARN_ON(status->band >= IEEE80211_NUM_BANDS)) ++ goto drop; ++ ++ sband = local->hw.wiphy->bands[status->band]; ++ if (WARN_ON(!sband)) ++ goto drop; ++ ++ /* ++ * If we're suspending, it is possible although not too likely ++ * that we'd be receiving frames after having already partially ++ * quiesced the stack. We can't process such frames then since ++ * that might, for example, cause stations to be added or other ++ * driver callbacks be invoked. ++ */ ++ if (unlikely(local->quiescing || local->suspended)) ++ goto drop; ++ ++ /* We might be during a HW reconfig, prevent Rx for the same reason */ ++ if (unlikely(local->in_reconfig)) ++ goto drop; ++ ++ /* ++ * The same happens when we're not even started, ++ * but that's worth a warning. ++ */ ++ if (WARN_ON(!local->started)) ++ goto drop; ++ ++ if (likely(!(status->flag & RX_FLAG_FAILED_PLCP_CRC))) { ++ /* ++ * Validate the rate, unless a PLCP error means that ++ * we probably can't have a valid rate here anyway. ++ */ ++ ++ if (status->flag & RX_FLAG_HT) { ++ /* ++ * rate_idx is MCS index, which can be [0-76] ++ * as documented on: ++ * ++ * http://wireless.kernel.org/en/developers/Documentation/ieee80211/802.11n ++ * ++ * Anything else would be some sort of driver or ++ * hardware error. The driver should catch hardware ++ * errors. ++ */ ++ if (WARN(status->rate_idx > 76, ++ "Rate marked as an HT rate but passed " ++ "status->rate_idx is not " ++ "an MCS index [0-76]: %d (0x%02x)\n", ++ status->rate_idx, ++ status->rate_idx)) ++ goto drop; ++ } else if (status->flag & RX_FLAG_VHT) { ++ if (WARN_ONCE(status->rate_idx > 9 || ++ !status->vht_nss || ++ status->vht_nss > 8, ++ "Rate marked as a VHT rate but data is invalid: MCS: %d, NSS: %d\n", ++ status->rate_idx, status->vht_nss)) ++ goto drop; ++ } else { ++ if (WARN_ON(status->rate_idx >= sband->n_bitrates)) ++ goto drop; ++ rate = &sband->bitrates[status->rate_idx]; ++ } ++ } ++ ++ status->rx_flags = 0; ++ ++ /* ++ * key references and virtual interfaces are protected using RCU ++ * and this requires that we are in a read-side RCU section during ++ * receive processing ++ */ ++ rcu_read_lock(); ++ ++ /* ++ * Frames with failed FCS/PLCP checksum are not returned, ++ * all other frames are returned without radiotap header ++ * if it was previously present. ++ * Also, frames with less than 16 bytes are dropped. ++ */ ++ skb = ieee80211_rx_monitor(local, skb, rate); ++ if (!skb) { ++ rcu_read_unlock(); ++ return; ++ } ++ ++ ieee80211_tpt_led_trig_rx(local, ++ ((struct ieee80211_hdr *)skb->data)->frame_control, ++ skb->len); ++ __ieee80211_rx_handle_packet(hw, skb); ++ ++ rcu_read_unlock(); ++ ++ return; ++ drop: ++ kfree_skb(skb); ++} ++EXPORT_SYMBOL(ieee80211_rx); ++ ++/* This is a version of the rx handler that can be called from hard irq ++ * context. Post the skb on the queue and schedule the tasklet */ ++void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb) ++{ ++ struct ieee80211_local *local = hw_to_local(hw); ++ ++ BUILD_BUG_ON(sizeof(struct ieee80211_rx_status) > sizeof(skb->cb)); ++ ++ skb->pkt_type = IEEE80211_RX_MSG; ++ skb_queue_tail(&local->skb_queue, skb); ++ tasklet_schedule(&local->tasklet); ++} ++EXPORT_SYMBOL(ieee80211_rx_irqsafe); +diff -Nur linux-3.18.12.orig/net/netfilter/core.c linux-3.18.12/net/netfilter/core.c +--- linux-3.18.12.orig/net/netfilter/core.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/net/netfilter/core.c 2015-04-26 13:32:22.471684003 -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.12.orig/net/packet/af_packet.c linux-3.18.12/net/packet/af_packet.c +--- linux-3.18.12.orig/net/packet/af_packet.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/net/packet/af_packet.c 2015-04-26 13:32:22.471684003 -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.12.orig/net/rds/ib_rdma.c linux-3.18.12/net/rds/ib_rdma.c +--- linux-3.18.12.orig/net/rds/ib_rdma.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/net/rds/ib_rdma.c 2015-04-26 13:32:22.471684003 -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.12.orig/net/sched/sch_generic.c linux-3.18.12/net/sched/sch_generic.c +--- linux-3.18.12.orig/net/sched/sch_generic.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/net/sched/sch_generic.c 2015-04-26 13:32:22.471684003 -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.12.orig/net/sunrpc/svc_xprt.c linux-3.18.12/net/sunrpc/svc_xprt.c +--- linux-3.18.12.orig/net/sunrpc/svc_xprt.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/net/sunrpc/svc_xprt.c 2015-04-26 13:32:22.475684003 -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.12.orig/scripts/mkcompile_h linux-3.18.12/scripts/mkcompile_h +--- linux-3.18.12.orig/scripts/mkcompile_h 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/scripts/mkcompile_h 2015-04-26 13:32:22.475684003 -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.12.orig/sound/core/pcm_native.c linux-3.18.12/sound/core/pcm_native.c +--- linux-3.18.12.orig/sound/core/pcm_native.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/sound/core/pcm_native.c 2015-04-26 13:32:22.475684003 -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.12.orig/virt/kvm/async_pf.c linux-3.18.12/virt/kvm/async_pf.c +--- linux-3.18.12.orig/virt/kvm/async_pf.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/virt/kvm/async_pf.c 2015-04-26 13:32:22.475684003 -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.12.orig/virt/kvm/kvm_main.c linux-3.18.12/virt/kvm/kvm_main.c +--- linux-3.18.12.orig/virt/kvm/kvm_main.c 2015-04-20 14:48:02.000000000 -0500 ++++ linux-3.18.12/virt/kvm/kvm_main.c 2015-04-26 13:32:22.475684003 -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); +@@ -1740,10 +1740,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); +@@ -1757,7 +1757,7 @@ + schedule(); + } + +- finish_wait(&vcpu->wq, &wait); ++ swait_finish(&vcpu->wq, &wait); + } + EXPORT_SYMBOL_GPL(kvm_vcpu_block); + +@@ -1769,11 +1769,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; + } + +@@ -1878,7 +1878,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.12/regmap-bool.patch b/target/linux/patches/3.18.12/regmap-bool.patch new file mode 100644 index 000000000..5c0ff5e2c --- /dev/null +++ b/target/linux/patches/3.18.12/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.12/relocs.patch b/target/linux/patches/3.18.12/relocs.patch new file mode 100644 index 000000000..69a7c88a9 --- /dev/null +++ b/target/linux/patches/3.18.12/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.12/sgidefs.patch b/target/linux/patches/3.18.12/sgidefs.patch new file mode 100644 index 000000000..f00a284d9 --- /dev/null +++ b/target/linux/patches/3.18.12/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.12/sortext.patch b/target/linux/patches/3.18.12/sortext.patch new file mode 100644 index 000000000..8fd4e1d6b --- /dev/null +++ b/target/linux/patches/3.18.12/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.12/startup.patch b/target/linux/patches/3.18.12/startup.patch new file mode 100644 index 000000000..d396b75e4 --- /dev/null +++ b/target/linux/patches/3.18.12/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.12/wlan-cf.patch b/target/linux/patches/3.18.12/wlan-cf.patch new file mode 100644 index 000000000..fc20759e2 --- /dev/null +++ b/target/linux/patches/3.18.12/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.12/xargs.patch b/target/linux/patches/3.18.12/xargs.patch new file mode 100644 index 000000000..2c7b3df59 --- /dev/null +++ b/target/linux/patches/3.18.12/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.12/yaffs2.patch b/target/linux/patches/3.18.12/yaffs2.patch new file mode 100644 index 000000000..bb244c7ca --- /dev/null +++ b/target/linux/patches/3.18.12/yaffs2.patch @@ -0,0 +1,16551 @@ +diff -Nur linux-3.15-rc5.orig/fs/Kconfig linux-3.15-rc5/fs/Kconfig +--- linux-3.15-rc5.orig/fs/Kconfig 2014-05-09 22:10:52.000000000 +0200 ++++ linux-3.15-rc5/fs/Kconfig 2014-05-17 01:53:17.000000000 +0200 +@@ -190,6 +190,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.15-rc5.orig/fs/Makefile linux-3.15-rc5/fs/Makefile +--- linux-3.15-rc5.orig/fs/Makefile 2014-05-09 22:10:52.000000000 +0200 ++++ linux-3.15-rc5/fs/Makefile 2014-05-17 01:53:25.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.15-rc5.orig/fs/yaffs2/Kconfig linux-3.15-rc5/fs/yaffs2/Kconfig +--- linux-3.15-rc5.orig/fs/yaffs2/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/Kconfig 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/Makefile linux-3.15-rc5/fs/yaffs2/Makefile +--- linux-3.15-rc5.orig/fs/yaffs2/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/Makefile 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_allocator.c linux-3.15-rc5/fs/yaffs2/yaffs_allocator.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_allocator.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_allocator.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_allocator.h linux-3.15-rc5/fs/yaffs2/yaffs_allocator.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_allocator.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_allocator.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_attribs.c linux-3.15-rc5/fs/yaffs2/yaffs_attribs.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_attribs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_attribs.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_attribs.h linux-3.15-rc5/fs/yaffs2/yaffs_attribs.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_attribs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_attribs.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_bitmap.c linux-3.15-rc5/fs/yaffs2/yaffs_bitmap.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_bitmap.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_bitmap.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_bitmap.h linux-3.15-rc5/fs/yaffs2/yaffs_bitmap.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_bitmap.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_bitmap.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_checkptrw.c linux-3.15-rc5/fs/yaffs2/yaffs_checkptrw.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_checkptrw.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_checkptrw.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_checkptrw.h linux-3.15-rc5/fs/yaffs2/yaffs_checkptrw.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_checkptrw.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_checkptrw.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_ecc.c linux-3.15-rc5/fs/yaffs2/yaffs_ecc.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_ecc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_ecc.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_ecc.h linux-3.15-rc5/fs/yaffs2/yaffs_ecc.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_ecc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_ecc.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_getblockinfo.h linux-3.15-rc5/fs/yaffs2/yaffs_getblockinfo.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_getblockinfo.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_getblockinfo.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_guts.c linux-3.15-rc5/fs/yaffs2/yaffs_guts.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_guts.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_guts.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_guts.h linux-3.15-rc5/fs/yaffs2/yaffs_guts.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_guts.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_guts.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_linux.h linux-3.15-rc5/fs/yaffs2/yaffs_linux.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_linux.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_linux.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_mtdif.c linux-3.15-rc5/fs/yaffs2/yaffs_mtdif.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_mtdif.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_mtdif.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_mtdif.h linux-3.15-rc5/fs/yaffs2/yaffs_mtdif.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_mtdif.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_mtdif.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_nameval.c linux-3.15-rc5/fs/yaffs2/yaffs_nameval.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_nameval.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_nameval.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_nameval.h linux-3.15-rc5/fs/yaffs2/yaffs_nameval.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_nameval.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_nameval.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_nand.c linux-3.15-rc5/fs/yaffs2/yaffs_nand.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_nand.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_nand.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_nand.h linux-3.15-rc5/fs/yaffs2/yaffs_nand.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_nand.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_nand.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_packedtags1.c linux-3.15-rc5/fs/yaffs2/yaffs_packedtags1.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_packedtags1.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_packedtags1.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_packedtags1.h linux-3.15-rc5/fs/yaffs2/yaffs_packedtags1.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_packedtags1.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_packedtags1.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_packedtags2.c linux-3.15-rc5/fs/yaffs2/yaffs_packedtags2.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_packedtags2.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_packedtags2.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_packedtags2.h linux-3.15-rc5/fs/yaffs2/yaffs_packedtags2.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_packedtags2.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_packedtags2.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_summary.c linux-3.15-rc5/fs/yaffs2/yaffs_summary.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_summary.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_summary.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_summary.h linux-3.15-rc5/fs/yaffs2/yaffs_summary.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_summary.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_summary.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_tagscompat.c linux-3.15-rc5/fs/yaffs2/yaffs_tagscompat.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_tagscompat.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_tagscompat.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_tagscompat.h linux-3.15-rc5/fs/yaffs2/yaffs_tagscompat.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_tagscompat.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_tagscompat.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_tagsmarshall.c linux-3.15-rc5/fs/yaffs2/yaffs_tagsmarshall.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_tagsmarshall.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_tagsmarshall.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_tagsmarshall.h linux-3.15-rc5/fs/yaffs2/yaffs_tagsmarshall.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_tagsmarshall.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_tagsmarshall.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_trace.h linux-3.15-rc5/fs/yaffs2/yaffs_trace.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_trace.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_trace.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_verify.c linux-3.15-rc5/fs/yaffs2/yaffs_verify.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_verify.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_verify.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_verify.h linux-3.15-rc5/fs/yaffs2/yaffs_verify.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_verify.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_verify.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_vfs.c linux-3.15-rc5/fs/yaffs2/yaffs_vfs.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_vfs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_vfs.c 2014-05-17 02:52:54.000000000 +0200 +@@ -0,0 +1,3604 @@ ++/* ++ * YAFFS: Yet Another Flash File System. 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; ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)) ++ ret = readlink_copy(buffer, buflen, alias); ++#else ++ ret = vfs_readlink(dentry, 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, 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.15-rc5.orig/fs/yaffs2/yaffs_yaffs1.c linux-3.15-rc5/fs/yaffs2/yaffs_yaffs1.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_yaffs1.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_yaffs1.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_yaffs1.h linux-3.15-rc5/fs/yaffs2/yaffs_yaffs1.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_yaffs1.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_yaffs1.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_yaffs2.c linux-3.15-rc5/fs/yaffs2/yaffs_yaffs2.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_yaffs2.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_yaffs2.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_yaffs2.h linux-3.15-rc5/fs/yaffs2/yaffs_yaffs2.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_yaffs2.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_yaffs2.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yportenv.h linux-3.15-rc5/fs/yaffs2/yportenv.h +--- linux-3.15-rc5.orig/fs/yaffs2/yportenv.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yportenv.h 2014-05-17 01:53:27.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.19.1/bsd-compatibility.patch b/target/linux/patches/3.19.1/bsd-compatibility.patch deleted file mode 100644 index b954b658f..000000000 --- a/target/linux/patches/3.19.1/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.19.1/cleankernel.patch b/target/linux/patches/3.19.1/cleankernel.patch deleted file mode 100644 index d8c055dc3..000000000 --- a/target/linux/patches/3.19.1/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.19.1/cris-header.patch b/target/linux/patches/3.19.1/cris-header.patch deleted file mode 100644 index 2b5a88461..000000000 --- a/target/linux/patches/3.19.1/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/3.19.1/export-symbol-for-exmap.patch b/target/linux/patches/3.19.1/export-symbol-for-exmap.patch deleted file mode 100644 index 4f0fc8449..000000000 --- a/target/linux/patches/3.19.1/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.19.1/fblogo.patch b/target/linux/patches/3.19.1/fblogo.patch deleted file mode 100644 index a090f393f..000000000 --- a/target/linux/patches/3.19.1/fblogo.patch +++ /dev/null @@ -1,2045 +0,0 @@ -diff -Nur linux-3.19.1.orig/Documentation/fb/00-INDEX linux-3.19.1/Documentation/fb/00-INDEX ---- linux-3.19.1.orig/Documentation/fb/00-INDEX 2015-03-06 16:57:59.000000000 -0600 -+++ linux-3.19.1/Documentation/fb/00-INDEX 2015-03-15 15:12:10.436122636 -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.19.1.orig/Documentation/fb/fbcondecor.txt linux-3.19.1/Documentation/fb/fbcondecor.txt ---- linux-3.19.1.orig/Documentation/fb/fbcondecor.txt 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.19.1/Documentation/fb/fbcondecor.txt 2015-03-15 15:12:10.452122956 -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.19.1.orig/drivers/Makefile linux-3.19.1/drivers/Makefile ---- linux-3.19.1.orig/drivers/Makefile 2015-03-06 16:57:59.000000000 -0600 -+++ linux-3.19.1/drivers/Makefile 2015-03-15 15:12:10.452122956 -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/ - -diff -Nur linux-3.19.1.orig/drivers/video/console/bitblit.c linux-3.19.1/drivers/video/console/bitblit.c ---- linux-3.19.1.orig/drivers/video/console/bitblit.c 2015-03-06 16:57:59.000000000 -0600 -+++ linux-3.19.1/drivers/video/console/bitblit.c 2015-03-15 15:12:10.452122956 -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.19.1.orig/drivers/video/console/cfbcondecor.c linux-3.19.1/drivers/video/console/cfbcondecor.c ---- linux-3.19.1.orig/drivers/video/console/cfbcondecor.c 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.19.1/drivers/video/console/cfbcondecor.c 2015-03-15 15:12:10.456122903 -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.19.1.orig/drivers/video/console/fbcon.c linux-3.19.1/drivers/video/console/fbcon.c ---- linux-3.19.1.orig/drivers/video/console/fbcon.c 2015-03-06 16:57:59.000000000 -0600 -+++ linux-3.19.1/drivers/video/console/fbcon.c 2015-03-15 15:12:10.456122903 -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.19.1.orig/drivers/video/console/fbcondecor.c linux-3.19.1/drivers/video/console/fbcondecor.c ---- linux-3.19.1.orig/drivers/video/console/fbcondecor.c 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.19.1/drivers/video/console/fbcondecor.c 2015-03-15 15:12:10.460122853 -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.19.1.orig/drivers/video/console/fbcondecor.h linux-3.19.1/drivers/video/console/fbcondecor.h ---- linux-3.19.1.orig/drivers/video/console/fbcondecor.h 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.19.1/drivers/video/console/fbcondecor.h 2015-03-15 15:12:10.460122853 -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.19.1.orig/drivers/video/console/Kconfig linux-3.19.1/drivers/video/console/Kconfig ---- linux-3.19.1.orig/drivers/video/console/Kconfig 2015-03-06 16:57:59.000000000 -0600 -+++ linux-3.19.1/drivers/video/console/Kconfig 2015-03-15 15:12:10.452122956 -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.19.1.orig/drivers/video/console/Makefile linux-3.19.1/drivers/video/console/Makefile ---- linux-3.19.1.orig/drivers/video/console/Makefile 2015-03-06 16:57:59.000000000 -0600 -+++ linux-3.19.1/drivers/video/console/Makefile 2015-03-15 15:12:10.452122956 -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.19.1.orig/drivers/video/fbdev/core/fbcmap.c linux-3.19.1/drivers/video/fbdev/core/fbcmap.c ---- linux-3.19.1.orig/drivers/video/fbdev/core/fbcmap.c 2015-03-06 16:57:59.000000000 -0600 -+++ linux-3.19.1/drivers/video/fbdev/core/fbcmap.c 2015-03-15 15:12:10.460122853 -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.19.1.orig/include/linux/console_decor.h linux-3.19.1/include/linux/console_decor.h ---- linux-3.19.1.orig/include/linux/console_decor.h 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.19.1/include/linux/console_decor.h 2015-03-15 15:12:10.460122853 -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.19.1.orig/include/linux/console_struct.h linux-3.19.1/include/linux/console_struct.h ---- linux-3.19.1.orig/include/linux/console_struct.h 2015-03-06 16:57:59.000000000 -0600 -+++ linux-3.19.1/include/linux/console_struct.h 2015-03-15 15:12:10.460122853 -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.19.1.orig/include/linux/fb.h linux-3.19.1/include/linux/fb.h ---- linux-3.19.1.orig/include/linux/fb.h 2015-03-06 16:57:59.000000000 -0600 -+++ linux-3.19.1/include/linux/fb.h 2015-03-15 15:12:10.464122809 -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.19.1.orig/include/uapi/linux/fb.h linux-3.19.1/include/uapi/linux/fb.h ---- linux-3.19.1.orig/include/uapi/linux/fb.h 2015-03-06 16:57:59.000000000 -0600 -+++ linux-3.19.1/include/uapi/linux/fb.h 2015-03-15 15:12:10.464122809 -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.19.1.orig/kernel/sysctl.c linux-3.19.1/kernel/sysctl.c ---- linux-3.19.1.orig/kernel/sysctl.c 2015-03-06 16:57:59.000000000 -0600 -+++ linux-3.19.1/kernel/sysctl.c 2015-03-15 15:12:10.464122809 -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.19.1/gemalto.patch b/target/linux/patches/3.19.1/gemalto.patch deleted file mode 100644 index 65f7af1d7..000000000 --- a/target/linux/patches/3.19.1/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.19.1/initramfs-nosizelimit.patch b/target/linux/patches/3.19.1/initramfs-nosizelimit.patch deleted file mode 100644 index 40d2f6bd8..000000000 --- a/target/linux/patches/3.19.1/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.19.1/lemote-rfkill.patch b/target/linux/patches/3.19.1/lemote-rfkill.patch deleted file mode 100644 index a61488434..000000000 --- a/target/linux/patches/3.19.1/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.19.1/microblaze-axi.patch b/target/linux/patches/3.19.1/microblaze-axi.patch deleted file mode 100644 index 1a4b17d8c..000000000 --- a/target/linux/patches/3.19.1/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.19.1/microblaze-ethernet.patch b/target/linux/patches/3.19.1/microblaze-ethernet.patch deleted file mode 100644 index 742ab477e..000000000 --- a/target/linux/patches/3.19.1/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.19.1/mkpiggy.patch b/target/linux/patches/3.19.1/mkpiggy.patch deleted file mode 100644 index 751678b74..000000000 --- a/target/linux/patches/3.19.1/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.19.1/mtd-rootfs.patch b/target/linux/patches/3.19.1/mtd-rootfs.patch deleted file mode 100644 index 775d5fc80..000000000 --- a/target/linux/patches/3.19.1/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.19.1/nfsv3-tcp.patch b/target/linux/patches/3.19.1/nfsv3-tcp.patch deleted file mode 100644 index d5e07e1c2..000000000 --- a/target/linux/patches/3.19.1/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.19.1/non-static.patch b/target/linux/patches/3.19.1/non-static.patch deleted file mode 100644 index a967703d0..000000000 --- a/target/linux/patches/3.19.1/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.19.1/ppc64-missing-zlib.patch b/target/linux/patches/3.19.1/ppc64-missing-zlib.patch deleted file mode 100644 index c6e0616be..000000000 --- a/target/linux/patches/3.19.1/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.19.1/regmap-bool.patch b/target/linux/patches/3.19.1/regmap-bool.patch deleted file mode 100644 index 5c0ff5e2c..000000000 --- a/target/linux/patches/3.19.1/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.19.1/relocs.patch b/target/linux/patches/3.19.1/relocs.patch deleted file mode 100644 index 69a7c88a9..000000000 --- a/target/linux/patches/3.19.1/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.19.1/sgidefs.patch b/target/linux/patches/3.19.1/sgidefs.patch deleted file mode 100644 index f00a284d9..000000000 --- a/target/linux/patches/3.19.1/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.19.1/sortext.patch b/target/linux/patches/3.19.1/sortext.patch deleted file mode 100644 index 8fd4e1d6b..000000000 --- a/target/linux/patches/3.19.1/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.19.1/sparc-aout.patch b/target/linux/patches/3.19.1/sparc-aout.patch deleted file mode 100644 index 3360d6a6e..000000000 --- a/target/linux/patches/3.19.1/sparc-aout.patch +++ /dev/null @@ -1,24 +0,0 @@ -diff -Nur linux-3.19.orig/arch/sparc/boot/Makefile linux-3.19/arch/sparc/boot/Makefile ---- linux-3.19.orig/arch/sparc/boot/Makefile 2015-02-08 20:54:22.000000000 -0600 -+++ linux-3.19/arch/sparc/boot/Makefile 2015-02-15 22:58:57.918666227 -0600 -@@ -25,9 +25,6 @@ - $(call if_changed,gzip) - @echo ' kernel: $@ is ready' - --$(obj)/vmlinux.aout: vmlinux FORCE -- $(call if_changed,elftoaout) -- @echo ' kernel: $@ is ready' - else - - $(obj)/zImage: $(obj)/image -@@ -62,6 +59,10 @@ - - endif - -+$(obj)/vmlinux.aout: $(obj)/image FORCE -+ $(call if_changed,elftoaout) -+ @echo ' kernel: $@ is ready' -+ - $(obj)/image: vmlinux FORCE - $(call if_changed,strip) - @echo ' kernel: $@ is ready' diff --git a/target/linux/patches/3.19.1/startup.patch b/target/linux/patches/3.19.1/startup.patch deleted file mode 100644 index d396b75e4..000000000 --- a/target/linux/patches/3.19.1/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.19.1/wlan-cf.patch b/target/linux/patches/3.19.1/wlan-cf.patch deleted file mode 100644 index fc20759e2..000000000 --- a/target/linux/patches/3.19.1/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.19.1/xargs.patch b/target/linux/patches/3.19.1/xargs.patch deleted file mode 100644 index 2c7b3df59..000000000 --- a/target/linux/patches/3.19.1/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.19.1/yaffs2.patch b/target/linux/patches/3.19.1/yaffs2.patch deleted file mode 100644 index bb244c7ca..000000000 --- a/target/linux/patches/3.19.1/yaffs2.patch +++ /dev/null @@ -1,16551 +0,0 @@ -diff -Nur linux-3.15-rc5.orig/fs/Kconfig linux-3.15-rc5/fs/Kconfig ---- linux-3.15-rc5.orig/fs/Kconfig 2014-05-09 22:10:52.000000000 +0200 -+++ linux-3.15-rc5/fs/Kconfig 2014-05-17 01:53:17.000000000 +0200 -@@ -190,6 +190,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.15-rc5.orig/fs/Makefile linux-3.15-rc5/fs/Makefile ---- linux-3.15-rc5.orig/fs/Makefile 2014-05-09 22:10:52.000000000 +0200 -+++ linux-3.15-rc5/fs/Makefile 2014-05-17 01:53:25.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.15-rc5.orig/fs/yaffs2/Kconfig linux-3.15-rc5/fs/yaffs2/Kconfig ---- linux-3.15-rc5.orig/fs/yaffs2/Kconfig 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/Kconfig 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/Makefile linux-3.15-rc5/fs/yaffs2/Makefile ---- linux-3.15-rc5.orig/fs/yaffs2/Makefile 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/Makefile 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_allocator.c linux-3.15-rc5/fs/yaffs2/yaffs_allocator.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_allocator.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_allocator.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_allocator.h linux-3.15-rc5/fs/yaffs2/yaffs_allocator.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_allocator.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_allocator.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_attribs.c linux-3.15-rc5/fs/yaffs2/yaffs_attribs.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_attribs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_attribs.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_attribs.h linux-3.15-rc5/fs/yaffs2/yaffs_attribs.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_attribs.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_attribs.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_bitmap.c linux-3.15-rc5/fs/yaffs2/yaffs_bitmap.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_bitmap.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_bitmap.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_bitmap.h linux-3.15-rc5/fs/yaffs2/yaffs_bitmap.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_bitmap.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_bitmap.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_checkptrw.c linux-3.15-rc5/fs/yaffs2/yaffs_checkptrw.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_checkptrw.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_checkptrw.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_checkptrw.h linux-3.15-rc5/fs/yaffs2/yaffs_checkptrw.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_checkptrw.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_checkptrw.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_ecc.c linux-3.15-rc5/fs/yaffs2/yaffs_ecc.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_ecc.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_ecc.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_ecc.h linux-3.15-rc5/fs/yaffs2/yaffs_ecc.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_ecc.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_ecc.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_getblockinfo.h linux-3.15-rc5/fs/yaffs2/yaffs_getblockinfo.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_getblockinfo.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_getblockinfo.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_guts.c linux-3.15-rc5/fs/yaffs2/yaffs_guts.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_guts.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_guts.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_guts.h linux-3.15-rc5/fs/yaffs2/yaffs_guts.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_guts.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_guts.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_linux.h linux-3.15-rc5/fs/yaffs2/yaffs_linux.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_linux.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_linux.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_mtdif.c linux-3.15-rc5/fs/yaffs2/yaffs_mtdif.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_mtdif.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_mtdif.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_mtdif.h linux-3.15-rc5/fs/yaffs2/yaffs_mtdif.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_mtdif.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_mtdif.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_nameval.c linux-3.15-rc5/fs/yaffs2/yaffs_nameval.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_nameval.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_nameval.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_nameval.h linux-3.15-rc5/fs/yaffs2/yaffs_nameval.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_nameval.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_nameval.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_nand.c linux-3.15-rc5/fs/yaffs2/yaffs_nand.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_nand.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_nand.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_nand.h linux-3.15-rc5/fs/yaffs2/yaffs_nand.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_nand.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_nand.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_packedtags1.c linux-3.15-rc5/fs/yaffs2/yaffs_packedtags1.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_packedtags1.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_packedtags1.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_packedtags1.h linux-3.15-rc5/fs/yaffs2/yaffs_packedtags1.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_packedtags1.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_packedtags1.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_packedtags2.c linux-3.15-rc5/fs/yaffs2/yaffs_packedtags2.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_packedtags2.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_packedtags2.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_packedtags2.h linux-3.15-rc5/fs/yaffs2/yaffs_packedtags2.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_packedtags2.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_packedtags2.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_summary.c linux-3.15-rc5/fs/yaffs2/yaffs_summary.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_summary.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_summary.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_summary.h linux-3.15-rc5/fs/yaffs2/yaffs_summary.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_summary.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_summary.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_tagscompat.c linux-3.15-rc5/fs/yaffs2/yaffs_tagscompat.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_tagscompat.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_tagscompat.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_tagscompat.h linux-3.15-rc5/fs/yaffs2/yaffs_tagscompat.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_tagscompat.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_tagscompat.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_tagsmarshall.c linux-3.15-rc5/fs/yaffs2/yaffs_tagsmarshall.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_tagsmarshall.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_tagsmarshall.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_tagsmarshall.h linux-3.15-rc5/fs/yaffs2/yaffs_tagsmarshall.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_tagsmarshall.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_tagsmarshall.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_trace.h linux-3.15-rc5/fs/yaffs2/yaffs_trace.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_trace.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_trace.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_verify.c linux-3.15-rc5/fs/yaffs2/yaffs_verify.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_verify.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_verify.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_verify.h linux-3.15-rc5/fs/yaffs2/yaffs_verify.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_verify.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_verify.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_vfs.c linux-3.15-rc5/fs/yaffs2/yaffs_vfs.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_vfs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_vfs.c 2014-05-17 02:52:54.000000000 +0200 -@@ -0,0 +1,3604 @@ -+/* -+ * YAFFS: Yet Another Flash File System. 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; -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)) -+ ret = readlink_copy(buffer, buflen, alias); -+#else -+ ret = vfs_readlink(dentry, 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, 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.15-rc5.orig/fs/yaffs2/yaffs_yaffs1.c linux-3.15-rc5/fs/yaffs2/yaffs_yaffs1.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_yaffs1.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_yaffs1.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_yaffs1.h linux-3.15-rc5/fs/yaffs2/yaffs_yaffs1.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_yaffs1.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_yaffs1.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_yaffs2.c linux-3.15-rc5/fs/yaffs2/yaffs_yaffs2.c ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_yaffs2.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_yaffs2.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_yaffs2.h linux-3.15-rc5/fs/yaffs2/yaffs_yaffs2.h ---- linux-3.15-rc5.orig/fs/yaffs2/yaffs_yaffs2.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yaffs_yaffs2.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yportenv.h linux-3.15-rc5/fs/yaffs2/yportenv.h ---- linux-3.15-rc5.orig/fs/yaffs2/yportenv.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.15-rc5/fs/yaffs2/yportenv.h 2014-05-17 01:53:27.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.19.5/bsd-compatibility.patch b/target/linux/patches/3.19.5/bsd-compatibility.patch new file mode 100644 index 000000000..b954b658f --- /dev/null +++ b/target/linux/patches/3.19.5/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.19.5/cleankernel.patch b/target/linux/patches/3.19.5/cleankernel.patch new file mode 100644 index 000000000..d8c055dc3 --- /dev/null +++ b/target/linux/patches/3.19.5/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.19.5/cris-header.patch b/target/linux/patches/3.19.5/cris-header.patch new file mode 100644 index 000000000..2b5a88461 --- /dev/null +++ b/target/linux/patches/3.19.5/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/3.19.5/export-symbol-for-exmap.patch b/target/linux/patches/3.19.5/export-symbol-for-exmap.patch new file mode 100644 index 000000000..4f0fc8449 --- /dev/null +++ b/target/linux/patches/3.19.5/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.19.5/fblogo.patch b/target/linux/patches/3.19.5/fblogo.patch new file mode 100644 index 000000000..a090f393f --- /dev/null +++ b/target/linux/patches/3.19.5/fblogo.patch @@ -0,0 +1,2045 @@ +diff -Nur linux-3.19.1.orig/Documentation/fb/00-INDEX linux-3.19.1/Documentation/fb/00-INDEX +--- linux-3.19.1.orig/Documentation/fb/00-INDEX 2015-03-06 16:57:59.000000000 -0600 ++++ linux-3.19.1/Documentation/fb/00-INDEX 2015-03-15 15:12:10.436122636 -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.19.1.orig/Documentation/fb/fbcondecor.txt linux-3.19.1/Documentation/fb/fbcondecor.txt +--- linux-3.19.1.orig/Documentation/fb/fbcondecor.txt 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.19.1/Documentation/fb/fbcondecor.txt 2015-03-15 15:12:10.452122956 -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.19.1.orig/drivers/Makefile linux-3.19.1/drivers/Makefile +--- linux-3.19.1.orig/drivers/Makefile 2015-03-06 16:57:59.000000000 -0600 ++++ linux-3.19.1/drivers/Makefile 2015-03-15 15:12:10.452122956 -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/ + +diff -Nur linux-3.19.1.orig/drivers/video/console/bitblit.c linux-3.19.1/drivers/video/console/bitblit.c +--- linux-3.19.1.orig/drivers/video/console/bitblit.c 2015-03-06 16:57:59.000000000 -0600 ++++ linux-3.19.1/drivers/video/console/bitblit.c 2015-03-15 15:12:10.452122956 -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.19.1.orig/drivers/video/console/cfbcondecor.c linux-3.19.1/drivers/video/console/cfbcondecor.c +--- linux-3.19.1.orig/drivers/video/console/cfbcondecor.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.19.1/drivers/video/console/cfbcondecor.c 2015-03-15 15:12:10.456122903 -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.19.1.orig/drivers/video/console/fbcon.c linux-3.19.1/drivers/video/console/fbcon.c +--- linux-3.19.1.orig/drivers/video/console/fbcon.c 2015-03-06 16:57:59.000000000 -0600 ++++ linux-3.19.1/drivers/video/console/fbcon.c 2015-03-15 15:12:10.456122903 -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.19.1.orig/drivers/video/console/fbcondecor.c linux-3.19.1/drivers/video/console/fbcondecor.c +--- linux-3.19.1.orig/drivers/video/console/fbcondecor.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.19.1/drivers/video/console/fbcondecor.c 2015-03-15 15:12:10.460122853 -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.19.1.orig/drivers/video/console/fbcondecor.h linux-3.19.1/drivers/video/console/fbcondecor.h +--- linux-3.19.1.orig/drivers/video/console/fbcondecor.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.19.1/drivers/video/console/fbcondecor.h 2015-03-15 15:12:10.460122853 -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.19.1.orig/drivers/video/console/Kconfig linux-3.19.1/drivers/video/console/Kconfig +--- linux-3.19.1.orig/drivers/video/console/Kconfig 2015-03-06 16:57:59.000000000 -0600 ++++ linux-3.19.1/drivers/video/console/Kconfig 2015-03-15 15:12:10.452122956 -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.19.1.orig/drivers/video/console/Makefile linux-3.19.1/drivers/video/console/Makefile +--- linux-3.19.1.orig/drivers/video/console/Makefile 2015-03-06 16:57:59.000000000 -0600 ++++ linux-3.19.1/drivers/video/console/Makefile 2015-03-15 15:12:10.452122956 -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.19.1.orig/drivers/video/fbdev/core/fbcmap.c linux-3.19.1/drivers/video/fbdev/core/fbcmap.c +--- linux-3.19.1.orig/drivers/video/fbdev/core/fbcmap.c 2015-03-06 16:57:59.000000000 -0600 ++++ linux-3.19.1/drivers/video/fbdev/core/fbcmap.c 2015-03-15 15:12:10.460122853 -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.19.1.orig/include/linux/console_decor.h linux-3.19.1/include/linux/console_decor.h +--- linux-3.19.1.orig/include/linux/console_decor.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.19.1/include/linux/console_decor.h 2015-03-15 15:12:10.460122853 -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.19.1.orig/include/linux/console_struct.h linux-3.19.1/include/linux/console_struct.h +--- linux-3.19.1.orig/include/linux/console_struct.h 2015-03-06 16:57:59.000000000 -0600 ++++ linux-3.19.1/include/linux/console_struct.h 2015-03-15 15:12:10.460122853 -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.19.1.orig/include/linux/fb.h linux-3.19.1/include/linux/fb.h +--- linux-3.19.1.orig/include/linux/fb.h 2015-03-06 16:57:59.000000000 -0600 ++++ linux-3.19.1/include/linux/fb.h 2015-03-15 15:12:10.464122809 -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.19.1.orig/include/uapi/linux/fb.h linux-3.19.1/include/uapi/linux/fb.h +--- linux-3.19.1.orig/include/uapi/linux/fb.h 2015-03-06 16:57:59.000000000 -0600 ++++ linux-3.19.1/include/uapi/linux/fb.h 2015-03-15 15:12:10.464122809 -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.19.1.orig/kernel/sysctl.c linux-3.19.1/kernel/sysctl.c +--- linux-3.19.1.orig/kernel/sysctl.c 2015-03-06 16:57:59.000000000 -0600 ++++ linux-3.19.1/kernel/sysctl.c 2015-03-15 15:12:10.464122809 -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.19.5/gemalto.patch b/target/linux/patches/3.19.5/gemalto.patch new file mode 100644 index 000000000..65f7af1d7 --- /dev/null +++ b/target/linux/patches/3.19.5/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.19.5/initramfs-nosizelimit.patch b/target/linux/patches/3.19.5/initramfs-nosizelimit.patch new file mode 100644 index 000000000..40d2f6bd8 --- /dev/null +++ b/target/linux/patches/3.19.5/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.19.5/lemote-rfkill.patch b/target/linux/patches/3.19.5/lemote-rfkill.patch new file mode 100644 index 000000000..a61488434 --- /dev/null +++ b/target/linux/patches/3.19.5/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.19.5/microblaze-axi.patch b/target/linux/patches/3.19.5/microblaze-axi.patch new file mode 100644 index 000000000..1a4b17d8c --- /dev/null +++ b/target/linux/patches/3.19.5/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.19.5/microblaze-ethernet.patch b/target/linux/patches/3.19.5/microblaze-ethernet.patch new file mode 100644 index 000000000..742ab477e --- /dev/null +++ b/target/linux/patches/3.19.5/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.19.5/mkpiggy.patch b/target/linux/patches/3.19.5/mkpiggy.patch new file mode 100644 index 000000000..751678b74 --- /dev/null +++ b/target/linux/patches/3.19.5/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.19.5/mtd-rootfs.patch b/target/linux/patches/3.19.5/mtd-rootfs.patch new file mode 100644 index 000000000..775d5fc80 --- /dev/null +++ b/target/linux/patches/3.19.5/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.19.5/nfsv3-tcp.patch b/target/linux/patches/3.19.5/nfsv3-tcp.patch new file mode 100644 index 000000000..d5e07e1c2 --- /dev/null +++ b/target/linux/patches/3.19.5/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.19.5/non-static.patch b/target/linux/patches/3.19.5/non-static.patch new file mode 100644 index 000000000..a967703d0 --- /dev/null +++ b/target/linux/patches/3.19.5/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.19.5/ppc64-missing-zlib.patch b/target/linux/patches/3.19.5/ppc64-missing-zlib.patch new file mode 100644 index 000000000..c6e0616be --- /dev/null +++ b/target/linux/patches/3.19.5/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.19.5/regmap-bool.patch b/target/linux/patches/3.19.5/regmap-bool.patch new file mode 100644 index 000000000..5c0ff5e2c --- /dev/null +++ b/target/linux/patches/3.19.5/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.19.5/relocs.patch b/target/linux/patches/3.19.5/relocs.patch new file mode 100644 index 000000000..69a7c88a9 --- /dev/null +++ b/target/linux/patches/3.19.5/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.19.5/sgidefs.patch b/target/linux/patches/3.19.5/sgidefs.patch new file mode 100644 index 000000000..f00a284d9 --- /dev/null +++ b/target/linux/patches/3.19.5/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.19.5/sortext.patch b/target/linux/patches/3.19.5/sortext.patch new file mode 100644 index 000000000..8fd4e1d6b --- /dev/null +++ b/target/linux/patches/3.19.5/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.19.5/sparc-aout.patch b/target/linux/patches/3.19.5/sparc-aout.patch new file mode 100644 index 000000000..3360d6a6e --- /dev/null +++ b/target/linux/patches/3.19.5/sparc-aout.patch @@ -0,0 +1,24 @@ +diff -Nur linux-3.19.orig/arch/sparc/boot/Makefile linux-3.19/arch/sparc/boot/Makefile +--- linux-3.19.orig/arch/sparc/boot/Makefile 2015-02-08 20:54:22.000000000 -0600 ++++ linux-3.19/arch/sparc/boot/Makefile 2015-02-15 22:58:57.918666227 -0600 +@@ -25,9 +25,6 @@ + $(call if_changed,gzip) + @echo ' kernel: $@ is ready' + +-$(obj)/vmlinux.aout: vmlinux FORCE +- $(call if_changed,elftoaout) +- @echo ' kernel: $@ is ready' + else + + $(obj)/zImage: $(obj)/image +@@ -62,6 +59,10 @@ + + endif + ++$(obj)/vmlinux.aout: $(obj)/image FORCE ++ $(call if_changed,elftoaout) ++ @echo ' kernel: $@ is ready' ++ + $(obj)/image: vmlinux FORCE + $(call if_changed,strip) + @echo ' kernel: $@ is ready' diff --git a/target/linux/patches/3.19.5/startup.patch b/target/linux/patches/3.19.5/startup.patch new file mode 100644 index 000000000..d396b75e4 --- /dev/null +++ b/target/linux/patches/3.19.5/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.19.5/wlan-cf.patch b/target/linux/patches/3.19.5/wlan-cf.patch new file mode 100644 index 000000000..fc20759e2 --- /dev/null +++ b/target/linux/patches/3.19.5/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.19.5/xargs.patch b/target/linux/patches/3.19.5/xargs.patch new file mode 100644 index 000000000..2c7b3df59 --- /dev/null +++ b/target/linux/patches/3.19.5/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.19.5/yaffs2.patch b/target/linux/patches/3.19.5/yaffs2.patch new file mode 100644 index 000000000..bb244c7ca --- /dev/null +++ b/target/linux/patches/3.19.5/yaffs2.patch @@ -0,0 +1,16551 @@ +diff -Nur linux-3.15-rc5.orig/fs/Kconfig linux-3.15-rc5/fs/Kconfig +--- linux-3.15-rc5.orig/fs/Kconfig 2014-05-09 22:10:52.000000000 +0200 ++++ linux-3.15-rc5/fs/Kconfig 2014-05-17 01:53:17.000000000 +0200 +@@ -190,6 +190,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.15-rc5.orig/fs/Makefile linux-3.15-rc5/fs/Makefile +--- linux-3.15-rc5.orig/fs/Makefile 2014-05-09 22:10:52.000000000 +0200 ++++ linux-3.15-rc5/fs/Makefile 2014-05-17 01:53:25.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.15-rc5.orig/fs/yaffs2/Kconfig linux-3.15-rc5/fs/yaffs2/Kconfig +--- linux-3.15-rc5.orig/fs/yaffs2/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/Kconfig 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/Makefile linux-3.15-rc5/fs/yaffs2/Makefile +--- linux-3.15-rc5.orig/fs/yaffs2/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/Makefile 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_allocator.c linux-3.15-rc5/fs/yaffs2/yaffs_allocator.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_allocator.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_allocator.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_allocator.h linux-3.15-rc5/fs/yaffs2/yaffs_allocator.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_allocator.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_allocator.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_attribs.c linux-3.15-rc5/fs/yaffs2/yaffs_attribs.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_attribs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_attribs.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_attribs.h linux-3.15-rc5/fs/yaffs2/yaffs_attribs.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_attribs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_attribs.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_bitmap.c linux-3.15-rc5/fs/yaffs2/yaffs_bitmap.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_bitmap.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_bitmap.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_bitmap.h linux-3.15-rc5/fs/yaffs2/yaffs_bitmap.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_bitmap.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_bitmap.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_checkptrw.c linux-3.15-rc5/fs/yaffs2/yaffs_checkptrw.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_checkptrw.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_checkptrw.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_checkptrw.h linux-3.15-rc5/fs/yaffs2/yaffs_checkptrw.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_checkptrw.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_checkptrw.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_ecc.c linux-3.15-rc5/fs/yaffs2/yaffs_ecc.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_ecc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_ecc.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_ecc.h linux-3.15-rc5/fs/yaffs2/yaffs_ecc.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_ecc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_ecc.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_getblockinfo.h linux-3.15-rc5/fs/yaffs2/yaffs_getblockinfo.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_getblockinfo.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_getblockinfo.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_guts.c linux-3.15-rc5/fs/yaffs2/yaffs_guts.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_guts.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_guts.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_guts.h linux-3.15-rc5/fs/yaffs2/yaffs_guts.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_guts.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_guts.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_linux.h linux-3.15-rc5/fs/yaffs2/yaffs_linux.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_linux.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_linux.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_mtdif.c linux-3.15-rc5/fs/yaffs2/yaffs_mtdif.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_mtdif.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_mtdif.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_mtdif.h linux-3.15-rc5/fs/yaffs2/yaffs_mtdif.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_mtdif.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_mtdif.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_nameval.c linux-3.15-rc5/fs/yaffs2/yaffs_nameval.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_nameval.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_nameval.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_nameval.h linux-3.15-rc5/fs/yaffs2/yaffs_nameval.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_nameval.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_nameval.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_nand.c linux-3.15-rc5/fs/yaffs2/yaffs_nand.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_nand.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_nand.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_nand.h linux-3.15-rc5/fs/yaffs2/yaffs_nand.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_nand.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_nand.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_packedtags1.c linux-3.15-rc5/fs/yaffs2/yaffs_packedtags1.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_packedtags1.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_packedtags1.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_packedtags1.h linux-3.15-rc5/fs/yaffs2/yaffs_packedtags1.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_packedtags1.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_packedtags1.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_packedtags2.c linux-3.15-rc5/fs/yaffs2/yaffs_packedtags2.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_packedtags2.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_packedtags2.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_packedtags2.h linux-3.15-rc5/fs/yaffs2/yaffs_packedtags2.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_packedtags2.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_packedtags2.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_summary.c linux-3.15-rc5/fs/yaffs2/yaffs_summary.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_summary.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_summary.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_summary.h linux-3.15-rc5/fs/yaffs2/yaffs_summary.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_summary.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_summary.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_tagscompat.c linux-3.15-rc5/fs/yaffs2/yaffs_tagscompat.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_tagscompat.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_tagscompat.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_tagscompat.h linux-3.15-rc5/fs/yaffs2/yaffs_tagscompat.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_tagscompat.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_tagscompat.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_tagsmarshall.c linux-3.15-rc5/fs/yaffs2/yaffs_tagsmarshall.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_tagsmarshall.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_tagsmarshall.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_tagsmarshall.h linux-3.15-rc5/fs/yaffs2/yaffs_tagsmarshall.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_tagsmarshall.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_tagsmarshall.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_trace.h linux-3.15-rc5/fs/yaffs2/yaffs_trace.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_trace.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_trace.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_verify.c linux-3.15-rc5/fs/yaffs2/yaffs_verify.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_verify.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_verify.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_verify.h linux-3.15-rc5/fs/yaffs2/yaffs_verify.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_verify.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_verify.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_vfs.c linux-3.15-rc5/fs/yaffs2/yaffs_vfs.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_vfs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_vfs.c 2014-05-17 02:52:54.000000000 +0200 +@@ -0,0 +1,3604 @@ ++/* ++ * YAFFS: Yet Another Flash File System. 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; ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)) ++ ret = readlink_copy(buffer, buflen, alias); ++#else ++ ret = vfs_readlink(dentry, 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, 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.15-rc5.orig/fs/yaffs2/yaffs_yaffs1.c linux-3.15-rc5/fs/yaffs2/yaffs_yaffs1.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_yaffs1.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_yaffs1.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_yaffs1.h linux-3.15-rc5/fs/yaffs2/yaffs_yaffs1.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_yaffs1.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_yaffs1.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_yaffs2.c linux-3.15-rc5/fs/yaffs2/yaffs_yaffs2.c +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_yaffs2.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_yaffs2.c 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yaffs_yaffs2.h linux-3.15-rc5/fs/yaffs2/yaffs_yaffs2.h +--- linux-3.15-rc5.orig/fs/yaffs2/yaffs_yaffs2.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yaffs_yaffs2.h 2014-05-17 01:53:27.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.15-rc5.orig/fs/yaffs2/yportenv.h linux-3.15-rc5/fs/yaffs2/yportenv.h +--- linux-3.15-rc5.orig/fs/yaffs2/yportenv.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.15-rc5/fs/yaffs2/yportenv.h 2014-05-17 01:53:27.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.106/aufs2.patch b/target/linux/patches/3.4.106/aufs2.patch deleted file mode 100644 index d40c9a3fe..000000000 --- a/target/linux/patches/3.4.106/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.106/bsd-compatibility.patch b/target/linux/patches/3.4.106/bsd-compatibility.patch deleted file mode 100644 index 9e91a62de..000000000 --- a/target/linux/patches/3.4.106/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.106/defaults.patch b/target/linux/patches/3.4.106/defaults.patch deleted file mode 100644 index 58aae610b..000000000 --- a/target/linux/patches/3.4.106/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.106/gemalto.patch b/target/linux/patches/3.4.106/gemalto.patch deleted file mode 100644 index 65f7af1d7..000000000 --- a/target/linux/patches/3.4.106/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.106/lemote-rfkill.patch b/target/linux/patches/3.4.106/lemote-rfkill.patch deleted file mode 100644 index a61488434..000000000 --- a/target/linux/patches/3.4.106/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.106/linux-gcc-check.patch b/target/linux/patches/3.4.106/linux-gcc-check.patch deleted file mode 100644 index 7cc381845..000000000 --- a/target/linux/patches/3.4.106/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.106/mips-error.patch b/target/linux/patches/3.4.106/mips-error.patch deleted file mode 100644 index 800abc80d..000000000 --- a/target/linux/patches/3.4.106/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.106/mkpiggy.patch b/target/linux/patches/3.4.106/mkpiggy.patch deleted file mode 100644 index d4e815cd2..000000000 --- a/target/linux/patches/3.4.106/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.106/module-alloc-size-check.patch b/target/linux/patches/3.4.106/module-alloc-size-check.patch deleted file mode 100644 index a792ac60a..000000000 --- a/target/linux/patches/3.4.106/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.106/non-static.patch b/target/linux/patches/3.4.106/non-static.patch deleted file mode 100644 index a967703d0..000000000 --- a/target/linux/patches/3.4.106/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.106/relocs.patch b/target/linux/patches/3.4.106/relocs.patch deleted file mode 100644 index 43c5bb580..000000000 --- a/target/linux/patches/3.4.106/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.106/sparc-aout.patch b/target/linux/patches/3.4.106/sparc-aout.patch deleted file mode 100644 index 5cd74c2e7..000000000 --- a/target/linux/patches/3.4.106/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.106/sparc-include.patch b/target/linux/patches/3.4.106/sparc-include.patch deleted file mode 100644 index 2f8ffd061..000000000 --- a/target/linux/patches/3.4.106/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.106/startup.patch b/target/linux/patches/3.4.106/startup.patch deleted file mode 100644 index c26430bcb..000000000 --- a/target/linux/patches/3.4.106/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.106/usb-defaults-off.patch b/target/linux/patches/3.4.106/usb-defaults-off.patch deleted file mode 100644 index 31367108a..000000000 --- a/target/linux/patches/3.4.106/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.106/vga-cons-default-off.patch b/target/linux/patches/3.4.106/vga-cons-default-off.patch deleted file mode 100644 index 178aeeeb9..000000000 --- a/target/linux/patches/3.4.106/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.106/wlan-cf.patch b/target/linux/patches/3.4.106/wlan-cf.patch deleted file mode 100644 index fc20759e2..000000000 --- a/target/linux/patches/3.4.106/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.106/yaffs2.patch b/target/linux/patches/3.4.106/yaffs2.patch deleted file mode 100644 index 44c95915f..000000000 --- a/target/linux/patches/3.4.106/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.106/zlib-inflate.patch b/target/linux/patches/3.4.106/zlib-inflate.patch deleted file mode 100644 index 58e1f6d21..000000000 --- a/target/linux/patches/3.4.106/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.107/aufs2.patch b/target/linux/patches/3.4.107/aufs2.patch new file mode 100644 index 000000000..d40c9a3fe --- /dev/null +++ b/target/linux/patches/3.4.107/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.107/bsd-compatibility.patch b/target/linux/patches/3.4.107/bsd-compatibility.patch new file mode 100644 index 000000000..9e91a62de --- /dev/null +++ b/target/linux/patches/3.4.107/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.107/defaults.patch b/target/linux/patches/3.4.107/defaults.patch new file mode 100644 index 000000000..58aae610b --- /dev/null +++ b/target/linux/patches/3.4.107/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.107/gemalto.patch b/target/linux/patches/3.4.107/gemalto.patch new file mode 100644 index 000000000..65f7af1d7 --- /dev/null +++ b/target/linux/patches/3.4.107/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.107/lemote-rfkill.patch b/target/linux/patches/3.4.107/lemote-rfkill.patch new file mode 100644 index 000000000..a61488434 --- /dev/null +++ b/target/linux/patches/3.4.107/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.107/linux-gcc-check.patch b/target/linux/patches/3.4.107/linux-gcc-check.patch new file mode 100644 index 000000000..7cc381845 --- /dev/null +++ b/target/linux/patches/3.4.107/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.107/mips-error.patch b/target/linux/patches/3.4.107/mips-error.patch new file mode 100644 index 000000000..800abc80d --- /dev/null +++ b/target/linux/patches/3.4.107/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.107/mkpiggy.patch b/target/linux/patches/3.4.107/mkpiggy.patch new file mode 100644 index 000000000..d4e815cd2 --- /dev/null +++ b/target/linux/patches/3.4.107/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.107/module-alloc-size-check.patch b/target/linux/patches/3.4.107/module-alloc-size-check.patch new file mode 100644 index 000000000..a792ac60a --- /dev/null +++ b/target/linux/patches/3.4.107/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.107/non-static.patch b/target/linux/patches/3.4.107/non-static.patch new file mode 100644 index 000000000..a967703d0 --- /dev/null +++ b/target/linux/patches/3.4.107/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.107/relocs.patch b/target/linux/patches/3.4.107/relocs.patch new file mode 100644 index 000000000..43c5bb580 --- /dev/null +++ b/target/linux/patches/3.4.107/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.107/sparc-aout.patch b/target/linux/patches/3.4.107/sparc-aout.patch new file mode 100644 index 000000000..5cd74c2e7 --- /dev/null +++ b/target/linux/patches/3.4.107/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.107/sparc-include.patch b/target/linux/patches/3.4.107/sparc-include.patch new file mode 100644 index 000000000..2f8ffd061 --- /dev/null +++ b/target/linux/patches/3.4.107/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.107/startup.patch b/target/linux/patches/3.4.107/startup.patch new file mode 100644 index 000000000..c26430bcb --- /dev/null +++ b/target/linux/patches/3.4.107/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.107/usb-defaults-off.patch b/target/linux/patches/3.4.107/usb-defaults-off.patch new file mode 100644 index 000000000..31367108a --- /dev/null +++ b/target/linux/patches/3.4.107/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.107/vga-cons-default-off.patch b/target/linux/patches/3.4.107/vga-cons-default-off.patch new file mode 100644 index 000000000..178aeeeb9 --- /dev/null +++ b/target/linux/patches/3.4.107/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.107/wlan-cf.patch b/target/linux/patches/3.4.107/wlan-cf.patch new file mode 100644 index 000000000..fc20759e2 --- /dev/null +++ b/target/linux/patches/3.4.107/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.107/yaffs2.patch b/target/linux/patches/3.4.107/yaffs2.patch new file mode 100644 index 000000000..44c95915f --- /dev/null +++ b/target/linux/patches/3.4.107/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.107/zlib-inflate.patch b/target/linux/patches/3.4.107/zlib-inflate.patch new file mode 100644 index 000000000..58e1f6d21 --- /dev/null +++ b/target/linux/patches/3.4.107/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 -- cgit v1.2.3