diff options
255 files changed, 5853 insertions, 4910 deletions
diff --git a/.gitignore b/.gitignore index 9ac91060ea64..acb6afe6b7a3 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,7 @@ Module.symvers /TAGS /linux /vmlinux +/vmlinux-gdb.py /vmlinuz /System.map /Module.markers diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index f2130586ef5d..faf09d4a0ea8 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -190,23 +190,6 @@ X!Edrivers/pnp/system.c !Idrivers/message/fusion/mptfc.c !Idrivers/message/fusion/mptlan.c </sect1> - <sect1><title>I2O message devices</title> -!Iinclude/linux/i2o.h -!Idrivers/message/i2o/core.h -!Edrivers/message/i2o/iop.c -!Idrivers/message/i2o/iop.c -!Idrivers/message/i2o/config-osm.c -!Edrivers/message/i2o/exec-osm.c -!Idrivers/message/i2o/exec-osm.c -!Idrivers/message/i2o/bus-osm.c -!Edrivers/message/i2o/device.c -!Idrivers/message/i2o/device.c -!Idrivers/message/i2o/driver.c -!Idrivers/message/i2o/pci.c -!Idrivers/message/i2o/i2o_block.c -!Idrivers/message/i2o/i2o_scsi.c -!Idrivers/message/i2o/i2o_proc.c - </sect1> </chapter> <chapter id="snddev"> diff --git a/Documentation/devicetree/bindings/mtd/atmel-nand.txt b/Documentation/devicetree/bindings/mtd/atmel-nand.txt index 1fe6dde98499..7d4c8eb775a5 100644 --- a/Documentation/devicetree/bindings/mtd/atmel-nand.txt +++ b/Documentation/devicetree/bindings/mtd/atmel-nand.txt @@ -1,7 +1,7 @@ Atmel NAND flash Required properties: -- compatible : "atmel,at91rm9200-nand". +- compatible : should be "atmel,at91rm9200-nand" or "atmel,sama5d4-nand". - reg : should specify localbus address and size used for the chip, and hardware ECC controller if available. If the hardware ECC is PMECC, it should contain address and size for diff --git a/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt index 823d13412195..4461dc71cb10 100644 --- a/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt +++ b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt @@ -1,7 +1,7 @@ * Freescale Quad Serial Peripheral Interface(QuadSPI) Required properties: - - compatible : Should be "fsl,vf610-qspi" + - compatible : Should be "fsl,vf610-qspi" or "fsl,imx6sx-qspi" - reg : the first contains the register location and length, the second contains the memory mapping address and length - reg-names: Should contain the reg names "QuadSPI" and "QuadSPI-memory" diff --git a/Documentation/devicetree/bindings/mtd/gpmi-nand.txt b/Documentation/devicetree/bindings/mtd/gpmi-nand.txt index a011fdf61dbf..d02acaff3c35 100644 --- a/Documentation/devicetree/bindings/mtd/gpmi-nand.txt +++ b/Documentation/devicetree/bindings/mtd/gpmi-nand.txt @@ -1,7 +1,7 @@ * Freescale General-Purpose Media Interface (GPMI) The GPMI nand controller provides an interface to control the -NAND flash chips. We support only one NAND chip now. +NAND flash chips. Required properties: - compatible : should be "fsl,<chip>-gpmi-nand" diff --git a/Documentation/devicetree/bindings/mtd/hisi504-nand.txt b/Documentation/devicetree/bindings/mtd/hisi504-nand.txt new file mode 100644 index 000000000000..2e35f0662912 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/hisi504-nand.txt @@ -0,0 +1,47 @@ +Hisilicon Hip04 Soc NAND controller DT binding + +Required properties: + +- compatible: Should be "hisilicon,504-nfc". +- reg: The first contains base physical address and size of + NAND controller's registers. The second contains base + physical address and size of NAND controller's buffer. +- interrupts: Interrupt number for nfc. +- nand-bus-width: See nand.txt. +- nand-ecc-mode: Support none and hw ecc mode. +- #address-cells: Partition address, should be set 1. +- #size-cells: Partition size, should be set 1. + +Optional properties: + +- nand-ecc-strength: Number of bits to correct per ECC step. +- nand-ecc-step-size: Number of data bytes covered by a single ECC step. + +The following ECC strength and step size are currently supported: + + - nand-ecc-strength = <16>, nand-ecc-step-size = <1024> + +Flash chip may optionally contain additional sub-nodes describing partitions of +the address space. See partition.txt for more detail. + +Example: + + nand: nand@4020000 { + compatible = "hisilicon,504-nfc"; + reg = <0x4020000 0x10000>, <0x5000000 0x1000>; + interrupts = <0 379 4>; + nand-bus-width = <8>; + nand-ecc-mode = "hw"; + nand-ecc-strength = <16>; + nand-ecc-step-size = <1024>; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "nand_text"; + reg = <0x00000000 0x00400000>; + }; + + ... + + }; diff --git a/Documentation/devicetree/bindings/mtd/mtd-physmap.txt b/Documentation/devicetree/bindings/mtd/mtd-physmap.txt index 6b9f680cb579..4a0a48bf4ecb 100644 --- a/Documentation/devicetree/bindings/mtd/mtd-physmap.txt +++ b/Documentation/devicetree/bindings/mtd/mtd-physmap.txt @@ -36,6 +36,11 @@ are defined: - vendor-id : Contains the flash chip's vendor id (1 byte). - device-id : Contains the flash chip's device id (1 byte). +For ROM compatible devices (and ROM fallback from cfi-flash), the following +additional (optional) property is defined: + + - erase-size : The chip's physical erase block size in bytes. + The device tree may optionally contain sub-nodes describing partitions of the address space. See partition.txt for more detail. diff --git a/Documentation/gdb-kernel-debugging.txt b/Documentation/gdb-kernel-debugging.txt new file mode 100644 index 000000000000..7050ce8794b9 --- /dev/null +++ b/Documentation/gdb-kernel-debugging.txt @@ -0,0 +1,160 @@ +Debugging kernel and modules via gdb +==================================== + +The kernel debugger kgdb, hypervisors like QEMU or JTAG-based hardware +interfaces allow to debug the Linux kernel and its modules during runtime +using gdb. Gdb comes with a powerful scripting interface for python. The +kernel provides a collection of helper scripts that can simplify typical +kernel debugging steps. This is a short tutorial about how to enable and use +them. It focuses on QEMU/KVM virtual machines as target, but the examples can +be transferred to the other gdb stubs as well. + + +Requirements +------------ + + o gdb 7.2+ (recommended: 7.4+) with python support enabled (typically true + for distributions) + + +Setup +----- + + o Create a virtual Linux machine for QEMU/KVM (see www.linux-kvm.org and + www.qemu.org for more details). For cross-development, + http://landley.net/aboriginal/bin keeps a pool of machine images and + toolchains that can be helpful to start from. + + o Build the kernel with CONFIG_GDB_SCRIPTS enabled, but leave + CONFIG_DEBUG_INFO_REDUCED off. If your architecture supports + CONFIG_FRAME_POINTER, keep it enabled. + + o Install that kernel on the guest. + + Alternatively, QEMU allows to boot the kernel directly using -kernel, + -append, -initrd command line switches. This is generally only useful if + you do not depend on modules. See QEMU documentation for more details on + this mode. + + o Enable the gdb stub of QEMU/KVM, either + - at VM startup time by appending "-s" to the QEMU command line + or + - during runtime by issuing "gdbserver" from the QEMU monitor + console + + o cd /path/to/linux-build + + o Start gdb: gdb vmlinux + + Note: Some distros may restrict auto-loading of gdb scripts to known safe + directories. In case gdb reports to refuse loading vmlinux-gdb.py, add + + add-auto-load-safe-path /path/to/linux-build + + to ~/.gdbinit. See gdb help for more details. + + o Attach to the booted guest: + (gdb) target remote :1234 + + +Examples of using the Linux-provided gdb helpers +------------------------------------------------ + + o Load module (and main kernel) symbols: + (gdb) lx-symbols + loading vmlinux + scanning for modules in /home/user/linux/build + loading @0xffffffffa0020000: /home/user/linux/build/net/netfilter/xt_tcpudp.ko + loading @0xffffffffa0016000: /home/user/linux/build/net/netfilter/xt_pkttype.ko + loading @0xffffffffa0002000: /home/user/linux/build/net/netfilter/xt_limit.ko + loading @0xffffffffa00ca000: /home/user/linux/build/net/packet/af_packet.ko + loading @0xffffffffa003c000: /home/user/linux/build/fs/fuse/fuse.ko + ... + loading @0xffffffffa0000000: /home/user/linux/build/drivers/ata/ata_generic.ko + + o Set a breakpoint on some not yet loaded module function, e.g.: + (gdb) b btrfs_init_sysfs + Function "btrfs_init_sysfs" not defined. + Make breakpoint pending on future shared library load? (y or [n]) y + Breakpoint 1 (btrfs_init_sysfs) pending. + + o Continue the target + (gdb) c + + o Load the module on the target and watch the symbols being loaded as well as + the breakpoint hit: + loading @0xffffffffa0034000: /home/user/linux/build/lib/libcrc32c.ko + loading @0xffffffffa0050000: /home/user/linux/build/lib/lzo/lzo_compress.ko + loading @0xffffffffa006e000: /home/user/linux/build/lib/zlib_deflate/zlib_deflate.ko + loading @0xffffffffa01b1000: /home/user/linux/build/fs/btrfs/btrfs.ko + + Breakpoint 1, btrfs_init_sysfs () at /home/user/linux/fs/btrfs/sysfs.c:36 + 36 btrfs_kset = kset_create_and_add("btrfs", NULL, fs_kobj); + + o Dump the log buffer of the target kernel: + (gdb) lx-dmesg + [ 0.000000] Initializing cgroup subsys cpuset + [ 0.000000] Initializing cgroup subsys cpu + [ 0.000000] Linux version 3.8.0-rc4-dbg+ (... + [ 0.000000] Command line: root=/dev/sda2 resume=/dev/sda1 vga=0x314 + [ 0.000000] e820: BIOS-provided physical RAM map: + [ 0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009fbff] usable + [ 0.000000] BIOS-e820: [mem 0x000000000009fc00-0x000000000009ffff] reserved + .... + + o Examine fields of the current task struct: + (gdb) p $lx_current().pid + $1 = 4998 + (gdb) p $lx_current().comm + $2 = "modprobe\000\000\000\000\000\000\000" + + o Make use of the per-cpu function for the current or a specified CPU: + (gdb) p $lx_per_cpu("runqueues").nr_running + $3 = 1 + (gdb) p $lx_per_cpu("runqueues", 2).nr_running + $4 = 0 + + o Dig into hrtimers using the container_of helper: + (gdb) set $next = $lx_per_cpu("hrtimer_bases").clock_base[0].active.next + (gdb) p *$container_of($next, "struct hrtimer", "node") + $5 = { + node = { + node = { + __rb_parent_color = 18446612133355256072, + rb_right = 0x0 <irq_stack_union>, + rb_left = 0x0 <irq_stack_union> + }, + expires = { + tv64 = 1835268000000 + } + }, + _softexpires = { + tv64 = 1835268000000 + }, + function = 0xffffffff81078232 <tick_sched_timer>, + base = 0xffff88003fd0d6f0, + state = 1, + start_pid = 0, + start_site = 0xffffffff81055c1f <hrtimer_start_range_ns+20>, + start_comm = "swapper/2\000\000\000\000\000\000" + } + + +List of commands and functions +------------------------------ + +The number of commands and convenience functions may evolve over the time, +this is just a snapshot of the initial version: + + (gdb) apropos lx + function lx_current -- Return current task + function lx_module -- Find module by name and return the module variable + function lx_per_cpu -- Return per-cpu variable + function lx_task_by_pid -- Find Linux task by PID and return the task_struct variable + function lx_thread_info -- Calculate Linux thread_info from task variable + lx-dmesg -- Print Linux kernel log buffer + lx-lsmod -- List currently loaded modules + lx-symbols -- (Re-)load symbols of Linux kernel and currently loaded modules + +Detailed help can be obtained via "help <command-name>" for commands and "help +function <function-name>" for convenience functions. diff --git a/MAINTAINERS b/MAINTAINERS index 85024e23309f..7ac95f8ba6ca 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4092,6 +4092,12 @@ S: Maintained F: include/linux/platform_data/video-imxfb.h F: drivers/video/fbdev/imxfb.c +FREESCALE QUAD SPI DRIVER +M: Han Xu <han.xu@freescale.com> +L: linux-mtd@lists.infradead.org +S: Maintained +F: drivers/mtd/spi-nor/fsl-quadspi.c + FREESCALE SOC FS_ENET DRIVER M: Pantelis Antoniou <pantelis.antoniou@gmail.com> M: Vitaly Bordug <vbordug@ru.mvista.com> @@ -4232,6 +4238,11 @@ W: http://www.icp-vortex.com/ S: Supported F: drivers/scsi/gdt* +GDB KERNEL DEBUGGING HELPER SCRIPTS +M: Jan Kiszka <jan.kiszka@siemens.com> +S: Supported +F: scripts/gdb/ + GEMTEK FM RADIO RECEIVER DRIVER M: Hans Verkuil <hverkuil@xs4all.nl> L: linux-media@vger.kernel.org @@ -6876,7 +6887,7 @@ F: drivers/scsi/nsp32* NIOS2 ARCHITECTURE M: Ley Foon Tan <lftan@altera.com> L: nios2-dev@lists.rocketboards.org (moderated for non-subscribers) -T: git git://git.rocketboards.org/linux-socfpga.git +T: git git://git.rocketboards.org/linux-socfpga-next.git S: Maintained F: arch/nios2/ @@ -927,6 +927,9 @@ endif ifdef CONFIG_BUILD_DOCSRC $(Q)$(MAKE) $(build)=Documentation endif +ifdef CONFIG_GDB_SCRIPTS + $(Q)ln -fsn `cd $(srctree) && /bin/pwd`/scripts/gdb/vmlinux-gdb.py +endif +$(call if_changed,link-vmlinux) # The actual objects are generated when descending, @@ -1181,7 +1184,7 @@ MRPROPER_FILES += .config .config.old .version .old_version $(version_h) \ Module.symvers tags TAGS cscope* GPATH GTAGS GRTAGS GSYMS \ signing_key.priv signing_key.x509 x509.genkey \ extra_certificates signing_key.x509.keyid \ - signing_key.x509.signer + signing_key.x509.signer vmlinux-gdb.py # clean - Delete most, but leave enough to build external modules # diff --git a/arch/mips/include/asm/mach-jz4740/jz4740_nand.h b/arch/mips/include/asm/mach-jz4740/jz4740_nand.h index 986982db7c38..79cff26d8b36 100644 --- a/arch/mips/include/asm/mach-jz4740/jz4740_nand.h +++ b/arch/mips/include/asm/mach-jz4740/jz4740_nand.h @@ -27,8 +27,6 @@ struct jz_nand_platform_data { struct nand_ecclayout *ecc_layout; - unsigned int busy_gpio; - unsigned char banks[JZ_NAND_NUM_BANKS]; void (*ident_callback)(struct platform_device *, struct nand_chip *, diff --git a/arch/mips/jz4740/board-qi_lb60.c b/arch/mips/jz4740/board-qi_lb60.c index c454525e7695..9dd051edb411 100644 --- a/arch/mips/jz4740/board-qi_lb60.c +++ b/arch/mips/jz4740/board-qi_lb60.c @@ -140,10 +140,18 @@ static void qi_lb60_nand_ident(struct platform_device *pdev, static struct jz_nand_platform_data qi_lb60_nand_pdata = { .ident_callback = qi_lb60_nand_ident, - .busy_gpio = 94, .banks = { 1 }, }; +static struct gpiod_lookup_table qi_lb60_nand_gpio_table = { + .dev_id = "jz4740-nand.0", + .table = { + GPIO_LOOKUP("Bank C", 30, "busy", 0), + { }, + }, +}; + + /* Keyboard*/ #define KEY_QI_QI KEY_F13 @@ -472,6 +480,7 @@ static int __init qi_lb60_init_platform_devices(void) jz4740_mmc_device.dev.platform_data = &qi_lb60_mmc_pdata; gpiod_add_lookup_table(&qi_lb60_audio_gpio_table); + gpiod_add_lookup_table(&qi_lb60_nand_gpio_table); jz4740_serial_device_register(); diff --git a/arch/nios2/Kconfig b/arch/nios2/Kconfig index 2361acf6d2b1..437555424bda 100644 --- a/arch/nios2/Kconfig +++ b/arch/nios2/Kconfig @@ -8,6 +8,7 @@ config NIOS2 select GENERIC_IRQ_PROBE select GENERIC_IRQ_SHOW select HAVE_ARCH_TRACEHOOK + select HAVE_ARCH_KGDB select IRQ_DOMAIN select MODULES_USE_ELF_RELA select OF @@ -134,6 +135,14 @@ config NIOS2_PASS_CMDLINE will override "Default kernel command string". Say N if you are unsure. +config NIOS2_BOOT_LINK_OFFSET + hex "Link address offset for booting" + default "0x00500000" + help + This option allows you to set the link address offset of the zImage. + This can be useful if you are on a board which has a small amount of + memory. + endmenu menu "Advanced setup" diff --git a/arch/nios2/Kconfig.debug b/arch/nios2/Kconfig.debug index 8d4e6bacd997..2fd08cbfdddb 100644 --- a/arch/nios2/Kconfig.debug +++ b/arch/nios2/Kconfig.debug @@ -14,4 +14,15 @@ config DEBUG_STACK_USAGE This option will slow down process creation somewhat. +config EARLY_PRINTK + bool "Activate early kernel debugging" + default y + select SERIAL_CORE_CONSOLE + depends on SERIAL_ALTERA_JTAGUART_CONSOLE || SERIAL_ALTERA_UART_CONSOLE + help + Enable early printk on console + This is useful for kernel debugging when your machine crashes very + early before the console code is initialized. + You should normally say N here, unless you want to debug such a crash. + endmenu diff --git a/arch/nios2/boot/Makefile b/arch/nios2/boot/Makefile index 59392dc0bdcb..c899876320df 100644 --- a/arch/nios2/boot/Makefile +++ b/arch/nios2/boot/Makefile @@ -24,6 +24,13 @@ $(obj)/vmImage: $(obj)/vmlinux.gz $(call if_changed,uimage) @$(kecho) 'Kernel: $@ is ready' +$(obj)/zImage: $(obj)/compressed/vmlinux FORCE + $(call if_changed,objcopy) + @$(kecho) 'Kernel: $@ is ready' + +$(obj)/compressed/vmlinux: $(obj)/vmlinux.gz FORCE + $(Q)$(MAKE) $(build)=$(obj)/compressed $@ + # Rule to build device tree blobs DTB_SRC := $(patsubst "%",%,$(CONFIG_NIOS2_DTB_SOURCE)) diff --git a/arch/nios2/boot/compressed/Makefile b/arch/nios2/boot/compressed/Makefile new file mode 100644 index 000000000000..5b0fb346d888 --- /dev/null +++ b/arch/nios2/boot/compressed/Makefile @@ -0,0 +1,19 @@ +# +# create a compressed vmlinux image from the original vmlinux +# + +targets := vmlinux head.o misc.o piggy.o vmlinux.lds +asflags-y := + +OBJECTS = $(obj)/head.o $(obj)/misc.o + +LDFLAGS_vmlinux := -T + +$(obj)/vmlinux: $(obj)/vmlinux.lds $(OBJECTS) $(obj)/piggy.o FORCE + $(call if_changed,ld) + @: + +LDFLAGS_piggy.o := -r --format binary --oformat elf32-littlenios2 -T + +$(obj)/piggy.o: $(obj)/vmlinux.scr $(obj)/../vmlinux.gz FORCE + $(call if_changed,ld) diff --git a/arch/nios2/boot/compressed/console.c b/arch/nios2/boot/compressed/console.c new file mode 100644 index 000000000000..2675e879b85a --- /dev/null +++ b/arch/nios2/boot/compressed/console.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2008-2010 Thomas Chou <thomas@wytron.com.tw> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <linux/io.h> + +#if (defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE) && defined(JTAG_UART_BASE))\ + || (defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE) && defined(UART0_BASE)) +static void *my_ioremap(unsigned long physaddr) +{ + return (void *)(physaddr | CONFIG_NIOS2_IO_REGION_BASE); +} +#endif + +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE) && defined(JTAG_UART_BASE) + +#define ALTERA_JTAGUART_SIZE 8 +#define ALTERA_JTAGUART_DATA_REG 0 +#define ALTERA_JTAGUART_CONTROL_REG 4 +#define ALTERA_JTAGUART_CONTROL_AC_MSK (0x00000400) +#define ALTERA_JTAGUART_CONTROL_WSPACE_MSK (0xFFFF0000) +static void *uartbase; + +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS) +static void jtag_putc(int ch) +{ + if (readl(uartbase + ALTERA_JTAGUART_CONTROL_REG) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) + writeb(ch, uartbase + ALTERA_JTAGUART_DATA_REG); +} +#else +static void jtag_putc(int ch) +{ + while ((readl(uartbase + ALTERA_JTAGUART_CONTROL_REG) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) + ; + writeb(ch, uartbase + ALTERA_JTAGUART_DATA_REG); +} +#endif + +static int putchar(int ch) +{ + jtag_putc(ch); + return ch; +} + +static void console_init(void) +{ + uartbase = my_ioremap((unsigned long) JTAG_UART_BASE); + writel(ALTERA_JTAGUART_CONTROL_AC_MSK, + uartbase + ALTERA_JTAGUART_CONTROL_REG); +} + +#elif defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE) && defined(UART0_BASE) + +#define ALTERA_UART_SIZE 32 +#define ALTERA_UART_TXDATA_REG 4 +#define ALTERA_UART_STATUS_REG 8 +#define ALTERA_UART_DIVISOR_REG 16 +#define ALTERA_UART_STATUS_TRDY_MSK (0x40) +static unsigned uartbase; + +static void uart_putc(int ch) +{ + int i; + + for (i = 0; (i < 0x10000); i++) { + if (readw(uartbase + ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_TRDY_MSK) + break; + } + writeb(ch, uartbase + ALTERA_UART_TXDATA_REG); +} + +static int putchar(int ch) +{ + uart_putc(ch); + if (ch == '\n') + uart_putc('\r'); + return ch; +} + +static void console_init(void) +{ + unsigned int baud, baudclk; + + uartbase = (unsigned long) my_ioremap((unsigned long) UART0_BASE); + baud = CONFIG_SERIAL_ALTERA_UART_BAUDRATE; + baudclk = UART0_FREQ / baud; + writew(baudclk, uartbase + ALTERA_UART_DIVISOR_REG); +} + +#else + +static int putchar(int ch) +{ + return ch; +} + +static void console_init(void) +{ +} + +#endif + +static int puts(const char *s) +{ + while (*s) + putchar(*s++); + return 0; +} diff --git a/arch/nios2/boot/compressed/head.S b/arch/nios2/boot/compressed/head.S new file mode 100644 index 000000000000..15c6c48dd909 --- /dev/null +++ b/arch/nios2/boot/compressed/head.S @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2009 Thomas Chou <thomas@wytron.com.tw> + * + * Based on arch/nios2/kernel/head.S + * + * 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. + * + */ + +/* + * This code can be loaded anywhere, eg FLASH ROM as reset vector, + * as long as output does not overlap it. + */ + +#include <linux/linkage.h> +#include <asm/cache.h> + + .text + .set noat +ENTRY(_start) + wrctl status, r0 /* disable interrupt */ + /* invalidate all instruction cache */ + movia r1, NIOS2_ICACHE_SIZE + movui r2, NIOS2_ICACHE_LINE_SIZE +1: initi r1 + sub r1, r1, r2 + bgt r1, r0, 1b + /* invalidate all data cache */ + movia r1, NIOS2_DCACHE_SIZE + movui r2, NIOS2_DCACHE_LINE_SIZE +1: initd 0(r1) + sub r1, r1, r2 + bgt r1, r0, 1b + + nextpc r1 /* Find out where we are */ +chkadr: + movia r2, chkadr + beq r1, r2, finish_move /* We are running in correct address, + done */ + /* move code, r1: src, r2: dest, r3: last dest */ + addi r1, r1, (_start - chkadr) /* Source */ + movia r2, _start /* Destination */ + movia r3, __bss_start /* End of copy */ +1: ldw r8, 0(r1) /* load a word from [r1] */ + stw r8, 0(r2) /* stort a word to dest [r2] */ + addi r1, r1, 4 /* inc the src addr */ + addi r2, r2, 4 /* inc the dest addr */ + blt r2, r3, 1b + /* flush the data cache after moving */ + movia r1, NIOS2_DCACHE_SIZE + movui r2, NIOS2_DCACHE_LINE_SIZE +1: flushd 0(r1) + sub r1, r1, r2 + bgt r1, r0, 1b + movia r1, finish_move + jmp r1 /* jmp to linked address */ + +finish_move: + /* zero out the .bss segment (uninitialized common data) */ + movia r2, __bss_start /* presume nothing is between */ + movia r1, _end /* the .bss and _end. */ +1: stb r0, 0(r2) + addi r2, r2, 1 + bne r1, r2, 1b + /* + * set up the stack pointer, some where higher than _end. + * The stack space must be greater than 32K for decompress. + */ + movia sp, 0x10000 + add sp, sp, r1 + /* save args passed from u-boot, maybe */ + addi sp, sp, -16 + stw r4, 0(sp) + stw r5, 4(sp) + stw r6, 8(sp) + stw r7, 12(sp) + /* decompress the kernel */ + call decompress_kernel + /* pass saved args to kernel */ + ldw r4, 0(sp) + ldw r5, 4(sp) + ldw r6, 8(sp) + ldw r7, 12(sp) + + /* flush all data cache after decompressing */ + movia r1, NIOS2_DCACHE_SIZE + movui r2, NIOS2_DCACHE_LINE_SIZE +1: flushd 0(r1) + sub r1, r1, r2 + bgt r1, r0, 1b + /* flush all instruction cache */ + movia r1, NIOS2_ICACHE_SIZE + movui r2, NIOS2_ICACHE_LINE_SIZE +1: flushi r1 + sub r1, r1, r2 + bgt r1, r0, 1b + flushp + /* jump to start real kernel */ + movia r1, (CONFIG_NIOS2_MEM_BASE | CONFIG_NIOS2_KERNEL_REGION_BASE) + jmp r1 + + .balign 512 +fake_headers_as_bzImage: + .short 0 + .ascii "HdrS" + .short 0x0202 + .short 0 + .short 0 + .byte 0x00, 0x10 + .short 0 + .byte 0 + .byte 1 + .byte 0x00, 0x80 + .long 0 + .long 0 diff --git a/arch/nios2/boot/compressed/misc.c b/arch/nios2/boot/compressed/misc.c new file mode 100644 index 000000000000..84377825ef1a --- /dev/null +++ b/arch/nios2/boot/compressed/misc.c @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2009 Thomas Chou <thomas@wytron.com.tw> + * + * This is a collection of several routines from gzip-1.0.3 + * adapted for Linux. + * + * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 + * + * Adapted for SH by Stuart Menefy, Aug 1999 + * + * Modified to use standard LinuxSH BIOS by Greg Banks 7Jul2000 + * + * Based on arch/sh/boot/compressed/misc.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <linux/string.h> + +/* + * gzip declarations + */ +#define OF(args) args +#define STATIC static + +#undef memset +#undef memcpy +#define memzero(s, n) memset((s), 0, (n)) + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; +#define WSIZE 0x8000 /* Window size must be at least 32k, */ + /* and a power of two */ + +static uch *inbuf; /* input buffer */ +static uch window[WSIZE]; /* Sliding window buffer */ + +static unsigned insize; /* valid bytes in inbuf */ +static unsigned inptr; /* index of next byte to be processed in inbuf */ +static unsigned outcnt; /* bytes in output buffer */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip + file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) + +#ifdef DEBUG +# define Assert(cond, msg) {if (!(cond)) error(msg); } +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ; } +# define Tracevv(x) {if (verbose > 1) fprintf x ; } +# define Tracec(c, x) {if (verbose && (c)) fprintf x ; } +# define Tracecv(c, x) {if (verbose > 1 && (c)) fprintf x ; } +#else +# define Assert(cond, msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c, x) +# define Tracecv(c, x) +#endif +static int fill_inbuf(void); +static void flush_window(void); +static void error(char *m); + +extern char input_data[]; +extern int input_len; + +static long bytes_out; +static uch *output_data; +static unsigned long output_ptr; + +#include "console.c" + +static void error(char *m); + +int puts(const char *); + +extern int _end; +static unsigned long free_mem_ptr; +static unsigned long free_mem_end_ptr; + +#define HEAP_SIZE 0x10000 + +#include "../../../../lib/inflate.c" + +void *memset(void *s, int c, size_t n) +{ + int i; + char *ss = (char *)s; + + for (i = 0; i < n; i++) + ss[i] = c; + return s; +} + +void *memcpy(void *__dest, __const void *__src, size_t __n) +{ + int i; + char *d = (char *)__dest, *s = (char *)__src; + + for (i = 0; i < __n; i++) + d[i] = s[i]; + return __dest; +} + +/* + * Fill the input buffer. This is called only when the buffer is empty + * and at least one byte is really needed. + */ +static int fill_inbuf(void) +{ + if (insize != 0) + error("ran out of input data"); + + inbuf = input_data; + insize = input_len; + inptr = 1; + return inbuf[0]; +} + +/* + * Write the output window window[0..outcnt-1] and update crc and bytes_out. + * (Used for the decompressed data only.) + */ +static void flush_window(void) +{ + ulg c = crc; /* temporary variable */ + unsigned n; + uch *in, *out, ch; + + in = window; + out = &output_data[output_ptr]; + for (n = 0; n < outcnt; n++) { + ch = *out++ = *in++; + c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); + } + crc = c; + bytes_out += (ulg)outcnt; + output_ptr += (ulg)outcnt; + outcnt = 0; +} + +static void error(char *x) +{ + puts("\nERROR\n"); + puts(x); + puts("\n\n -- System halted"); + + while (1) /* Halt */ + ; +} + +void decompress_kernel(void) +{ + output_data = (void *) (CONFIG_NIOS2_MEM_BASE | + CONFIG_NIOS2_KERNEL_REGION_BASE); + output_ptr = 0; + free_mem_ptr = (unsigned long)&_end; + free_mem_end_ptr = free_mem_ptr + HEAP_SIZE; + + console_init(); + makecrc(); + puts("Uncompressing Linux... "); + gunzip(); + puts("Ok, booting the kernel.\n"); +} diff --git a/arch/nios2/boot/compressed/vmlinux.lds.S b/arch/nios2/boot/compressed/vmlinux.lds.S new file mode 100644 index 000000000000..e867b3756059 --- /dev/null +++ b/arch/nios2/boot/compressed/vmlinux.lds.S @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2009 Thomas Chou <thomas@wytron.com.tw> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <asm-generic/vmlinux.lds.h> + +OUTPUT_FORMAT("elf32-littlenios2", "elf32-littlenios2", "elf32-littlenios2") + +OUTPUT_ARCH(nios) +ENTRY(_start) /* Defined in head.S */ + +SECTIONS +{ + . = (CONFIG_NIOS2_MEM_BASE + CONFIG_NIOS2_BOOT_LINK_OFFSET) | \ + CONFIG_NIOS2_KERNEL_REGION_BASE; + + _text = .; + .text : { *(.text) } = 0 + .rodata : { *(.rodata) *(.rodata.*) } + _etext = .; + + . = ALIGN(32 / 8); + .data : { *(.data) } + . = ALIGN(32 / 8); + _got = .; + .got : { + *(.got.plt) + *(.igot.plt) + *(.got) + *(.igot) + } + _egot = .; + _edata = .; + + . = ALIGN(32 / 8); + __bss_start = .; + .bss : { *(.bss) *(.sbss) } + . = ALIGN(32 / 8); + _ebss = .; + end = . ; + _end = . ; + + got_len = (_egot - _got); +} diff --git a/arch/nios2/boot/compressed/vmlinux.scr b/arch/nios2/boot/compressed/vmlinux.scr new file mode 100644 index 000000000000..28c42f1d127e --- /dev/null +++ b/arch/nios2/boot/compressed/vmlinux.scr @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2009 Thomas Chou <thomas@wytron.com.tw> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +SECTIONS +{ + .data : { + input_len = .; + LONG(input_data_end - input_data) input_data = .; + *(.data) + . = ALIGN(4); + input_data_end = .; + } +} diff --git a/arch/nios2/configs/3c120_defconfig b/arch/nios2/configs/3c120_defconfig index 87541f0a5d6e..9451940678a0 100644 --- a/arch/nios2/configs/3c120_defconfig +++ b/arch/nios2/configs/3c120_defconfig @@ -22,6 +22,7 @@ CONFIG_NIOS2_DCACHE_SIZE=0x8000 CONFIG_NIOS2_ICACHE_SIZE=0x8000 # CONFIG_NIOS2_CMDLINE_IGNORE_DTB is not set CONFIG_NIOS2_PASS_CMDLINE=y +CONFIG_NIOS2_BOOT_LINK_OFFSET=0x00800000 CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y diff --git a/arch/nios2/include/asm/kgdb.h b/arch/nios2/include/asm/kgdb.h new file mode 100644 index 000000000000..8fd5e3b66c57 --- /dev/null +++ b/arch/nios2/include/asm/kgdb.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2015 Altera Corporation + * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch> + * + * Based on the code posted by Kazuyasu on the Altera Forum at: + * http://www.alteraforum.com/forum/showpost.php?p=77003&postcount=20 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef _ASM_NIOS2_KGDB_H +#define _ASM_NIOS2_KGDB_H + +#define CACHE_FLUSH_IS_SAFE 1 +#define BUFMAX 2048 + +enum regnames { + GDB_R0 = 0, + GDB_AT, + GDB_R2, + GDB_R3, + GDB_R4, + GDB_R5, + GDB_R6, + GDB_R7, + GDB_R8, + GDB_R9, + GDB_R10, + GDB_R11, + GDB_R12, + GDB_R13, + GDB_R14, + GDB_R15, + GDB_R16, + GDB_R17, + GDB_R18, + GDB_R19, + GDB_R20, + GDB_R21, + GDB_R22, + GDB_R23, + GDB_ET, + GDB_BT, + GDB_GP, + GDB_SP, + GDB_FP, + GDB_EA, + GDB_BA, + GDB_RA, + GDB_PC, + GDB_STATUS, + GDB_ESTATUS, + GDB_BSTATUS, + GDB_IENABLE, + GDB_IPENDING, + GDB_CPUID, + GDB_CTL6, + GDB_EXCEPTION, + GDB_PTEADDR, + GDB_TLBACC, + GDB_TLBMISC, + GDB_ECCINJ, + GDB_BADADDR, + GDB_CONFIG, + GDB_MPUBASE, + GDB_MPUACC, + /* do not change the last entry or anything below! */ + GDB_NUMREGBYTES /* number of registers */ +}; + +#define GDB_SIZEOF_REG sizeof(u32) +#define DBG_MAX_REG_NUM (49) +#define NUMREGBYTES (DBG_MAX_REG_NUM * sizeof(GDB_SIZEOF_REG)) + +#define BREAK_INSTR_SIZE 4 +static inline void arch_kgdb_breakpoint(void) +{ + __asm__ __volatile__("trap 30\n"); +} + +#endif /* _ASM_NIOS2_KGDB_H */ diff --git a/arch/nios2/include/asm/processor.h b/arch/nios2/include/asm/processor.h index 3bd349473b06..c2ba45c159c7 100644 --- a/arch/nios2/include/asm/processor.h +++ b/arch/nios2/include/asm/processor.h @@ -85,9 +85,6 @@ static inline void exit_thread(void) extern unsigned long get_wchan(struct task_struct *p); -/* Prepare to copy thread state - unlazy all lazy status */ -#define prepare_to_copy(tsk) do { } while (0) - #define task_pt_regs(p) \ ((struct pt_regs *)(THREAD_SIZE + task_stack_page(p)) - 1) diff --git a/arch/nios2/include/asm/prom.h b/arch/nios2/include/asm/prom.h new file mode 100644 index 000000000000..75fffb42cfa5 --- /dev/null +++ b/arch/nios2/include/asm/prom.h @@ -0,0 +1,22 @@ +/* + * Copyright Altera Corporation (C) <2015>. All rights reserved + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __ASM_NIOS2_PROM_H__ +#define __ASM_NIOS2_PROM_H__ + +extern unsigned long __init of_early_console(void); + +#endif diff --git a/arch/nios2/kernel/Makefile b/arch/nios2/kernel/Makefile index 8ae76823ff93..1aae25703657 100644 --- a/arch/nios2/kernel/Makefile +++ b/arch/nios2/kernel/Makefile @@ -20,5 +20,7 @@ obj-y += syscall_table.o obj-y += time.o obj-y += traps.o +obj-$(CONFIG_EARLY_PRINTK) += early_printk.o +obj-$(CONFIG_KGDB) += kgdb.o obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_NIOS2_ALIGNMENT_TRAP) += misaligned.o diff --git a/arch/nios2/kernel/early_printk.c b/arch/nios2/kernel/early_printk.c new file mode 100644 index 000000000000..c08e4c1486fc --- /dev/null +++ b/arch/nios2/kernel/early_printk.c @@ -0,0 +1,118 @@ +/* + * Early printk for Nios2. + * + * Copyright (C) 2015, Altera Corporation + * Copyright (C) 2010, Tobias Klauser <tklauser@distanz.ch> + * Copyright (C) 2009, Wind River Systems Inc + * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com + * + * 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 <linux/console.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/io.h> + +#include <asm/prom.h> + +static unsigned long base_addr; + +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE) + +#define ALTERA_JTAGUART_DATA_REG 0 +#define ALTERA_JTAGUART_CONTROL_REG 4 +#define ALTERA_JTAGUART_CONTROL_WSPACE_MSK 0xFFFF0000 +#define ALTERA_JTAGUART_CONTROL_AC_MSK 0x00000400 + +#define JUART_GET_CR() \ + __builtin_ldwio((void *)(base_addr + ALTERA_JTAGUART_CONTROL_REG)) +#define JUART_SET_CR(v) \ + __builtin_stwio((void *)(base_addr + ALTERA_JTAGUART_CONTROL_REG), v) +#define JUART_SET_TX(v) \ + __builtin_stwio((void *)(base_addr + ALTERA_JTAGUART_DATA_REG), v) + +static void early_console_write(struct console *con, const char *s, unsigned n) +{ + unsigned long status; + + while (n-- && *s) { + while (((status = JUART_GET_CR()) + & ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) { +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS) + if ((status & ALTERA_JTAGUART_CONTROL_AC_MSK) == 0) + return; /* no connection activity */ +#endif + } + JUART_SET_TX(*s); + s++; + } +} + +#elif defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE) + +#define ALTERA_UART_TXDATA_REG 4 +#define ALTERA_UART_STATUS_REG 8 +#define ALTERA_UART_STATUS_TRDY 0x0040 + +#define UART_GET_SR() \ + __builtin_ldwio((void *)(base_addr + ALTERA_UART_STATUS_REG)) +#define UART_SET_TX(v) \ + __builtin_stwio((void *)(base_addr + ALTERA_UART_TXDATA_REG), v) + +static void early_console_putc(char c) +{ + while (!(UART_GET_SR() & ALTERA_UART_STATUS_TRDY)) + ; + + UART_SET_TX(c); +} + +static void early_console_write(struct console *con, const char *s, unsigned n) +{ + while (n-- && *s) { + early_console_putc(*s); + if (*s == '\n') + early_console_putc('\r'); + s++; + } +} + +#else +# error Neither SERIAL_ALTERA_JTAGUART_CONSOLE nor SERIAL_ALTERA_UART_CONSOLE \ +selected +#endif + +static struct console early_console_prom = { + .name = "early", + .write = early_console_write, + .flags = CON_PRINTBUFFER | CON_BOOT, + .index = -1 +}; + +void __init setup_early_printk(void) +{ +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE) || \ + defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE) + base_addr = of_early_console(); +#else + base_addr = 0; +#endif + + if (!base_addr) + return; + +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS) + /* Clear activity bit so BYPASS doesn't stall if we've used JTAG for + * downloading the kernel. This might cause early data to be lost even + * if the JTAG terminal is running. + */ + JUART_SET_CR(JUART_GET_CR() | ALTERA_JTAGUART_CONTROL_AC_MSK); +#endif + + early_console = &early_console_prom; + register_console(early_console); + pr_info("early_console initialized at 0x%08lx\n", base_addr); +} diff --git a/arch/nios2/kernel/entry.S b/arch/nios2/kernel/entry.S index 0bdfd13ff98b..7729bd3f2e79 100644 --- a/arch/nios2/kernel/entry.S +++ b/arch/nios2/kernel/entry.S @@ -121,7 +121,11 @@ trap_table: .word instruction_trap /* 27 */ .word instruction_trap /* 28 */ .word instruction_trap /* 29 */ - .word instruction_trap /* 30 */ +#ifdef CONFIG_KGDB + .word handle_kgdb_breakpoint /* 30 KGDB breakpoint */ +#else + .word instruction_trap /* 30 */ +#endif .word handle_breakpoint /* 31 */ .text @@ -445,6 +449,12 @@ handle_diverror: call handle_diverror_c br ret_from_exception +#ifdef CONFIG_KGDB +handle_kgdb_breakpoint: + call kgdb_breakpoint_c + br ret_from_exception +#endif + /* * Beware - when entering resume, prev (the current task) is * in r4, next (the new task) is in r5, don't change these diff --git a/arch/nios2/kernel/kgdb.c b/arch/nios2/kernel/kgdb.c new file mode 100644 index 000000000000..117859122d1c --- /dev/null +++ b/arch/nios2/kernel/kgdb.c @@ -0,0 +1,171 @@ +/* + * Nios2 KGDB support + * + * Copyright (C) 2015 Altera Corporation + * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch> + * + * Based on the code posted by Kazuyasu on the Altera Forum at: + * http://www.alteraforum.com/forum/showpost.php?p=77003&postcount=20 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +#include <linux/ptrace.h> +#include <linux/kgdb.h> +#include <linux/kdebug.h> +#include <linux/io.h> + +static int wait_for_remote_debugger; + +struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = +{ + { "zero", GDB_SIZEOF_REG, -1 }, + { "at", GDB_SIZEOF_REG, offsetof(struct pt_regs, r1) }, + { "r2", GDB_SIZEOF_REG, offsetof(struct pt_regs, r2) }, + { "r3", GDB_SIZEOF_REG, offsetof(struct pt_regs, r3) }, + { "r4", GDB_SIZEOF_REG, offsetof(struct pt_regs, r4) }, + { "r5", GDB_SIZEOF_REG, offsetof(struct pt_regs, r5) }, + { "r6", GDB_SIZEOF_REG, offsetof(struct pt_regs, r6) }, + { "r7", GDB_SIZEOF_REG, offsetof(struct pt_regs, r7) }, + { "r8", GDB_SIZEOF_REG, offsetof(struct pt_regs, r8) }, + { "r9", GDB_SIZEOF_REG, offsetof(struct pt_regs, r9) }, + { "r10", GDB_SIZEOF_REG, offsetof(struct pt_regs, r10) }, + { "r11", GDB_SIZEOF_REG, offsetof(struct pt_regs, r11) }, + { "r12", GDB_SIZEOF_REG, offsetof(struct pt_regs, r12) }, + { "r13", GDB_SIZEOF_REG, offsetof(struct pt_regs, r13) }, + { "r14", GDB_SIZEOF_REG, offsetof(struct pt_regs, r14) }, + { "r15", GDB_SIZEOF_REG, offsetof(struct pt_regs, r15) }, + { "r16", GDB_SIZEOF_REG, -1 }, + { "r17", GDB_SIZEOF_REG, -1 }, + { "r18", GDB_SIZEOF_REG, -1 }, + { "r19", GDB_SIZEOF_REG, -1 }, + { "r20", GDB_SIZEOF_REG, -1 }, + { "r21", GDB_SIZEOF_REG, -1 }, + { "r22", GDB_SIZEOF_REG, -1 }, + { "r23", GDB_SIZEOF_REG, -1 }, + { "et", GDB_SIZEOF_REG, -1 }, + { "bt", GDB_SIZEOF_REG, -1 }, + { "gp", GDB_SIZEOF_REG, offsetof(struct pt_regs, gp) }, + { "sp", GDB_SIZEOF_REG, offsetof(struct pt_regs, sp) }, + { "fp", GDB_SIZEOF_REG, offsetof(struct pt_regs, fp) }, + { "ea", GDB_SIZEOF_REG, -1 }, + { "ba", GDB_SIZEOF_REG, -1 }, + { "ra", GDB_SIZEOF_REG, offsetof(struct pt_regs, ra) }, + { "pc", GDB_SIZEOF_REG, offsetof(struct pt_regs, ea) }, + { "status", GDB_SIZEOF_REG, -1 }, + { "estatus", GDB_SIZEOF_REG, offsetof(struct pt_regs, estatus) }, + { "bstatus", GDB_SIZEOF_REG, -1 }, + { "ienable", GDB_SIZEOF_REG, -1 }, + { "ipending", GDB_SIZEOF_REG, -1}, + { "cpuid", GDB_SIZEOF_REG, -1 }, + { "ctl6", GDB_SIZEOF_REG, -1 }, + { "exception", GDB_SIZEOF_REG, -1 }, + { "pteaddr", GDB_SIZEOF_REG, -1 }, + { "tlbacc", GDB_SIZEOF_REG, -1 }, + { "tlbmisc", GDB_SIZEOF_REG, -1 }, + { "eccinj", GDB_SIZEOF_REG, -1 }, + { "badaddr", GDB_SIZEOF_REG, -1 }, + { "config", GDB_SIZEOF_REG, -1 }, + { "mpubase", GDB_SIZEOF_REG, -1 }, + { "mpuacc", GDB_SIZEOF_REG, -1 }, +}; + +char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs) +{ + if (regno >= DBG_MAX_REG_NUM || regno < 0) + return NULL; + + if (dbg_reg_def[regno].offset != -1) + memcpy(mem, (void *)regs + dbg_reg_def[regno].offset, + dbg_reg_def[regno].size); + else + memset(mem, 0, dbg_reg_def[regno].size); + + return dbg_reg_def[regno].name; +} + +int dbg_set_reg(int regno, void *mem, struct pt_regs *regs) +{ + if (regno >= DBG_MAX_REG_NUM || regno < 0) + return -EINVAL; + + if (dbg_reg_def[regno].offset != -1) + memcpy((void *)regs + dbg_reg_def[regno].offset, mem, + dbg_reg_def[regno].size); + + return 0; +} + +void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) +{ + memset((char *)gdb_regs, 0, NUMREGBYTES); + gdb_regs[GDB_SP] = p->thread.kregs->sp; + gdb_regs[GDB_PC] = p->thread.kregs->ea; +} + +void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc) +{ + regs->ea = pc; +} + +int kgdb_arch_handle_exception(int vector, int signo, int err_code, + char *remcom_in_buffer, char *remcom_out_buffer, + struct pt_regs *regs) +{ + char *ptr; + unsigned long addr; + + switch (remcom_in_buffer[0]) { + case 's': + case 'c': + /* handle the optional parameters */ + ptr = &remcom_in_buffer[1]; + if (kgdb_hex2long(&ptr, &addr)) + regs->ea = addr; + + return 0; + } + + return -1; /* this means that we do not want to exit from the handler */ +} + +asmlinkage void kgdb_breakpoint_c(struct pt_regs *regs) +{ + /* + * The breakpoint entry code has moved the PC on by 4 bytes, so we must + * move it back. This could be done on the host but we do it here + */ + if (!wait_for_remote_debugger) + regs->ea -= 4; + else /* pass the first trap 30 code */ + wait_for_remote_debugger = 0; + + kgdb_handle_exception(30, SIGTRAP, 0, regs); +} + +int kgdb_arch_init(void) +{ + wait_for_remote_debugger = 1; + return 0; +} + +void kgdb_arch_exit(void) +{ + /* Nothing to do */ +} + +struct kgdb_arch arch_kgdb_ops = { + /* Breakpoint instruction: trap 30 */ + .gdb_bpt_instr = { 0xba, 0x6f, 0x3b, 0x00 }, +}; diff --git a/arch/nios2/kernel/prom.c b/arch/nios2/kernel/prom.c index 0522d3378e3f..718dd197909f 100644 --- a/arch/nios2/kernel/prom.c +++ b/arch/nios2/kernel/prom.c @@ -1,7 +1,7 @@ /* * Device tree support * - * Copyright (C) 2013 Altera Corporation + * Copyright (C) 2013, 2015 Altera Corporation * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw> * * Based on MIPS support for CONFIG_OF device tree support @@ -30,6 +30,7 @@ #include <linux/of_fdt.h> #include <linux/io.h> +#include <asm/prom.h> #include <asm/sections.h> void __init early_init_dt_add_memory_arch(u64 base, u64 size) @@ -63,3 +64,52 @@ void __init early_init_devtree(void *params) early_init_dt_scan(params); } + +#ifdef CONFIG_EARLY_PRINTK +static int __init early_init_dt_scan_serial(unsigned long node, + const char *uname, int depth, void *data) +{ + u64 *addr64 = (u64 *) data; + const char *p; + + /* only consider serial nodes */ + if (strncmp(uname, "serial", 6) != 0) + return 0; + + p = of_get_flat_dt_prop(node, "compatible", NULL); + if (!p) + return 0; + + /* + * We found an altera_jtaguart but it wasn't configured for console, so + * skip it. + */ +#ifndef CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE + if (strncmp(p, "altr,juart", 10) == 0) + return 0; +#endif + + /* + * Same for altera_uart. + */ +#ifndef CONFIG_SERIAL_ALTERA_UART_CONSOLE + if (strncmp(p, "altr,uart", 9) == 0) + return 0; +#endif + + *addr64 = fdt_translate_address((const void *)initial_boot_params, + node); + + return *addr64 == OF_BAD_ADDR ? 0 : 1; +} + +unsigned long __init of_early_console(void) +{ + u64 base = 0; + + if (of_scan_flat_dt(early_init_dt_scan_serial, &base)) + return (u32)ioremap(base, 32); + else + return 0; +} +#endif /* CONFIG_EARLY_PRINTK */ diff --git a/arch/nios2/kernel/setup.c b/arch/nios2/kernel/setup.c index cb3121f975d4..b101a43d3c5a 100644 --- a/arch/nios2/kernel/setup.c +++ b/arch/nios2/kernel/setup.c @@ -139,6 +139,10 @@ void __init setup_arch(char **cmdline_p) console_verbose(); +#ifdef CONFIG_EARLY_PRINTK + setup_early_printk(); +#endif + memory_start = PAGE_ALIGN((unsigned long)__pa(_end)); memory_end = (unsigned long) CONFIG_NIOS2_MEM_BASE + memory_size; diff --git a/arch/nios2/mm/fault.c b/arch/nios2/mm/fault.c index d194c0427b26..0d231adfe576 100644 --- a/arch/nios2/mm/fault.c +++ b/arch/nios2/mm/fault.c @@ -47,7 +47,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long cause, struct mm_struct *mm = tsk->mm; int code = SEGV_MAPERR; int fault; - unsigned int flags = 0; + unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE; cause >>= 2; @@ -86,6 +86,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long cause, if (!down_read_trylock(&mm->mmap_sem)) { if (!user_mode(regs) && !search_exception_tables(regs->ea)) goto bad_area_nosemaphore; +retry: down_read(&mm->mmap_sem); } @@ -132,6 +133,10 @@ survive: * the fault. */ fault = handle_mm_fault(mm, vma, address, flags); + + if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) + return; + if (unlikely(fault & VM_FAULT_ERROR)) { if (fault & VM_FAULT_OOM) goto out_of_memory; @@ -141,10 +146,32 @@ survive: goto do_sigbus; BUG(); } - if (fault & VM_FAULT_MAJOR) - tsk->maj_flt++; - else - tsk->min_flt++; + + /* + * Major/minor page fault accounting is only done on the + * initial attempt. If we go through a retry, it is extremely + * likely that the page will be found in page cache at that point. + */ + if (flags & FAULT_FLAG_ALLOW_RETRY) { + if (fault & VM_FAULT_MAJOR) + current->maj_flt++; + else + current->min_flt++; + if (fault & VM_FAULT_RETRY) { + /* Clear FAULT_FLAG_ALLOW_RETRY to avoid any risk + * of starvation. */ + flags &= ~FAULT_FLAG_ALLOW_RETRY; + flags |= FAULT_FLAG_TRIED; + + /* + * No need to up_read(&mm->mmap_sem) as we would + * have already released it in __lock_page_or_retry + * in mm/filemap.c. + */ + + goto retry; + } + } up_read(&mm->mmap_sem); return; diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index 1554a6f2a5bb..8014727a2743 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -291,10 +291,6 @@ config SYSVIPC_COMPAT config AUDIT_ARCH def_bool y -config HPUX - bool "Support for HP-UX binaries" - depends on !64BIT - config NR_CPUS int "Maximum number of CPUs (2-32)" range 2 32 diff --git a/arch/parisc/Makefile b/arch/parisc/Makefile index 5db8882f732c..91fbb6ee702c 100644 --- a/arch/parisc/Makefile +++ b/arch/parisc/Makefile @@ -84,7 +84,6 @@ head-y := arch/parisc/kernel/head.o KBUILD_CFLAGS += $(cflags-y) kernel-y := mm/ kernel/ math-emu/ -kernel-$(CONFIG_HPUX) += hpux/ core-y += $(addprefix arch/parisc/, $(kernel-y)) libs-y += arch/parisc/lib/ $(LIBGCC) diff --git a/arch/parisc/hpux/Makefile b/arch/parisc/hpux/Makefile deleted file mode 100644 index 1048fb69f06d..000000000000 --- a/arch/parisc/hpux/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# -# Makefile for HPUX emulation -# - -obj-y := entry_hpux.o gate.o wrappers.o fs.o ioctl.o sys_hpux.o diff --git a/arch/parisc/hpux/entry_hpux.S b/arch/parisc/hpux/entry_hpux.S deleted file mode 100644 index d15a413572f0..000000000000 --- a/arch/parisc/hpux/entry_hpux.S +++ /dev/null @@ -1,546 +0,0 @@ -/* syscall table for HPUX specific syscalls - * - * Linux/PA-RISC Project (http://www.parisc-linux.org/) - * Copyright (C) 1999 Matthew Wilcox <willy at debian . org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <asm/unistd.h> -#include <asm/assembly.h> -#include <linux/sys.h> -#include <linux/linkage.h> - -#define ENTRY_NAME(_name_) ASM_ULONG_INSN _name_ - - .section .rodata,"a" - .import hpux_unimplemented_wrapper -ENTRY(hpux_call_table) - ENTRY_NAME(sys_ni_syscall) /* 0 */ - ENTRY_NAME(sys_exit) - ENTRY_NAME(hpux_fork_wrapper) - ENTRY_NAME(sys_read) - ENTRY_NAME(sys_write) - ENTRY_NAME(sys_open) /* 5 */ - ENTRY_NAME(sys_close) - ENTRY_NAME(hpux_wait) - ENTRY_NAME(sys_creat) - ENTRY_NAME(sys_link) - ENTRY_NAME(sys_unlink) /* 10 */ - ENTRY_NAME(hpux_execv_wrapper) - ENTRY_NAME(sys_chdir) - ENTRY_NAME(sys_time) - ENTRY_NAME(sys_mknod) - ENTRY_NAME(sys_chmod) /* 15 */ - ENTRY_NAME(sys_chown) - ENTRY_NAME(hpux_brk) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(sys_lseek) - ENTRY_NAME(sys_getpid) /* 20 */ - ENTRY_NAME(hpux_mount) - ENTRY_NAME(sys_oldumount) - ENTRY_NAME(sys_setuid) - ENTRY_NAME(sys_getuid) - ENTRY_NAME(sys_stime) /* 25 */ - ENTRY_NAME(hpux_ptrace) - ENTRY_NAME(sys_alarm) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(sys_pause) - ENTRY_NAME(sys_utime) /* 30 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(sys_access) - ENTRY_NAME(hpux_nice) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 35 */ - ENTRY_NAME(sys_sync) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(sys_newstat) - ENTRY_NAME(hpux_setpgrp3) - ENTRY_NAME(sys_newlstat) /* 40 */ - ENTRY_NAME(sys_dup) - ENTRY_NAME(hpux_pipe_wrapper) - ENTRY_NAME(sys_times) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 45 */ - ENTRY_NAME(sys_setgid) - ENTRY_NAME(sys_getgid) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 50 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_ioctl) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 55 */ - ENTRY_NAME(sys_symlink) - ENTRY_NAME(hpux_utssys) - ENTRY_NAME(sys_readlink) - ENTRY_NAME(hpux_execve_wrapper) - ENTRY_NAME(sys_umask) /* 60 */ - ENTRY_NAME(sys_chroot) - ENTRY_NAME(sys_fcntl) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 65 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_sbrk) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 70 */ - ENTRY_NAME(sys_mmap) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 75 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 80 */ - ENTRY_NAME(sys_getpgid) - ENTRY_NAME(sys_setpgid) - ENTRY_NAME(sys_setitimer) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 85 */ - ENTRY_NAME(sys_getitimer) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(sys_dup2) /* 90 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(sys_newfstat) - ENTRY_NAME(sys_select) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 95 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 100 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 105 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 110 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 115 */ - ENTRY_NAME(sys_gettimeofday) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 120 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(sys_fchown) - ENTRY_NAME(sys_fchmod) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 125 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(sys_rename) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 130 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_sysconf) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 135 */ - ENTRY_NAME(sys_mkdir) - ENTRY_NAME(sys_rmdir) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 140 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(sys_getrlimit) - ENTRY_NAME(sys_setrlimit) /* 145 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 150 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_lockf) /* 155 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 160 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 165 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 170 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 175 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 180 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(sys_sigprocmask) /* 185 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 190 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_getdomainname) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 195 */ - ENTRY_NAME(hpux_statfs) - ENTRY_NAME(hpux_fstatfs) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(sys_waitpid) /* 200 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 205 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 210 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 215 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 220 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 225 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 230 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 235 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 240 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 245 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 250 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 255 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 260 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 265 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 270 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(sys_fchdir) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(sys_accept) /* 275 */ - ENTRY_NAME(sys_bind) - ENTRY_NAME(sys_connect) - ENTRY_NAME(sys_getpeername) - ENTRY_NAME(sys_getsockname) - ENTRY_NAME(sys_getsockopt) /* 280 */ - ENTRY_NAME(sys_listen) - ENTRY_NAME(sys_recv) - ENTRY_NAME(sys_recvfrom) - ENTRY_NAME(sys_recvmsg) - ENTRY_NAME(sys_send) /* 285 */ - ENTRY_NAME(sys_sendmsg) - ENTRY_NAME(sys_sendto) - ENTRY_NAME(sys_setsockopt) - ENTRY_NAME(sys_shutdown) - ENTRY_NAME(sys_socket) /* 290 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 295 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 300 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 305 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 310 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 315 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 320 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 325 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 330 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(sys_lchown) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_sysfs) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 335 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 340 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 345 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 350 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(sys_nanosleep) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 355 */ - ENTRY_NAME(hpux_getdents) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 360 */ - ENTRY_NAME(hpux_fstat64) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 365 */ - ENTRY_NAME(hpux_lstat64) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_stat64) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 370 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 375 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 380 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_setpgrp) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 385 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 390 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 395 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 400 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 405 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 410 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 415 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 420 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 425 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 430 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 435 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 440 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 445 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 450 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 455 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 460 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 465 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 470 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 475 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 480 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 485 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 490 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 495 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 500 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 505 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) /* 510 */ - ENTRY_NAME(hpux_unimplemented_wrapper) - ENTRY_NAME(hpux_unimplemented_wrapper) -END(hpux_call_table) -.end - diff --git a/arch/parisc/hpux/fs.c b/arch/parisc/hpux/fs.c deleted file mode 100644 index 97a7bf8df348..000000000000 --- a/arch/parisc/hpux/fs.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Implements HPUX syscalls. - * - * Copyright (C) 1999 Matthew Wilcox <willy with parisc-linux.org> - * Copyright (C) 2000 Michael Ang <mang with subcarrier.org> - * Copyright (C) 2000 John Marvin <jsm with parisc-linux.org> - * Copyright (C) 2000 Philipp Rumpf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/fs.h> -#include <linux/sched.h> -#include <linux/file.h> -#include <linux/ptrace.h> -#include <linux/slab.h> -#include <asm/errno.h> -#include <asm/uaccess.h> - -int hpux_execve(struct pt_regs *regs) -{ - return do_execve(getname((const char __user *) regs->gr[26]), - (const char __user *const __user *) regs->gr[25], - (const char __user *const __user *) regs->gr[24]); -} - -struct hpux_dirent { - loff_t d_off; - ino_t d_ino; - short d_reclen; - short d_namlen; - char d_name[1]; -}; - -struct getdents_callback { - struct dir_context ctx; - struct hpux_dirent __user *current_dir; - struct hpux_dirent __user *previous; - int count; - int error; -}; - -#define NAME_OFFSET(de) ((int) ((de)->d_name - (char __user *) (de))) - -static int filldir(struct dir_context *ctx, const char *name, int namlen, - loff_t offset, u64 ino, unsigned d_type) -{ - struct hpux_dirent __user * dirent; - struct getdents_callback *buf = - container_of(ctx, struct getdents_callback, ctx); - ino_t d_ino; - int reclen = ALIGN(NAME_OFFSET(dirent) + namlen + 1, sizeof(long)); - - buf->error = -EINVAL; /* only used if we fail.. */ - if (reclen > buf->count) - return -EINVAL; - d_ino = ino; - if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) { - buf->error = -EOVERFLOW; - return -EOVERFLOW; - } - dirent = buf->previous; - if (dirent) - if (put_user(offset, &dirent->d_off)) - goto Efault; - dirent = buf->current_dir; - if (put_user(d_ino, &dirent->d_ino) || - put_user(reclen, &dirent->d_reclen) || - put_user(namlen, &dirent->d_namlen) || - copy_to_user(dirent->d_name, name, namlen) || - put_user(0, dirent->d_name + namlen)) - goto Efault; - buf->previous = dirent; - buf->current_dir = (void __user *)dirent + reclen; - buf->count -= reclen; - return 0; -Efault: - buf->error = -EFAULT; - return -EFAULT; -} - -#undef NAME_OFFSET - -int hpux_getdents(unsigned int fd, struct hpux_dirent __user *dirent, unsigned int count) -{ - struct fd arg; - struct hpux_dirent __user * lastdirent; - struct getdents_callback buf = { - .ctx.actor = filldir, - .current_dir = dirent, - .count = count - }; - int error; - - arg = fdget(fd); - if (!arg.file) - return -EBADF; - - error = iterate_dir(arg.file, &buf.ctx); - if (error >= 0) - error = buf.error; - lastdirent = buf.previous; - if (lastdirent) { - if (put_user(buf.ctx.pos, &lastdirent->d_off)) - error = -EFAULT; - else - error = count - buf.count; - } - - fdput(arg); - return error; -} - -int hpux_mount(const char *fs, const char *path, int mflag, - const char *fstype, const char *dataptr, int datalen) -{ - return -ENOSYS; -} - -static int cp_hpux_stat(struct kstat *stat, struct hpux_stat64 __user *statbuf) -{ - struct hpux_stat64 tmp; - - /* we probably want a different split here - is hpux 12:20? */ - - if (!new_valid_dev(stat->dev) || !new_valid_dev(stat->rdev)) - return -EOVERFLOW; - - memset(&tmp, 0, sizeof(tmp)); - tmp.st_dev = new_encode_dev(stat->dev); - tmp.st_ino = stat->ino; - tmp.st_mode = stat->mode; - tmp.st_nlink = stat->nlink; - tmp.st_uid = from_kuid_munged(current_user_ns(), stat->uid); - tmp.st_gid = from_kgid_munged(current_user_ns(), stat->gid); - tmp.st_rdev = new_encode_dev(stat->rdev); - tmp.st_size = stat->size; - tmp.st_atime = stat->atime.tv_sec; - tmp.st_mtime = stat->mtime.tv_sec; - tmp.st_ctime = stat->ctime.tv_sec; - tmp.st_blocks = stat->blocks; - tmp.st_blksize = stat->blksize; - return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; -} - -long hpux_stat64(const char __user *filename, struct hpux_stat64 __user *statbuf) -{ - struct kstat stat; - int error = vfs_stat(filename, &stat); - - if (!error) - error = cp_hpux_stat(&stat, statbuf); - - return error; -} - -long hpux_fstat64(unsigned int fd, struct hpux_stat64 __user *statbuf) -{ - struct kstat stat; - int error = vfs_fstat(fd, &stat); - - if (!error) - error = cp_hpux_stat(&stat, statbuf); - - return error; -} - -long hpux_lstat64(const char __user *filename, - struct hpux_stat64 __user *statbuf) -{ - struct kstat stat; - int error = vfs_lstat(filename, &stat); - - if (!error) - error = cp_hpux_stat(&stat, statbuf); - - return error; -} diff --git a/arch/parisc/hpux/gate.S b/arch/parisc/hpux/gate.S deleted file mode 100644 index 011468857e98..000000000000 --- a/arch/parisc/hpux/gate.S +++ /dev/null @@ -1,107 +0,0 @@ -/* - * - * Linux/PARISC Project (http://www.parisc-linux.org/) - * - * System call entry code Copyright (c) Matthew Wilcox 1999 <willy@bofh.ai> - * Licensed under the GNU GPL. - * thanks to Philipp Rumpf, Mike Shaver and various others - * sorry about the wall, puffin.. - */ - -#include <asm/assembly.h> -#include <asm/asm-offsets.h> -#include <asm/unistd.h> -#include <asm/errno.h> -#include <linux/linkage.h> - - .level LEVEL - .text - - .import hpux_call_table - .import hpux_syscall_exit,code - - .align PAGE_SIZE -ENTRY(hpux_gateway_page) - nop -#ifdef CONFIG_64BIT -#warning NEEDS WORK for 64-bit -#endif - ldw -64(%r30), %r29 ;! 8th argument - ldw -60(%r30), %r19 ;! 7th argument - ldw -56(%r30), %r20 ;! 6th argument - ldw -52(%r30), %r21 ;! 5th argument - gate .+8, %r0 /* become privileged */ - mtsp %r0,%sr4 /* get kernel space into sr4 */ - mtsp %r0,%sr5 /* get kernel space into sr5 */ - mtsp %r0,%sr6 /* get kernel space into sr6 */ - mfsp %sr7,%r1 /* save user sr7 */ - mtsp %r1,%sr3 /* and store it in sr3 */ - - mtctl %r30,%cr28 - mfctl %cr30,%r1 - xor %r1,%r30,%r30 /* ye olde xor trick */ - xor %r1,%r30,%r1 - xor %r1,%r30,%r30 - ldo TASK_SZ_ALGN+FRAME_SIZE(%r30),%r30 /* set up kernel stack */ - - /* N.B.: It is critical that we don't set sr7 to 0 until r30 - * contains a valid kernel stack pointer. It is also - * critical that we don't start using the kernel stack - * until after sr7 has been set to 0. - */ - - mtsp %r0,%sr7 /* get kernel space into sr7 */ - STREG %r1,TASK_PT_GR30-TASK_SZ_ALGN-FRAME_SIZE(%r30) /* save usp */ - ldo -TASK_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get task ptr in %r1 */ - - /* Save some registers for sigcontext and potential task - switch (see entry.S for the details of which ones are - saved/restored). TASK_PT_PSW is zeroed so we can see whether - a process is on a syscall or not. For an interrupt the real - PSW value is stored. This is needed for gdb and sys_ptrace. */ - STREG %r0, TASK_PT_PSW(%r1) - STREG %r2, TASK_PT_GR2(%r1) /* preserve rp */ - STREG %r19, TASK_PT_GR19(%r1) /* 7th argument */ - STREG %r20, TASK_PT_GR20(%r1) /* 6th argument */ - STREG %r21, TASK_PT_GR21(%r1) /* 5th argument */ - STREG %r22, TASK_PT_GR22(%r1) /* syscall # */ - STREG %r23, TASK_PT_GR23(%r1) /* 4th argument */ - STREG %r24, TASK_PT_GR24(%r1) /* 3rd argument */ - STREG %r25, TASK_PT_GR25(%r1) /* 2nd argument */ - STREG %r26, TASK_PT_GR26(%r1) /* 1st argument */ - STREG %r27, TASK_PT_GR27(%r1) /* user dp */ - STREG %r28, TASK_PT_GR28(%r1) /* return value 0 */ - STREG %r0, TASK_PT_ORIG_R28(%r1) /* don't prohibit restarts */ - STREG %r29, TASK_PT_GR29(%r1) /* 8th argument */ - STREG %r31, TASK_PT_GR31(%r1) /* preserve syscall return ptr */ - - ldo TASK_PT_FR0(%r1), %r27 /* save fpregs from the kernel */ - save_fp %r27 /* or potential task switch */ - - mfctl %cr11, %r27 /* i.e. SAR */ - STREG %r27, TASK_PT_SAR(%r1) - - loadgp - - stw %r21, -52(%r30) ;! 5th argument - stw %r20, -56(%r30) ;! 6th argument - stw %r19, -60(%r30) ;! 7th argument - stw %r29, -64(%r30) ;! 8th argument - - ldil L%hpux_call_table, %r21 - ldo R%hpux_call_table(%r21), %r21 - comiclr,>>= __NR_HPUX_syscalls, %r22, %r0 - b,n syscall_nosys - LDREGX %r22(%r21), %r21 - ldil L%hpux_syscall_exit,%r2 - be 0(%sr7,%r21) - ldo R%hpux_syscall_exit(%r2),%r2 - -syscall_nosys: - ldil L%hpux_syscall_exit,%r1 - be R%hpux_syscall_exit(%sr7,%r1) - ldo -ENOSYS(%r0),%r28 -ENDPROC(hpux_gateway_page) - - .align PAGE_SIZE -ENTRY(end_hpux_gateway_page) diff --git a/arch/parisc/hpux/ioctl.c b/arch/parisc/hpux/ioctl.c deleted file mode 100644 index dede4765852e..000000000000 --- a/arch/parisc/hpux/ioctl.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Implements some necessary HPUX ioctls. - * - * Copyright (C) 1999-2002 Matthew Wilcox <willy with parisc-linux.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* - * Supported ioctls: - * TCGETA - * TCSETA - * TCSETAW - * TCSETAF - * TCSBRK - * TCXONC - * TCFLSH - * TIOCGWINSZ - * TIOCSWINSZ - * TIOCGPGRP - * TIOCSPGRP - */ - -#include <linux/sched.h> -#include <linux/syscalls.h> -#include <asm/errno.h> -#include <asm/ioctl.h> -#include <asm/termios.h> -#include <asm/uaccess.h> - -static int hpux_ioctl_t(int fd, unsigned long cmd, unsigned long arg) -{ - int result = -EOPNOTSUPP; - int nr = _IOC_NR(cmd); - switch (nr) { - case 106: - result = sys_ioctl(fd, TIOCSWINSZ, arg); - break; - case 107: - result = sys_ioctl(fd, TIOCGWINSZ, arg); - break; - } - return result; -} - -int hpux_ioctl(int fd, unsigned long cmd, unsigned long arg) -{ - int result = -EOPNOTSUPP; - int type = _IOC_TYPE(cmd); - switch (type) { - case 'T': - /* Our structures are now compatible with HPUX's */ - result = sys_ioctl(fd, cmd, arg); - break; - case 't': - result = hpux_ioctl_t(fd, cmd, arg); - break; - } - return result; -} diff --git a/arch/parisc/hpux/sys_hpux.c b/arch/parisc/hpux/sys_hpux.c deleted file mode 100644 index e5c4da035810..000000000000 --- a/arch/parisc/hpux/sys_hpux.c +++ /dev/null @@ -1,963 +0,0 @@ -/* - * Implements HPUX syscalls. - * - * Copyright (C) 1999 Matthew Wilcox <willy with parisc-linux.org> - * Copyright (C) 2000 Philipp Rumpf - * Copyright (C) 2000 John Marvin <jsm with parisc-linux.org> - * Copyright (C) 2000 Michael Ang <mang with subcarrier.org> - * Copyright (C) 2001 Nathan Neulinger <nneul at umr.edu> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <linux/capability.h> -#include <linux/file.h> -#include <linux/fs.h> -#include <linux/namei.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/syscalls.h> -#include <linux/utsname.h> -#include <linux/vfs.h> -#include <linux/vmalloc.h> - -#include <asm/errno.h> -#include <asm/pgalloc.h> -#include <asm/uaccess.h> - -unsigned long hpux_brk(unsigned long addr) -{ - /* Sigh. Looks like HP/UX libc relies on kernel bugs. */ - return sys_brk(addr + PAGE_SIZE); -} - -int hpux_sbrk(void) -{ - return -ENOSYS; -} - -/* Random other syscalls */ - -int hpux_nice(int priority_change) -{ - return -ENOSYS; -} - -int hpux_ptrace(void) -{ - return -ENOSYS; -} - -int hpux_wait(int __user *stat_loc) -{ - return sys_waitpid(-1, stat_loc, 0); -} - -int hpux_setpgrp(void) -{ - return sys_setpgid(0,0); -} - -int hpux_setpgrp3(void) -{ - return hpux_setpgrp(); -} - -#define _SC_CPU_VERSION 10001 -#define _SC_OPEN_MAX 4 -#define CPU_PA_RISC1_1 0x210 - -int hpux_sysconf(int which) -{ - switch (which) { - case _SC_CPU_VERSION: - return CPU_PA_RISC1_1; - case _SC_OPEN_MAX: - return INT_MAX; - default: - return -EINVAL; - } -} - -/*****************************************************************************/ - -#define HPUX_UTSLEN 9 -#define HPUX_SNLEN 15 - -struct hpux_utsname { - char sysname[HPUX_UTSLEN]; - char nodename[HPUX_UTSLEN]; - char release[HPUX_UTSLEN]; - char version[HPUX_UTSLEN]; - char machine[HPUX_UTSLEN]; - char idnumber[HPUX_SNLEN]; -} ; - -struct hpux_ustat { - int32_t f_tfree; /* total free (daddr_t) */ - u_int32_t f_tinode; /* total inodes free (ino_t) */ - char f_fname[6]; /* filsys name */ - char f_fpack[6]; /* filsys pack name */ - u_int32_t f_blksize; /* filsys block size (int) */ -}; - -/* - * HPUX's utssys() call. It's a collection of miscellaneous functions, - * alas, so there's no nice way of splitting them up. - */ - -/* This function is called from hpux_utssys(); HP-UX implements - * ustat() as an option to utssys(). - * - * Now, struct ustat on HP-UX is exactly the same as on Linux, except - * that it contains one addition field on the end, int32_t f_blksize. - * So, we could have written this function to just call the Linux - * sys_ustat(), (defined in linux/fs/super.c), and then just - * added this additional field to the user's structure. But I figure - * if we're gonna be digging through filesystem structures to get - * this, we might as well just do the whole enchilada all in one go. - * - * So, most of this function is almost identical to sys_ustat(). - * I have placed comments at the few lines changed or added, to - * aid in porting forward if and when sys_ustat() is changed from - * its form in kernel 2.2.5. - */ -static int hpux_ustat(dev_t dev, struct hpux_ustat __user *ubuf) -{ - struct hpux_ustat tmp; /* Changed to hpux_ustat */ - struct kstatfs sbuf; - int err = vfs_ustat(dev, &sbuf); - if (err) - goto out; - - memset(&tmp,0,sizeof(tmp)); - - tmp.f_tfree = (int32_t)sbuf.f_bfree; - tmp.f_tinode = (u_int32_t)sbuf.f_ffree; - tmp.f_blksize = (u_int32_t)sbuf.f_bsize; /* Added this line */ - - err = copy_to_user(ubuf, &tmp, sizeof(tmp)) ? -EFAULT : 0; -out: - return err; -} - -/* - * Wrapper for hpux statfs call. At the moment, just calls the linux native one - * and ignores the extra fields at the end of the hpux statfs struct. - * - */ - -typedef int32_t hpux_fsid_t[2]; /* file system ID type */ -typedef uint16_t hpux_site_t; - -struct hpux_statfs { - int32_t f_type; /* type of info, zero for now */ - int32_t f_bsize; /* fundamental file system block size */ - int32_t f_blocks; /* total blocks in file system */ - int32_t f_bfree; /* free block in fs */ - int32_t f_bavail; /* free blocks avail to non-superuser */ - int32_t f_files; /* total file nodes in file system */ - int32_t f_ffree; /* free file nodes in fs */ - hpux_fsid_t f_fsid; /* file system ID */ - int32_t f_magic; /* file system magic number */ - int32_t f_featurebits; /* file system features */ - int32_t f_spare[4]; /* spare for later */ - hpux_site_t f_cnode; /* cluster node where mounted */ - int16_t f_pad; -}; - -static int do_statfs_hpux(struct kstatfs *st, struct hpux_statfs __user *p) -{ - struct hpux_statfs buf; - memset(&buf, 0, sizeof(buf)); - buf.f_type = st->f_type; - buf.f_bsize = st->f_bsize; - buf.f_blocks = st->f_blocks; - buf.f_bfree = st->f_bfree; - buf.f_bavail = st->f_bavail; - buf.f_files = st->f_files; - buf.f_ffree = st->f_ffree; - buf.f_fsid[0] = st->f_fsid.val[0]; - buf.f_fsid[1] = st->f_fsid.val[1]; - if (copy_to_user(p, &buf, sizeof(buf))) - return -EFAULT; - return 0; -} - -/* hpux statfs */ -asmlinkage long hpux_statfs(const char __user *pathname, - struct hpux_statfs __user *buf) -{ - struct kstatfs st; - int error = user_statfs(pathname, &st); - if (!error) - error = do_statfs_hpux(&st, buf); - return error; -} - -asmlinkage long hpux_fstatfs(unsigned int fd, struct hpux_statfs __user * buf) -{ - struct kstatfs st; - int error = fd_statfs(fd, &st); - if (!error) - error = do_statfs_hpux(&st, buf); - return error; -} - - -/* This function is called from hpux_utssys(); HP-UX implements - * uname() as an option to utssys(). - * - * The form of this function is pretty much copied from sys_olduname(), - * defined in linux/arch/i386/kernel/sys_i386.c. - */ -/* TODO: Are these put_user calls OK? Should they pass an int? - * (I copied it from sys_i386.c like this.) - */ -static int hpux_uname(struct hpux_utsname __user *name) -{ - int error; - - if (!name) - return -EFAULT; - if (!access_ok(VERIFY_WRITE,name,sizeof(struct hpux_utsname))) - return -EFAULT; - - down_read(&uts_sem); - - error = __copy_to_user(&name->sysname, &utsname()->sysname, - HPUX_UTSLEN - 1); - error |= __put_user(0, name->sysname + HPUX_UTSLEN - 1); - error |= __copy_to_user(&name->nodename, &utsname()->nodename, - HPUX_UTSLEN - 1); - error |= __put_user(0, name->nodename + HPUX_UTSLEN - 1); - error |= __copy_to_user(&name->release, &utsname()->release, - HPUX_UTSLEN - 1); - error |= __put_user(0, name->release + HPUX_UTSLEN - 1); - error |= __copy_to_user(&name->version, &utsname()->version, - HPUX_UTSLEN - 1); - error |= __put_user(0, name->version + HPUX_UTSLEN - 1); - error |= __copy_to_user(&name->machine, &utsname()->machine, - HPUX_UTSLEN - 1); - error |= __put_user(0, name->machine + HPUX_UTSLEN - 1); - - up_read(&uts_sem); - - /* HP-UX utsname has no domainname field. */ - - /* TODO: Implement idnumber!!! */ -#if 0 - error |= __put_user(0,name->idnumber); - error |= __put_user(0,name->idnumber+HPUX_SNLEN-1); -#endif - - error = error ? -EFAULT : 0; - - return error; -} - -/* Note: HP-UX just uses the old suser() function to check perms - * in this system call. We'll use capable(CAP_SYS_ADMIN). - */ -int hpux_utssys(char __user *ubuf, int n, int type) -{ - int len; - int error; - switch( type ) { - case 0: - /* uname(): */ - return hpux_uname((struct hpux_utsname __user *)ubuf); - break ; - case 1: - /* Obsolete (used to be umask().) */ - return -EFAULT ; - break ; - case 2: - /* ustat(): */ - return hpux_ustat(new_decode_dev(n), - (struct hpux_ustat __user *)ubuf); - break; - case 3: - /* setuname(): - * - * On linux (unlike HP-UX), utsname.nodename - * is the same as the hostname. - * - * sys_sethostname() is defined in linux/kernel/sys.c. - */ - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - /* Unlike Linux, HP-UX returns an error if n==0: */ - if ( n <= 0 ) - return -EINVAL ; - /* Unlike Linux, HP-UX truncates it if n is too big: */ - len = (n <= __NEW_UTS_LEN) ? n : __NEW_UTS_LEN ; - return sys_sethostname(ubuf, len); - break ; - case 4: - /* sethostname(): - * - * sys_sethostname() is defined in linux/kernel/sys.c. - */ - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - /* Unlike Linux, HP-UX returns an error if n==0: */ - if ( n <= 0 ) - return -EINVAL ; - /* Unlike Linux, HP-UX truncates it if n is too big: */ - len = (n <= __NEW_UTS_LEN) ? n : __NEW_UTS_LEN ; - return sys_sethostname(ubuf, len); - break ; - case 5: - /* gethostname(): - * - * sys_gethostname() is defined in linux/kernel/sys.c. - */ - /* Unlike Linux, HP-UX returns an error if n==0: */ - if ( n <= 0 ) - return -EINVAL ; - return sys_gethostname(ubuf, n); - break ; - case 6: - /* Supposedly called from setuname() in libc. - * TODO: When and why is this called? - * Is it ever even called? - * - * This code should look a lot like sys_sethostname(), - * defined in linux/kernel/sys.c. If that gets updated, - * update this code similarly. - */ - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - /* Unlike Linux, HP-UX returns an error if n==0: */ - if ( n <= 0 ) - return -EINVAL ; - /* Unlike Linux, HP-UX truncates it if n is too big: */ - len = (n <= __NEW_UTS_LEN) ? n : __NEW_UTS_LEN ; - /**/ - /* TODO: print a warning about using this? */ - down_write(&uts_sem); - error = -EFAULT; - if (!copy_from_user(utsname()->sysname, ubuf, len)) { - utsname()->sysname[len] = 0; - error = 0; - } - up_write(&uts_sem); - return error; - break ; - case 7: - /* Sets utsname.release, if you're allowed. - * Undocumented. Used by swinstall to change the - * OS version, during OS updates. Yuck!!! - * - * This code should look a lot like sys_sethostname() - * in linux/kernel/sys.c. If that gets updated, update - * this code similarly. - */ - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - /* Unlike Linux, HP-UX returns an error if n==0: */ - if ( n <= 0 ) - return -EINVAL ; - /* Unlike Linux, HP-UX truncates it if n is too big: */ - len = (n <= __NEW_UTS_LEN) ? n : __NEW_UTS_LEN ; - /**/ - /* TODO: print a warning about this? */ - down_write(&uts_sem); - error = -EFAULT; - if (!copy_from_user(utsname()->release, ubuf, len)) { - utsname()->release[len] = 0; - error = 0; - } - up_write(&uts_sem); - return error; - break ; - default: - /* This system call returns -EFAULT if given an unknown type. - * Why not -EINVAL? I don't know, it's just not what they did. - */ - return -EFAULT ; - } -} - -int hpux_getdomainname(char __user *name, int len) -{ - int nlen; - int err = -EFAULT; - - down_read(&uts_sem); - - nlen = strlen(utsname()->domainname) + 1; - - if (nlen < len) - len = nlen; - if(len > __NEW_UTS_LEN) - goto done; - if(copy_to_user(name, utsname()->domainname, len)) - goto done; - err = 0; -done: - up_read(&uts_sem); - return err; - -} - -int hpux_pipe(int *kstack_fildes) -{ - return do_pipe_flags(kstack_fildes, 0); -} - -/* lies - says it works, but it really didn't lock anything */ -int hpux_lockf(int fildes, int function, off_t size) -{ - return 0; -} - -int hpux_sysfs(int opcode, unsigned long arg1, unsigned long arg2) -{ - char *fsname = NULL; - int len = 0; - int fstype; - -/*Unimplemented HP-UX syscall emulation. Syscall #334 (sysfs) - Args: 1 80057bf4 0 400179f0 0 0 0 */ - printk(KERN_DEBUG "in hpux_sysfs\n"); - printk(KERN_DEBUG "hpux_sysfs called with opcode = %d\n", opcode); - printk(KERN_DEBUG "hpux_sysfs called with arg1='%lx'\n", arg1); - - if ( opcode == 1 ) { /* GETFSIND */ - char __user *user_fsname = (char __user *)arg1; - len = strlen_user(user_fsname); - printk(KERN_DEBUG "len of arg1 = %d\n", len); - if (len == 0) - return 0; - fsname = kmalloc(len, GFP_KERNEL); - if (!fsname) { - printk(KERN_DEBUG "failed to kmalloc fsname\n"); - return 0; - } - - if (copy_from_user(fsname, user_fsname, len)) { - printk(KERN_DEBUG "failed to copy_from_user fsname\n"); - kfree(fsname); - return 0; - } - - /* String could be altered by userspace after strlen_user() */ - fsname[len - 1] = '\0'; - - printk(KERN_DEBUG "that is '%s' as (char *)\n", fsname); - if ( !strcmp(fsname, "hfs") ) { - fstype = 0; - } else { - fstype = 0; - } - - kfree(fsname); - - printk(KERN_DEBUG "returning fstype=%d\n", fstype); - return fstype; /* something other than default */ - } - - - return 0; -} - - -/* Table of syscall names and handle for unimplemented routines */ -static const char * const syscall_names[] = { - "nosys", /* 0 */ - "exit", - "fork", - "read", - "write", - "open", /* 5 */ - "close", - "wait", - "creat", - "link", - "unlink", /* 10 */ - "execv", - "chdir", - "time", - "mknod", - "chmod", /* 15 */ - "chown", - "brk", - "lchmod", - "lseek", - "getpid", /* 20 */ - "mount", - "umount", - "setuid", - "getuid", - "stime", /* 25 */ - "ptrace", - "alarm", - NULL, - "pause", - "utime", /* 30 */ - "stty", - "gtty", - "access", - "nice", - "ftime", /* 35 */ - "sync", - "kill", - "stat", - "setpgrp3", - "lstat", /* 40 */ - "dup", - "pipe", - "times", - "profil", - "ki_call", /* 45 */ - "setgid", - "getgid", - NULL, - NULL, - NULL, /* 50 */ - "acct", - "set_userthreadid", - NULL, - "ioctl", - "reboot", /* 55 */ - "symlink", - "utssys", - "readlink", - "execve", - "umask", /* 60 */ - "chroot", - "fcntl", - "ulimit", - NULL, - NULL, /* 65 */ - "vfork", - NULL, - NULL, - NULL, - NULL, /* 70 */ - "mmap", - NULL, - "munmap", - "mprotect", - "madvise", /* 75 */ - "vhangup", - "swapoff", - NULL, - "getgroups", - "setgroups", /* 80 */ - "getpgrp2", - "setpgid/setpgrp2", - "setitimer", - "wait3", - "swapon", /* 85 */ - "getitimer", - NULL, - NULL, - NULL, - "dup2", /* 90 */ - NULL, - "fstat", - "select", - NULL, - "fsync", /* 95 */ - "setpriority", - NULL, - NULL, - NULL, - "getpriority", /* 100 */ - NULL, - NULL, - NULL, - NULL, - NULL, /* 105 */ - NULL, - NULL, - "sigvector", - "sigblock", - "sigsetmask", /* 110 */ - "sigpause", - "sigstack", - NULL, - NULL, - NULL, /* 115 */ - "gettimeofday", - "getrusage", - NULL, - NULL, - "readv", /* 120 */ - "writev", - "settimeofday", - "fchown", - "fchmod", - NULL, /* 125 */ - "setresuid", - "setresgid", - "rename", - "truncate", - "ftruncate", /* 130 */ - NULL, - "sysconf", - NULL, - NULL, - NULL, /* 135 */ - "mkdir", - "rmdir", - NULL, - "sigcleanup", - "setcore", /* 140 */ - NULL, - "gethostid", - "sethostid", - "getrlimit", - "setrlimit", /* 145 */ - NULL, - NULL, - "quotactl", - "get_sysinfo", - NULL, /* 150 */ - "privgrp", - "rtprio", - "plock", - NULL, - "lockf", /* 155 */ - "semget", - NULL, - "semop", - "msgget", - NULL, /* 160 */ - "msgsnd", - "msgrcv", - "shmget", - NULL, - "shmat", /* 165 */ - "shmdt", - NULL, - "csp/nsp_init", - "cluster", - "mkrnod", /* 170 */ - "test", - "unsp_open", - NULL, - "getcontext", - "osetcontext", /* 175 */ - "bigio", - "pipenode", - "lsync", - "getmachineid", - "cnodeid/mysite", /* 180 */ - "cnodes/sitels", - "swapclients", - "rmtprocess", - "dskless_stats", - "sigprocmask", /* 185 */ - "sigpending", - "sigsuspend", - "sigaction", - NULL, - "nfssvc", /* 190 */ - "getfh", - "getdomainname", - "setdomainname", - "async_daemon", - "getdirentries", /* 195 */ - NULL, - NULL, - "vfsmount", - NULL, - "waitpid", /* 200 */ - NULL, - NULL, - NULL, - NULL, - NULL, /* 205 */ - NULL, - NULL, - NULL, - NULL, - NULL, /* 210 */ - NULL, - NULL, - NULL, - NULL, - NULL, /* 215 */ - NULL, - NULL, - NULL, - NULL, - NULL, /* 220 */ - NULL, - NULL, - NULL, - "sigsetreturn", - "sigsetstatemask", /* 225 */ - "bfactl", - "cs", - "cds", - NULL, - "pathconf", /* 230 */ - "fpathconf", - NULL, - NULL, - "nfs_fcntl", - "ogetacl", /* 235 */ - "ofgetacl", - "osetacl", - "ofsetacl", - "pstat", - "getaudid", /* 240 */ - "setaudid", - "getaudproc", - "setaudproc", - "getevent", - "setevent", /* 245 */ - "audwrite", - "audswitch", - "audctl", - "ogetaccess", - "fsctl", /* 250 */ - "ulconnect", - "ulcontrol", - "ulcreate", - "uldest", - "ulrecv", /* 255 */ - "ulrecvcn", - "ulsend", - "ulshutdown", - "swapfs", - "fss", /* 260 */ - NULL, - NULL, - NULL, - NULL, - NULL, /* 265 */ - NULL, - "tsync", - "getnumfds", - "poll", - "getmsg", /* 270 */ - "putmsg", - "fchdir", - "getmount_cnt", - "getmount_entry", - "accept", /* 275 */ - "bind", - "connect", - "getpeername", - "getsockname", - "getsockopt", /* 280 */ - "listen", - "recv", - "recvfrom", - "recvmsg", - "send", /* 285 */ - "sendmsg", - "sendto", - "setsockopt", - "shutdown", - "socket", /* 290 */ - "socketpair", - "proc_open", - "proc_close", - "proc_send", - "proc_recv", /* 295 */ - "proc_sendrecv", - "proc_syscall", - "ipccreate", - "ipcname", - "ipcnamerase", /* 300 */ - "ipclookup", - "ipcselect", - "ipcconnect", - "ipcrecvcn", - "ipcsend", /* 305 */ - "ipcrecv", - "ipcgetnodename", - "ipcsetnodename", - "ipccontrol", - "ipcshutdown", /* 310 */ - "ipcdest", - "semctl", - "msgctl", - "shmctl", - "mpctl", /* 315 */ - "exportfs", - "getpmsg", - "putpmsg", - "strioctl", - "msync", /* 320 */ - "msleep", - "mwakeup", - "msem_init", - "msem_remove", - "adjtime", /* 325 */ - "kload", - "fattach", - "fdetach", - "serialize", - "statvfs", /* 330 */ - "fstatvfs", - "lchown", - "getsid", - "sysfs", - NULL, /* 335 */ - NULL, - "sched_setparam", - "sched_getparam", - "sched_setscheduler", - "sched_getscheduler", /* 340 */ - "sched_yield", - "sched_get_priority_max", - "sched_get_priority_min", - "sched_rr_get_interval", - "clock_settime", /* 345 */ - "clock_gettime", - "clock_getres", - "timer_create", - "timer_delete", - "timer_settime", /* 350 */ - "timer_gettime", - "timer_getoverrun", - "nanosleep", - "toolbox", - NULL, /* 355 */ - "getdents", - "getcontext", - "sysinfo", - "fcntl64", - "ftruncate64", /* 360 */ - "fstat64", - "getdirentries64", - "getrlimit64", - "lockf64", - "lseek64", /* 365 */ - "lstat64", - "mmap64", - "setrlimit64", - "stat64", - "truncate64", /* 370 */ - "ulimit64", - NULL, - NULL, - NULL, - NULL, /* 375 */ - NULL, - NULL, - NULL, - NULL, - "setcontext", /* 380 */ - "sigaltstack", - "waitid", - "setpgrp", - "recvmsg2", - "sendmsg2", /* 385 */ - "socket2", - "socketpair2", - "setregid", - "lwp_create", - "lwp_terminate", /* 390 */ - "lwp_wait", - "lwp_suspend", - "lwp_resume", - "lwp_self", - "lwp_abort_syscall", /* 395 */ - "lwp_info", - "lwp_kill", - "ksleep", - "kwakeup", - "ksleep_abort", /* 400 */ - "lwp_proc_info", - "lwp_exit", - "lwp_continue", - "getacl", - "fgetacl", /* 405 */ - "setacl", - "fsetacl", - "getaccess", - "lwp_mutex_init", - "lwp_mutex_lock_sys", /* 410 */ - "lwp_mutex_unlock", - "lwp_cond_init", - "lwp_cond_signal", - "lwp_cond_broadcast", - "lwp_cond_wait_sys", /* 415 */ - "lwp_getscheduler", - "lwp_setscheduler", - "lwp_getprivate", - "lwp_setprivate", - "lwp_detach", /* 420 */ - "mlock", - "munlock", - "mlockall", - "munlockall", - "shm_open", /* 425 */ - "shm_unlink", - "sigqueue", - "sigwaitinfo", - "sigtimedwait", - "sigwait", /* 430 */ - "aio_read", - "aio_write", - "lio_listio", - "aio_error", - "aio_return", /* 435 */ - "aio_cancel", - "aio_suspend", - "aio_fsync", - "mq_open", - "mq_unlink", /* 440 */ - "mq_send", - "mq_receive", - "mq_notify", - "mq_setattr", - "mq_getattr", /* 445 */ - "ksem_open", - "ksem_unlink", - "ksem_close", - "ksem_destroy", - "lw_sem_incr", /* 450 */ - "lw_sem_decr", - "lw_sem_read", - "mq_close", -}; -static const int syscall_names_max = 453; - -int -hpux_unimplemented(unsigned long arg1,unsigned long arg2,unsigned long arg3, - unsigned long arg4,unsigned long arg5,unsigned long arg6, - unsigned long arg7,unsigned long sc_num) -{ - /* NOTE: sc_num trashes arg8 for the few syscalls that actually - * have a valid 8th argument. - */ - const char *name = NULL; - if ( sc_num <= syscall_names_max && sc_num >= 0 ) { - name = syscall_names[sc_num]; - } - - if ( name ) { - printk(KERN_DEBUG "Unimplemented HP-UX syscall emulation. Syscall #%lu (%s)\n", - sc_num, name); - } else { - printk(KERN_DEBUG "Unimplemented unknown HP-UX syscall emulation. Syscall #%lu\n", - sc_num); - } - - printk(KERN_DEBUG " Args: %lx %lx %lx %lx %lx %lx %lx\n", - arg1, arg2, arg3, arg4, arg5, arg6, arg7); - - return -ENOSYS; -} diff --git a/arch/parisc/hpux/wrappers.S b/arch/parisc/hpux/wrappers.S deleted file mode 100644 index 58c53c879c02..000000000000 --- a/arch/parisc/hpux/wrappers.S +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Linux/PARISC Project (http://www.parisc-linux.org/) - * - * HP-UX System Call Wrapper routines and System Call Return Path - * - * Copyright (C) 2000 Hewlett-Packard (John Marvin) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifdef CONFIG_64BIT -#warning PA64 support needs more work...did first cut -#endif - -#include <asm/asm-offsets.h> -#include <asm/assembly.h> -#include <asm/signal.h> -#include <linux/linkage.h> - - .level LEVEL - .text - - /* These should probably go in a header file somewhere. - * They are duplicated in kernel/wrappers.S - * Possibly we should consider consolidating these - * register save/restore macros. - */ - .macro reg_save regs -#ifdef CONFIG_64BIT -#warning NEEDS WORK for 64-bit -#endif - STREG %r3, PT_GR3(\regs) - STREG %r4, PT_GR4(\regs) - STREG %r5, PT_GR5(\regs) - STREG %r6, PT_GR6(\regs) - STREG %r7, PT_GR7(\regs) - STREG %r8, PT_GR8(\regs) - STREG %r9, PT_GR9(\regs) - STREG %r10,PT_GR10(\regs) - STREG %r11,PT_GR11(\regs) - STREG %r12,PT_GR12(\regs) - STREG %r13,PT_GR13(\regs) - STREG %r14,PT_GR14(\regs) - STREG %r15,PT_GR15(\regs) - STREG %r16,PT_GR16(\regs) - STREG %r17,PT_GR17(\regs) - STREG %r18,PT_GR18(\regs) - .endm - - .macro reg_restore regs - LDREG PT_GR3(\regs), %r3 - LDREG PT_GR4(\regs), %r4 - LDREG PT_GR5(\regs), %r5 - LDREG PT_GR6(\regs), %r6 - LDREG PT_GR7(\regs), %r7 - LDREG PT_GR8(\regs), %r8 - LDREG PT_GR9(\regs), %r9 - LDREG PT_GR10(\regs),%r10 - LDREG PT_GR11(\regs),%r11 - LDREG PT_GR12(\regs),%r12 - LDREG PT_GR13(\regs),%r13 - LDREG PT_GR14(\regs),%r14 - LDREG PT_GR15(\regs),%r15 - LDREG PT_GR16(\regs),%r16 - LDREG PT_GR17(\regs),%r17 - LDREG PT_GR18(\regs),%r18 - .endm - - - .import sys_fork - -ENTRY(hpux_fork_wrapper) - ldo TASK_REGS-TASK_SZ_ALGN-64(%r30),%r1 ;! get pt regs - ;! pointer in task - reg_save %r1 - - STREG %r2,-20(%r30) - ldo 64(%r30),%r30 - STREG %r2,PT_GR19(%r1) ;! save for child - STREG %r30,PT_GR21(%r1) ;! save for child - - LDREG PT_GR30(%r1),%r25 - mtctl %r25,%cr29 - copy %r1,%r24 - bl sys_clone,%r2 - ldi SIGCHLD,%r26 - - LDREG -84(%r30),%r2 -fork_return: - ldo -64(%r30),%r30 - ldo TASK_REGS-TASK_SZ_ALGN-64(%r30),%r1 ;! get pt regs - - reg_restore %r1 - - /* - * HP-UX wants pid (child gets parent pid, parent gets child pid) - * in r28 and a flag in r29 (r29 == 1 for child, 0 for parent). - * Linux fork returns 0 for child, pid for parent. Since HP-UX - * libc stub throws away parent pid and returns 0 for child, - * we'll just return 0 for parent pid now. Only applications - * that jump directly to the gateway page (not supported) will - * know the difference. We can fix this later if necessary. - */ - - ldo -1024(%r0),%r1 - comb,>>=,n %r28,%r1,fork_exit /* just let the syscall exit handle it */ - or,= %r28,%r0,%r0 - or,tr %r0,%r0,%r29 /* r28 <> 0, we are parent, set r29 to 0 */ - ldo 1(%r0),%r29 /* r28 == 0, we are child, set r29 to 1 */ - -fork_exit: - bv %r0(%r2) - nop -ENDPROC(hpux_fork_wrapper) - - /* Set the return value for the child */ - -ENTRY(hpux_child_return) -#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) - bl,n schedule_tail, %r2 -#endif - - LDREG TASK_PT_GR19-TASK_SZ_ALGN-128(%r30),%r2 - b fork_return - copy %r0,%r28 -ENDPROC(hpux_child_return) - - .import hpux_execve - -ENTRY(hpux_execv_wrapper) - copy %r0,%r24 /* NULL environment */ - -ENTRY(hpux_execve_wrapper) - - ldo TASK_REGS-TASK_SZ_ALGN-64(%r30),%r1 ;! get pt regs - - /* - * Do we need to save/restore r3-r18 here? - * I don't think so. why would new thread need old - * threads registers? - */ - - /* Store arg0, arg1 and arg2 so that hpux_execve will find them */ - - STREG %r26,PT_GR26(%r1) - STREG %r25,PT_GR25(%r1) - STREG %r24,PT_GR24(%r1) - - STREG %r2,-20(%r30) - ldo 64(%r30),%r30 - bl hpux_execve,%r2 - copy %r1,%arg0 - - ldo -64(%r30),%r30 - LDREG -20(%r30),%r2 - - /* If exec succeeded we need to load the args */ - - ldo -1024(%r0),%r1 - comb,>>= %r28,%r1,exec_error - copy %r2,%r19 - ldo -TASK_SZ_ALGN-64(%r30),%r1 ;! get task ptr - LDREG TASK_PT_GR26(%r1),%r26 - LDREG TASK_PT_GR25(%r1),%r25 - LDREG TASK_PT_GR24(%r1),%r24 - LDREG TASK_PT_GR23(%r1),%r23 - copy %r0,%r2 /* Flag to syscall_exit not to clear args */ - -exec_error: - bv %r0(%r19) - nop -ENDPROC(hpux_execv_wrapper) - - .import hpux_pipe - - /* HP-UX expects pipefd's returned in r28 & r29 */ - -ENTRY(hpux_pipe_wrapper) - STREG %r2,-20(%r30) - ldo 64(%r30),%r30 - bl hpux_pipe,%r2 - ldo -56(%r30),%r26 /* pass local array to hpux_pipe */ - - - ldo -1024(%r0),%r1 - comb,>>= %r28,%r1,pipe_exit /* let syscall exit handle it */ - LDREG -84(%r30),%r2 - - /* if success, load fd's from stack array */ - - LDREG -56(%r30),%r28 - LDREG -52(%r30),%r29 - -pipe_exit: - bv %r0(%r2) - ldo -64(%r30),%r30 -ENDPROC(hpux_pipe_wrapper) - - .import syscall_exit - -ENTRY(hpux_syscall_exit) - /* - * - * HP-UX call return conventions: - * - * if error: - * r22 = 1 - * r28 = errno value - * r29 = secondary return value - * else - * r22 = 0 - * r28 = return value - * r29 = secondary return value - * - * For now, we'll just check to see if r28 is < (unsigned long)-1024 - * (to handle addresses > 2 Gb) and if so set r22 to zero. If not, - * we'll complement r28 and set r22 to 1. Wrappers will be - * needed for syscalls that care about the secondary return value. - * The wrapper may also need a way of avoiding the following code, - * but we'll deal with that when it becomes necessary. - */ - - ldo -1024(%r0),%r1 - comb,<< %r28,%r1,no_error - copy %r0,%r22 - subi 0,%r28,%r28 - ldo 1(%r0),%r22 - -no_error: - b,n syscall_exit -ENDPROC(hpux_syscall_exit) - - .import hpux_unimplemented - -ENTRY(hpux_unimplemented_wrapper) - b hpux_unimplemented - STREG %r22,-64(%r30) /* overwrite arg8 with syscall number */ -ENDPROC(hpux_unimplemented_wrapper) diff --git a/arch/parisc/include/asm/processor.h b/arch/parisc/include/asm/processor.h index 689a8ade3606..54adb60c0a42 100644 --- a/arch/parisc/include/asm/processor.h +++ b/arch/parisc/include/asm/processor.h @@ -330,8 +330,6 @@ struct mm_struct; /* Free all resources held by a thread. */ extern void release_thread(struct task_struct *); -extern void map_hpux_gateway_page(struct task_struct *tsk, struct mm_struct *mm); - extern unsigned long get_wchan(struct task_struct *p); #define KSTK_EIP(tsk) ((tsk)->thread.regs.iaoq[0]) diff --git a/arch/parisc/include/asm/uaccess.h b/arch/parisc/include/asm/uaccess.h index a5cb070b54bf..0abdd4c607ed 100644 --- a/arch/parisc/include/asm/uaccess.h +++ b/arch/parisc/include/asm/uaccess.h @@ -17,7 +17,7 @@ #define KERNEL_DS ((mm_segment_t){0}) #define USER_DS ((mm_segment_t){1}) -#define segment_eq(a,b) ((a).seg == (b).seg) +#define segment_eq(a, b) ((a).seg == (b).seg) #define get_ds() (KERNEL_DS) #define get_fs() (current_thread_info()->addr_limit) @@ -42,14 +42,14 @@ static inline long access_ok(int type, const void __user * addr, #if !defined(CONFIG_64BIT) #define LDD_KERNEL(ptr) BUILD_BUG() #define LDD_USER(ptr) BUILD_BUG() -#define STD_KERNEL(x, ptr) __put_kernel_asm64(x,ptr) -#define STD_USER(x, ptr) __put_user_asm64(x,ptr) +#define STD_KERNEL(x, ptr) __put_kernel_asm64(x, ptr) +#define STD_USER(x, ptr) __put_user_asm64(x, ptr) #define ASM_WORD_INSN ".word\t" #else -#define LDD_KERNEL(ptr) __get_kernel_asm("ldd",ptr) -#define LDD_USER(ptr) __get_user_asm("ldd",ptr) -#define STD_KERNEL(x, ptr) __put_kernel_asm("std",x,ptr) -#define STD_USER(x, ptr) __put_user_asm("std",x,ptr) +#define LDD_KERNEL(ptr) __get_kernel_asm("ldd", ptr) +#define LDD_USER(ptr) __get_user_asm("ldd", ptr) +#define STD_KERNEL(x, ptr) __put_kernel_asm("std", x, ptr) +#define STD_USER(x, ptr) __put_user_asm("std", x, ptr) #define ASM_WORD_INSN ".dword\t" #endif @@ -80,68 +80,68 @@ struct exception_data { unsigned long fault_addr; }; -#define __get_user(x,ptr) \ -({ \ - register long __gu_err __asm__ ("r8") = 0; \ - register long __gu_val __asm__ ("r9") = 0; \ - \ - if (segment_eq(get_fs(),KERNEL_DS)) { \ - switch (sizeof(*(ptr))) { \ - case 1: __get_kernel_asm("ldb",ptr); break; \ - case 2: __get_kernel_asm("ldh",ptr); break; \ - case 4: __get_kernel_asm("ldw",ptr); break; \ - case 8: LDD_KERNEL(ptr); break; \ - default: BUILD_BUG(); break; \ - } \ - } \ - else { \ - switch (sizeof(*(ptr))) { \ - case 1: __get_user_asm("ldb",ptr); break; \ - case 2: __get_user_asm("ldh",ptr); break; \ - case 4: __get_user_asm("ldw",ptr); break; \ - case 8: LDD_USER(ptr); break; \ - default: BUILD_BUG(); break; \ - } \ - } \ - \ - (x) = (__typeof__(*(ptr))) __gu_val; \ - __gu_err; \ +#define __get_user(x, ptr) \ +({ \ + register long __gu_err __asm__ ("r8") = 0; \ + register long __gu_val __asm__ ("r9") = 0; \ + \ + if (segment_eq(get_fs(), KERNEL_DS)) { \ + switch (sizeof(*(ptr))) { \ + case 1: __get_kernel_asm("ldb", ptr); break; \ + case 2: __get_kernel_asm("ldh", ptr); break; \ + case 4: __get_kernel_asm("ldw", ptr); break; \ + case 8: LDD_KERNEL(ptr); break; \ + default: BUILD_BUG(); break; \ + } \ + } \ + else { \ + switch (sizeof(*(ptr))) { \ + case 1: __get_user_asm("ldb", ptr); break; \ + case 2: __get_user_asm("ldh", ptr); break; \ + case 4: __get_user_asm("ldw", ptr); break; \ + case 8: LDD_USER(ptr); break; \ + default: BUILD_BUG(); break; \ + } \ + } \ + \ + (x) = (__force __typeof__(*(ptr))) __gu_val; \ + __gu_err; \ }) -#define __get_kernel_asm(ldx,ptr) \ +#define __get_kernel_asm(ldx, ptr) \ __asm__("\n1:\t" ldx "\t0(%2),%0\n\t" \ ASM_EXCEPTIONTABLE_ENTRY(1b, fixup_get_user_skip_1)\ : "=r"(__gu_val), "=r"(__gu_err) \ : "r"(ptr), "1"(__gu_err) \ : "r1"); -#define __get_user_asm(ldx,ptr) \ +#define __get_user_asm(ldx, ptr) \ __asm__("\n1:\t" ldx "\t0(%%sr3,%2),%0\n\t" \ - ASM_EXCEPTIONTABLE_ENTRY(1b,fixup_get_user_skip_1)\ + ASM_EXCEPTIONTABLE_ENTRY(1b, fixup_get_user_skip_1)\ : "=r"(__gu_val), "=r"(__gu_err) \ : "r"(ptr), "1"(__gu_err) \ : "r1"); -#define __put_user(x,ptr) \ +#define __put_user(x, ptr) \ ({ \ register long __pu_err __asm__ ("r8") = 0; \ __typeof__(*(ptr)) __x = (__typeof__(*(ptr)))(x); \ \ - if (segment_eq(get_fs(),KERNEL_DS)) { \ + if (segment_eq(get_fs(), KERNEL_DS)) { \ switch (sizeof(*(ptr))) { \ - case 1: __put_kernel_asm("stb",__x,ptr); break; \ - case 2: __put_kernel_asm("sth",__x,ptr); break; \ - case 4: __put_kernel_asm("stw",__x,ptr); break; \ - case 8: STD_KERNEL(__x,ptr); break; \ + case 1: __put_kernel_asm("stb", __x, ptr); break; \ + case 2: __put_kernel_asm("sth", __x, ptr); break; \ + case 4: __put_kernel_asm("stw", __x, ptr); break; \ + case 8: STD_KERNEL(__x, ptr); break; \ default: BUILD_BUG(); break; \ } \ } \ else { \ switch (sizeof(*(ptr))) { \ - case 1: __put_user_asm("stb",__x,ptr); break; \ - case 2: __put_user_asm("sth",__x,ptr); break; \ - case 4: __put_user_asm("stw",__x,ptr); break; \ - case 8: STD_USER(__x,ptr); break; \ + case 1: __put_user_asm("stb", __x, ptr); break; \ + case 2: __put_user_asm("sth", __x, ptr); break; \ + case 4: __put_user_asm("stw", __x, ptr); break; \ + case 8: STD_USER(__x, ptr); break; \ default: BUILD_BUG(); break; \ } \ } \ @@ -159,18 +159,18 @@ struct exception_data { * r8/r9 are already listed as err/val. */ -#define __put_kernel_asm(stx,x,ptr) \ +#define __put_kernel_asm(stx, x, ptr) \ __asm__ __volatile__ ( \ "\n1:\t" stx "\t%2,0(%1)\n\t" \ - ASM_EXCEPTIONTABLE_ENTRY(1b,fixup_put_user_skip_1)\ + ASM_EXCEPTIONTABLE_ENTRY(1b, fixup_put_user_skip_1)\ : "=r"(__pu_err) \ : "r"(ptr), "r"(x), "0"(__pu_err) \ : "r1") -#define __put_user_asm(stx,x,ptr) \ +#define __put_user_asm(stx, x, ptr) \ __asm__ __volatile__ ( \ "\n1:\t" stx "\t%2,0(%%sr3,%1)\n\t" \ - ASM_EXCEPTIONTABLE_ENTRY(1b,fixup_put_user_skip_1)\ + ASM_EXCEPTIONTABLE_ENTRY(1b, fixup_put_user_skip_1)\ : "=r"(__pu_err) \ : "r"(ptr), "r"(x), "0"(__pu_err) \ : "r1") @@ -178,23 +178,23 @@ struct exception_data { #if !defined(CONFIG_64BIT) -#define __put_kernel_asm64(__val,ptr) do { \ +#define __put_kernel_asm64(__val, ptr) do { \ __asm__ __volatile__ ( \ "\n1:\tstw %2,0(%1)" \ "\n2:\tstw %R2,4(%1)\n\t" \ - ASM_EXCEPTIONTABLE_ENTRY(1b,fixup_put_user_skip_2)\ - ASM_EXCEPTIONTABLE_ENTRY(2b,fixup_put_user_skip_1)\ + ASM_EXCEPTIONTABLE_ENTRY(1b, fixup_put_user_skip_2)\ + ASM_EXCEPTIONTABLE_ENTRY(2b, fixup_put_user_skip_1)\ : "=r"(__pu_err) \ : "r"(ptr), "r"(__val), "0"(__pu_err) \ : "r1"); \ } while (0) -#define __put_user_asm64(__val,ptr) do { \ +#define __put_user_asm64(__val, ptr) do { \ __asm__ __volatile__ ( \ "\n1:\tstw %2,0(%%sr3,%1)" \ "\n2:\tstw %R2,4(%%sr3,%1)\n\t" \ - ASM_EXCEPTIONTABLE_ENTRY(1b,fixup_put_user_skip_2)\ - ASM_EXCEPTIONTABLE_ENTRY(2b,fixup_put_user_skip_1)\ + ASM_EXCEPTIONTABLE_ENTRY(1b, fixup_put_user_skip_2)\ + ASM_EXCEPTIONTABLE_ENTRY(2b, fixup_put_user_skip_1)\ : "=r"(__pu_err) \ : "r"(ptr), "r"(__val), "0"(__pu_err) \ : "r1"); \ @@ -211,8 +211,8 @@ extern unsigned long lcopy_to_user(void __user *, const void *, unsigned long); extern unsigned long lcopy_from_user(void *, const void __user *, unsigned long); extern unsigned long lcopy_in_user(void __user *, const void __user *, unsigned long); extern long strncpy_from_user(char *, const char __user *, long); -extern unsigned lclear_user(void __user *,unsigned long); -extern long lstrnlen_user(const char __user *,long); +extern unsigned lclear_user(void __user *, unsigned long); +extern long lstrnlen_user(const char __user *, long); /* * Complex access routines -- macros */ diff --git a/arch/parisc/include/uapi/asm/unistd.h b/arch/parisc/include/uapi/asm/unistd.h index 5f5c0373de63..2e639d7604f6 100644 --- a/arch/parisc/include/uapi/asm/unistd.h +++ b/arch/parisc/include/uapi/asm/unistd.h @@ -2,480 +2,6 @@ #define _UAPI_ASM_PARISC_UNISTD_H_ /* - * This file contains the system call numbers. - */ - -/* - * HP-UX system calls get their native numbers for binary compatibility. - */ - -#define __NR_HPUX_exit 1 -#define __NR_HPUX_fork 2 -#define __NR_HPUX_read 3 -#define __NR_HPUX_write 4 -#define __NR_HPUX_open 5 -#define __NR_HPUX_close 6 -#define __NR_HPUX_wait 7 -#define __NR_HPUX_creat 8 -#define __NR_HPUX_link 9 -#define __NR_HPUX_unlink 10 -#define __NR_HPUX_execv 11 -#define __NR_HPUX_chdir 12 -#define __NR_HPUX_time 13 -#define __NR_HPUX_mknod 14 -#define __NR_HPUX_chmod 15 -#define __NR_HPUX_chown 16 -#define __NR_HPUX_break 17 -#define __NR_HPUX_lchmod 18 -#define __NR_HPUX_lseek 19 -#define __NR_HPUX_getpid 20 -#define __NR_HPUX_mount 21 -#define __NR_HPUX_umount 22 -#define __NR_HPUX_setuid 23 -#define __NR_HPUX_getuid 24 -#define __NR_HPUX_stime 25 -#define __NR_HPUX_ptrace 26 -#define __NR_HPUX_alarm 27 -#define __NR_HPUX_oldfstat 28 -#define __NR_HPUX_pause 29 -#define __NR_HPUX_utime 30 -#define __NR_HPUX_stty 31 -#define __NR_HPUX_gtty 32 -#define __NR_HPUX_access 33 -#define __NR_HPUX_nice 34 -#define __NR_HPUX_ftime 35 -#define __NR_HPUX_sync 36 -#define __NR_HPUX_kill 37 -#define __NR_HPUX_stat 38 -#define __NR_HPUX_setpgrp3 39 -#define __NR_HPUX_lstat 40 -#define __NR_HPUX_dup 41 -#define __NR_HPUX_pipe 42 -#define __NR_HPUX_times 43 -#define __NR_HPUX_profil 44 -#define __NR_HPUX_ki_call 45 -#define __NR_HPUX_setgid 46 -#define __NR_HPUX_getgid 47 -#define __NR_HPUX_sigsys 48 -#define __NR_HPUX_reserved1 49 -#define __NR_HPUX_reserved2 50 -#define __NR_HPUX_acct 51 -#define __NR_HPUX_set_userthreadid 52 -#define __NR_HPUX_oldlock 53 -#define __NR_HPUX_ioctl 54 -#define __NR_HPUX_reboot 55 -#define __NR_HPUX_symlink 56 -#define __NR_HPUX_utssys 57 -#define __NR_HPUX_readlink 58 -#define __NR_HPUX_execve 59 -#define __NR_HPUX_umask 60 -#define __NR_HPUX_chroot 61 -#define __NR_HPUX_fcntl 62 -#define __NR_HPUX_ulimit 63 -#define __NR_HPUX_getpagesize 64 -#define __NR_HPUX_mremap 65 -#define __NR_HPUX_vfork 66 -#define __NR_HPUX_vread 67 -#define __NR_HPUX_vwrite 68 -#define __NR_HPUX_sbrk 69 -#define __NR_HPUX_sstk 70 -#define __NR_HPUX_mmap 71 -#define __NR_HPUX_vadvise 72 -#define __NR_HPUX_munmap 73 -#define __NR_HPUX_mprotect 74 -#define __NR_HPUX_madvise 75 -#define __NR_HPUX_vhangup 76 -#define __NR_HPUX_swapoff 77 -#define __NR_HPUX_mincore 78 -#define __NR_HPUX_getgroups 79 -#define __NR_HPUX_setgroups 80 -#define __NR_HPUX_getpgrp2 81 -#define __NR_HPUX_setpgrp2 82 -#define __NR_HPUX_setitimer 83 -#define __NR_HPUX_wait3 84 -#define __NR_HPUX_swapon 85 -#define __NR_HPUX_getitimer 86 -#define __NR_HPUX_gethostname42 87 -#define __NR_HPUX_sethostname42 88 -#define __NR_HPUX_getdtablesize 89 -#define __NR_HPUX_dup2 90 -#define __NR_HPUX_getdopt 91 -#define __NR_HPUX_fstat 92 -#define __NR_HPUX_select 93 -#define __NR_HPUX_setdopt 94 -#define __NR_HPUX_fsync 95 -#define __NR_HPUX_setpriority 96 -#define __NR_HPUX_socket_old 97 -#define __NR_HPUX_connect_old 98 -#define __NR_HPUX_accept_old 99 -#define __NR_HPUX_getpriority 100 -#define __NR_HPUX_send_old 101 -#define __NR_HPUX_recv_old 102 -#define __NR_HPUX_socketaddr_old 103 -#define __NR_HPUX_bind_old 104 -#define __NR_HPUX_setsockopt_old 105 -#define __NR_HPUX_listen_old 106 -#define __NR_HPUX_vtimes_old 107 -#define __NR_HPUX_sigvector 108 -#define __NR_HPUX_sigblock 109 -#define __NR_HPUX_siggetmask 110 -#define __NR_HPUX_sigpause 111 -#define __NR_HPUX_sigstack 112 -#define __NR_HPUX_recvmsg_old 113 -#define __NR_HPUX_sendmsg_old 114 -#define __NR_HPUX_vtrace_old 115 -#define __NR_HPUX_gettimeofday 116 -#define __NR_HPUX_getrusage 117 -#define __NR_HPUX_getsockopt_old 118 -#define __NR_HPUX_resuba_old 119 -#define __NR_HPUX_readv 120 -#define __NR_HPUX_writev 121 -#define __NR_HPUX_settimeofday 122 -#define __NR_HPUX_fchown 123 -#define __NR_HPUX_fchmod 124 -#define __NR_HPUX_recvfrom_old 125 -#define __NR_HPUX_setresuid 126 -#define __NR_HPUX_setresgid 127 -#define __NR_HPUX_rename 128 -#define __NR_HPUX_truncate 129 -#define __NR_HPUX_ftruncate 130 -#define __NR_HPUX_flock_old 131 -#define __NR_HPUX_sysconf 132 -#define __NR_HPUX_sendto_old 133 -#define __NR_HPUX_shutdown_old 134 -#define __NR_HPUX_socketpair_old 135 -#define __NR_HPUX_mkdir 136 -#define __NR_HPUX_rmdir 137 -#define __NR_HPUX_utimes_old 138 -#define __NR_HPUX_sigcleanup_old 139 -#define __NR_HPUX_setcore 140 -#define __NR_HPUX_getpeername_old 141 -#define __NR_HPUX_gethostid 142 -#define __NR_HPUX_sethostid 143 -#define __NR_HPUX_getrlimit 144 -#define __NR_HPUX_setrlimit 145 -#define __NR_HPUX_killpg_old 146 -#define __NR_HPUX_cachectl 147 -#define __NR_HPUX_quotactl 148 -#define __NR_HPUX_get_sysinfo 149 -#define __NR_HPUX_getsockname_old 150 -#define __NR_HPUX_privgrp 151 -#define __NR_HPUX_rtprio 152 -#define __NR_HPUX_plock 153 -#define __NR_HPUX_reserved3 154 -#define __NR_HPUX_lockf 155 -#define __NR_HPUX_semget 156 -#define __NR_HPUX_osemctl 157 -#define __NR_HPUX_semop 158 -#define __NR_HPUX_msgget 159 -#define __NR_HPUX_omsgctl 160 -#define __NR_HPUX_msgsnd 161 -#define __NR_HPUX_msgrecv 162 -#define __NR_HPUX_shmget 163 -#define __NR_HPUX_oshmctl 164 -#define __NR_HPUX_shmat 165 -#define __NR_HPUX_shmdt 166 -#define __NR_HPUX_m68020_advise 167 -/* [168,189] are for Discless/DUX */ -#define __NR_HPUX_csp 168 -#define __NR_HPUX_cluster 169 -#define __NR_HPUX_mkrnod 170 -#define __NR_HPUX_test 171 -#define __NR_HPUX_unsp_open 172 -#define __NR_HPUX_reserved4 173 -#define __NR_HPUX_getcontext_old 174 -#define __NR_HPUX_osetcontext 175 -#define __NR_HPUX_bigio 176 -#define __NR_HPUX_pipenode 177 -#define __NR_HPUX_lsync 178 -#define __NR_HPUX_getmachineid 179 -#define __NR_HPUX_cnodeid 180 -#define __NR_HPUX_cnodes 181 -#define __NR_HPUX_swapclients 182 -#define __NR_HPUX_rmt_process 183 -#define __NR_HPUX_dskless_stats 184 -#define __NR_HPUX_sigprocmask 185 -#define __NR_HPUX_sigpending 186 -#define __NR_HPUX_sigsuspend 187 -#define __NR_HPUX_sigaction 188 -#define __NR_HPUX_reserved5 189 -#define __NR_HPUX_nfssvc 190 -#define __NR_HPUX_getfh 191 -#define __NR_HPUX_getdomainname 192 -#define __NR_HPUX_setdomainname 193 -#define __NR_HPUX_async_daemon 194 -#define __NR_HPUX_getdirentries 195 -#define __NR_HPUX_statfs 196 -#define __NR_HPUX_fstatfs 197 -#define __NR_HPUX_vfsmount 198 -#define __NR_HPUX_reserved6 199 -#define __NR_HPUX_waitpid 200 -/* 201 - 223 missing */ -#define __NR_HPUX_sigsetreturn 224 -#define __NR_HPUX_sigsetstatemask 225 -/* 226 missing */ -#define __NR_HPUX_cs 227 -#define __NR_HPUX_cds 228 -#define __NR_HPUX_set_no_trunc 229 -#define __NR_HPUX_pathconf 230 -#define __NR_HPUX_fpathconf 231 -/* 232, 233 missing */ -#define __NR_HPUX_nfs_fcntl 234 -#define __NR_HPUX_ogetacl 235 -#define __NR_HPUX_ofgetacl 236 -#define __NR_HPUX_osetacl 237 -#define __NR_HPUX_ofsetacl 238 -#define __NR_HPUX_pstat 239 -#define __NR_HPUX_getaudid 240 -#define __NR_HPUX_setaudid 241 -#define __NR_HPUX_getaudproc 242 -#define __NR_HPUX_setaudproc 243 -#define __NR_HPUX_getevent 244 -#define __NR_HPUX_setevent 245 -#define __NR_HPUX_audwrite 246 -#define __NR_HPUX_audswitch 247 -#define __NR_HPUX_audctl 248 -#define __NR_HPUX_ogetaccess 249 -#define __NR_HPUX_fsctl 250 -/* 251 - 258 missing */ -#define __NR_HPUX_swapfs 259 -#define __NR_HPUX_fss 260 -/* 261 - 266 missing */ -#define __NR_HPUX_tsync 267 -#define __NR_HPUX_getnumfds 268 -#define __NR_HPUX_poll 269 -#define __NR_HPUX_getmsg 270 -#define __NR_HPUX_putmsg 271 -#define __NR_HPUX_fchdir 272 -#define __NR_HPUX_getmount_cnt 273 -#define __NR_HPUX_getmount_entry 274 -#define __NR_HPUX_accept 275 -#define __NR_HPUX_bind 276 -#define __NR_HPUX_connect 277 -#define __NR_HPUX_getpeername 278 -#define __NR_HPUX_getsockname 279 -#define __NR_HPUX_getsockopt 280 -#define __NR_HPUX_listen 281 -#define __NR_HPUX_recv 282 -#define __NR_HPUX_recvfrom 283 -#define __NR_HPUX_recvmsg 284 -#define __NR_HPUX_send 285 -#define __NR_HPUX_sendmsg 286 -#define __NR_HPUX_sendto 287 -#define __NR_HPUX_setsockopt 288 -#define __NR_HPUX_shutdown 289 -#define __NR_HPUX_socket 290 -#define __NR_HPUX_socketpair 291 -#define __NR_HPUX_proc_open 292 -#define __NR_HPUX_proc_close 293 -#define __NR_HPUX_proc_send 294 -#define __NR_HPUX_proc_recv 295 -#define __NR_HPUX_proc_sendrecv 296 -#define __NR_HPUX_proc_syscall 297 -/* 298 - 311 missing */ -#define __NR_HPUX_semctl 312 -#define __NR_HPUX_msgctl 313 -#define __NR_HPUX_shmctl 314 -#define __NR_HPUX_mpctl 315 -#define __NR_HPUX_exportfs 316 -#define __NR_HPUX_getpmsg 317 -#define __NR_HPUX_putpmsg 318 -/* 319 missing */ -#define __NR_HPUX_msync 320 -#define __NR_HPUX_msleep 321 -#define __NR_HPUX_mwakeup 322 -#define __NR_HPUX_msem_init 323 -#define __NR_HPUX_msem_remove 324 -#define __NR_HPUX_adjtime 325 -#define __NR_HPUX_kload 326 -#define __NR_HPUX_fattach 327 -#define __NR_HPUX_fdetach 328 -#define __NR_HPUX_serialize 329 -#define __NR_HPUX_statvfs 330 -#define __NR_HPUX_fstatvfs 331 -#define __NR_HPUX_lchown 332 -#define __NR_HPUX_getsid 333 -#define __NR_HPUX_sysfs 334 -/* 335, 336 missing */ -#define __NR_HPUX_sched_setparam 337 -#define __NR_HPUX_sched_getparam 338 -#define __NR_HPUX_sched_setscheduler 339 -#define __NR_HPUX_sched_getscheduler 340 -#define __NR_HPUX_sched_yield 341 -#define __NR_HPUX_sched_get_priority_max 342 -#define __NR_HPUX_sched_get_priority_min 343 -#define __NR_HPUX_sched_rr_get_interval 344 -#define __NR_HPUX_clock_settime 345 -#define __NR_HPUX_clock_gettime 346 -#define __NR_HPUX_clock_getres 347 -#define __NR_HPUX_timer_create 348 -#define __NR_HPUX_timer_delete 349 -#define __NR_HPUX_timer_settime 350 -#define __NR_HPUX_timer_gettime 351 -#define __NR_HPUX_timer_getoverrun 352 -#define __NR_HPUX_nanosleep 353 -#define __NR_HPUX_toolbox 354 -/* 355 missing */ -#define __NR_HPUX_getdents 356 -#define __NR_HPUX_getcontext 357 -#define __NR_HPUX_sysinfo 358 -#define __NR_HPUX_fcntl64 359 -#define __NR_HPUX_ftruncate64 360 -#define __NR_HPUX_fstat64 361 -#define __NR_HPUX_getdirentries64 362 -#define __NR_HPUX_getrlimit64 363 -#define __NR_HPUX_lockf64 364 -#define __NR_HPUX_lseek64 365 -#define __NR_HPUX_lstat64 366 -#define __NR_HPUX_mmap64 367 -#define __NR_HPUX_setrlimit64 368 -#define __NR_HPUX_stat64 369 -#define __NR_HPUX_truncate64 370 -#define __NR_HPUX_ulimit64 371 -#define __NR_HPUX_pread 372 -#define __NR_HPUX_preadv 373 -#define __NR_HPUX_pwrite 374 -#define __NR_HPUX_pwritev 375 -#define __NR_HPUX_pread64 376 -#define __NR_HPUX_preadv64 377 -#define __NR_HPUX_pwrite64 378 -#define __NR_HPUX_pwritev64 379 -#define __NR_HPUX_setcontext 380 -#define __NR_HPUX_sigaltstack 381 -#define __NR_HPUX_waitid 382 -#define __NR_HPUX_setpgrp 383 -#define __NR_HPUX_recvmsg2 384 -#define __NR_HPUX_sendmsg2 385 -#define __NR_HPUX_socket2 386 -#define __NR_HPUX_socketpair2 387 -#define __NR_HPUX_setregid 388 -#define __NR_HPUX_lwp_create 389 -#define __NR_HPUX_lwp_terminate 390 -#define __NR_HPUX_lwp_wait 391 -#define __NR_HPUX_lwp_suspend 392 -#define __NR_HPUX_lwp_resume 393 -/* 394 missing */ -#define __NR_HPUX_lwp_abort_syscall 395 -#define __NR_HPUX_lwp_info 396 -#define __NR_HPUX_lwp_kill 397 -#define __NR_HPUX_ksleep 398 -#define __NR_HPUX_kwakeup 399 -/* 400 missing */ -#define __NR_HPUX_pstat_getlwp 401 -#define __NR_HPUX_lwp_exit 402 -#define __NR_HPUX_lwp_continue 403 -#define __NR_HPUX_getacl 404 -#define __NR_HPUX_fgetacl 405 -#define __NR_HPUX_setacl 406 -#define __NR_HPUX_fsetacl 407 -#define __NR_HPUX_getaccess 408 -#define __NR_HPUX_lwp_mutex_init 409 -#define __NR_HPUX_lwp_mutex_lock_sys 410 -#define __NR_HPUX_lwp_mutex_unlock 411 -#define __NR_HPUX_lwp_cond_init 412 -#define __NR_HPUX_lwp_cond_signal 413 -#define __NR_HPUX_lwp_cond_broadcast 414 -#define __NR_HPUX_lwp_cond_wait_sys 415 -#define __NR_HPUX_lwp_getscheduler 416 -#define __NR_HPUX_lwp_setscheduler 417 -#define __NR_HPUX_lwp_getstate 418 -#define __NR_HPUX_lwp_setstate 419 -#define __NR_HPUX_lwp_detach 420 -#define __NR_HPUX_mlock 421 -#define __NR_HPUX_munlock 422 -#define __NR_HPUX_mlockall 423 -#define __NR_HPUX_munlockall 424 -#define __NR_HPUX_shm_open 425 -#define __NR_HPUX_shm_unlink 426 -#define __NR_HPUX_sigqueue 427 -#define __NR_HPUX_sigwaitinfo 428 -#define __NR_HPUX_sigtimedwait 429 -#define __NR_HPUX_sigwait 430 -#define __NR_HPUX_aio_read 431 -#define __NR_HPUX_aio_write 432 -#define __NR_HPUX_lio_listio 433 -#define __NR_HPUX_aio_error 434 -#define __NR_HPUX_aio_return 435 -#define __NR_HPUX_aio_cancel 436 -#define __NR_HPUX_aio_suspend 437 -#define __NR_HPUX_aio_fsync 438 -#define __NR_HPUX_mq_open 439 -#define __NR_HPUX_mq_close 440 -#define __NR_HPUX_mq_unlink 441 -#define __NR_HPUX_mq_send 442 -#define __NR_HPUX_mq_receive 443 -#define __NR_HPUX_mq_notify 444 -#define __NR_HPUX_mq_setattr 445 -#define __NR_HPUX_mq_getattr 446 -#define __NR_HPUX_ksem_open 447 -#define __NR_HPUX_ksem_unlink 448 -#define __NR_HPUX_ksem_close 449 -#define __NR_HPUX_ksem_post 450 -#define __NR_HPUX_ksem_wait 451 -#define __NR_HPUX_ksem_read 452 -#define __NR_HPUX_ksem_trywait 453 -#define __NR_HPUX_lwp_rwlock_init 454 -#define __NR_HPUX_lwp_rwlock_destroy 455 -#define __NR_HPUX_lwp_rwlock_rdlock_sys 456 -#define __NR_HPUX_lwp_rwlock_wrlock_sys 457 -#define __NR_HPUX_lwp_rwlock_tryrdlock 458 -#define __NR_HPUX_lwp_rwlock_trywrlock 459 -#define __NR_HPUX_lwp_rwlock_unlock 460 -#define __NR_HPUX_ttrace 461 -#define __NR_HPUX_ttrace_wait 462 -#define __NR_HPUX_lf_wire_mem 463 -#define __NR_HPUX_lf_unwire_mem 464 -#define __NR_HPUX_lf_send_pin_map 465 -#define __NR_HPUX_lf_free_buf 466 -#define __NR_HPUX_lf_wait_nq 467 -#define __NR_HPUX_lf_wakeup_conn_q 468 -#define __NR_HPUX_lf_unused 469 -#define __NR_HPUX_lwp_sema_init 470 -#define __NR_HPUX_lwp_sema_post 471 -#define __NR_HPUX_lwp_sema_wait 472 -#define __NR_HPUX_lwp_sema_trywait 473 -#define __NR_HPUX_lwp_sema_destroy 474 -#define __NR_HPUX_statvfs64 475 -#define __NR_HPUX_fstatvfs64 476 -#define __NR_HPUX_msh_register 477 -#define __NR_HPUX_ptrace64 478 -#define __NR_HPUX_sendfile 479 -#define __NR_HPUX_sendpath 480 -#define __NR_HPUX_sendfile64 481 -#define __NR_HPUX_sendpath64 482 -#define __NR_HPUX_modload 483 -#define __NR_HPUX_moduload 484 -#define __NR_HPUX_modpath 485 -#define __NR_HPUX_getksym 486 -#define __NR_HPUX_modadm 487 -#define __NR_HPUX_modstat 488 -#define __NR_HPUX_lwp_detached_exit 489 -#define __NR_HPUX_crashconf 490 -#define __NR_HPUX_siginhibit 491 -#define __NR_HPUX_sigenable 492 -#define __NR_HPUX_spuctl 493 -#define __NR_HPUX_zerokernelsum 494 -#define __NR_HPUX_nfs_kstat 495 -#define __NR_HPUX_aio_read64 496 -#define __NR_HPUX_aio_write64 497 -#define __NR_HPUX_aio_error64 498 -#define __NR_HPUX_aio_return64 499 -#define __NR_HPUX_aio_cancel64 500 -#define __NR_HPUX_aio_suspend64 501 -#define __NR_HPUX_aio_fsync64 502 -#define __NR_HPUX_lio_listio64 503 -#define __NR_HPUX_recv2 504 -#define __NR_HPUX_recvfrom2 505 -#define __NR_HPUX_send2 506 -#define __NR_HPUX_sendto2 507 -#define __NR_HPUX_acl 508 -#define __NR_HPUX___cnx_p2p_ctl 509 -#define __NR_HPUX___cnx_gsched_ctl 510 -#define __NR_HPUX___cnx_pmon_ctl 511 - -#define __NR_HPUX_syscalls 512 - -/* * Linux system call numbers. * * Cary Coutant says that we should just use another syscall gateway @@ -484,9 +10,6 @@ * very least. If we decide to change it later, we can ``just'' tweak * the LINUX_GATEWAY_ADDR define at the bottom and make __NR_Linux be * 1024 or something. Oh, and recompile libc. =) - * - * 64-bit HPUX binaries get the syscall gateway address passed in a register - * from the kernel at startup, which seems a sane strategy. */ #define __NR_Linux 0 @@ -834,15 +357,15 @@ #define __NR_getrandom (__NR_Linux + 339) #define __NR_memfd_create (__NR_Linux + 340) #define __NR_bpf (__NR_Linux + 341) +#define __NR_execveat (__NR_Linux + 342) -#define __NR_Linux_syscalls (__NR_bpf + 1) +#define __NR_Linux_syscalls (__NR_execveat + 1) #define __IGNORE_select /* newselect */ #define __IGNORE_fadvise64 /* fadvise64_64 */ -#define HPUX_GATEWAY_ADDR 0xC0000004 #define LINUX_GATEWAY_ADDR 0x100 #endif /* _UAPI_ASM_PARISC_UNISTD_H_ */ diff --git a/arch/parisc/kernel/entry.S b/arch/parisc/kernel/entry.S index e8f07dd28401..2ab16bb160a8 100644 --- a/arch/parisc/kernel/entry.S +++ b/arch/parisc/kernel/entry.S @@ -1774,10 +1774,6 @@ ENTRY(sys_rt_sigreturn_wrapper) ENDPROC(sys_rt_sigreturn_wrapper) ENTRY(syscall_exit) - /* NOTE: HP-UX syscalls also come through here - * after hpux_syscall_exit fixes up return - * values. */ - /* NOTE: Not all syscalls exit this way. rt_sigreturn will exit * via syscall_exit_rfi if the signal was received while the process * was running. @@ -1789,22 +1785,6 @@ ENTRY(syscall_exit) LDREG TI_TASK(%r1),%r1 STREG %r28,TASK_PT_GR28(%r1) -#ifdef CONFIG_HPUX -/* <linux/personality.h> cannot be easily included */ -#define PER_HPUX 0x10 - ldw TASK_PERSONALITY(%r1),%r19 - - /* We can't use "CMPIB<> PER_HPUX" since "im5" field is sign extended */ - ldo -PER_HPUX(%r19), %r19 - cmpib,COND(<>),n 0,%r19,1f - - /* Save other hpux returns if personality is PER_HPUX */ - STREG %r22,TASK_PT_GR22(%r1) - STREG %r29,TASK_PT_GR29(%r1) -1: - -#endif /* CONFIG_HPUX */ - /* Seems to me that dp could be wrong here, if the syscall involved * calling a module, and nothing got round to restoring dp on return. */ diff --git a/arch/parisc/kernel/process.c b/arch/parisc/kernel/process.c index 0bbbf0d3f608..8a488c22a99f 100644 --- a/arch/parisc/kernel/process.c +++ b/arch/parisc/kernel/process.c @@ -193,9 +193,7 @@ copy_thread(unsigned long clone_flags, unsigned long usp, * Make them const so the compiler knows they live in .text */ extern void * const ret_from_kernel_thread; extern void * const child_return; -#ifdef CONFIG_HPUX - extern void * const hpux_child_return; -#endif + if (unlikely(p->flags & PF_KTHREAD)) { memset(cregs, 0, sizeof(struct pt_regs)); if (!usp) /* idle thread */ @@ -229,15 +227,8 @@ copy_thread(unsigned long clone_flags, unsigned long usp, cregs->gr[30] = usp; } cregs->ksp = (unsigned long)stack + THREAD_SZ_ALGN + FRAME_SIZE; - if (personality(p->personality) == PER_HPUX) { -#ifdef CONFIG_HPUX - cregs->kpc = (unsigned long) &hpux_child_return; -#else - BUG(); -#endif - } else { - cregs->kpc = (unsigned long) &child_return; - } + cregs->kpc = (unsigned long) &child_return; + /* Setup thread TLS area from the 4th parameter in clone */ if (clone_flags & CLONE_SETTLS) cregs->cr27 = cregs->gr[23]; diff --git a/arch/parisc/kernel/signal.c b/arch/parisc/kernel/signal.c index 9b910a0251b8..dc1ea796fd60 100644 --- a/arch/parisc/kernel/signal.c +++ b/arch/parisc/kernel/signal.c @@ -9,8 +9,7 @@ * * Like the IA-64, we are a recent enough port (we are *starting* * with glibc2.2) that we do not need to support the old non-realtime - * Linux signals. Therefore we don't. HP/UX signals will go in - * arch/parisc/hpux/signal.c when we figure out how to do them. + * Linux signals. Therefore we don't. */ #include <linux/sched.h> @@ -476,6 +475,9 @@ insert_restart_trampoline(struct pt_regs *regs) case -ERESTART_RESTARTBLOCK: { /* Restart the system call - no handlers present */ unsigned int *usp = (unsigned int *)regs->gr[30]; + unsigned long start = (unsigned long) &usp[2]; + unsigned long end = (unsigned long) &usp[5]; + long err = 0; /* Setup a trampoline to restart the syscall * with __NR_restart_syscall @@ -487,23 +489,21 @@ insert_restart_trampoline(struct pt_regs *regs) * 16: ldi __NR_restart_syscall, %r20 */ #ifdef CONFIG_64BIT - put_user(regs->gr[31] >> 32, &usp[0]); - put_user(regs->gr[31] & 0xffffffff, &usp[1]); - put_user(0x0fc010df, &usp[2]); + err |= put_user(regs->gr[31] >> 32, &usp[0]); + err |= put_user(regs->gr[31] & 0xffffffff, &usp[1]); + err |= put_user(0x0fc010df, &usp[2]); #else - put_user(regs->gr[31], &usp[0]); - put_user(0x0fc0109f, &usp[2]); + err |= put_user(regs->gr[31], &usp[0]); + err |= put_user(0x0fc0109f, &usp[2]); #endif - put_user(0xe0008200, &usp[3]); - put_user(0x34140000, &usp[4]); + err |= put_user(0xe0008200, &usp[3]); + err |= put_user(0x34140000, &usp[4]); - /* Stack is 64-byte aligned, and we only need - * to flush 1 cache line. - * Flushing one cacheline is cheap. - * "sync" on bigger (> 4 way) boxes is not. - */ - flush_user_dcache_range(regs->gr[30], regs->gr[30] + 4); - flush_user_icache_range(regs->gr[30], regs->gr[30] + 4); + WARN_ON(err); + + /* flush data/instruction cache for new insns */ + flush_user_dcache_range(start, end); + flush_user_icache_range(start, end); regs->gr[31] = regs->gr[30] + 8; return; diff --git a/arch/parisc/kernel/smp.c b/arch/parisc/kernel/smp.c index ceda229ea6c2..52e85973a283 100644 --- a/arch/parisc/kernel/smp.c +++ b/arch/parisc/kernel/smp.c @@ -230,9 +230,6 @@ send_IPI_allbutself(enum ipi_message_type op) inline void smp_send_stop(void) { send_IPI_allbutself(IPI_CPU_STOP); } -static inline void -smp_send_start(void) { send_IPI_allbutself(IPI_CPU_START); } - void smp_send_reschedule(int cpu) { send_IPI_single(cpu, IPI_RESCHEDULE); } diff --git a/arch/parisc/kernel/syscall_table.S b/arch/parisc/kernel/syscall_table.S index fe4f0b89bf8f..5a8997d63899 100644 --- a/arch/parisc/kernel/syscall_table.S +++ b/arch/parisc/kernel/syscall_table.S @@ -437,6 +437,7 @@ ENTRY_SAME(getrandom) ENTRY_SAME(memfd_create) /* 340 */ ENTRY_SAME(bpf) + ENTRY_COMP(execveat) /* Nothing yet */ diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c index 0bef864264c0..15dbe81cf5f3 100644 --- a/arch/parisc/mm/init.c +++ b/arch/parisc/mm/init.c @@ -750,78 +750,6 @@ static void __init gateway_init(void) PAGE_SIZE, PAGE_GATEWAY, 1); } -#ifdef CONFIG_HPUX -void -map_hpux_gateway_page(struct task_struct *tsk, struct mm_struct *mm) -{ - pgd_t *pg_dir; - pmd_t *pmd; - pte_t *pg_table; - unsigned long start_pmd; - unsigned long start_pte; - unsigned long address; - unsigned long hpux_gw_page_addr; - /* FIXME: This is 'const' in order to trick the compiler - into not treating it as DP-relative data. */ - extern void * const hpux_gateway_page; - - hpux_gw_page_addr = HPUX_GATEWAY_ADDR & PAGE_MASK; - - /* - * Setup HP-UX Gateway page. - * - * The HP-UX gateway page resides in the user address space, - * so it needs to be aliased into each process. - */ - - pg_dir = pgd_offset(mm,hpux_gw_page_addr); - -#if PTRS_PER_PMD == 1 - start_pmd = 0; -#else - start_pmd = ((hpux_gw_page_addr >> PMD_SHIFT) & (PTRS_PER_PMD - 1)); -#endif - start_pte = ((hpux_gw_page_addr >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)); - - address = __pa(&hpux_gateway_page); -#if PTRS_PER_PMD == 1 - pmd = (pmd_t *)__pa(pg_dir); -#else - pmd = (pmd_t *) pgd_address(*pg_dir); - - /* - * pmd is physical at this point - */ - - if (!pmd) { - pmd = (pmd_t *) get_zeroed_page(GFP_KERNEL); - pmd = (pmd_t *) __pa(pmd); - } - - __pgd_val_set(*pg_dir, PxD_FLAG_PRESENT | PxD_FLAG_VALID | (unsigned long) pmd); -#endif - /* now change pmd to kernel virtual addresses */ - - pmd = (pmd_t *)__va(pmd) + start_pmd; - - /* - * pg_table is physical at this point - */ - - pg_table = (pte_t *) pmd_address(*pmd); - if (!pg_table) - pg_table = (pte_t *) __pa(get_zeroed_page(GFP_KERNEL)); - - __pmd_val_set(*pmd, PxD_FLAG_PRESENT | PxD_FLAG_VALID | (unsigned long) pg_table); - - /* now change pg_table to kernel virtual addresses */ - - pg_table = (pte_t *) __va(pg_table) + start_pte; - set_pte(pg_table, __mk_pte(address, PAGE_GATEWAY)); -} -EXPORT_SYMBOL(map_hpux_gateway_page); -#endif - void __init paging_init(void) { int i; diff --git a/arch/powerpc/kernel/machine_kexec_64.c b/arch/powerpc/kernel/machine_kexec_64.c index f96d1ec24189..1a74446fd9e5 100644 --- a/arch/powerpc/kernel/machine_kexec_64.c +++ b/arch/powerpc/kernel/machine_kexec_64.c @@ -96,8 +96,6 @@ int default_machine_kexec_prepare(struct kimage *image) return 0; } -#define IND_FLAGS (IND_DESTINATION | IND_INDIRECTION | IND_DONE | IND_SOURCE) - static void copy_segments(unsigned long ind) { unsigned long entry; diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c index 3be9fa69f875..3c036cb4a370 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c @@ -152,14 +152,11 @@ static struct mce_log mcelog = { void mce_log(struct mce *mce) { unsigned next, entry; - int ret = 0; /* Emit the trace record: */ trace_mce_record(mce); - ret = atomic_notifier_call_chain(&x86_mce_decoder_chain, 0, mce); - if (ret == NOTIFY_STOP) - return; + atomic_notifier_call_chain(&x86_mce_decoder_chain, 0, mce); mce->finished = 0; wmb(); diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index c256bd7fbd78..c6bb9f1257c9 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -732,9 +732,8 @@ static int acpi_idle_play_dead(struct cpuidle_device *dev, int index) static bool acpi_idle_fallback_to_c1(struct acpi_processor *pr) { - return IS_ENABLED(CONFIG_HOTPLUG_CPU) && num_online_cpus() > 1 && - !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED) && - !pr->flags.has_cst; + return IS_ENABLED(CONFIG_HOTPLUG_CPU) && !pr->flags.has_cst && + !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED); } static int c3_cpu_count; @@ -744,9 +743,10 @@ static DEFINE_RAW_SPINLOCK(c3_lock); * acpi_idle_enter_bm - enters C3 with proper BM handling * @pr: Target processor * @cx: Target state context + * @timer_bc: Whether or not to change timer mode to broadcast */ static void acpi_idle_enter_bm(struct acpi_processor *pr, - struct acpi_processor_cx *cx) + struct acpi_processor_cx *cx, bool timer_bc) { acpi_unlazy_tlb(smp_processor_id()); @@ -754,7 +754,8 @@ static void acpi_idle_enter_bm(struct acpi_processor *pr, * Must be done before busmaster disable as we might need to * access HPET ! */ - lapic_timer_state_broadcast(pr, cx, 1); + if (timer_bc) + lapic_timer_state_broadcast(pr, cx, 1); /* * disable bus master @@ -784,7 +785,8 @@ static void acpi_idle_enter_bm(struct acpi_processor *pr, raw_spin_unlock(&c3_lock); } - lapic_timer_state_broadcast(pr, cx, 0); + if (timer_bc) + lapic_timer_state_broadcast(pr, cx, 0); } static int acpi_idle_enter(struct cpuidle_device *dev, @@ -798,12 +800,12 @@ static int acpi_idle_enter(struct cpuidle_device *dev, return -EINVAL; if (cx->type != ACPI_STATE_C1) { - if (acpi_idle_fallback_to_c1(pr)) { + if (acpi_idle_fallback_to_c1(pr) && num_online_cpus() > 1) { index = CPUIDLE_DRIVER_STATE_START; cx = per_cpu(acpi_cstate[index], dev->cpu); } else if (cx->type == ACPI_STATE_C3 && pr->flags.bm_check) { if (cx->bm_sts_skip || !acpi_idle_bm_check()) { - acpi_idle_enter_bm(pr, cx); + acpi_idle_enter_bm(pr, cx, true); return index; } else if (drv->safe_state_index >= 0) { index = drv->safe_state_index; @@ -827,6 +829,27 @@ static int acpi_idle_enter(struct cpuidle_device *dev, return index; } +static void acpi_idle_enter_freeze(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) +{ + struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); + + if (cx->type == ACPI_STATE_C3) { + struct acpi_processor *pr = __this_cpu_read(processors); + + if (unlikely(!pr)) + return; + + if (pr->flags.bm_check) { + acpi_idle_enter_bm(pr, cx, false); + return; + } else { + ACPI_FLUSH_CPU_CACHE(); + } + } + acpi_idle_do_entry(cx); +} + struct cpuidle_driver acpi_idle_driver = { .name = "acpi_idle", .owner = THIS_MODULE, @@ -925,6 +948,15 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) state->enter_dead = acpi_idle_play_dead; drv->safe_state_index = count; } + /* + * Halt-induced C1 is not good for ->enter_freeze, because it + * re-enables interrupts on exit. Moreover, C1 is generally not + * particularly interesting from the suspend-to-idle angle, so + * avoid C1 and the situations in which we may need to fall back + * to it altogether. + */ + if (cx->type != ACPI_STATE_C1 && !acpi_idle_fallback_to_c1(pr)) + state->enter_freeze = acpi_idle_enter_freeze; count++; if (count == CPUIDLE_STATE_MAX) diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index bf53a3771da5..e85d3416d899 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -901,8 +901,10 @@ int tpm_pm_suspend(struct device *dev) if (chip == NULL) return -ENODEV; - if (chip->flags & TPM_CHIP_FLAG_TPM2) - return tpm2_shutdown(chip, TPM2_SU_CLEAR); + if (chip->flags & TPM_CHIP_FLAG_TPM2) { + tpm2_shutdown(chip, TPM2_SU_STATE); + return 0; + } /* for buggy tpm, flush pcrs with extend to selected dummy */ if (tpm_suspend_pcr) { diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 7b0727c5e803..f8319a0860fd 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -432,7 +432,8 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value, const char *desc); extern int tpm2_startup(struct tpm_chip *chip, u16 startup_type); -extern int tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type); +extern void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type); extern unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *, u32); extern int tpm2_do_selftest(struct tpm_chip *chip); -extern int tpm2_gen_interrupt(struct tpm_chip *chip, bool quiet); +extern int tpm2_gen_interrupt(struct tpm_chip *chip); +extern int tpm2_probe(struct tpm_chip *chip); diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index 1abe6502219f..011909a9be96 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -456,20 +456,23 @@ static const struct tpm_input_header tpm2_shutdown_header = { * @chip: TPM chip to use. * @shutdown_type shutdown type. The value is either * TPM_SU_CLEAR or TPM_SU_STATE. - * - * 0 is returned when the operation is successful. If a negative number is - * returned it remarks a POSIX error code. If a positive number is returned - * it remarks a TPM error. */ -int tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type) +void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type) { struct tpm2_cmd cmd; + int rc; cmd.header.in = tpm2_shutdown_header; - cmd.params.startup_in.startup_type = cpu_to_be16(shutdown_type); - return tpm_transmit_cmd(chip, &cmd, sizeof(cmd), - "stopping the TPM"); + + rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), "stopping the TPM"); + + /* In places where shutdown command is sent there's no much we can do + * except print the error code on a system failure. + */ + if (rc < 0) + dev_warn(chip->pdev, "transmit returned %d while stopping the TPM", + rc); } EXPORT_SYMBOL_GPL(tpm2_shutdown); @@ -598,20 +601,46 @@ EXPORT_SYMBOL_GPL(tpm2_do_selftest); /** * tpm2_gen_interrupt() - generate an interrupt * @chip: TPM chip to use - * @quiet: surpress the error message * * 0 is returned when the operation is successful. If a negative number is * returned it remarks a POSIX error code. If a positive number is returned * it remarks a TPM error. */ -int tpm2_gen_interrupt(struct tpm_chip *chip, bool quiet) +int tpm2_gen_interrupt(struct tpm_chip *chip) { - const char *desc = NULL; u32 dummy; - if (!quiet) - desc = "attempting to generate an interrupt"; - - return tpm2_get_tpm_pt(chip, TPM2_CAP_TPM_PROPERTIES, &dummy, desc); + return tpm2_get_tpm_pt(chip, 0x100, &dummy, + "attempting to generate an interrupt"); } EXPORT_SYMBOL_GPL(tpm2_gen_interrupt); + +/** + * tpm2_probe() - probe TPM 2.0 + * @chip: TPM chip to use + * + * Send idempotent TPM 2.0 command and see whether TPM 2.0 chip replied based on + * the reply tag. + */ +int tpm2_probe(struct tpm_chip *chip) +{ + struct tpm2_cmd cmd; + int rc; + + cmd.header.in = tpm2_get_tpm_pt_header; + cmd.params.get_tpm_pt_in.cap_id = cpu_to_be32(TPM2_CAP_TPM_PROPERTIES); + cmd.params.get_tpm_pt_in.property_id = cpu_to_be32(0x100); + cmd.params.get_tpm_pt_in.property_cnt = cpu_to_be32(1); + + rc = tpm_transmit(chip, (const char *) &cmd, sizeof(cmd)); + if (rc < 0) + return rc; + else if (rc < TPM_HEADER_SIZE) + return -EFAULT; + + if (be16_to_cpu(cmd.header.out.tag) == TPM2_ST_NO_SESSIONS) + chip->flags |= TPM_CHIP_FLAG_TPM2; + + return 0; +} +EXPORT_SYMBOL_GPL(tpm2_probe); diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 3dd23cfae4fe..b26ceee3585e 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -95,21 +95,7 @@ struct crb_priv { u8 __iomem *rsp; }; -#ifdef CONFIG_PM_SLEEP -static int crb_resume(struct device *dev) -{ - int rc; - struct tpm_chip *chip = dev_get_drvdata(dev); - - rc = tpm2_shutdown(chip, TPM2_SU_STATE); - if (!rc) - rc = tpm2_do_selftest(chip); - - return rc; -} -#endif - -static SIMPLE_DEV_PM_OPS(crb_pm, tpm_pm_suspend, crb_resume); +static SIMPLE_DEV_PM_OPS(crb_pm, tpm_pm_suspend, tpm_pm_resume); static u8 crb_status(struct tpm_chip *chip) { @@ -326,6 +312,10 @@ static int crb_acpi_remove(struct acpi_device *device) struct tpm_chip *chip = dev_get_drvdata(dev); tpm_chip_unregister(chip); + + if (chip->flags & TPM_CHIP_FLAG_TPM2) + tpm2_shutdown(chip, TPM2_SU_CLEAR); + return 0; } diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c index 0840347e251c..b1e53e3aece5 100644 --- a/drivers/char/tpm/tpm_ibmvtpm.c +++ b/drivers/char/tpm/tpm_ibmvtpm.c @@ -148,7 +148,8 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) crq.len = (u16)count; crq.data = ibmvtpm->rtce_dma_handle; - rc = ibmvtpm_send_crq(ibmvtpm->vdev, word[0], word[1]); + rc = ibmvtpm_send_crq(ibmvtpm->vdev, cpu_to_be64(word[0]), + cpu_to_be64(word[1])); if (rc != H_SUCCESS) { dev_err(ibmvtpm->dev, "tpm_ibmvtpm_send failed rc=%d\n", rc); rc = 0; @@ -186,7 +187,8 @@ static int ibmvtpm_crq_get_rtce_size(struct ibmvtpm_dev *ibmvtpm) crq.valid = (u8)IBMVTPM_VALID_CMD; crq.msg = (u8)VTPM_GET_RTCE_BUFFER_SIZE; - rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]); + rc = ibmvtpm_send_crq(ibmvtpm->vdev, cpu_to_be64(buf[0]), + cpu_to_be64(buf[1])); if (rc != H_SUCCESS) dev_err(ibmvtpm->dev, "ibmvtpm_crq_get_rtce_size failed rc=%d\n", rc); @@ -212,7 +214,8 @@ static int ibmvtpm_crq_get_version(struct ibmvtpm_dev *ibmvtpm) crq.valid = (u8)IBMVTPM_VALID_CMD; crq.msg = (u8)VTPM_GET_VERSION; - rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]); + rc = ibmvtpm_send_crq(ibmvtpm->vdev, cpu_to_be64(buf[0]), + cpu_to_be64(buf[1])); if (rc != H_SUCCESS) dev_err(ibmvtpm->dev, "ibmvtpm_crq_get_version failed rc=%d\n", rc); @@ -336,7 +339,8 @@ static int tpm_ibmvtpm_suspend(struct device *dev) crq.valid = (u8)IBMVTPM_VALID_CMD; crq.msg = (u8)VTPM_PREPARE_TO_SUSPEND; - rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]); + rc = ibmvtpm_send_crq(ibmvtpm->vdev, cpu_to_be64(buf[0]), + cpu_to_be64(buf[1])); if (rc != H_SUCCESS) dev_err(ibmvtpm->dev, "tpm_ibmvtpm_suspend failed rc=%d\n", rc); @@ -481,11 +485,11 @@ static void ibmvtpm_crq_process(struct ibmvtpm_crq *crq, case IBMVTPM_VALID_CMD: switch (crq->msg) { case VTPM_GET_RTCE_BUFFER_SIZE_RES: - if (crq->len <= 0) { + if (be16_to_cpu(crq->len) <= 0) { dev_err(ibmvtpm->dev, "Invalid rtce size\n"); return; } - ibmvtpm->rtce_size = crq->len; + ibmvtpm->rtce_size = be16_to_cpu(crq->len); ibmvtpm->rtce_buf = kmalloc(ibmvtpm->rtce_size, GFP_KERNEL); if (!ibmvtpm->rtce_buf) { @@ -506,11 +510,11 @@ static void ibmvtpm_crq_process(struct ibmvtpm_crq *crq, return; case VTPM_GET_VERSION_RES: - ibmvtpm->vtpm_version = crq->data; + ibmvtpm->vtpm_version = be32_to_cpu(crq->data); return; case VTPM_TPM_COMMAND_RES: /* len of the data in rtce buffer */ - ibmvtpm->res_len = crq->len; + ibmvtpm->res_len = be16_to_cpu(crq->len); wake_up_interruptible(&ibmvtpm->wq); return; default: diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 6725bef7cb96..f2dffa770b8e 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -588,6 +588,9 @@ MODULE_PARM_DESC(interrupts, "Enable interrupts"); static void tpm_tis_remove(struct tpm_chip *chip) { + if (chip->flags & TPM_CHIP_FLAG_TPM2) + tpm2_shutdown(chip, TPM2_SU_CLEAR); + iowrite32(~TPM_GLOBAL_INT_ENABLE & ioread32(chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor. @@ -639,12 +642,9 @@ static int tpm_tis_init(struct device *dev, acpi_handle acpi_dev_handle, goto out_err; } - /* Every TPM 2.x command has a higher ordinal than TPM 1.x commands. - * Therefore, we can use an idempotent TPM 2.x command to probe TPM 2.x. - */ - rc = tpm2_gen_interrupt(chip, true); - if (rc == 0 || rc == TPM2_RC_INITIALIZE) - chip->flags |= TPM_CHIP_FLAG_TPM2; + rc = tpm2_probe(chip); + if (rc) + goto out_err; vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0)); chip->vendor.manufacturer_id = vendor; @@ -747,7 +747,7 @@ static int tpm_tis_init(struct device *dev, acpi_handle acpi_dev_handle, /* Generate Interrupts */ if (chip->flags & TPM_CHIP_FLAG_TPM2) - tpm2_gen_interrupt(chip, false); + tpm2_gen_interrupt(chip); else tpm_gen_interrupt(chip); @@ -865,25 +865,22 @@ static void tpm_tis_reenable_interrupts(struct tpm_chip *chip) static int tpm_tis_resume(struct device *dev) { struct tpm_chip *chip = dev_get_drvdata(dev); - int ret = 0; + int ret; if (chip->vendor.irq) tpm_tis_reenable_interrupts(chip); - if (chip->flags & TPM_CHIP_FLAG_TPM2) { - /* NOP if firmware properly does this. */ - tpm2_startup(chip, TPM2_SU_STATE); + ret = tpm_pm_resume(dev); + if (ret) + return ret; - ret = tpm2_shutdown(chip, TPM2_SU_STATE); - if (!ret) - ret = tpm2_do_selftest(chip); - } else { - ret = tpm_pm_resume(dev); - if (!ret) - tpm_do_selftest(chip); - } + /* TPM 1.2 requires self-test on resume. This function actually returns + * an error code but for unknown reason it isn't handled. + */ + if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) + tpm_do_selftest(chip); - return ret; + return 0; } #endif diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 125150dc6e81..4d534582514e 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -19,6 +19,8 @@ #include <linux/ktime.h> #include <linux/hrtimer.h> #include <linux/module.h> +#include <linux/suspend.h> +#include <linux/tick.h> #include <trace/events/power.h> #include "cpuidle.h" @@ -32,7 +34,6 @@ LIST_HEAD(cpuidle_detected_devices); static int enabled_devices; static int off __read_mostly; static int initialized __read_mostly; -static bool use_deepest_state __read_mostly; int cpuidle_disabled(void) { @@ -66,36 +67,23 @@ int cpuidle_play_dead(void) } /** - * cpuidle_use_deepest_state - Enable/disable the "deepest idle" mode. - * @enable: Whether enable or disable the feature. - * - * If the "deepest idle" mode is enabled, cpuidle will ignore the governor and - * always use the state with the greatest exit latency (out of the states that - * are not disabled). - * - * This function can only be called after cpuidle_pause() to avoid races. - */ -void cpuidle_use_deepest_state(bool enable) -{ - use_deepest_state = enable; -} - -/** - * cpuidle_find_deepest_state - Find the state of the greatest exit latency. - * @drv: cpuidle driver for a given CPU. - * @dev: cpuidle device for a given CPU. + * cpuidle_find_deepest_state - Find deepest state meeting specific conditions. + * @drv: cpuidle driver for the given CPU. + * @dev: cpuidle device for the given CPU. + * @freeze: Whether or not the state should be suitable for suspend-to-idle. */ static int cpuidle_find_deepest_state(struct cpuidle_driver *drv, - struct cpuidle_device *dev) + struct cpuidle_device *dev, bool freeze) { unsigned int latency_req = 0; - int i, ret = CPUIDLE_DRIVER_STATE_START - 1; + int i, ret = freeze ? -1 : CPUIDLE_DRIVER_STATE_START - 1; for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) { struct cpuidle_state *s = &drv->states[i]; struct cpuidle_state_usage *su = &dev->states_usage[i]; - if (s->disabled || su->disable || s->exit_latency <= latency_req) + if (s->disabled || su->disable || s->exit_latency <= latency_req + || (freeze && !s->enter_freeze)) continue; latency_req = s->exit_latency; @@ -104,6 +92,63 @@ static int cpuidle_find_deepest_state(struct cpuidle_driver *drv, return ret; } +static void enter_freeze_proper(struct cpuidle_driver *drv, + struct cpuidle_device *dev, int index) +{ + tick_freeze(); + /* + * The state used here cannot be a "coupled" one, because the "coupled" + * cpuidle mechanism enables interrupts and doing that with timekeeping + * suspended is generally unsafe. + */ + drv->states[index].enter_freeze(dev, drv, index); + WARN_ON(!irqs_disabled()); + /* + * timekeeping_resume() that will be called by tick_unfreeze() for the + * last CPU executing it calls functions containing RCU read-side + * critical sections, so tell RCU about that. + */ + RCU_NONIDLE(tick_unfreeze()); +} + +/** + * cpuidle_enter_freeze - Enter an idle state suitable for suspend-to-idle. + * + * If there are states with the ->enter_freeze callback, find the deepest of + * them and enter it with frozen tick. Otherwise, find the deepest state + * available and enter it normally. + */ +void cpuidle_enter_freeze(void) +{ + struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); + struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); + int index; + + /* + * Find the deepest state with ->enter_freeze present, which guarantees + * that interrupts won't be enabled when it exits and allows the tick to + * be frozen safely. + */ + index = cpuidle_find_deepest_state(drv, dev, true); + if (index >= 0) { + enter_freeze_proper(drv, dev, index); + return; + } + + /* + * It is not safe to freeze the tick, find the deepest state available + * at all and try to enter it normally. + */ + index = cpuidle_find_deepest_state(drv, dev, false); + if (index >= 0) + cpuidle_enter(drv, dev, index); + else + arch_cpu_idle(); + + /* Interrupts are enabled again here. */ + local_irq_disable(); +} + /** * cpuidle_enter_state - enter the state and update stats * @dev: cpuidle device for this cpu @@ -166,9 +211,6 @@ int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) if (!drv || !dev || !dev->enabled) return -EBUSY; - if (unlikely(use_deepest_state)) - return cpuidle_find_deepest_state(drv, dev); - return cpuidle_curr_governor->select(drv, dev); } @@ -200,7 +242,7 @@ int cpuidle_enter(struct cpuidle_driver *drv, struct cpuidle_device *dev, */ void cpuidle_reflect(struct cpuidle_device *dev, int index) { - if (cpuidle_curr_governor->reflect && !unlikely(use_deepest_state)) + if (cpuidle_curr_governor->reflect) cpuidle_curr_governor->reflect(dev, index); } diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index eb6935c8ad94..d6a09b9cd8cc 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1246,14 +1246,14 @@ static const u32 model_textual_descriptor[] = { static struct fw_descriptor vendor_id_descriptor = { .length = ARRAY_SIZE(vendor_textual_descriptor), - .immediate = 0x03d00d1e, + .immediate = 0x03001f11, .key = 0x81000000, .data = vendor_textual_descriptor, }; static struct fw_descriptor model_id_descriptor = { .length = ARRAY_SIZE(model_textual_descriptor), - .immediate = 0x17000001, + .immediate = 0x17023901, .key = 0x81000000, .data = model_textual_descriptor, }; diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index aff9018d0658..f51d376d10ba 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -718,11 +718,6 @@ static inline unsigned int ar_next_buffer_index(unsigned int index) return (index + 1) % AR_BUFFERS; } -static inline unsigned int ar_prev_buffer_index(unsigned int index) -{ - return (index - 1 + AR_BUFFERS) % AR_BUFFERS; -} - static inline unsigned int ar_first_buffer_index(struct ar_context *ctx) { return ar_next_buffer_index(ctx->last_buffer_index); diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c index 64ac8f8f5098..c22606fe3d44 100644 --- a/drivers/firewire/sbp2.c +++ b/drivers/firewire/sbp2.c @@ -1463,17 +1463,6 @@ static int sbp2_scsi_queuecommand(struct Scsi_Host *shost, struct sbp2_command_orb *orb; int generation, retval = SCSI_MLQUEUE_HOST_BUSY; - /* - * Bidirectional commands are not yet implemented, and unknown - * transfer direction not handled. - */ - if (cmd->sc_data_direction == DMA_BIDIRECTIONAL) { - dev_err(lu_dev(lu), "cannot handle bidirectional command\n"); - cmd->result = DID_ERROR << 16; - cmd->scsi_done(cmd); - return 0; - } - orb = kzalloc(sizeof(*orb), GFP_ATOMIC); if (orb == NULL) return SCSI_MLQUEUE_HOST_BUSY; diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 1bc0c170f12a..b0e58522780d 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -97,6 +97,8 @@ static const struct idle_cpu *icpu; static struct cpuidle_device __percpu *intel_idle_cpuidle_devices; static int intel_idle(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index); +static void intel_idle_freeze(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index); static int intel_idle_cpu_init(int cpu); static struct cpuidle_state *cpuidle_state_table; @@ -131,28 +133,32 @@ static struct cpuidle_state nehalem_cstates[] = { .flags = MWAIT2flg(0x00), .exit_latency = 3, .target_residency = 6, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C1E-NHM", .desc = "MWAIT 0x01", .flags = MWAIT2flg(0x01), .exit_latency = 10, .target_residency = 20, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C3-NHM", .desc = "MWAIT 0x10", .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 20, .target_residency = 80, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C6-NHM", .desc = "MWAIT 0x20", .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 200, .target_residency = 800, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .enter = NULL } }; @@ -164,35 +170,40 @@ static struct cpuidle_state snb_cstates[] = { .flags = MWAIT2flg(0x00), .exit_latency = 2, .target_residency = 2, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C1E-SNB", .desc = "MWAIT 0x01", .flags = MWAIT2flg(0x01), .exit_latency = 10, .target_residency = 20, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C3-SNB", .desc = "MWAIT 0x10", .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 80, .target_residency = 211, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C6-SNB", .desc = "MWAIT 0x20", .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 104, .target_residency = 345, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C7-SNB", .desc = "MWAIT 0x30", .flags = MWAIT2flg(0x30) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 109, .target_residency = 345, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .enter = NULL } }; @@ -204,42 +215,48 @@ static struct cpuidle_state byt_cstates[] = { .flags = MWAIT2flg(0x00), .exit_latency = 1, .target_residency = 1, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C1E-BYT", .desc = "MWAIT 0x01", .flags = MWAIT2flg(0x01), .exit_latency = 15, .target_residency = 30, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C6N-BYT", .desc = "MWAIT 0x58", .flags = MWAIT2flg(0x58) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 40, .target_residency = 275, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C6S-BYT", .desc = "MWAIT 0x52", .flags = MWAIT2flg(0x52) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 140, .target_residency = 560, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C7-BYT", .desc = "MWAIT 0x60", .flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 1200, .target_residency = 1500, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C7S-BYT", .desc = "MWAIT 0x64", .flags = MWAIT2flg(0x64) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 10000, .target_residency = 20000, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .enter = NULL } }; @@ -251,35 +268,40 @@ static struct cpuidle_state ivb_cstates[] = { .flags = MWAIT2flg(0x00), .exit_latency = 1, .target_residency = 1, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C1E-IVB", .desc = "MWAIT 0x01", .flags = MWAIT2flg(0x01), .exit_latency = 10, .target_residency = 20, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C3-IVB", .desc = "MWAIT 0x10", .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 59, .target_residency = 156, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C6-IVB", .desc = "MWAIT 0x20", .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 80, .target_residency = 300, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C7-IVB", .desc = "MWAIT 0x30", .flags = MWAIT2flg(0x30) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 87, .target_residency = 300, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .enter = NULL } }; @@ -291,28 +313,32 @@ static struct cpuidle_state ivt_cstates[] = { .flags = MWAIT2flg(0x00), .exit_latency = 1, .target_residency = 1, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C1E-IVT", .desc = "MWAIT 0x01", .flags = MWAIT2flg(0x01), .exit_latency = 10, .target_residency = 80, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C3-IVT", .desc = "MWAIT 0x10", .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 59, .target_residency = 156, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C6-IVT", .desc = "MWAIT 0x20", .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 82, .target_residency = 300, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .enter = NULL } }; @@ -324,28 +350,32 @@ static struct cpuidle_state ivt_cstates_4s[] = { .flags = MWAIT2flg(0x00), .exit_latency = 1, .target_residency = 1, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C1E-IVT-4S", .desc = "MWAIT 0x01", .flags = MWAIT2flg(0x01), .exit_latency = 10, .target_residency = 250, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C3-IVT-4S", .desc = "MWAIT 0x10", .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 59, .target_residency = 300, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C6-IVT-4S", .desc = "MWAIT 0x20", .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 84, .target_residency = 400, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .enter = NULL } }; @@ -357,28 +387,32 @@ static struct cpuidle_state ivt_cstates_8s[] = { .flags = MWAIT2flg(0x00), .exit_latency = 1, .target_residency = 1, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C1E-IVT-8S", .desc = "MWAIT 0x01", .flags = MWAIT2flg(0x01), .exit_latency = 10, .target_residency = 500, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C3-IVT-8S", .desc = "MWAIT 0x10", .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 59, .target_residency = 600, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C6-IVT-8S", .desc = "MWAIT 0x20", .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 88, .target_residency = 700, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .enter = NULL } }; @@ -390,56 +424,64 @@ static struct cpuidle_state hsw_cstates[] = { .flags = MWAIT2flg(0x00), .exit_latency = 2, .target_residency = 2, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C1E-HSW", .desc = "MWAIT 0x01", .flags = MWAIT2flg(0x01), .exit_latency = 10, .target_residency = 20, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C3-HSW", .desc = "MWAIT 0x10", .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 33, .target_residency = 100, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C6-HSW", .desc = "MWAIT 0x20", .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 133, .target_residency = 400, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C7s-HSW", .desc = "MWAIT 0x32", .flags = MWAIT2flg(0x32) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 166, .target_residency = 500, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C8-HSW", .desc = "MWAIT 0x40", .flags = MWAIT2flg(0x40) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 300, .target_residency = 900, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C9-HSW", .desc = "MWAIT 0x50", .flags = MWAIT2flg(0x50) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 600, .target_residency = 1800, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C10-HSW", .desc = "MWAIT 0x60", .flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 2600, .target_residency = 7700, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .enter = NULL } }; @@ -450,56 +492,64 @@ static struct cpuidle_state bdw_cstates[] = { .flags = MWAIT2flg(0x00), .exit_latency = 2, .target_residency = 2, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C1E-BDW", .desc = "MWAIT 0x01", .flags = MWAIT2flg(0x01), .exit_latency = 10, .target_residency = 20, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C3-BDW", .desc = "MWAIT 0x10", .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 40, .target_residency = 100, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C6-BDW", .desc = "MWAIT 0x20", .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 133, .target_residency = 400, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C7s-BDW", .desc = "MWAIT 0x32", .flags = MWAIT2flg(0x32) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 166, .target_residency = 500, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C8-BDW", .desc = "MWAIT 0x40", .flags = MWAIT2flg(0x40) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 300, .target_residency = 900, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C9-BDW", .desc = "MWAIT 0x50", .flags = MWAIT2flg(0x50) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 600, .target_residency = 1800, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C10-BDW", .desc = "MWAIT 0x60", .flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 2600, .target_residency = 7700, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .enter = NULL } }; @@ -511,28 +561,32 @@ static struct cpuidle_state atom_cstates[] = { .flags = MWAIT2flg(0x00), .exit_latency = 10, .target_residency = 20, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C2-ATM", .desc = "MWAIT 0x10", .flags = MWAIT2flg(0x10), .exit_latency = 20, .target_residency = 80, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C4-ATM", .desc = "MWAIT 0x30", .flags = MWAIT2flg(0x30) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 100, .target_residency = 400, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C6-ATM", .desc = "MWAIT 0x52", .flags = MWAIT2flg(0x52) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 140, .target_residency = 560, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .enter = NULL } }; @@ -543,14 +597,16 @@ static struct cpuidle_state avn_cstates[] = { .flags = MWAIT2flg(0x00), .exit_latency = 2, .target_residency = 2, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .name = "C6-AVN", .desc = "MWAIT 0x51", .flags = MWAIT2flg(0x51) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 15, .target_residency = 45, - .enter = &intel_idle }, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, { .enter = NULL } }; @@ -592,6 +648,21 @@ static int intel_idle(struct cpuidle_device *dev, return index; } +/** + * intel_idle_freeze - simplified "enter" callback routine for suspend-to-idle + * @dev: cpuidle_device + * @drv: cpuidle driver + * @index: state index + */ +static void intel_idle_freeze(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) +{ + unsigned long ecx = 1; /* break on interrupt flag */ + unsigned long eax = flg2MWAIT(drv->states[index].flags); + + mwait_idle_with_hints(eax, ecx); +} + static void __setup_broadcast_timer(void *arg) { unsigned long reason = (unsigned long)arg; diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c index aafdbcd84fc4..8fb295e4a9ab 100644 --- a/drivers/infiniband/hw/cxgb4/device.c +++ b/drivers/infiniband/hw/cxgb4/device.c @@ -700,37 +700,24 @@ static const struct file_operations ep_debugfs_fops = { static int setup_debugfs(struct c4iw_dev *devp) { - struct dentry *de; - if (!devp->debugfs_root) return -1; - de = debugfs_create_file("qps", S_IWUSR, devp->debugfs_root, - (void *)devp, &qp_debugfs_fops); - if (de && de->d_inode) - de->d_inode->i_size = 4096; + debugfs_create_file_size("qps", S_IWUSR, devp->debugfs_root, + (void *)devp, &qp_debugfs_fops, 4096); - de = debugfs_create_file("stags", S_IWUSR, devp->debugfs_root, - (void *)devp, &stag_debugfs_fops); - if (de && de->d_inode) - de->d_inode->i_size = 4096; + debugfs_create_file_size("stags", S_IWUSR, devp->debugfs_root, + (void *)devp, &stag_debugfs_fops, 4096); - de = debugfs_create_file("stats", S_IWUSR, devp->debugfs_root, - (void *)devp, &stats_debugfs_fops); - if (de && de->d_inode) - de->d_inode->i_size = 4096; + debugfs_create_file_size("stats", S_IWUSR, devp->debugfs_root, + (void *)devp, &stats_debugfs_fops, 4096); - de = debugfs_create_file("eps", S_IWUSR, devp->debugfs_root, - (void *)devp, &ep_debugfs_fops); - if (de && de->d_inode) - de->d_inode->i_size = 4096; + debugfs_create_file_size("eps", S_IWUSR, devp->debugfs_root, + (void *)devp, &ep_debugfs_fops, 4096); - if (c4iw_wr_log) { - de = debugfs_create_file("wr_log", S_IWUSR, devp->debugfs_root, - (void *)devp, &wr_log_debugfs_fops); - if (de && de->d_inode) - de->d_inode->i_size = 4096; - } + if (c4iw_wr_log) + debugfs_create_file_size("wr_log", S_IWUSR, devp->debugfs_root, + (void *)devp, &wr_log_debugfs_fops, 4096); return 0; } diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 5dd0c2e59ab9..4153da5d4011 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -2196,7 +2196,8 @@ static int narrow_write_error(struct r1bio *r1_bio, int i) if (rdev->badblocks.shift < 0) return 0; - block_sectors = 1 << rdev->badblocks.shift; + block_sectors = roundup(1 << rdev->badblocks.shift, + bdev_logical_block_size(rdev->bdev) >> 9); sector = r1_bio->sector; sectors = ((sector + block_sectors) & ~(sector_t)(block_sectors - 1)) diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index b8d76b1fba64..a7196c49d15d 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -2572,7 +2572,8 @@ static int narrow_write_error(struct r10bio *r10_bio, int i) if (rdev->badblocks.shift < 0) return 0; - block_sectors = 1 << rdev->badblocks.shift; + block_sectors = roundup(1 << rdev->badblocks.shift, + bdev_logical_block_size(rdev->bdev) >> 9); sector = r10_bio->sector; sectors = ((r10_bio->sector + block_sectors) & ~(sector_t)(block_sectors - 1)) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index aa76865b804b..e75d48c0421a 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3170,7 +3170,8 @@ static void handle_stripe_dirtying(struct r5conf *conf, * generate correct data from the parity. */ if (conf->max_degraded == 2 || - (recovery_cp < MaxSector && sh->sector >= recovery_cp)) { + (recovery_cp < MaxSector && sh->sector >= recovery_cp && + s->failed == 0)) { /* Calculate the real rcw later - for now make it * look like rcw is cheaper */ diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c index cc13ea5ce4d5..c0720c1ee4c9 100644 --- a/drivers/mtd/bcm47xxpart.c +++ b/drivers/mtd/bcm47xxpart.c @@ -15,6 +15,8 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> +#include <uapi/linux/magic.h> + /* * NAND flash on Netgear R6250 was verified to contain 15 partitions. * This will result in allocating too big array for some old devices, but the @@ -39,7 +41,8 @@ #define ML_MAGIC1 0x39685a42 #define ML_MAGIC2 0x26594131 #define TRX_MAGIC 0x30524448 -#define SQSH_MAGIC 0x71736873 /* shsq */ +#define SHSQ_MAGIC 0x71736873 /* shsq (weird ZTE H218N endianness) */ +#define UBI_EC_MAGIC 0x23494255 /* UBI# */ struct trx_header { uint32_t magic; @@ -50,7 +53,7 @@ struct trx_header { uint32_t offset[3]; } __packed; -static void bcm47xxpart_add_part(struct mtd_partition *part, char *name, +static void bcm47xxpart_add_part(struct mtd_partition *part, const char *name, u64 offset, uint32_t mask_flags) { part->name = name; @@ -58,6 +61,26 @@ static void bcm47xxpart_add_part(struct mtd_partition *part, char *name, part->mask_flags = mask_flags; } +static const char *bcm47xxpart_trx_data_part_name(struct mtd_info *master, + size_t offset) +{ + uint32_t buf; + size_t bytes_read; + + if (mtd_read(master, offset, sizeof(buf), &bytes_read, + (uint8_t *)&buf) < 0) { + pr_err("mtd_read error while parsing (offset: 0x%X)!\n", + offset); + goto out_default; + } + + if (buf == UBI_EC_MAGIC) + return "ubi"; + +out_default: + return "rootfs"; +} + static int bcm47xxpart_parse(struct mtd_info *master, struct mtd_partition **pparts, struct mtd_part_parser_data *data) @@ -73,8 +96,12 @@ static int bcm47xxpart_parse(struct mtd_info *master, int last_trx_part = -1; int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, }; - if (blocksize <= 0x10000) - blocksize = 0x10000; + /* + * Some really old flashes (like AT45DB*) had smaller erasesize-s, but + * partitions were aligned to at least 0x1000 anyway. + */ + if (blocksize < 0x1000) + blocksize = 0x1000; /* Alloc */ parts = kzalloc(sizeof(struct mtd_partition) * BCM47XXPART_MAX_PARTS, @@ -186,8 +213,11 @@ static int bcm47xxpart_parse(struct mtd_info *master, * we want to have jffs2 (overlay) in the same mtd. */ if (trx->offset[i]) { + const char *name; + + name = bcm47xxpart_trx_data_part_name(master, offset + trx->offset[i]); bcm47xxpart_add_part(&parts[curr_part++], - "rootfs", + name, offset + trx->offset[i], 0); i++; @@ -205,7 +235,8 @@ static int bcm47xxpart_parse(struct mtd_info *master, } /* Squashfs on devices not using TRX */ - if (buf[0x000 / 4] == SQSH_MAGIC) { + if (le32_to_cpu(buf[0x000 / 4]) == SQUASHFS_MAGIC || + buf[0x000 / 4] == SHSQ_MAGIC) { bcm47xxpart_add_part(&parts[curr_part++], "rootfs", offset, 0); continue; diff --git a/drivers/mtd/chips/map_ram.c b/drivers/mtd/chips/map_ram.c index 991c2a1c05d3..afb43d5e1782 100644 --- a/drivers/mtd/chips/map_ram.c +++ b/drivers/mtd/chips/map_ram.c @@ -68,6 +68,7 @@ static struct mtd_info *map_ram_probe(struct map_info *map) mtd->_get_unmapped_area = mapram_unmapped_area; mtd->_read = mapram_read; mtd->_write = mapram_write; + mtd->_panic_write = mapram_write; mtd->_sync = mapram_nop; mtd->flags = MTD_CAP_RAM; mtd->writesize = 1; diff --git a/drivers/mtd/chips/map_rom.c b/drivers/mtd/chips/map_rom.c index 47a43cf7e5c6..e67f73ab44c9 100644 --- a/drivers/mtd/chips/map_rom.c +++ b/drivers/mtd/chips/map_rom.c @@ -11,6 +11,7 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/init.h> +#include <linux/of.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> @@ -28,6 +29,15 @@ static struct mtd_chip_driver maprom_chipdrv = { .module = THIS_MODULE }; +static unsigned int default_erasesize(struct map_info *map) +{ + const __be32 *erase_size = NULL; + + erase_size = of_get_property(map->device_node, "erase-size", NULL); + + return !erase_size ? map->size : be32_to_cpu(*erase_size); +} + static struct mtd_info *map_rom_probe(struct map_info *map) { struct mtd_info *mtd; @@ -47,8 +57,9 @@ static struct mtd_info *map_rom_probe(struct map_info *map) mtd->_sync = maprom_nop; mtd->_erase = maprom_erase; mtd->flags = MTD_CAP_ROM; - mtd->erasesize = map->size; + mtd->erasesize = default_erasesize(map); mtd->writesize = 1; + mtd->writebufsize = 1; __module_get(THIS_MODULE); return mtd; diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 54ffe5223e64..3060025c8af4 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -24,6 +24,7 @@ #include <linux/delay.h> #include <linux/io.h> #include <linux/of.h> +#include <linux/clk.h> #include "serial_flash_cmds.h" @@ -262,6 +263,7 @@ struct stfsm { struct mtd_info mtd; struct mutex lock; struct flash_info *info; + struct clk *clk; uint32_t configuration; uint32_t fifo_dir_delay; @@ -663,6 +665,23 @@ static struct stfsm_seq stfsm_seq_write_status = { SEQ_CFG_STARTSEQ), }; +/* Dummy sequence to read one byte of data from flash into the FIFO */ +static const struct stfsm_seq stfsm_seq_load_fifo_byte = { + .data_size = TRANSFER_SIZE(1), + .seq_opc[0] = (SEQ_OPC_PADS_1 | + SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(SPINOR_OP_RDID)), + .seq = { + STFSM_INST_CMD1, + STFSM_INST_DATA_READ, + STFSM_INST_STOP, + }, + .seq_cfg = (SEQ_CFG_PADS_1 | + SEQ_CFG_READNOTWRITE | + SEQ_CFG_CSDEASSERT | + SEQ_CFG_STARTSEQ), +}; + static int stfsm_n25q_en_32bit_addr_seq(struct stfsm_seq *seq) { seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | @@ -695,22 +714,6 @@ static inline uint32_t stfsm_fifo_available(struct stfsm *fsm) return (readl(fsm->base + SPI_FAST_SEQ_STA) >> 5) & 0x7f; } -static void stfsm_clear_fifo(struct stfsm *fsm) -{ - uint32_t avail; - - for (;;) { - avail = stfsm_fifo_available(fsm); - if (!avail) - break; - - while (avail) { - readl(fsm->base + SPI_FAST_SEQ_DATA_REG); - avail--; - } - } -} - static inline void stfsm_load_seq(struct stfsm *fsm, const struct stfsm_seq *seq) { @@ -772,6 +775,68 @@ static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, uint32_t size) } } +/* + * Clear the data FIFO + * + * Typically, this is only required during driver initialisation, where no + * assumptions can be made regarding the state of the FIFO. + * + * The process of clearing the FIFO is complicated by fact that while it is + * possible for the FIFO to contain an arbitrary number of bytes [1], the + * SPI_FAST_SEQ_STA register only reports the number of complete 32-bit words + * present. Furthermore, data can only be drained from the FIFO by reading + * complete 32-bit words. + * + * With this in mind, a two stage process is used to the clear the FIFO: + * + * 1. Read any complete 32-bit words from the FIFO, as reported by the + * SPI_FAST_SEQ_STA register. + * + * 2. Mop up any remaining bytes. At this point, it is not known if there + * are 0, 1, 2, or 3 bytes in the FIFO. To handle all cases, a dummy FSM + * sequence is used to load one byte at a time, until a complete 32-bit + * word is formed; at most, 4 bytes will need to be loaded. + * + * [1] It is theoretically possible for the FIFO to contain an arbitrary number + * of bits. However, since there are no known use-cases that leave + * incomplete bytes in the FIFO, only words and bytes are considered here. + */ +static void stfsm_clear_fifo(struct stfsm *fsm) +{ + const struct stfsm_seq *seq = &stfsm_seq_load_fifo_byte; + uint32_t words, i; + + /* 1. Clear any 32-bit words */ + words = stfsm_fifo_available(fsm); + if (words) { + for (i = 0; i < words; i++) + readl(fsm->base + SPI_FAST_SEQ_DATA_REG); + dev_dbg(fsm->dev, "cleared %d words from FIFO\n", words); + } + + /* + * 2. Clear any remaining bytes + * - Load the FIFO, one byte at a time, until a complete 32-bit word + * is available. + */ + for (i = 0, words = 0; i < 4 && !words; i++) { + stfsm_load_seq(fsm, seq); + stfsm_wait_seq(fsm); + words = stfsm_fifo_available(fsm); + } + + /* - A single word must be available now */ + if (words != 1) { + dev_err(fsm->dev, "failed to clear bytes from the data FIFO\n"); + return; + } + + /* - Read the 32-bit word */ + readl(fsm->base + SPI_FAST_SEQ_DATA_REG); + + dev_dbg(fsm->dev, "cleared %d byte(s) from the data FIFO\n", 4 - i); +} + static int stfsm_write_fifo(struct stfsm *fsm, const uint32_t *buf, uint32_t size) { @@ -1521,11 +1586,11 @@ static int stfsm_write(struct stfsm *fsm, const uint8_t *buf, uint32_t size_lb; uint32_t size_mop; uint32_t tmp[4]; + uint32_t i; uint32_t page_buf[FLASH_PAGESIZE_32]; uint8_t *t = (uint8_t *)&tmp; const uint8_t *p; int ret; - int i; dev_dbg(fsm->dev, "writing %d bytes to 0x%08x\n", size, offset); @@ -1843,8 +1908,7 @@ static void stfsm_set_freq(struct stfsm *fsm, uint32_t spi_freq) uint32_t emi_freq; uint32_t clk_div; - /* TODO: Make this dynamic */ - emi_freq = STFSM_DEFAULT_EMI_FREQ; + emi_freq = clk_get_rate(fsm->clk); /* * Calculate clk_div - values between 2 and 128 @@ -1994,6 +2058,18 @@ static int stfsm_probe(struct platform_device *pdev) return PTR_ERR(fsm->base); } + fsm->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(fsm->clk)) { + dev_err(fsm->dev, "Couldn't find EMI clock.\n"); + return PTR_ERR(fsm->clk); + } + + ret = clk_prepare_enable(fsm->clk); + if (ret) { + dev_err(fsm->dev, "Failed to enable EMI clock.\n"); + return ret; + } + mutex_init(&fsm->lock); ret = stfsm_init(fsm); @@ -2058,6 +2134,28 @@ static int stfsm_remove(struct platform_device *pdev) return mtd_device_unregister(&fsm->mtd); } +#ifdef CONFIG_PM_SLEEP +static int stfsmfsm_suspend(struct device *dev) +{ + struct stfsm *fsm = dev_get_drvdata(dev); + + clk_disable_unprepare(fsm->clk); + + return 0; +} + +static int stfsmfsm_resume(struct device *dev) +{ + struct stfsm *fsm = dev_get_drvdata(dev); + + clk_prepare_enable(fsm->clk); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(stfsm_pm_ops, stfsmfsm_suspend, stfsmfsm_resume); + static const struct of_device_id stfsm_match[] = { { .compatible = "st,spi-fsm", }, {}, @@ -2070,6 +2168,7 @@ static struct platform_driver stfsm_driver = { .driver = { .name = "st-spi-fsm", .of_match_table = stfsm_match, + .pm = &stfsm_pm_ops, }, }; module_platform_driver(stfsm_driver); diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index f35cd2081314..ff26e979b1a1 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -269,6 +269,16 @@ static int of_flash_probe(struct platform_device *dev) info->list[i].mtd = obsolete_probe(dev, &info->list[i].map); } + + /* Fall back to mapping region as ROM */ + if (!info->list[i].mtd) { + dev_warn(&dev->dev, + "do_map_probe() failed for type %s\n", + probe_type); + + info->list[i].mtd = do_map_probe("map_rom", + &info->list[i].map); + } mtd_list[i] = info->list[i].mtd; err = -ENXIO; diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 485ea751c7f9..bb4c14f83c75 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -45,8 +45,6 @@ struct mtdblk_dev { enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state; }; -static DEFINE_MUTEX(mtdblks_lock); - /* * Cache stuff... * @@ -286,10 +284,8 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd) pr_debug("mtdblock_open\n"); - mutex_lock(&mtdblks_lock); if (mtdblk->count) { mtdblk->count++; - mutex_unlock(&mtdblks_lock); return 0; } @@ -302,8 +298,6 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd) mtdblk->cache_data = NULL; } - mutex_unlock(&mtdblks_lock); - pr_debug("ok\n"); return 0; @@ -315,8 +309,6 @@ static void mtdblock_release(struct mtd_blktrans_dev *mbd) pr_debug("mtdblock_release\n"); - mutex_lock(&mtdblks_lock); - mutex_lock(&mtdblk->cache_mutex); write_cached_data(mtdblk); mutex_unlock(&mtdblk->cache_mutex); @@ -331,8 +323,6 @@ static void mtdblock_release(struct mtd_blktrans_dev *mbd) vfree(mtdblk->cache_data); } - mutex_unlock(&mtdblks_lock); - pr_debug("ok\n"); } diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index eacc3aac7327..239a8c806b67 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -311,7 +311,8 @@ concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) devops.len = subdev->size - to; err = mtd_write_oob(subdev, to, &devops); - ops->retlen += devops.oobretlen; + ops->retlen += devops.retlen; + ops->oobretlen += devops.oobretlen; if (err) return err; diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 0ec4d6ea1e4b..11883bd26d9d 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -37,6 +37,7 @@ #include <linux/backing-dev.h> #include <linux/gfp.h> #include <linux/slab.h> +#include <linux/reboot.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> @@ -356,6 +357,17 @@ unsigned mtd_mmap_capabilities(struct mtd_info *mtd) EXPORT_SYMBOL_GPL(mtd_mmap_capabilities); #endif +static int mtd_reboot_notifier(struct notifier_block *n, unsigned long state, + void *cmd) +{ + struct mtd_info *mtd; + + mtd = container_of(n, struct mtd_info, reboot_notifier); + mtd->_reboot(mtd); + + return NOTIFY_DONE; +} + /** * add_mtd_device - register an MTD device * @mtd: pointer to new MTD device info structure @@ -544,6 +556,19 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, err = -ENODEV; } + /* + * FIXME: some drivers unfortunately call this function more than once. + * So we have to check if we've already assigned the reboot notifier. + * + * Generally, we can make multiple calls work for most cases, but it + * does cause problems with parse_mtd_partitions() above (e.g., + * cmdlineparts will register partitions more than once). + */ + if (mtd->_reboot && !mtd->reboot_notifier.notifier_call) { + mtd->reboot_notifier.notifier_call = mtd_reboot_notifier; + register_reboot_notifier(&mtd->reboot_notifier); + } + return err; } EXPORT_SYMBOL_GPL(mtd_device_parse_register); @@ -558,6 +583,9 @@ int mtd_device_unregister(struct mtd_info *master) { int err; + if (master->_reboot) + unregister_reboot_notifier(&master->reboot_notifier); + err = del_mtd_partitions(master); if (err) return err; diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 7d0150d20432..5b76a173cd95 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -421,7 +421,7 @@ config MTD_NAND_ORION config MTD_NAND_FSL_ELBC tristate "NAND support for Freescale eLBC controllers" - depends on PPC_OF + depends on PPC select FSL_LBC help Various Freescale chips, including the 8313, include a NAND Flash @@ -524,4 +524,9 @@ config MTD_NAND_SUNXI help Enables support for NAND Flash chips on Allwinner SoCs. +config MTD_NAND_HISI504 + tristate "Support for NAND controller on Hisilicon SoC Hip04" + help + Enables support for NAND controller on Hisilicon SoC Hip04. + endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index bd38f21d2e28..582bbd05aff7 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -51,5 +51,6 @@ obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/ obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o +obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o nand-objs := nand_base.o nand_bbt.o nand_timings.o diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index f1d555cfb332..842f8fe91b56 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -183,7 +183,7 @@ static int ams_delta_init(struct platform_device *pdev) return -ENXIO; /* Allocate memory for MTD device structure and private data */ - ams_delta_mtd = kmalloc(sizeof(struct mtd_info) + + ams_delta_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); if (!ams_delta_mtd) { printk (KERN_WARNING "Unable to allocate E3 NAND MTD device structure.\n"); @@ -196,10 +196,6 @@ static int ams_delta_init(struct platform_device *pdev) /* Get pointer to private data */ this = (struct nand_chip *) (&ams_delta_mtd[1]); - /* Initialize structures */ - memset(ams_delta_mtd, 0, sizeof(struct mtd_info)); - memset(this, 0, sizeof(struct nand_chip)); - /* Link the private data with the MTD structure */ ams_delta_mtd->priv = this; diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index a345e7b2463a..d93c849b70b5 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -63,6 +63,10 @@ module_param(on_flash_bbt, int, 0); #include "atmel_nand_ecc.h" /* Hardware ECC registers */ #include "atmel_nand_nfc.h" /* Nand Flash Controller definition */ +struct atmel_nand_caps { + bool pmecc_correct_erase_page; +}; + /* oob layout for large page size * bad block info is on bytes 0 and 1 * the bytes have to be consecutives to avoid @@ -124,6 +128,7 @@ struct atmel_nand_host { struct atmel_nfc *nfc; + struct atmel_nand_caps *caps; bool has_pmecc; u8 pmecc_corr_cap; u16 pmecc_sector_size; @@ -847,7 +852,11 @@ static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf, struct atmel_nand_host *host = nand_chip->priv; int i, err_nbr; uint8_t *buf_pos; - int total_err = 0; + int max_bitflips = 0; + + /* If can correct bitfilps from erased page, do the normal check */ + if (host->caps->pmecc_correct_erase_page) + goto normal_check; for (i = 0; i < nand_chip->ecc.total; i++) if (ecc[i] != 0xff) @@ -874,13 +883,13 @@ normal_check: pmecc_correct_data(mtd, buf_pos, ecc, i, nand_chip->ecc.bytes, err_nbr); mtd->ecc_stats.corrected += err_nbr; - total_err += err_nbr; + max_bitflips = max_t(int, max_bitflips, err_nbr); } } pmecc_stat >>= 1; } - return total_err; + return max_bitflips; } static void pmecc_enable(struct atmel_nand_host *host, int ecc_op) @@ -1474,6 +1483,8 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) ecc_writel(host->ecc, CR, ATMEL_ECC_RST); } +static const struct of_device_id atmel_nand_dt_ids[]; + static int atmel_of_init_port(struct atmel_nand_host *host, struct device_node *np) { @@ -1483,6 +1494,9 @@ static int atmel_of_init_port(struct atmel_nand_host *host, struct atmel_nand_data *board = &host->board; enum of_gpio_flags flags = 0; + host->caps = (struct atmel_nand_caps *) + of_match_device(atmel_nand_dt_ids, host->dev)->data; + if (of_property_read_u32(np, "atmel,nand-addr-offset", &val) == 0) { if (val >= 32) { dev_err(host->dev, "invalid addr-offset %u\n", val); @@ -2288,8 +2302,17 @@ static int atmel_nand_remove(struct platform_device *pdev) return 0; } +static struct atmel_nand_caps at91rm9200_caps = { + .pmecc_correct_erase_page = false, +}; + +static struct atmel_nand_caps sama5d4_caps = { + .pmecc_correct_erase_page = true, +}; + static const struct of_device_id atmel_nand_dt_ids[] = { - { .compatible = "atmel,at91rm9200-nand" }, + { .compatible = "atmel,at91rm9200-nand", .data = &at91rm9200_caps }, + { .compatible = "atmel,sama5d4-nand", .data = &sama5d4_caps }, { /* sentinel */ } }; diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index b3b7ca1bafb8..f44c6061536a 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1041,7 +1041,7 @@ static void denali_setup_dma(struct denali_nand_info *denali, int op) index_addr(denali, mode | ((addr >> 16) << 8), 0x2200); /* 3. set memory low address bits 23:8 */ - index_addr(denali, mode | ((addr & 0xff) << 8), 0x2300); + index_addr(denali, mode | ((addr & 0xffff) << 8), 0x2300); /* 4. interrupt when complete, burst len = 64 bytes */ index_addr(denali, mode | 0x14000, 0x2400); @@ -1328,35 +1328,6 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col, break; } } - -/* stubs for ECC functions not used by the NAND core */ -static int denali_ecc_calculate(struct mtd_info *mtd, const uint8_t *data, - uint8_t *ecc_code) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - - dev_err(denali->dev, "denali_ecc_calculate called unexpectedly\n"); - BUG(); - return -EIO; -} - -static int denali_ecc_correct(struct mtd_info *mtd, uint8_t *data, - uint8_t *read_ecc, uint8_t *calc_ecc) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - - dev_err(denali->dev, "denali_ecc_correct called unexpectedly\n"); - BUG(); - return -EIO; -} - -static void denali_ecc_hwctl(struct mtd_info *mtd, int mode) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - - dev_err(denali->dev, "denali_ecc_hwctl called unexpectedly\n"); - BUG(); -} /* end NAND core entry points */ /* Initialization code to bring the device up to a known good state */ @@ -1609,15 +1580,6 @@ int denali_init(struct denali_nand_info *denali) denali->totalblks = denali->mtd.size >> denali->nand.phys_erase_shift; denali->blksperchip = denali->totalblks / denali->nand.numchips; - /* - * These functions are required by the NAND core framework, otherwise, - * the NAND core will assert. However, we don't need them, so we'll stub - * them out. - */ - denali->nand.ecc.calculate = denali_ecc_calculate; - denali->nand.ecc.correct = denali_ecc_correct; - denali->nand.ecc.hwctl = denali_ecc_hwctl; - /* override the default read operations */ denali->nand.ecc.size = ECC_SECTOR_SIZE * denali->devnum; denali->nand.ecc.read_page = denali_read_page; diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 4f3851a24bb2..33f3c3c54dbc 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -1294,14 +1294,6 @@ exit_auxiliary: * ecc.read_page or ecc.read_page_raw function. Thus, the fact that MTD wants an * ECC-based or raw view of the page is implicit in which function it calls * (there is a similar pair of ECC-based/raw functions for writing). - * - * FIXME: The following paragraph is incorrect, now that there exist - * ecc.read_oob_raw and ecc.write_oob_raw functions. - * - * Since MTD assumes the OOB is not covered by ECC, there is no pair of - * ECC-based/raw functions for reading or or writing the OOB. The fact that the - * caller wants an ECC-based or raw view of the page is not propagated down to - * this driver. */ static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) @@ -2029,7 +2021,6 @@ static int gpmi_nand_probe(struct platform_device *pdev) exit_nfc_init: release_resources(this); exit_acquire_resources: - dev_err(this->dev, "driver registration failed: %d\n", ret); return ret; } diff --git a/drivers/mtd/nand/hisi504_nand.c b/drivers/mtd/nand/hisi504_nand.c new file mode 100644 index 000000000000..289ad3ac3e80 --- /dev/null +++ b/drivers/mtd/nand/hisi504_nand.c @@ -0,0 +1,891 @@ +/* + * Hisilicon NAND Flash controller driver + * + * Copyright © 2012-2014 HiSilicon Technologies Co., Ltd. + * http://www.hisilicon.com + * + * Author: Zhou Wang <wangzhou.bry@gmail.com> + * The initial developer of the original code is Zhiyong Cai + * <caizhiyong@huawei.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <linux/of.h> +#include <linux/of_mtd.h> +#include <linux/mtd/mtd.h> +#include <linux/sizes.h> +#include <linux/clk.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/mtd/nand.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> +#include <linux/mtd/partitions.h> + +#define HINFC504_MAX_CHIP (4) +#define HINFC504_W_LATCH (5) +#define HINFC504_R_LATCH (7) +#define HINFC504_RW_LATCH (3) + +#define HINFC504_NFC_TIMEOUT (2 * HZ) +#define HINFC504_NFC_PM_TIMEOUT (1 * HZ) +#define HINFC504_NFC_DMA_TIMEOUT (5 * HZ) +#define HINFC504_CHIP_DELAY (25) + +#define HINFC504_REG_BASE_ADDRESS_LEN (0x100) +#define HINFC504_BUFFER_BASE_ADDRESS_LEN (2048 + 128) + +#define HINFC504_ADDR_CYCLE_MASK 0x4 + +#define HINFC504_CON 0x00 +#define HINFC504_CON_OP_MODE_NORMAL BIT(0) +#define HINFC504_CON_PAGEISZE_SHIFT (1) +#define HINFC504_CON_PAGESIZE_MASK (0x07) +#define HINFC504_CON_BUS_WIDTH BIT(4) +#define HINFC504_CON_READY_BUSY_SEL BIT(8) +#define HINFC504_CON_ECCTYPE_SHIFT (9) +#define HINFC504_CON_ECCTYPE_MASK (0x07) + +#define HINFC504_PWIDTH 0x04 +#define SET_HINFC504_PWIDTH(_w_lcnt, _r_lcnt, _rw_hcnt) \ + ((_w_lcnt) | (((_r_lcnt) & 0x0F) << 4) | (((_rw_hcnt) & 0x0F) << 8)) + +#define HINFC504_CMD 0x0C +#define HINFC504_ADDRL 0x10 +#define HINFC504_ADDRH 0x14 +#define HINFC504_DATA_NUM 0x18 + +#define HINFC504_OP 0x1C +#define HINFC504_OP_READ_DATA_EN BIT(1) +#define HINFC504_OP_WAIT_READY_EN BIT(2) +#define HINFC504_OP_CMD2_EN BIT(3) +#define HINFC504_OP_WRITE_DATA_EN BIT(4) +#define HINFC504_OP_ADDR_EN BIT(5) +#define HINFC504_OP_CMD1_EN BIT(6) +#define HINFC504_OP_NF_CS_SHIFT (7) +#define HINFC504_OP_NF_CS_MASK (3) +#define HINFC504_OP_ADDR_CYCLE_SHIFT (9) +#define HINFC504_OP_ADDR_CYCLE_MASK (7) + +#define HINFC504_STATUS 0x20 +#define HINFC504_READY BIT(0) + +#define HINFC504_INTEN 0x24 +#define HINFC504_INTEN_DMA BIT(9) +#define HINFC504_INTEN_UE BIT(6) +#define HINFC504_INTEN_CE BIT(5) + +#define HINFC504_INTS 0x28 +#define HINFC504_INTS_DMA BIT(9) +#define HINFC504_INTS_UE BIT(6) +#define HINFC504_INTS_CE BIT(5) + +#define HINFC504_INTCLR 0x2C +#define HINFC504_INTCLR_DMA BIT(9) +#define HINFC504_INTCLR_UE BIT(6) +#define HINFC504_INTCLR_CE BIT(5) + +#define HINFC504_ECC_STATUS 0x5C +#define HINFC504_ECC_16_BIT_SHIFT 12 + +#define HINFC504_DMA_CTRL 0x60 +#define HINFC504_DMA_CTRL_DMA_START BIT(0) +#define HINFC504_DMA_CTRL_WE BIT(1) +#define HINFC504_DMA_CTRL_DATA_AREA_EN BIT(2) +#define HINFC504_DMA_CTRL_OOB_AREA_EN BIT(3) +#define HINFC504_DMA_CTRL_BURST4_EN BIT(4) +#define HINFC504_DMA_CTRL_BURST8_EN BIT(5) +#define HINFC504_DMA_CTRL_BURST16_EN BIT(6) +#define HINFC504_DMA_CTRL_ADDR_NUM_SHIFT (7) +#define HINFC504_DMA_CTRL_ADDR_NUM_MASK (1) +#define HINFC504_DMA_CTRL_CS_SHIFT (8) +#define HINFC504_DMA_CTRL_CS_MASK (0x03) + +#define HINFC504_DMA_ADDR_DATA 0x64 +#define HINFC504_DMA_ADDR_OOB 0x68 + +#define HINFC504_DMA_LEN 0x6C +#define HINFC504_DMA_LEN_OOB_SHIFT (16) +#define HINFC504_DMA_LEN_OOB_MASK (0xFFF) + +#define HINFC504_DMA_PARA 0x70 +#define HINFC504_DMA_PARA_DATA_RW_EN BIT(0) +#define HINFC504_DMA_PARA_OOB_RW_EN BIT(1) +#define HINFC504_DMA_PARA_DATA_EDC_EN BIT(2) +#define HINFC504_DMA_PARA_OOB_EDC_EN BIT(3) +#define HINFC504_DMA_PARA_DATA_ECC_EN BIT(4) +#define HINFC504_DMA_PARA_OOB_ECC_EN BIT(5) + +#define HINFC_VERSION 0x74 +#define HINFC504_LOG_READ_ADDR 0x7C +#define HINFC504_LOG_READ_LEN 0x80 + +#define HINFC504_NANDINFO_LEN 0x10 + +struct hinfc_host { + struct nand_chip chip; + struct mtd_info mtd; + struct device *dev; + void __iomem *iobase; + void __iomem *mmio; + struct completion cmd_complete; + unsigned int offset; + unsigned int command; + int chipselect; + unsigned int addr_cycle; + u32 addr_value[2]; + u32 cache_addr_value[2]; + char *buffer; + dma_addr_t dma_buffer; + dma_addr_t dma_oob; + int version; + unsigned int irq_status; /* interrupt status */ +}; + +static inline unsigned int hinfc_read(struct hinfc_host *host, unsigned int reg) +{ + return readl(host->iobase + reg); +} + +static inline void hinfc_write(struct hinfc_host *host, unsigned int value, + unsigned int reg) +{ + writel(value, host->iobase + reg); +} + +static void wait_controller_finished(struct hinfc_host *host) +{ + unsigned long timeout = jiffies + HINFC504_NFC_TIMEOUT; + int val; + + while (time_before(jiffies, timeout)) { + val = hinfc_read(host, HINFC504_STATUS); + if (host->command == NAND_CMD_ERASE2) { + /* nfc is ready */ + while (!(val & HINFC504_READY)) { + usleep_range(500, 1000); + val = hinfc_read(host, HINFC504_STATUS); + } + return; + } + + if (val & HINFC504_READY) + return; + } + + /* wait cmd timeout */ + dev_err(host->dev, "Wait NAND controller exec cmd timeout.\n"); +} + +static void hisi_nfc_dma_transfer(struct hinfc_host *host, int todev) +{ + struct mtd_info *mtd = &host->mtd; + struct nand_chip *chip = mtd->priv; + unsigned long val; + int ret; + + hinfc_write(host, host->dma_buffer, HINFC504_DMA_ADDR_DATA); + hinfc_write(host, host->dma_oob, HINFC504_DMA_ADDR_OOB); + + if (chip->ecc.mode == NAND_ECC_NONE) { + hinfc_write(host, ((mtd->oobsize & HINFC504_DMA_LEN_OOB_MASK) + << HINFC504_DMA_LEN_OOB_SHIFT), HINFC504_DMA_LEN); + + hinfc_write(host, HINFC504_DMA_PARA_DATA_RW_EN + | HINFC504_DMA_PARA_OOB_RW_EN, HINFC504_DMA_PARA); + } else { + if (host->command == NAND_CMD_READOOB) + hinfc_write(host, HINFC504_DMA_PARA_OOB_RW_EN + | HINFC504_DMA_PARA_OOB_EDC_EN + | HINFC504_DMA_PARA_OOB_ECC_EN, HINFC504_DMA_PARA); + else + hinfc_write(host, HINFC504_DMA_PARA_DATA_RW_EN + | HINFC504_DMA_PARA_OOB_RW_EN + | HINFC504_DMA_PARA_DATA_EDC_EN + | HINFC504_DMA_PARA_OOB_EDC_EN + | HINFC504_DMA_PARA_DATA_ECC_EN + | HINFC504_DMA_PARA_OOB_ECC_EN, HINFC504_DMA_PARA); + + } + + val = (HINFC504_DMA_CTRL_DMA_START | HINFC504_DMA_CTRL_BURST4_EN + | HINFC504_DMA_CTRL_BURST8_EN | HINFC504_DMA_CTRL_BURST16_EN + | HINFC504_DMA_CTRL_DATA_AREA_EN | HINFC504_DMA_CTRL_OOB_AREA_EN + | ((host->addr_cycle == 4 ? 1 : 0) + << HINFC504_DMA_CTRL_ADDR_NUM_SHIFT) + | ((host->chipselect & HINFC504_DMA_CTRL_CS_MASK) + << HINFC504_DMA_CTRL_CS_SHIFT)); + + if (todev) + val |= HINFC504_DMA_CTRL_WE; + + init_completion(&host->cmd_complete); + + hinfc_write(host, val, HINFC504_DMA_CTRL); + ret = wait_for_completion_timeout(&host->cmd_complete, + HINFC504_NFC_DMA_TIMEOUT); + + if (!ret) { + dev_err(host->dev, "DMA operation(irq) timeout!\n"); + /* sanity check */ + val = hinfc_read(host, HINFC504_DMA_CTRL); + if (!(val & HINFC504_DMA_CTRL_DMA_START)) + dev_err(host->dev, "DMA is already done but without irq ACK!\n"); + else + dev_err(host->dev, "DMA is really timeout!\n"); + } +} + +static int hisi_nfc_send_cmd_pageprog(struct hinfc_host *host) +{ + host->addr_value[0] &= 0xffff0000; + + hinfc_write(host, host->addr_value[0], HINFC504_ADDRL); + hinfc_write(host, host->addr_value[1], HINFC504_ADDRH); + hinfc_write(host, NAND_CMD_PAGEPROG << 8 | NAND_CMD_SEQIN, + HINFC504_CMD); + + hisi_nfc_dma_transfer(host, 1); + + return 0; +} + +static int hisi_nfc_send_cmd_readstart(struct hinfc_host *host) +{ + struct mtd_info *mtd = &host->mtd; + + if ((host->addr_value[0] == host->cache_addr_value[0]) && + (host->addr_value[1] == host->cache_addr_value[1])) + return 0; + + host->addr_value[0] &= 0xffff0000; + + hinfc_write(host, host->addr_value[0], HINFC504_ADDRL); + hinfc_write(host, host->addr_value[1], HINFC504_ADDRH); + hinfc_write(host, NAND_CMD_READSTART << 8 | NAND_CMD_READ0, + HINFC504_CMD); + + hinfc_write(host, 0, HINFC504_LOG_READ_ADDR); + hinfc_write(host, mtd->writesize + mtd->oobsize, + HINFC504_LOG_READ_LEN); + + hisi_nfc_dma_transfer(host, 0); + + host->cache_addr_value[0] = host->addr_value[0]; + host->cache_addr_value[1] = host->addr_value[1]; + + return 0; +} + +static int hisi_nfc_send_cmd_erase(struct hinfc_host *host) +{ + hinfc_write(host, host->addr_value[0], HINFC504_ADDRL); + hinfc_write(host, (NAND_CMD_ERASE2 << 8) | NAND_CMD_ERASE1, + HINFC504_CMD); + + hinfc_write(host, HINFC504_OP_WAIT_READY_EN + | HINFC504_OP_CMD2_EN + | HINFC504_OP_CMD1_EN + | HINFC504_OP_ADDR_EN + | ((host->chipselect & HINFC504_OP_NF_CS_MASK) + << HINFC504_OP_NF_CS_SHIFT) + | ((host->addr_cycle & HINFC504_OP_ADDR_CYCLE_MASK) + << HINFC504_OP_ADDR_CYCLE_SHIFT), + HINFC504_OP); + + wait_controller_finished(host); + + return 0; +} + +static int hisi_nfc_send_cmd_readid(struct hinfc_host *host) +{ + hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM); + hinfc_write(host, NAND_CMD_READID, HINFC504_CMD); + hinfc_write(host, 0, HINFC504_ADDRL); + + hinfc_write(host, HINFC504_OP_CMD1_EN | HINFC504_OP_ADDR_EN + | HINFC504_OP_READ_DATA_EN + | ((host->chipselect & HINFC504_OP_NF_CS_MASK) + << HINFC504_OP_NF_CS_SHIFT) + | 1 << HINFC504_OP_ADDR_CYCLE_SHIFT, HINFC504_OP); + + wait_controller_finished(host); + + return 0; +} + +static int hisi_nfc_send_cmd_status(struct hinfc_host *host) +{ + hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM); + hinfc_write(host, NAND_CMD_STATUS, HINFC504_CMD); + hinfc_write(host, HINFC504_OP_CMD1_EN + | HINFC504_OP_READ_DATA_EN + | ((host->chipselect & HINFC504_OP_NF_CS_MASK) + << HINFC504_OP_NF_CS_SHIFT), + HINFC504_OP); + + wait_controller_finished(host); + + return 0; +} + +static int hisi_nfc_send_cmd_reset(struct hinfc_host *host, int chipselect) +{ + hinfc_write(host, NAND_CMD_RESET, HINFC504_CMD); + + hinfc_write(host, HINFC504_OP_CMD1_EN + | ((chipselect & HINFC504_OP_NF_CS_MASK) + << HINFC504_OP_NF_CS_SHIFT) + | HINFC504_OP_WAIT_READY_EN, + HINFC504_OP); + + wait_controller_finished(host); + + return 0; +} + +static void hisi_nfc_select_chip(struct mtd_info *mtd, int chipselect) +{ + struct nand_chip *chip = mtd->priv; + struct hinfc_host *host = chip->priv; + + if (chipselect < 0) + return; + + host->chipselect = chipselect; +} + +static uint8_t hisi_nfc_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct hinfc_host *host = chip->priv; + + if (host->command == NAND_CMD_STATUS) + return *(uint8_t *)(host->mmio); + + host->offset++; + + if (host->command == NAND_CMD_READID) + return *(uint8_t *)(host->mmio + host->offset - 1); + + return *(uint8_t *)(host->buffer + host->offset - 1); +} + +static u16 hisi_nfc_read_word(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct hinfc_host *host = chip->priv; + + host->offset += 2; + return *(u16 *)(host->buffer + host->offset - 2); +} + +static void +hisi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct hinfc_host *host = chip->priv; + + memcpy(host->buffer + host->offset, buf, len); + host->offset += len; +} + +static void hisi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct hinfc_host *host = chip->priv; + + memcpy(buf, host->buffer + host->offset, len); + host->offset += len; +} + +static void set_addr(struct mtd_info *mtd, int column, int page_addr) +{ + struct nand_chip *chip = mtd->priv; + struct hinfc_host *host = chip->priv; + unsigned int command = host->command; + + host->addr_cycle = 0; + host->addr_value[0] = 0; + host->addr_value[1] = 0; + + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (chip->options & NAND_BUSWIDTH_16 && + !nand_opcode_8bits(command)) + column >>= 1; + + host->addr_value[0] = column & 0xffff; + host->addr_cycle = 2; + } + if (page_addr != -1) { + host->addr_value[0] |= (page_addr & 0xffff) + << (host->addr_cycle * 8); + host->addr_cycle += 2; + /* One more address cycle for devices > 128MiB */ + if (chip->chipsize > (128 << 20)) { + host->addr_cycle += 1; + if (host->command == NAND_CMD_ERASE1) + host->addr_value[0] |= ((page_addr >> 16) & 0xff) << 16; + else + host->addr_value[1] |= ((page_addr >> 16) & 0xff); + } + } +} + +static void hisi_nfc_cmdfunc(struct mtd_info *mtd, unsigned command, int column, + int page_addr) +{ + struct nand_chip *chip = mtd->priv; + struct hinfc_host *host = chip->priv; + int is_cache_invalid = 1; + unsigned int flag = 0; + + host->command = command; + + switch (command) { + case NAND_CMD_READ0: + case NAND_CMD_READOOB: + if (command == NAND_CMD_READ0) + host->offset = column; + else + host->offset = column + mtd->writesize; + + is_cache_invalid = 0; + set_addr(mtd, column, page_addr); + hisi_nfc_send_cmd_readstart(host); + break; + + case NAND_CMD_SEQIN: + host->offset = column; + set_addr(mtd, column, page_addr); + break; + + case NAND_CMD_ERASE1: + set_addr(mtd, column, page_addr); + break; + + case NAND_CMD_PAGEPROG: + hisi_nfc_send_cmd_pageprog(host); + break; + + case NAND_CMD_ERASE2: + hisi_nfc_send_cmd_erase(host); + break; + + case NAND_CMD_READID: + host->offset = column; + memset(host->mmio, 0, 0x10); + hisi_nfc_send_cmd_readid(host); + break; + + case NAND_CMD_STATUS: + flag = hinfc_read(host, HINFC504_CON); + if (chip->ecc.mode == NAND_ECC_HW) + hinfc_write(host, + flag & ~(HINFC504_CON_ECCTYPE_MASK << + HINFC504_CON_ECCTYPE_SHIFT), HINFC504_CON); + + host->offset = 0; + memset(host->mmio, 0, 0x10); + hisi_nfc_send_cmd_status(host); + hinfc_write(host, flag, HINFC504_CON); + break; + + case NAND_CMD_RESET: + hisi_nfc_send_cmd_reset(host, host->chipselect); + break; + + default: + dev_err(host->dev, "Error: unsupported cmd(cmd=%x, col=%x, page=%x)\n", + command, column, page_addr); + } + + if (is_cache_invalid) { + host->cache_addr_value[0] = ~0; + host->cache_addr_value[1] = ~0; + } +} + +static irqreturn_t hinfc_irq_handle(int irq, void *devid) +{ + struct hinfc_host *host = devid; + unsigned int flag; + + flag = hinfc_read(host, HINFC504_INTS); + /* store interrupts state */ + host->irq_status |= flag; + + if (flag & HINFC504_INTS_DMA) { + hinfc_write(host, HINFC504_INTCLR_DMA, HINFC504_INTCLR); + complete(&host->cmd_complete); + } else if (flag & HINFC504_INTS_CE) { + hinfc_write(host, HINFC504_INTCLR_CE, HINFC504_INTCLR); + } else if (flag & HINFC504_INTS_UE) { + hinfc_write(host, HINFC504_INTCLR_UE, HINFC504_INTCLR); + } + + return IRQ_HANDLED; +} + +static int hisi_nand_read_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int oob_required, int page) +{ + struct hinfc_host *host = chip->priv; + int max_bitflips = 0, stat = 0, stat_max = 0, status_ecc; + int stat_1, stat_2; + + chip->read_buf(mtd, buf, mtd->writesize); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + /* errors which can not be corrected by ECC */ + if (host->irq_status & HINFC504_INTS_UE) { + mtd->ecc_stats.failed++; + } else if (host->irq_status & HINFC504_INTS_CE) { + /* TODO: need add other ECC modes! */ + switch (chip->ecc.strength) { + case 16: + status_ecc = hinfc_read(host, HINFC504_ECC_STATUS) >> + HINFC504_ECC_16_BIT_SHIFT & 0x0fff; + stat_2 = status_ecc & 0x3f; + stat_1 = status_ecc >> 6 & 0x3f; + stat = stat_1 + stat_2; + stat_max = max_t(int, stat_1, stat_2); + } + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(int, max_bitflips, stat_max); + } + host->irq_status = 0; + + return max_bitflips; +} + +static int hisi_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct hinfc_host *host = chip->priv; + + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + if (host->irq_status & HINFC504_INTS_UE) { + host->irq_status = 0; + return -EBADMSG; + } + + host->irq_status = 0; + return 0; +} + +static int hisi_nand_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, int oob_required) +{ + chip->write_buf(mtd, buf, mtd->writesize); + if (oob_required) + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +static void hisi_nfc_host_init(struct hinfc_host *host) +{ + struct nand_chip *chip = &host->chip; + unsigned int flag = 0; + + host->version = hinfc_read(host, HINFC_VERSION); + host->addr_cycle = 0; + host->addr_value[0] = 0; + host->addr_value[1] = 0; + host->cache_addr_value[0] = ~0; + host->cache_addr_value[1] = ~0; + host->chipselect = 0; + + /* default page size: 2K, ecc_none. need modify */ + flag = HINFC504_CON_OP_MODE_NORMAL | HINFC504_CON_READY_BUSY_SEL + | ((0x001 & HINFC504_CON_PAGESIZE_MASK) + << HINFC504_CON_PAGEISZE_SHIFT) + | ((0x0 & HINFC504_CON_ECCTYPE_MASK) + << HINFC504_CON_ECCTYPE_SHIFT) + | ((chip->options & NAND_BUSWIDTH_16) ? + HINFC504_CON_BUS_WIDTH : 0); + hinfc_write(host, flag, HINFC504_CON); + + memset(host->mmio, 0xff, HINFC504_BUFFER_BASE_ADDRESS_LEN); + + hinfc_write(host, SET_HINFC504_PWIDTH(HINFC504_W_LATCH, + HINFC504_R_LATCH, HINFC504_RW_LATCH), HINFC504_PWIDTH); + + /* enable DMA irq */ + hinfc_write(host, HINFC504_INTEN_DMA, HINFC504_INTEN); +} + +static struct nand_ecclayout nand_ecc_2K_16bits = { + .oobavail = 6, + .oobfree = { {2, 6} }, +}; + +static int hisi_nfc_ecc_probe(struct hinfc_host *host) +{ + unsigned int flag; + int size, strength, ecc_bits; + struct device *dev = host->dev; + struct nand_chip *chip = &host->chip; + struct mtd_info *mtd = &host->mtd; + struct device_node *np = host->dev->of_node; + + size = of_get_nand_ecc_step_size(np); + strength = of_get_nand_ecc_strength(np); + if (size != 1024) { + dev_err(dev, "error ecc size: %d\n", size); + return -EINVAL; + } + + if ((size == 1024) && ((strength != 8) && (strength != 16) && + (strength != 24) && (strength != 40))) { + dev_err(dev, "ecc size and strength do not match\n"); + return -EINVAL; + } + + chip->ecc.size = size; + chip->ecc.strength = strength; + + chip->ecc.read_page = hisi_nand_read_page_hwecc; + chip->ecc.read_oob = hisi_nand_read_oob; + chip->ecc.write_page = hisi_nand_write_page_hwecc; + + switch (chip->ecc.strength) { + case 16: + ecc_bits = 6; + if (mtd->writesize == 2048) + chip->ecc.layout = &nand_ecc_2K_16bits; + + /* TODO: add more page size support */ + break; + + /* TODO: add more ecc strength support */ + default: + dev_err(dev, "not support strength: %d\n", chip->ecc.strength); + return -EINVAL; + } + + flag = hinfc_read(host, HINFC504_CON); + /* add ecc type configure */ + flag |= ((ecc_bits & HINFC504_CON_ECCTYPE_MASK) + << HINFC504_CON_ECCTYPE_SHIFT); + hinfc_write(host, flag, HINFC504_CON); + + /* enable ecc irq */ + flag = hinfc_read(host, HINFC504_INTEN) & 0xfff; + hinfc_write(host, flag | HINFC504_INTEN_UE | HINFC504_INTEN_CE, + HINFC504_INTEN); + + return 0; +} + +static int hisi_nfc_probe(struct platform_device *pdev) +{ + int ret = 0, irq, buswidth, flag, max_chips = HINFC504_MAX_CHIP; + struct device *dev = &pdev->dev; + struct hinfc_host *host; + struct nand_chip *chip; + struct mtd_info *mtd; + struct resource *res; + struct device_node *np = dev->of_node; + struct mtd_part_parser_data ppdata; + + host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + host->dev = dev; + + platform_set_drvdata(pdev, host); + chip = &host->chip; + mtd = &host->mtd; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "no IRQ resource defined\n"); + ret = -ENXIO; + goto err_res; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->iobase = devm_ioremap_resource(dev, res); + if (IS_ERR(host->iobase)) { + ret = PTR_ERR(host->iobase); + goto err_res; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + host->mmio = devm_ioremap_resource(dev, res); + if (IS_ERR(host->mmio)) { + ret = PTR_ERR(host->mmio); + dev_err(dev, "devm_ioremap_resource[1] fail\n"); + goto err_res; + } + + mtd->priv = chip; + mtd->owner = THIS_MODULE; + mtd->name = "hisi_nand"; + mtd->dev.parent = &pdev->dev; + + chip->priv = host; + chip->cmdfunc = hisi_nfc_cmdfunc; + chip->select_chip = hisi_nfc_select_chip; + chip->read_byte = hisi_nfc_read_byte; + chip->read_word = hisi_nfc_read_word; + chip->write_buf = hisi_nfc_write_buf; + chip->read_buf = hisi_nfc_read_buf; + chip->chip_delay = HINFC504_CHIP_DELAY; + + chip->ecc.mode = of_get_nand_ecc_mode(np); + + buswidth = of_get_nand_bus_width(np); + if (buswidth == 16) + chip->options |= NAND_BUSWIDTH_16; + + hisi_nfc_host_init(host); + + ret = devm_request_irq(dev, irq, hinfc_irq_handle, IRQF_DISABLED, + "nandc", host); + if (ret) { + dev_err(dev, "failed to request IRQ\n"); + goto err_res; + } + + ret = nand_scan_ident(mtd, max_chips, NULL); + if (ret) { + ret = -ENODEV; + goto err_res; + } + + host->buffer = dmam_alloc_coherent(dev, mtd->writesize + mtd->oobsize, + &host->dma_buffer, GFP_KERNEL); + if (!host->buffer) { + ret = -ENOMEM; + goto err_res; + } + + host->dma_oob = host->dma_buffer + mtd->writesize; + memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize); + + flag = hinfc_read(host, HINFC504_CON); + flag &= ~(HINFC504_CON_PAGESIZE_MASK << HINFC504_CON_PAGEISZE_SHIFT); + switch (mtd->writesize) { + case 2048: + flag |= (0x001 << HINFC504_CON_PAGEISZE_SHIFT); break; + /* + * TODO: add more pagesize support, + * default pagesize has been set in hisi_nfc_host_init + */ + default: + dev_err(dev, "NON-2KB page size nand flash\n"); + ret = -EINVAL; + goto err_res; + } + hinfc_write(host, flag, HINFC504_CON); + + if (chip->ecc.mode == NAND_ECC_HW) + hisi_nfc_ecc_probe(host); + + ret = nand_scan_tail(mtd); + if (ret) { + dev_err(dev, "nand_scan_tail failed: %d\n", ret); + goto err_res; + } + + ppdata.of_node = np; + ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); + if (ret) { + dev_err(dev, "Err MTD partition=%d\n", ret); + goto err_mtd; + } + + return 0; + +err_mtd: + nand_release(mtd); +err_res: + return ret; +} + +static int hisi_nfc_remove(struct platform_device *pdev) +{ + struct hinfc_host *host = platform_get_drvdata(pdev); + struct mtd_info *mtd = &host->mtd; + + nand_release(mtd); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int hisi_nfc_suspend(struct device *dev) +{ + struct hinfc_host *host = dev_get_drvdata(dev); + unsigned long timeout = jiffies + HINFC504_NFC_PM_TIMEOUT; + + while (time_before(jiffies, timeout)) { + if (((hinfc_read(host, HINFC504_STATUS) & 0x1) == 0x0) && + (hinfc_read(host, HINFC504_DMA_CTRL) & + HINFC504_DMA_CTRL_DMA_START)) { + cond_resched(); + return 0; + } + } + + dev_err(host->dev, "nand controller suspend timeout.\n"); + + return -EAGAIN; +} + +static int hisi_nfc_resume(struct device *dev) +{ + int cs; + struct hinfc_host *host = dev_get_drvdata(dev); + struct nand_chip *chip = &host->chip; + + for (cs = 0; cs < chip->numchips; cs++) + hisi_nfc_send_cmd_reset(host, cs); + hinfc_write(host, SET_HINFC504_PWIDTH(HINFC504_W_LATCH, + HINFC504_R_LATCH, HINFC504_RW_LATCH), HINFC504_PWIDTH); + + return 0; +} +#endif +static SIMPLE_DEV_PM_OPS(hisi_nfc_pm_ops, hisi_nfc_suspend, hisi_nfc_resume); + +static const struct of_device_id nfc_id_table[] = { + { .compatible = "hisilicon,504-nfc" }, + {} +}; +MODULE_DEVICE_TABLE(of, nfc_id_table); + +static struct platform_driver hisi_nfc_driver = { + .driver = { + .name = "hisi_nand", + .of_match_table = nfc_id_table, + .pm = &hisi_nfc_pm_ops, + }, + .probe = hisi_nfc_probe, + .remove = hisi_nfc_remove, +}; + +module_platform_driver(hisi_nfc_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Zhou Wang"); +MODULE_AUTHOR("Zhiyong Cai"); +MODULE_DESCRIPTION("Hisilicon Nand Flash Controller Driver"); diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index 1633ec9c5108..ebf2cce04cba 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -69,7 +69,7 @@ struct jz_nand { int selected_bank; - struct jz_nand_platform_data *pdata; + struct gpio_desc *busy_gpio; bool is_reading; }; @@ -131,7 +131,7 @@ static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) static int jz_nand_dev_ready(struct mtd_info *mtd) { struct jz_nand *nand = mtd_to_jz_nand(mtd); - return gpio_get_value_cansleep(nand->pdata->busy_gpio); + return gpiod_get_value_cansleep(nand->busy_gpio); } static void jz_nand_hwctl(struct mtd_info *mtd, int mode) @@ -423,14 +423,12 @@ static int jz_nand_probe(struct platform_device *pdev) if (ret) goto err_free; - if (pdata && gpio_is_valid(pdata->busy_gpio)) { - ret = gpio_request(pdata->busy_gpio, "NAND busy pin"); - if (ret) { - dev_err(&pdev->dev, - "Failed to request busy gpio %d: %d\n", - pdata->busy_gpio, ret); - goto err_iounmap_mmio; - } + nand->busy_gpio = devm_gpiod_get_optional(&pdev->dev, "busy", GPIOD_IN); + if (IS_ERR(nand->busy_gpio)) { + ret = PTR_ERR(nand->busy_gpio); + dev_err(&pdev->dev, "Failed to request busy gpio %d\n", + ret); + goto err_iounmap_mmio; } mtd = &nand->mtd; @@ -454,10 +452,9 @@ static int jz_nand_probe(struct platform_device *pdev) chip->cmd_ctrl = jz_nand_cmd_ctrl; chip->select_chip = jz_nand_select_chip; - if (pdata && gpio_is_valid(pdata->busy_gpio)) + if (nand->busy_gpio) chip->dev_ready = jz_nand_dev_ready; - nand->pdata = pdata; platform_set_drvdata(pdev, nand); /* We are going to autodetect NAND chips in the banks specified in the @@ -496,7 +493,7 @@ static int jz_nand_probe(struct platform_device *pdev) } if (chipnr == 0) { dev_err(&pdev->dev, "No NAND chips found\n"); - goto err_gpio_busy; + goto err_iounmap_mmio; } if (pdata && pdata->ident_callback) { @@ -533,9 +530,6 @@ err_unclaim_banks: nand->bank_base[bank - 1]); } writel(0, nand->base + JZ_REG_NAND_CTRL); -err_gpio_busy: - if (pdata && gpio_is_valid(pdata->busy_gpio)) - gpio_free(pdata->busy_gpio); err_iounmap_mmio: jz_nand_iounmap_resource(nand->mem, nand->base); err_free: @@ -546,7 +540,6 @@ err_free: static int jz_nand_remove(struct platform_device *pdev) { struct jz_nand *nand = platform_get_drvdata(pdev); - struct jz_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); size_t i; nand_release(&nand->mtd); @@ -562,8 +555,6 @@ static int jz_nand_remove(struct platform_device *pdev) gpio_free(JZ_GPIO_MEM_CS0 + bank - 1); } } - if (pdata && gpio_is_valid(pdata->busy_gpio)) - gpio_free(pdata->busy_gpio); jz_nand_iounmap_resource(nand->mem, nand->base); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 41585dfb206f..df7eb4ff07d1 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -157,7 +157,6 @@ static uint8_t nand_read_byte(struct mtd_info *mtd) /** * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip - * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip * @mtd: MTD device structure * * Default read function for 16bit buswidth with endianness conversion. @@ -1751,11 +1750,10 @@ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int page) { - uint8_t *buf = chip->oob_poi; int length = mtd->oobsize; int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; int eccsize = chip->ecc.size; - uint8_t *bufpoi = buf; + uint8_t *bufpoi = chip->oob_poi; int i, toread, sndrnd = 0, pos; chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page); @@ -2944,6 +2942,16 @@ static void nand_resume(struct mtd_info *mtd) __func__); } +/** + * nand_shutdown - [MTD Interface] Finish the current NAND operation and + * prevent further operations + * @mtd: MTD device structure + */ +static void nand_shutdown(struct mtd_info *mtd) +{ + nand_get_device(mtd, FL_SHUTDOWN); +} + /* Set default functions */ static void nand_set_defaults(struct nand_chip *chip, int busw) { @@ -4028,22 +4036,24 @@ int nand_scan_tail(struct mtd_info *mtd) ecc->read_oob = nand_read_oob_std; ecc->write_oob = nand_write_oob_std; /* - * Board driver should supply ecc.size and ecc.bytes values to - * select how many bits are correctable; see nand_bch_init() - * for details. Otherwise, default to 4 bits for large page - * devices. + * Board driver should supply ecc.size and ecc.strength values + * to select how many bits are correctable. Otherwise, default + * to 4 bits for large page devices. */ if (!ecc->size && (mtd->oobsize >= 64)) { ecc->size = 512; - ecc->bytes = DIV_ROUND_UP(13 * ecc->strength, 8); + ecc->strength = 4; } + + /* See nand_bch_init() for details. */ + ecc->bytes = DIV_ROUND_UP( + ecc->strength * fls(8 * ecc->size), 8); ecc->priv = nand_bch_init(mtd, ecc->size, ecc->bytes, &ecc->layout); if (!ecc->priv) { pr_warn("BCH ECC initialization failed!\n"); BUG(); } - ecc->strength = ecc->bytes * 8 / fls(8 * ecc->size); break; case NAND_ECC_NONE: @@ -4146,6 +4156,7 @@ int nand_scan_tail(struct mtd_info *mtd) mtd->_unlock = NULL; mtd->_suspend = nand_suspend; mtd->_resume = nand_resume; + mtd->_reboot = nand_shutdown; mtd->_block_isreserved = nand_block_isreserved; mtd->_block_isbad = nand_block_isbad; mtd->_block_markbad = nand_block_markbad; @@ -4161,7 +4172,7 @@ int nand_scan_tail(struct mtd_info *mtd) * properly set. */ if (!mtd->bitflip_threshold) - mtd->bitflip_threshold = mtd->ecc_strength; + mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4); /* Check, if we should skip the bad block table scan */ if (chip->options & NAND_SKIP_BBTSCAN) diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index ab5bbf567439..f2324271b94e 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -245,7 +245,6 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should " #define STATE_DATAOUT 0x00001000 /* waiting for page data output */ #define STATE_DATAOUT_ID 0x00002000 /* waiting for ID bytes output */ #define STATE_DATAOUT_STATUS 0x00003000 /* waiting for status output */ -#define STATE_DATAOUT_STATUS_M 0x00004000 /* waiting for multi-plane status output */ #define STATE_DATAOUT_MASK 0x00007000 /* data output states mask */ /* Previous operation is done, ready to accept new requests */ @@ -269,7 +268,6 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should " #define OPT_ANY 0xFFFFFFFF /* any chip supports this operation */ #define OPT_PAGE512 0x00000002 /* 512-byte page chips */ #define OPT_PAGE2048 0x00000008 /* 2048-byte page chips */ -#define OPT_SMARTMEDIA 0x00000010 /* SmartMedia technology chips */ #define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */ #define OPT_PAGE4096 0x00000080 /* 4096-byte page chips */ #define OPT_LARGEPAGE (OPT_PAGE2048 | OPT_PAGE4096) /* 2048 & 4096-byte page chips */ @@ -1096,8 +1094,6 @@ static char *get_state_name(uint32_t state) return "STATE_DATAOUT_ID"; case STATE_DATAOUT_STATUS: return "STATE_DATAOUT_STATUS"; - case STATE_DATAOUT_STATUS_M: - return "STATE_DATAOUT_STATUS_M"; case STATE_READY: return "STATE_READY"; case STATE_UNKNOWN: @@ -1865,7 +1861,6 @@ static void switch_state(struct nandsim *ns) break; case STATE_DATAOUT_STATUS: - case STATE_DATAOUT_STATUS_M: ns->regs.count = ns->regs.num = 0; break; @@ -2005,7 +2000,6 @@ static void ns_nand_write_byte(struct mtd_info *mtd, u_char byte) } if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS - || NS_STATE(ns->state) == STATE_DATAOUT_STATUS_M || NS_STATE(ns->state) == STATE_DATAOUT) { int row = ns->regs.row; @@ -2343,6 +2337,7 @@ static int __init ns_init_module(void) } chip->ecc.mode = NAND_ECC_SOFT_BCH; chip->ecc.size = 512; + chip->ecc.strength = bch; chip->ecc.bytes = eccbytes; NS_INFO("using %u-bit/%u bytes BCH ECC\n", bch, chip->ecc.size); } diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 63f858e6bf39..60fa89939c24 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1048,10 +1048,9 @@ static int omap_dev_ready(struct mtd_info *mtd) * @mtd: MTD device structure * @mode: Read/Write mode * - * When using BCH, sector size is hardcoded to 512 bytes. - * Using wrapping mode 6 both for reading and writing if ELM module not uses - * for error correction. - * On writing, + * When using BCH with SW correction (i.e. no ELM), sector size is set + * to 512 bytes and we use BCH_WRAPMODE_6 wrapping mode + * for both reading and writing with: * eccsize0 = 0 (no additional protected byte in spare area) * eccsize1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area) */ @@ -1071,15 +1070,9 @@ static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode) case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: bch_type = 0; nsectors = 1; - if (mode == NAND_ECC_READ) { - wr_mode = BCH_WRAPMODE_6; - ecc_size0 = BCH_ECC_SIZE0; - ecc_size1 = BCH_ECC_SIZE1; - } else { - wr_mode = BCH_WRAPMODE_6; - ecc_size0 = BCH_ECC_SIZE0; - ecc_size1 = BCH_ECC_SIZE1; - } + wr_mode = BCH_WRAPMODE_6; + ecc_size0 = BCH_ECC_SIZE0; + ecc_size1 = BCH_ECC_SIZE1; break; case OMAP_ECC_BCH4_CODE_HW: bch_type = 0; @@ -1097,15 +1090,9 @@ static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode) case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: bch_type = 1; nsectors = 1; - if (mode == NAND_ECC_READ) { - wr_mode = BCH_WRAPMODE_6; - ecc_size0 = BCH_ECC_SIZE0; - ecc_size1 = BCH_ECC_SIZE1; - } else { - wr_mode = BCH_WRAPMODE_6; - ecc_size0 = BCH_ECC_SIZE0; - ecc_size1 = BCH_ECC_SIZE1; - } + wr_mode = BCH_WRAPMODE_6; + ecc_size0 = BCH_ECC_SIZE0; + ecc_size1 = BCH_ECC_SIZE1; break; case OMAP_ECC_BCH8_CODE_HW: bch_type = 1; diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index ccaa8e283388..6f93b2990d25 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -1110,8 +1110,6 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc, switch (ecc->mode) { case NAND_ECC_SOFT_BCH: - ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * ecc->size), - 8); break; case NAND_ECC_HW: ret = sunxi_nand_hw_ecc_ctrl_init(mtd, ecc, np); diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c index 51b9d6af307f..a5dfbfbebfca 100644 --- a/drivers/mtd/nftlmount.c +++ b/drivers/mtd/nftlmount.c @@ -89,9 +89,10 @@ static int find_boot_record(struct NFTLrecord *nftl) } /* To be safer with BIOS, also use erase mark as discriminant */ - if ((ret = nftl_read_oob(mtd, block * nftl->EraseSize + + ret = nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, &retlen, - (char *)&h1) < 0)) { + (char *)&h1); + if (ret < 0) { printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n", block * nftl->EraseSize, nftl->mbd.mtd->index, ret); continue; @@ -109,8 +110,9 @@ static int find_boot_record(struct NFTLrecord *nftl) } /* Finally reread to check ECC */ - if ((ret = mtd->read(mtd, block * nftl->EraseSize, SECTORSIZE, - &retlen, buf) < 0)) { + ret = mtd->read(mtd, block * nftl->EraseSize, SECTORSIZE, + &retlen, buf); + if (ret < 0) { printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n", block * nftl->EraseSize, nftl->mbd.mtd->index, ret); continue; @@ -228,9 +230,11 @@ device is already correct. The new DiskOnChip driver already scanned the bad block table. Just query it. if ((i & (SECTORSIZE - 1)) == 0) { /* read one sector for every SECTORSIZE of blocks */ - if ((ret = mtd->read(nftl->mbd.mtd, block * nftl->EraseSize + - i + SECTORSIZE, SECTORSIZE, &retlen, - buf)) < 0) { + ret = mtd->read(nftl->mbd.mtd, + block * nftl->EraseSize + i + + SECTORSIZE, SECTORSIZE, + &retlen, buf); + if (ret < 0) { printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n", ret); kfree(nftl->ReplUnitTable); diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 39763b94f67d..1c7308c2c77d 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -57,7 +57,9 @@ #define QUADSPI_BUF3CR 0x1c #define QUADSPI_BUF3CR_ALLMST_SHIFT 31 -#define QUADSPI_BUF3CR_ALLMST (1 << QUADSPI_BUF3CR_ALLMST_SHIFT) +#define QUADSPI_BUF3CR_ALLMST_MASK (1 << QUADSPI_BUF3CR_ALLMST_SHIFT) +#define QUADSPI_BUF3CR_ADATSZ_SHIFT 8 +#define QUADSPI_BUF3CR_ADATSZ_MASK (0xFF << QUADSPI_BUF3CR_ADATSZ_SHIFT) #define QUADSPI_BFGENCR 0x20 #define QUADSPI_BFGENCR_PAR_EN_SHIFT 16 @@ -198,18 +200,21 @@ struct fsl_qspi_devtype_data { enum fsl_qspi_devtype devtype; int rxfifo; int txfifo; + int ahb_buf_size; }; static struct fsl_qspi_devtype_data vybrid_data = { .devtype = FSL_QUADSPI_VYBRID, .rxfifo = 128, - .txfifo = 64 + .txfifo = 64, + .ahb_buf_size = 1024 }; static struct fsl_qspi_devtype_data imx6sx_data = { .devtype = FSL_QUADSPI_IMX6SX, .rxfifo = 128, - .txfifo = 512 + .txfifo = 512, + .ahb_buf_size = 1024 }; #define FSL_QSPI_MAX_CHIP 4 @@ -227,6 +232,7 @@ struct fsl_qspi { u32 nor_num; u32 clk_rate; unsigned int chip_base_addr; /* We may support two chips. */ + bool has_second_chip; }; static inline int is_vybrid_qspi(struct fsl_qspi *q) @@ -583,7 +589,12 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q) writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR); writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF1CR); writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF2CR); - writel(QUADSPI_BUF3CR_ALLMST, base + QUADSPI_BUF3CR); + /* + * Set ADATSZ with the maximum AHB buffer size to improve the + * read performance. + */ + writel(QUADSPI_BUF3CR_ALLMST_MASK | ((q->devtype_data->ahb_buf_size / 8) + << QUADSPI_BUF3CR_ADATSZ_SHIFT), base + QUADSPI_BUF3CR); /* We only use the buffer3 */ writel(0, base + QUADSPI_BUF0IND); @@ -783,7 +794,6 @@ static int fsl_qspi_probe(struct platform_device *pdev) struct spi_nor *nor; struct mtd_info *mtd; int ret, i = 0; - bool has_second_chip = false; const struct of_device_id *of_id = of_match_device(fsl_qspi_dt_ids, &pdev->dev); @@ -798,37 +808,30 @@ static int fsl_qspi_probe(struct platform_device *pdev) /* find the resources */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI"); q->iobase = devm_ioremap_resource(dev, res); - if (IS_ERR(q->iobase)) { - ret = PTR_ERR(q->iobase); - goto map_failed; - } + if (IS_ERR(q->iobase)) + return PTR_ERR(q->iobase); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI-memory"); q->ahb_base = devm_ioremap_resource(dev, res); - if (IS_ERR(q->ahb_base)) { - ret = PTR_ERR(q->ahb_base); - goto map_failed; - } + if (IS_ERR(q->ahb_base)) + return PTR_ERR(q->ahb_base); + q->memmap_phy = res->start; /* find the clocks */ q->clk_en = devm_clk_get(dev, "qspi_en"); - if (IS_ERR(q->clk_en)) { - ret = PTR_ERR(q->clk_en); - goto map_failed; - } + if (IS_ERR(q->clk_en)) + return PTR_ERR(q->clk_en); q->clk = devm_clk_get(dev, "qspi"); - if (IS_ERR(q->clk)) { - ret = PTR_ERR(q->clk); - goto map_failed; - } + if (IS_ERR(q->clk)) + return PTR_ERR(q->clk); ret = clk_prepare_enable(q->clk_en); if (ret) { dev_err(dev, "can not enable the qspi_en clock\n"); - goto map_failed; + return ret; } ret = clk_prepare_enable(q->clk); @@ -860,14 +863,14 @@ static int fsl_qspi_probe(struct platform_device *pdev) goto irq_failed; if (of_get_property(np, "fsl,qspi-has-second-chip", NULL)) - has_second_chip = true; + q->has_second_chip = true; /* iterate the subnodes. */ for_each_available_child_of_node(dev->of_node, np) { char modalias[40]; /* skip the holes */ - if (!has_second_chip) + if (!q->has_second_chip) i *= 2; nor = &q->nor[i]; @@ -890,24 +893,24 @@ static int fsl_qspi_probe(struct platform_device *pdev) ret = of_modalias_node(np, modalias, sizeof(modalias)); if (ret < 0) - goto map_failed; + goto irq_failed; ret = of_property_read_u32(np, "spi-max-frequency", &q->clk_rate); if (ret < 0) - goto map_failed; + goto irq_failed; /* set the chip address for READID */ fsl_qspi_set_base_addr(q, nor); ret = spi_nor_scan(nor, modalias, SPI_NOR_QUAD); if (ret) - goto map_failed; + goto irq_failed; ppdata.of_node = np; ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); if (ret) - goto map_failed; + goto irq_failed; /* Set the correct NOR size now. */ if (q->nor_size == 0) { @@ -939,19 +942,19 @@ static int fsl_qspi_probe(struct platform_device *pdev) clk_disable(q->clk); clk_disable(q->clk_en); - dev_info(dev, "QuadSPI SPI NOR flash driver\n"); return 0; last_init_failed: - for (i = 0; i < q->nor_num; i++) + for (i = 0; i < q->nor_num; i++) { + /* skip the holes */ + if (!q->has_second_chip) + i *= 2; mtd_device_unregister(&q->mtd[i]); - + } irq_failed: clk_disable_unprepare(q->clk); clk_failed: clk_disable_unprepare(q->clk_en); -map_failed: - dev_err(dev, "Freescale QuadSPI probe failed\n"); return ret; } @@ -960,8 +963,12 @@ static int fsl_qspi_remove(struct platform_device *pdev) struct fsl_qspi *q = platform_get_drvdata(pdev); int i; - for (i = 0; i < q->nor_num; i++) + for (i = 0; i < q->nor_num; i++) { + /* skip the holes */ + if (!q->has_second_chip) + i *= 2; mtd_device_unregister(&q->mtd[i]); + } /* disable the hardware */ writel(QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR); @@ -972,6 +979,22 @@ static int fsl_qspi_remove(struct platform_device *pdev) return 0; } +static int fsl_qspi_suspend(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +static int fsl_qspi_resume(struct platform_device *pdev) +{ + struct fsl_qspi *q = platform_get_drvdata(pdev); + + fsl_qspi_nor_setup(q); + fsl_qspi_set_map_addr(q); + fsl_qspi_nor_setup_last(q); + + return 0; +} + static struct platform_driver fsl_qspi_driver = { .driver = { .name = "fsl-quadspi", @@ -980,6 +1003,8 @@ static struct platform_driver fsl_qspi_driver = { }, .probe = fsl_qspi_probe, .remove = fsl_qspi_remove, + .suspend = fsl_qspi_suspend, + .resume = fsl_qspi_resume, }; module_platform_driver(fsl_qspi_driver); diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 0f8ec3c2d015..b6a5a0c269e1 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -538,6 +538,7 @@ static const struct spi_device_id spi_nor_ids[] = { /* GigaDevice */ { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) }, { "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) }, + { "gd25q128", INFO(0xc84018, 0, 64 * 1024, 256, SECT_4K) }, /* Intel/Numonyx -- xxxs33b */ { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, @@ -560,14 +561,14 @@ static const struct spi_device_id spi_nor_ids[] = { { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) }, /* Micron */ - { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, 0) }, - { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) }, - { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0) }, - { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, - { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, - { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) }, - { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, USE_FSR) }, - { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, USE_FSR) }, + { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) }, + { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, SPI_NOR_QUAD_READ) }, + { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, SPI_NOR_QUAD_READ) }, + { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, SPI_NOR_QUAD_READ) }, + { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ) }, + { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, + { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, + { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, /* PMC */ { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, @@ -891,6 +892,45 @@ static int spansion_quad_enable(struct spi_nor *nor) return 0; } +static int micron_quad_enable(struct spi_nor *nor) +{ + int ret; + u8 val; + + ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &val, 1); + if (ret < 0) { + dev_err(nor->dev, "error %d reading EVCR\n", ret); + return ret; + } + + write_enable(nor); + + /* set EVCR, enable quad I/O */ + nor->cmd_buf[0] = val & ~EVCR_QUAD_EN_MICRON; + ret = nor->write_reg(nor, SPINOR_OP_WD_EVCR, nor->cmd_buf, 1, 0); + if (ret < 0) { + dev_err(nor->dev, "error while writing EVCR register\n"); + return ret; + } + + ret = spi_nor_wait_till_ready(nor); + if (ret) + return ret; + + /* read EVCR and check it */ + ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &val, 1); + if (ret < 0) { + dev_err(nor->dev, "error %d reading EVCR\n", ret); + return ret; + } + if (val & EVCR_QUAD_EN_MICRON) { + dev_err(nor->dev, "Micron EVCR Quad bit not clear\n"); + return -EINVAL; + } + + return 0; +} + static int set_quad_mode(struct spi_nor *nor, struct flash_info *info) { int status; @@ -903,6 +943,13 @@ static int set_quad_mode(struct spi_nor *nor, struct flash_info *info) return -EINVAL; } return status; + case CFI_MFR_ST: + status = micron_quad_enable(nor); + if (status) { + dev_err(nor->dev, "Micron quad-read not enabled\n"); + return -EINVAL; + } + return status; default: status = spansion_quad_enable(nor); if (status) { diff --git a/drivers/net/arcnet/com20020-pci.c b/drivers/net/arcnet/com20020-pci.c index 945f532078e9..96edc1346124 100644 --- a/drivers/net/arcnet/com20020-pci.c +++ b/drivers/net/arcnet/com20020-pci.c @@ -214,8 +214,17 @@ static struct com20020_pci_card_info card_info_sohard = { .flags = ARC_CAN_10MBIT, }; -static struct com20020_pci_card_info card_info_eae = { - .name = "EAE PLX-PCI", +static struct com20020_pci_card_info card_info_eae_arc1 = { + .name = "EAE PLX-PCI ARC1", + .devcount = 1, + .chan_map_tbl = { + { 2, 0x00, 0x08 }, + }, + .flags = ARC_CAN_10MBIT, +}; + +static struct com20020_pci_card_info card_info_eae_ma1 = { + .name = "EAE PLX-PCI MA1", .devcount = 2, .chan_map_tbl = { { 2, 0x00, 0x08 }, @@ -359,9 +368,15 @@ static const struct pci_device_id com20020pci_id_table[] = { }, { 0x10B5, 0x9050, + 0x10B5, 0x3263, + 0, 0, + (kernel_ulong_t)&card_info_eae_arc1 + }, + { + 0x10B5, 0x9050, 0x10B5, 0x3292, 0, 0, - (kernel_ulong_t)&card_info_eae + (kernel_ulong_t)&card_info_eae_ma1 }, { 0x14BA, 0x6000, diff --git a/drivers/net/ethernet/3com/3c589_cs.c b/drivers/net/ethernet/3com/3c589_cs.c index f18647c23559..c5a320507556 100644 --- a/drivers/net/ethernet/3com/3c589_cs.c +++ b/drivers/net/ethernet/3com/3c589_cs.c @@ -518,11 +518,8 @@ static int el3_open(struct net_device *dev) netif_start_queue(dev); tc589_reset(dev); - init_timer(&lp->media); - lp->media.function = media_check; - lp->media.data = (unsigned long) dev; - lp->media.expires = jiffies + HZ; - add_timer(&lp->media); + setup_timer(&lp->media, media_check, (unsigned long)dev); + mod_timer(&lp->media, jiffies + HZ); dev_dbg(&link->dev, "%s: opened, status %4.4x.\n", dev->name, inw(dev->base_addr + EL3_STATUS)); diff --git a/drivers/net/ethernet/agere/et131x.c b/drivers/net/ethernet/agere/et131x.c index 384dc163851b..e0f3d197e7f2 100644 --- a/drivers/net/ethernet/agere/et131x.c +++ b/drivers/net/ethernet/agere/et131x.c @@ -3127,7 +3127,8 @@ static void et131x_error_timer_handler(unsigned long data) } /* This is a periodic timer, so reschedule */ - mod_timer(&adapter->error_timer, jiffies + TX_ERROR_PERIOD * HZ / 1000); + mod_timer(&adapter->error_timer, jiffies + + msecs_to_jiffies(TX_ERROR_PERIOD)); } static void et131x_adapter_memory_free(struct et131x_adapter *adapter) @@ -3647,7 +3648,8 @@ static int et131x_open(struct net_device *netdev) /* Start the timer to track NIC errors */ init_timer(&adapter->error_timer); - adapter->error_timer.expires = jiffies + TX_ERROR_PERIOD * HZ / 1000; + adapter->error_timer.expires = jiffies + + msecs_to_jiffies(TX_ERROR_PERIOD); adapter->error_timer.function = et131x_error_timer_handler; adapter->error_timer.data = (unsigned long)adapter; add_timer(&adapter->error_timer); diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index 44b15373d6b3..4de62b210c85 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -1030,12 +1030,14 @@ static const struct acpi_device_id xgene_enet_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, xgene_enet_acpi_match); #endif +#ifdef CONFIG_OF static struct of_device_id xgene_enet_of_match[] = { {.compatible = "apm,xgene-enet",}, {}, }; MODULE_DEVICE_TABLE(of, xgene_enet_of_match); +#endif static struct platform_driver xgene_enet_driver = { .driver = { diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c index d86d6baf9681..bd5916a60cb5 100644 --- a/drivers/net/ethernet/broadcom/b44.c +++ b/drivers/net/ethernet/broadcom/b44.c @@ -121,7 +121,7 @@ static struct pci_driver b44_pci_driver = { static const struct ssb_device_id b44_ssb_tbl[] = { SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_ETHERNET, SSB_ANY_REV), - SSB_DEVTABLE_END + {}, }; MODULE_DEVICE_TABLE(ssb, b44_ssb_tbl); diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index 3007d95fbb9f..676ffe093180 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -21,7 +21,7 @@ static const struct bcma_device_id bgmac_bcma_tbl[] = { BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_4706_MAC_GBIT, BCMA_ANY_REV, BCMA_ANY_CLASS), BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_MAC_GBIT, BCMA_ANY_REV, BCMA_ANY_CLASS), - BCMA_CORETABLE_END + {}, }; MODULE_DEVICE_TABLE(bcma, bgmac_bcma_tbl); @@ -1412,6 +1412,7 @@ static void bgmac_mii_unregister(struct bgmac *bgmac) /* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipattach */ static int bgmac_probe(struct bcma_device *core) { + struct bcma_chipinfo *ci = &core->bus->chipinfo; struct net_device *net_dev; struct bgmac *bgmac; struct ssb_sprom *sprom = &core->bus->sprom; @@ -1474,8 +1475,8 @@ static int bgmac_probe(struct bcma_device *core) bgmac_chip_reset(bgmac); /* For Northstar, we have to take all GMAC core out of reset */ - if (core->id.id == BCMA_CHIP_ID_BCM4707 || - core->id.id == BCMA_CHIP_ID_BCM53018) { + if (ci->id == BCMA_CHIP_ID_BCM4707 || + ci->id == BCMA_CHIP_ID_BCM53018) { struct bcma_device *ns_core; int ns_gmac; diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 615a6dbde047..23a019cee279 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -17855,8 +17855,10 @@ static int tg3_init_one(struct pci_dev *pdev, */ if ((tr32(HOSTCC_MODE) & HOSTCC_MODE_ENABLE) || (tr32(WDMAC_MODE) & WDMAC_MODE_ENABLE)) { + tg3_full_lock(tp, 0); tw32(MEMARB_MODE, MEMARB_MODE_ENABLE); tg3_halt(tp, RESET_KIND_SHUTDOWN, 1); + tg3_full_unlock(tp); } err = tg3_test_dma(tp); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index d221f6b28fcd..78854ceb0870 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -1950,12 +1950,9 @@ static void set_debugfs_file_size(struct dentry *de, loff_t size) static void add_debugfs_mem(struct adapter *adap, const char *name, unsigned int idx, unsigned int size_mb) { - struct dentry *de; - - de = debugfs_create_file(name, S_IRUSR, adap->debugfs_root, - (void *)adap + idx, &mem_debugfs_fops); - if (de && de->d_inode) - de->d_inode->i_size = size_mb << 20; + debugfs_create_file_size(name, S_IRUSR, adap->debugfs_root, + (void *)adap + idx, &mem_debugfs_fops, + size_mb << 20); } /* Add an array of Debug FS files. diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.h index b63cfee2d963..8f418ba868bd 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.h @@ -55,7 +55,7 @@ static const struct file_operations name##_debugfs_fops = { \ struct t4_debugfs_entry { const char *name; const struct file_operations *ops; - mode_t mode; + umode_t mode; unsigned char data; }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index d6651937d899..5394a8486558 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -291,6 +291,7 @@ static void copy_rw_fields(void *to, struct mlx5_caps *from) MLX5_SET(cmd_hca_cap, to, log_max_ra_req_dc, from->gen.log_max_ra_req_dc); MLX5_SET(cmd_hca_cap, to, log_max_ra_res_dc, from->gen.log_max_ra_res_dc); MLX5_SET(cmd_hca_cap, to, pkey_table_size, to_fw_pkey_sz(from->gen.pkey_table_size)); + MLX5_SET(cmd_hca_cap, to, log_uar_page_sz, PAGE_SHIFT - 12); v64 = from->gen.flags & MLX5_CAP_BITS_RW_MASK; *flags_off = cpu_to_be64(v64); } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index e56c1bb36141..fa4317611fd6 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -848,10 +848,17 @@ struct qlcnic_cardrsp_tx_ctx { #define QLCNIC_MAC_VLAN_ADD 3 #define QLCNIC_MAC_VLAN_DEL 4 +enum qlcnic_mac_type { + QLCNIC_UNICAST_MAC, + QLCNIC_MULTICAST_MAC, + QLCNIC_BROADCAST_MAC, +}; + struct qlcnic_mac_vlan_list { struct list_head list; uint8_t mac_addr[ETH_ALEN+2]; u16 vlan_id; + enum qlcnic_mac_type mac_type; }; /* MAC Learn */ @@ -1615,7 +1622,9 @@ void qlcnic_watchdog_task(struct work_struct *work); void qlcnic_post_rx_buffers(struct qlcnic_adapter *adapter, struct qlcnic_host_rds_ring *rds_ring, u8 ring_id); void qlcnic_set_multi(struct net_device *netdev); -int qlcnic_nic_add_mac(struct qlcnic_adapter *, const u8 *, u16); +void qlcnic_flush_mcast_mac(struct qlcnic_adapter *); +int qlcnic_nic_add_mac(struct qlcnic_adapter *, const u8 *, u16, + enum qlcnic_mac_type); int qlcnic_nic_del_mac(struct qlcnic_adapter *, const u8 *); void qlcnic_82xx_free_mac_list(struct qlcnic_adapter *adapter); int qlcnic_82xx_read_phys_port_id(struct qlcnic_adapter *); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c index 69b46c051cc0..3e0f705a4311 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c @@ -487,7 +487,8 @@ int qlcnic_nic_del_mac(struct qlcnic_adapter *adapter, const u8 *addr) return err; } -int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr, u16 vlan) +int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr, u16 vlan, + enum qlcnic_mac_type mac_type) { struct qlcnic_mac_vlan_list *cur; struct list_head *head; @@ -513,10 +514,29 @@ int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr, u16 vlan) } cur->vlan_id = vlan; + cur->mac_type = mac_type; + list_add_tail(&cur->list, &adapter->mac_list); return 0; } +void qlcnic_flush_mcast_mac(struct qlcnic_adapter *adapter) +{ + struct qlcnic_mac_vlan_list *cur; + struct list_head *head, *tmp; + + list_for_each_safe(head, tmp, &adapter->mac_list) { + cur = list_entry(head, struct qlcnic_mac_vlan_list, list); + if (cur->mac_type != QLCNIC_MULTICAST_MAC) + continue; + + qlcnic_sre_macaddr_change(adapter, cur->mac_addr, + cur->vlan_id, QLCNIC_MAC_DEL); + list_del(&cur->list); + kfree(cur); + } +} + static void __qlcnic_set_multi(struct net_device *netdev, u16 vlan) { struct qlcnic_adapter *adapter = netdev_priv(netdev); @@ -530,8 +550,9 @@ static void __qlcnic_set_multi(struct net_device *netdev, u16 vlan) if (!test_bit(__QLCNIC_FW_ATTACHED, &adapter->state)) return; - qlcnic_nic_add_mac(adapter, adapter->mac_addr, vlan); - qlcnic_nic_add_mac(adapter, bcast_addr, vlan); + qlcnic_nic_add_mac(adapter, adapter->mac_addr, vlan, + QLCNIC_UNICAST_MAC); + qlcnic_nic_add_mac(adapter, bcast_addr, vlan, QLCNIC_BROADCAST_MAC); if (netdev->flags & IFF_PROMISC) { if (!(adapter->flags & QLCNIC_PROMISC_DISABLED)) @@ -540,8 +561,10 @@ static void __qlcnic_set_multi(struct net_device *netdev, u16 vlan) (netdev_mc_count(netdev) > ahw->max_mc_count)) { mode = VPORT_MISS_MODE_ACCEPT_MULTI; } else if (!netdev_mc_empty(netdev)) { + qlcnic_flush_mcast_mac(adapter); netdev_for_each_mc_addr(ha, netdev) - qlcnic_nic_add_mac(adapter, ha->addr, vlan); + qlcnic_nic_add_mac(adapter, ha->addr, vlan, + QLCNIC_MULTICAST_MAC); } /* configure unicast MAC address, if there is not sufficient space @@ -551,7 +574,8 @@ static void __qlcnic_set_multi(struct net_device *netdev, u16 vlan) mode = VPORT_MISS_MODE_ACCEPT_ALL; } else if (!netdev_uc_empty(netdev)) { netdev_for_each_uc_addr(ha, netdev) - qlcnic_nic_add_mac(adapter, ha->addr, vlan); + qlcnic_nic_add_mac(adapter, ha->addr, vlan, + QLCNIC_UNICAST_MAC); } if (mode == VPORT_MISS_MODE_ACCEPT_ALL && diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c index 1659c804f1d5..e6312465fe45 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c @@ -1489,7 +1489,8 @@ out: return ret; } -static void qlcnic_vf_add_mc_list(struct net_device *netdev, const u8 *mac) +static void qlcnic_vf_add_mc_list(struct net_device *netdev, const u8 *mac, + enum qlcnic_mac_type mac_type) { struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_sriov *sriov = adapter->ahw->sriov; @@ -1500,17 +1501,18 @@ static void qlcnic_vf_add_mc_list(struct net_device *netdev, const u8 *mac) vf = &adapter->ahw->sriov->vf_info[0]; if (!qlcnic_sriov_check_any_vlan(vf)) { - qlcnic_nic_add_mac(adapter, mac, 0); + qlcnic_nic_add_mac(adapter, mac, 0, mac_type); } else { spin_lock(&vf->vlan_list_lock); for (i = 0; i < sriov->num_allowed_vlans; i++) { vlan_id = vf->sriov_vlans[i]; if (vlan_id) - qlcnic_nic_add_mac(adapter, mac, vlan_id); + qlcnic_nic_add_mac(adapter, mac, vlan_id, + mac_type); } spin_unlock(&vf->vlan_list_lock); if (qlcnic_84xx_check(adapter)) - qlcnic_nic_add_mac(adapter, mac, 0); + qlcnic_nic_add_mac(adapter, mac, 0, mac_type); } } @@ -1549,10 +1551,12 @@ void qlcnic_sriov_vf_set_multi(struct net_device *netdev) (netdev_mc_count(netdev) > ahw->max_mc_count)) { mode = VPORT_MISS_MODE_ACCEPT_MULTI; } else { - qlcnic_vf_add_mc_list(netdev, bcast_addr); + qlcnic_vf_add_mc_list(netdev, bcast_addr, QLCNIC_BROADCAST_MAC); if (!netdev_mc_empty(netdev)) { + qlcnic_flush_mcast_mac(adapter); netdev_for_each_mc_addr(ha, netdev) - qlcnic_vf_add_mc_list(netdev, ha->addr); + qlcnic_vf_add_mc_list(netdev, ha->addr, + QLCNIC_MULTICAST_MAC); } } @@ -1563,7 +1567,8 @@ void qlcnic_sriov_vf_set_multi(struct net_device *netdev) mode = VPORT_MISS_MODE_ACCEPT_ALL; } else if (!netdev_uc_empty(netdev)) { netdev_for_each_uc_addr(ha, netdev) - qlcnic_vf_add_mc_list(netdev, ha->addr); + qlcnic_vf_add_mc_list(netdev, ha->addr, + QLCNIC_UNICAST_MAC); } if (adapter->pdev->is_virtfn) { diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c index 2b10b85d8a08..22e0cad1b4b5 100644 --- a/drivers/net/ethernet/sun/sunvnet.c +++ b/drivers/net/ethernet/sun/sunvnet.c @@ -1192,23 +1192,16 @@ static int vnet_handle_offloads(struct vnet_port *port, struct sk_buff *skb) skb_pull(skb, maclen); if (port->tso && gso_size < datalen) { + if (skb_unclone(skb, GFP_ATOMIC)) + goto out_dropped; + /* segment to TSO size */ skb_shinfo(skb)->gso_size = datalen; skb_shinfo(skb)->gso_segs = gso_segs; - - segs = skb_gso_segment(skb, dev->features & ~NETIF_F_TSO); - - /* restore gso_size & gso_segs */ - skb_shinfo(skb)->gso_size = gso_size; - skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len - hlen, - gso_size); - } else - segs = skb_gso_segment(skb, dev->features & ~NETIF_F_TSO); - if (IS_ERR(segs)) { - dev->stats.tx_dropped++; - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; } + segs = skb_gso_segment(skb, dev->features & ~NETIF_F_TSO); + if (IS_ERR(segs)) + goto out_dropped; skb_push(skb, maclen); skb_reset_mac_header(skb); @@ -1246,6 +1239,10 @@ static int vnet_handle_offloads(struct vnet_port *port, struct sk_buff *skb) if (!(status & NETDEV_TX_MASK)) dev_kfree_skb_any(skb); return status; +out_dropped: + dev->stats.tx_dropped++; + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; } static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev) diff --git a/drivers/net/ipvlan/ipvlan.h b/drivers/net/ipvlan/ipvlan.h index 2729f64b3e7e..924ea98bd531 100644 --- a/drivers/net/ipvlan/ipvlan.h +++ b/drivers/net/ipvlan/ipvlan.h @@ -67,7 +67,7 @@ struct ipvl_dev { struct list_head addrs; int ipv4cnt; int ipv6cnt; - struct ipvl_pcpu_stats *pcpu_stats; + struct ipvl_pcpu_stats __percpu *pcpu_stats; DECLARE_BITMAP(mac_filters, IPVLAN_MAC_FILTER_SIZE); netdev_features_t sfeatures; u32 msg_enable; diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 3ad8ca76196d..1190fd8f0088 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -32,6 +32,7 @@ /* Operation Mode Strap Override */ #define MII_KSZPHY_OMSO 0x16 #define KSZPHY_OMSO_B_CAST_OFF BIT(9) +#define KSZPHY_OMSO_NAND_TREE_ON BIT(5) #define KSZPHY_OMSO_RMII_OVERRIDE BIT(1) #define KSZPHY_OMSO_MII_OVERRIDE BIT(0) @@ -76,6 +77,7 @@ struct kszphy_type { u32 led_mode_reg; u16 interrupt_level_mask; bool has_broadcast_disable; + bool has_nand_tree_disable; bool has_rmii_ref_clk_sel; }; @@ -89,6 +91,7 @@ struct kszphy_priv { static const struct kszphy_type ksz8021_type = { .led_mode_reg = MII_KSZPHY_CTRL_2, .has_broadcast_disable = true, + .has_nand_tree_disable = true, .has_rmii_ref_clk_sel = true, }; @@ -98,11 +101,13 @@ static const struct kszphy_type ksz8041_type = { static const struct kszphy_type ksz8051_type = { .led_mode_reg = MII_KSZPHY_CTRL_2, + .has_nand_tree_disable = true, }; static const struct kszphy_type ksz8081_type = { .led_mode_reg = MII_KSZPHY_CTRL_2, .has_broadcast_disable = true, + .has_nand_tree_disable = true, .has_rmii_ref_clk_sel = true, }; @@ -231,6 +236,26 @@ out: return ret; } +static int kszphy_nand_tree_disable(struct phy_device *phydev) +{ + int ret; + + ret = phy_read(phydev, MII_KSZPHY_OMSO); + if (ret < 0) + goto out; + + if (!(ret & KSZPHY_OMSO_NAND_TREE_ON)) + return 0; + + ret = phy_write(phydev, MII_KSZPHY_OMSO, + ret & ~KSZPHY_OMSO_NAND_TREE_ON); +out: + if (ret) + dev_err(&phydev->dev, "failed to disable NAND tree mode\n"); + + return ret; +} + static int kszphy_config_init(struct phy_device *phydev) { struct kszphy_priv *priv = phydev->priv; @@ -245,6 +270,9 @@ static int kszphy_config_init(struct phy_device *phydev) if (type->has_broadcast_disable) kszphy_broadcast_disable(phydev); + if (type->has_nand_tree_disable) + kszphy_nand_tree_disable(phydev); + if (priv->rmii_ref_clk_sel) { ret = kszphy_rmii_clk_sel(phydev, priv->rmii_ref_clk_sel_val); if (ret) { diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 6b8efcabb816..9cdfb3fe9c15 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -914,7 +914,7 @@ static void packetizeRx(struct hso_net *odev, unsigned char *ip_pkt, /* We got no receive buffer. */ D1("could not allocate memory"); odev->rx_parse_state = WAIT_SYNC; - return; + continue; } /* Copy what we got so far. make room for iphdr diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 5980ac6c48dd..438fc6bcaef1 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -40,6 +40,7 @@ #define PLA_RXFIFO_CTRL0 0xc0a0 #define PLA_RXFIFO_CTRL1 0xc0a4 #define PLA_RXFIFO_CTRL2 0xc0a8 +#define PLA_DMY_REG0 0xc0b0 #define PLA_FMC 0xc0b4 #define PLA_CFG_WOL 0xc0b6 #define PLA_TEREDO_CFG 0xc0bc @@ -90,8 +91,14 @@ #define PLA_BP_7 0xfc36 #define PLA_BP_EN 0xfc38 +#define USB_USB2PHY 0xb41e +#define USB_SSPHYLINK2 0xb428 #define USB_U2P3_CTRL 0xb460 +#define USB_CSR_DUMMY1 0xb464 +#define USB_CSR_DUMMY2 0xb466 #define USB_DEV_STAT 0xb808 +#define USB_CONNECT_TIMER 0xcbf8 +#define USB_BURST_SIZE 0xcfc0 #define USB_USB_CTRL 0xd406 #define USB_PHY_CTRL 0xd408 #define USB_TX_AGG 0xd40a @@ -170,6 +177,9 @@ #define TXFIFO_THR_NORMAL 0x00400008 #define TXFIFO_THR_NORMAL2 0x01000008 +/* PLA_DMY_REG0 */ +#define ECM_ALDPS 0x0002 + /* PLA_FMC */ #define FMC_FCR_MCU_EN 0x0001 @@ -289,6 +299,20 @@ /* PLA_BOOT_CTRL */ #define AUTOLOAD_DONE 0x0002 +/* USB_USB2PHY */ +#define USB2PHY_SUSPEND 0x0001 +#define USB2PHY_L1 0x0002 + +/* USB_SSPHYLINK2 */ +#define pwd_dn_scale_mask 0x3ffe +#define pwd_dn_scale(x) ((x) << 1) + +/* USB_CSR_DUMMY1 */ +#define DYNAMIC_BURST 0x0001 + +/* USB_CSR_DUMMY2 */ +#define EP4_FULL_FC 0x0001 + /* USB_DEV_STAT */ #define STAT_SPEED_MASK 0x0006 #define STAT_SPEED_HIGH 0x0000 @@ -334,9 +358,13 @@ #define TIMER11_EN 0x0001 /* USB_LPM_CTRL */ +/* bit 4 ~ 5: fifo empty boundary */ +#define FIFO_EMPTY_1FB 0x30 /* 0x1fb * 64 = 32448 bytes */ +/* bit 2 ~ 3: LMP timer */ #define LPM_TIMER_MASK 0x0c #define LPM_TIMER_500MS 0x04 /* 500 ms */ #define LPM_TIMER_500US 0x0c /* 500 us */ +#define ROK_EXIT_LPM 0x02 /* USB_AFE_CTRL2 */ #define SEN_VAL_MASK 0xf800 @@ -3230,6 +3258,32 @@ static void r8153_init(struct r8152 *tp) r8153_u2p3en(tp, false); + if (tp->version == RTL_VER_04) { + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_SSPHYLINK2); + ocp_data &= ~pwd_dn_scale_mask; + ocp_data |= pwd_dn_scale(96); + ocp_write_word(tp, MCU_TYPE_USB, USB_SSPHYLINK2, ocp_data); + + ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_USB2PHY); + ocp_data |= USB2PHY_L1 | USB2PHY_SUSPEND; + ocp_write_byte(tp, MCU_TYPE_USB, USB_USB2PHY, ocp_data); + } else if (tp->version == RTL_VER_05) { + ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_DMY_REG0); + ocp_data &= ~ECM_ALDPS; + ocp_write_byte(tp, MCU_TYPE_PLA, PLA_DMY_REG0, ocp_data); + + ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY1); + if (ocp_read_word(tp, MCU_TYPE_USB, USB_BURST_SIZE) == 0) + ocp_data &= ~DYNAMIC_BURST; + else + ocp_data |= DYNAMIC_BURST; + ocp_write_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY1, ocp_data); + } + + ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY2); + ocp_data |= EP4_FULL_FC; + ocp_write_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY2, ocp_data); + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_WDT11_CTRL); ocp_data &= ~TIMER11_EN; ocp_write_word(tp, MCU_TYPE_USB, USB_WDT11_CTRL, ocp_data); @@ -3238,8 +3292,7 @@ static void r8153_init(struct r8152 *tp) ocp_data &= ~LED_MODE_MASK; ocp_write_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE, ocp_data); - ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_LPM_CTRL); - ocp_data &= ~LPM_TIMER_MASK; + ocp_data = FIFO_EMPTY_1FB | ROK_EXIT_LPM; if (tp->version == RTL_VER_04 && tp->udev->speed != USB_SPEED_SUPER) ocp_data |= LPM_TIMER_500MS; else @@ -3251,6 +3304,8 @@ static void r8153_init(struct r8152 *tp) ocp_data |= SEN_VAL_NORMAL | SEL_RXIDLE; ocp_write_word(tp, MCU_TYPE_USB, USB_AFE_CTRL2, ocp_data); + ocp_write_word(tp, MCU_TYPE_USB, USB_CONNECT_TIMER, 0x0001); + r8153_power_cut_en(tp, false); r8153_u1u2en(tp, true); diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 0e57e862c399..1e0a775ea882 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -555,12 +555,13 @@ static int vxlan_fdb_append(struct vxlan_fdb *f, static struct vxlanhdr *vxlan_gro_remcsum(struct sk_buff *skb, unsigned int off, struct vxlanhdr *vh, size_t hdrlen, - u32 data) + u32 data, struct gro_remcsum *grc, + bool nopartial) { size_t start, offset, plen; if (skb->remcsum_offload) - return vh; + return NULL; if (!NAPI_GRO_CB(skb)->csum_valid) return NULL; @@ -579,7 +580,8 @@ static struct vxlanhdr *vxlan_gro_remcsum(struct sk_buff *skb, return NULL; } - skb_gro_remcsum_process(skb, (void *)vh + hdrlen, start, offset); + skb_gro_remcsum_process(skb, (void *)vh + hdrlen, + start, offset, grc, nopartial); skb->remcsum_offload = 1; @@ -597,6 +599,9 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, struct vxlan_sock *vs = container_of(uoff, struct vxlan_sock, udp_offloads); u32 flags; + struct gro_remcsum grc; + + skb_gro_remcsum_init(&grc); off_vx = skb_gro_offset(skb); hlen = off_vx + sizeof(*vh); @@ -614,7 +619,9 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, if ((flags & VXLAN_HF_RCO) && (vs->flags & VXLAN_F_REMCSUM_RX)) { vh = vxlan_gro_remcsum(skb, off_vx, vh, sizeof(struct vxlanhdr), - ntohl(vh->vx_vni)); + ntohl(vh->vx_vni), &grc, + !!(vs->flags & + VXLAN_F_REMCSUM_NOPARTIAL)); if (!vh) goto out; @@ -637,6 +644,7 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, pp = eth_gro_receive(head, skb); out: + skb_gro_remcsum_cleanup(skb, &grc); NAPI_GRO_CB(skb)->flush |= flush; return pp; @@ -1150,16 +1158,10 @@ static void vxlan_igmp_leave(struct work_struct *work) } static struct vxlanhdr *vxlan_remcsum(struct sk_buff *skb, struct vxlanhdr *vh, - size_t hdrlen, u32 data) + size_t hdrlen, u32 data, bool nopartial) { size_t start, offset, plen; - if (skb->remcsum_offload) { - /* Already processed in GRO path */ - skb->remcsum_offload = 0; - return vh; - } - start = (data & VXLAN_RCO_MASK) << VXLAN_RCO_SHIFT; offset = start + ((data & VXLAN_RCO_UDP) ? offsetof(struct udphdr, check) : @@ -1172,7 +1174,8 @@ static struct vxlanhdr *vxlan_remcsum(struct sk_buff *skb, struct vxlanhdr *vh, vh = (struct vxlanhdr *)(udp_hdr(skb) + 1); - skb_remcsum_process(skb, (void *)vh + hdrlen, start, offset); + skb_remcsum_process(skb, (void *)vh + hdrlen, start, offset, + nopartial); return vh; } @@ -1209,7 +1212,8 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb) goto drop; if ((flags & VXLAN_HF_RCO) && (vs->flags & VXLAN_F_REMCSUM_RX)) { - vxh = vxlan_remcsum(skb, vxh, sizeof(struct vxlanhdr), vni); + vxh = vxlan_remcsum(skb, vxh, sizeof(struct vxlanhdr), vni, + !!(vs->flags & VXLAN_F_REMCSUM_NOPARTIAL)); if (!vxh) goto drop; @@ -2438,6 +2442,7 @@ static const struct nla_policy vxlan_policy[IFLA_VXLAN_MAX + 1] = { [IFLA_VXLAN_REMCSUM_TX] = { .type = NLA_U8 }, [IFLA_VXLAN_REMCSUM_RX] = { .type = NLA_U8 }, [IFLA_VXLAN_GBP] = { .type = NLA_FLAG, }, + [IFLA_VXLAN_REMCSUM_NOPARTIAL] = { .type = NLA_FLAG }, }; static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[]) @@ -2761,6 +2766,9 @@ static int vxlan_newlink(struct net *src_net, struct net_device *dev, if (data[IFLA_VXLAN_GBP]) vxlan->flags |= VXLAN_F_GBP; + if (data[IFLA_VXLAN_REMCSUM_NOPARTIAL]) + vxlan->flags |= VXLAN_F_REMCSUM_NOPARTIAL; + if (vxlan_find_vni(src_net, vni, use_ipv6 ? AF_INET6 : AF_INET, vxlan->dst_port, vxlan->flags)) { pr_info("duplicate VNI %u\n", vni); @@ -2910,6 +2918,10 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put_flag(skb, IFLA_VXLAN_GBP)) goto nla_put_failure; + if (vxlan->flags & VXLAN_F_REMCSUM_NOPARTIAL && + nla_put_flag(skb, IFLA_VXLAN_REMCSUM_NOPARTIAL)) + goto nla_put_failure; + return 0; nla_put_failure: diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 2c9088633ec6..ccbdb05b28cd 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -127,7 +127,7 @@ static const struct bcma_device_id b43_bcma_tbl[] = { BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1E, BCMA_ANY_CLASS), BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x28, BCMA_ANY_CLASS), BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x2A, BCMA_ANY_CLASS), - BCMA_CORETABLE_END + {}, }; MODULE_DEVICE_TABLE(bcma, b43_bcma_tbl); #endif @@ -144,7 +144,7 @@ static const struct ssb_device_id b43_ssb_tbl[] = { SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 13), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 15), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 16), - SSB_DEVTABLE_END + {}, }; MODULE_DEVICE_TABLE(ssb, b43_ssb_tbl); #endif diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index 1aec2146a2bf..4e58c0069830 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -86,7 +86,7 @@ MODULE_PARM_DESC(fwpostfix, "Postfix for the firmware files to load."); static const struct ssb_device_id b43legacy_ssb_tbl[] = { SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 2), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 4), - SSB_DEVTABLE_END + {}, }; MODULE_DEVICE_TABLE(ssb, b43legacy_ssb_tbl); diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index f95b52442281..48135063347e 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -99,7 +99,7 @@ static struct bcma_device_id brcms_coreid_table[] = { BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 17, BCMA_ANY_CLASS), BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 23, BCMA_ANY_CLASS), BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 24, BCMA_ANY_CLASS), - BCMA_CORETABLE_END + {}, }; MODULE_DEVICE_TABLE(bcma, brcms_coreid_table); diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c index ec456f0d972e..a62170ea0481 100644 --- a/drivers/net/wireless/rtlwifi/pci.c +++ b/drivers/net/wireless/rtlwifi/pci.c @@ -822,11 +822,8 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw) /* get a new skb - if fail, old one will be reused */ new_skb = dev_alloc_skb(rtlpci->rxbuffersize); - if (unlikely(!new_skb)) { - pr_err("Allocation of new skb failed in %s\n", - __func__); + if (unlikely(!new_skb)) goto no_new; - } if (rtlpriv->use_new_trx_flow) { buffer_desc = &rtlpci->rx_ring[rxring_idx].buffer_desc diff --git a/drivers/scsi/csiostor/csio_init.c b/drivers/scsi/csiostor/csio_init.c index 9b9794d42ffe..d9631e15f7b5 100644 --- a/drivers/scsi/csiostor/csio_init.c +++ b/drivers/scsi/csiostor/csio_init.c @@ -113,12 +113,9 @@ static const struct file_operations csio_mem_debugfs_fops = { void csio_add_debugfs_mem(struct csio_hw *hw, const char *name, unsigned int idx, unsigned int size_mb) { - struct dentry *de; - - de = debugfs_create_file(name, S_IRUSR, hw->debugfs_root, - (void *)hw + idx, &csio_mem_debugfs_fops); - if (de && de->d_inode) - de->d_inode->i_size = size_mb << 20; + debugfs_create_file_size(name, S_IRUSR, hw->debugfs_root, + (void *)hw + idx, &csio_mem_debugfs_fops, + size_mb << 20); } static int csio_setup_debugfs(struct csio_hw *hw) diff --git a/drivers/spi/spi-bcm53xx.c b/drivers/spi/spi-bcm53xx.c index 17b34cbadc03..3fb91c81015a 100644 --- a/drivers/spi/spi-bcm53xx.c +++ b/drivers/spi/spi-bcm53xx.c @@ -216,7 +216,7 @@ static struct spi_board_info bcm53xx_info = { static const struct bcma_device_id bcm53xxspi_bcma_tbl[] = { BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_QSPI, BCMA_ANY_REV, BCMA_ANY_CLASS), - BCMA_CORETABLE_END + {}, }; MODULE_DEVICE_TABLE(bcma, bcm53xxspi_bcma_tbl); diff --git a/drivers/ssb/driver_gige.c b/drivers/ssb/driver_gige.c index 21f71a1581fa..e9734051e3c4 100644 --- a/drivers/ssb/driver_gige.c +++ b/drivers/ssb/driver_gige.c @@ -24,7 +24,7 @@ MODULE_LICENSE("GPL"); static const struct ssb_device_id ssb_gige_tbl[] = { SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_ETHERNET_GBIT, SSB_ANY_REV), - SSB_DEVTABLE_END + {}, }; /* MODULE_DEVICE_TABLE(ssb, ssb_gige_tbl); */ diff --git a/drivers/staging/lustre/lustre/llite/dcache.c b/drivers/staging/lustre/lustre/llite/dcache.c index 5bb9c85cec81..88614b71cf6d 100644 --- a/drivers/staging/lustre/lustre/llite/dcache.c +++ b/drivers/staging/lustre/lustre/llite/dcache.c @@ -263,14 +263,6 @@ void ll_invalidate_aliases(struct inode *inode) dentry, dentry, dentry->d_parent, dentry->d_inode, dentry->d_flags); - if (unlikely(dentry == dentry->d_sb->s_root)) { - CERROR("%s: called on root dentry=%p, fid="DFID"\n", - ll_get_fsname(dentry->d_sb, NULL, 0), - dentry, PFID(ll_inode2fid(inode))); - lustre_dump_dentry(dentry, 1); - dump_stack(); - } - d_lustre_invalidate(dentry, 0); } ll_unlock_dcache(inode); diff --git a/drivers/staging/lustre/lustre/llite/llite_internal.h b/drivers/staging/lustre/lustre/llite/llite_internal.h index 37306e0c7aad..d032c2b086cc 100644 --- a/drivers/staging/lustre/lustre/llite/llite_internal.h +++ b/drivers/staging/lustre/lustre/llite/llite_internal.h @@ -816,7 +816,6 @@ int ll_show_options(struct seq_file *seq, struct dentry *dentry); void ll_dirty_page_discard_warn(struct page *page, int ioret); int ll_prep_inode(struct inode **inode, struct ptlrpc_request *req, struct super_block *, struct lookup_intent *); -void lustre_dump_dentry(struct dentry *, int recur); int ll_obd_statfs(struct inode *inode, void *arg); int ll_get_max_mdsize(struct ll_sb_info *sbi, int *max_mdsize); int ll_get_default_mdsize(struct ll_sb_info *sbi, int *default_mdsize); diff --git a/drivers/staging/lustre/lustre/llite/llite_lib.c b/drivers/staging/lustre/lustre/llite/llite_lib.c index 45aaa1cc56bc..0c1b583a4ea1 100644 --- a/drivers/staging/lustre/lustre/llite/llite_lib.c +++ b/drivers/staging/lustre/lustre/llite/llite_lib.c @@ -665,48 +665,6 @@ int ll_get_default_cookiesize(struct ll_sb_info *sbi, int *lmmsize) return rc; } -static void ll_dump_inode(struct inode *inode) -{ - struct ll_d_hlist_node *tmp; - int dentry_count = 0; - - LASSERT(inode != NULL); - - ll_d_hlist_for_each(tmp, &inode->i_dentry) - dentry_count++; - - CERROR("inode %p dump: dev=%s ino=%lu mode=%o count=%u, %d dentries\n", - inode, ll_i2mdexp(inode)->exp_obd->obd_name, inode->i_ino, - inode->i_mode, atomic_read(&inode->i_count), dentry_count); -} - -void lustre_dump_dentry(struct dentry *dentry, int recur) -{ - struct list_head *tmp; - int subdirs = 0; - - LASSERT(dentry != NULL); - - list_for_each(tmp, &dentry->d_subdirs) - subdirs++; - - CERROR("dentry %p dump: name=%pd parent=%pd (%p), inode=%p, count=%u, flags=0x%x, fsdata=%p, %d subdirs\n", - dentry, dentry, dentry->d_parent, dentry->d_parent, - dentry->d_inode, d_count(dentry), - dentry->d_flags, dentry->d_fsdata, subdirs); - if (dentry->d_inode != NULL) - ll_dump_inode(dentry->d_inode); - - if (recur == 0) - return; - - list_for_each(tmp, &dentry->d_subdirs) { - struct dentry *d = list_entry(tmp, struct dentry, d_child); - - lustre_dump_dentry(d, recur - 1); - } -} - static void client_common_put_super(struct super_block *sb) { struct ll_sb_info *sbi = ll_s2sbi(sb); diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index c0410862c2a1..d79cb35dbf8a 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -265,14 +265,17 @@ static void usba_init_debugfs(struct usba_udc *udc) goto err_root; udc->debugfs_root = root; - regs = debugfs_create_file("regs", 0400, root, udc, ®s_dbg_fops); - if (!regs) - goto err_regs; - regs_resource = platform_get_resource(udc->pdev, IORESOURCE_MEM, CTRL_IOMEM_ID); - regs->d_inode->i_size = resource_size(regs_resource); - udc->debugfs_regs = regs; + + if (regs_resource) { + regs = debugfs_create_file_size("regs", 0400, root, udc, + ®s_dbg_fops, + resource_size(regs_resource)); + if (!regs) + goto err_regs; + udc->debugfs_regs = regs; + } usba_ep_init_debugfs(udc, to_usba_ep(udc->gadget.ep0)); diff --git a/drivers/usb/host/bcma-hcd.c b/drivers/usb/host/bcma-hcd.c index cd6d0afb6b8f..526cfab41d5f 100644 --- a/drivers/usb/host/bcma-hcd.c +++ b/drivers/usb/host/bcma-hcd.c @@ -306,7 +306,7 @@ static int bcma_hcd_resume(struct bcma_device *dev) static const struct bcma_device_id bcma_hcd_table[] = { BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_USB20_HOST, BCMA_ANY_REV, BCMA_ANY_CLASS), - BCMA_CORETABLE_END + {}, }; MODULE_DEVICE_TABLE(bcma, bcma_hcd_table); diff --git a/drivers/usb/host/ssb-hcd.c b/drivers/usb/host/ssb-hcd.c index 0196f766df73..ffc32f4b1b1b 100644 --- a/drivers/usb/host/ssb-hcd.c +++ b/drivers/usb/host/ssb-hcd.c @@ -251,7 +251,7 @@ static const struct ssb_device_id ssb_hcd_table[] = { SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB20_HOST, SSB_ANY_REV), - SSB_DEVTABLE_END + {}, }; MODULE_DEVICE_TABLE(ssb, ssb_hcd_table); diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 8dccca9013ed..afa06d28725d 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -528,9 +528,9 @@ static void handle_rx(struct vhost_net *net) .msg_controllen = 0, .msg_flags = MSG_DONTWAIT, }; - struct virtio_net_hdr_mrg_rxbuf hdr = { - .hdr.flags = 0, - .hdr.gso_type = VIRTIO_NET_HDR_GSO_NONE + struct virtio_net_hdr hdr = { + .flags = 0, + .gso_type = VIRTIO_NET_HDR_GSO_NONE }; size_t total_len = 0; int err, mergeable; @@ -539,6 +539,7 @@ static void handle_rx(struct vhost_net *net) size_t vhost_len, sock_len; struct socket *sock; struct iov_iter fixup; + __virtio16 num_buffers; mutex_lock(&vq->mutex); sock = vq->private_data; @@ -616,9 +617,9 @@ static void handle_rx(struct vhost_net *net) } /* TODO: Should check and handle checksum. */ - hdr.num_buffers = cpu_to_vhost16(vq, headcount); + num_buffers = cpu_to_vhost16(vq, headcount); if (likely(mergeable) && - copy_to_iter(&hdr.num_buffers, 2, &fixup) != 2) { + copy_to_iter(&num_buffers, 2, &fixup) != 2) { vq_err(vq, "Failed num_buffers write"); vhost_discard_vq_desc(vq, headcount); break; diff --git a/fs/Kconfig.binfmt b/fs/Kconfig.binfmt index c055d56ec63d..270c48148f79 100644 --- a/fs/Kconfig.binfmt +++ b/fs/Kconfig.binfmt @@ -149,13 +149,6 @@ config BINFMT_EM86 later load the module when you want to use a Linux/Intel binary. The module will be called binfmt_em86. If unsure, say Y. -config BINFMT_SOM - tristate "Kernel support for SOM binaries" - depends on PARISC && HPUX - help - SOM is a binary executable format inherited from HP/UX. Say - Y here to be able to load and execute SOM binaries directly. - config BINFMT_MISC tristate "Kernel support for MISC binaries" ---help--- diff --git a/fs/Makefile b/fs/Makefile index 0f4635f7c49c..a88ac4838c9e 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -38,7 +38,6 @@ obj-$(CONFIG_BINFMT_SCRIPT) += binfmt_script.o obj-$(CONFIG_BINFMT_ELF) += binfmt_elf.o obj-$(CONFIG_COMPAT_BINFMT_ELF) += compat_binfmt_elf.o obj-$(CONFIG_BINFMT_ELF_FDPIC) += binfmt_elf_fdpic.o -obj-$(CONFIG_BINFMT_SOM) += binfmt_som.o obj-$(CONFIG_BINFMT_FLAT) += binfmt_flat.o obj-$(CONFIG_FS_MBCACHE) += mbcache.o diff --git a/fs/affs/affs.h b/fs/affs/affs.h index ff44ff3ff015..c8764bd7497d 100644 --- a/fs/affs/affs.h +++ b/fs/affs/affs.h @@ -30,6 +30,8 @@ #define AFFS_AC_SIZE (AFFS_CACHE_SIZE/sizeof(struct affs_ext_key)/2) #define AFFS_AC_MASK (AFFS_AC_SIZE-1) +#define AFFSNAMEMAX 30U + struct affs_ext_key { u32 ext; /* idx of the extended block */ u32 key; /* block number */ diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c index c852f2fa1710..388da1ea815d 100644 --- a/fs/affs/amigaffs.c +++ b/fs/affs/amigaffs.c @@ -30,7 +30,7 @@ affs_insert_hash(struct inode *dir, struct buffer_head *bh) ino = bh->b_blocknr; offset = affs_hash_name(sb, AFFS_TAIL(sb, bh)->name + 1, AFFS_TAIL(sb, bh)->name[0]); - pr_debug("%s(dir=%u, ino=%d)\n", __func__, (u32)dir->i_ino, ino); + pr_debug("%s(dir=%lu, ino=%d)\n", __func__, dir->i_ino, ino); dir_bh = affs_bread(sb, dir->i_ino); if (!dir_bh) @@ -80,8 +80,8 @@ affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh) sb = dir->i_sb; rem_ino = rem_bh->b_blocknr; offset = affs_hash_name(sb, AFFS_TAIL(sb, rem_bh)->name+1, AFFS_TAIL(sb, rem_bh)->name[0]); - pr_debug("%s(dir=%d, ino=%d, hashval=%d)\n", - __func__, (u32)dir->i_ino, rem_ino, offset); + pr_debug("%s(dir=%lu, ino=%d, hashval=%d)\n", __func__, dir->i_ino, + rem_ino, offset); bh = affs_bread(sb, dir->i_ino); if (!bh) @@ -483,11 +483,10 @@ affs_check_name(const unsigned char *name, int len, bool notruncate) { int i; - if (len > 30) { + if (len > AFFSNAMEMAX) { if (notruncate) return -ENAMETOOLONG; - else - len = 30; + len = AFFSNAMEMAX; } for (i = 0; i < len; i++) { if (name[i] < ' ' || name[i] == ':' @@ -508,7 +507,7 @@ affs_check_name(const unsigned char *name, int len, bool notruncate) int affs_copy_name(unsigned char *bstr, struct dentry *dentry) { - int len = min(dentry->d_name.len, 30u); + u32 len = min(dentry->d_name.len, AFFSNAMEMAX); *bstr++ = len; memcpy(bstr, dentry->d_name.name, len); diff --git a/fs/affs/bitmap.c b/fs/affs/bitmap.c index c8de51185c23..675148950fed 100644 --- a/fs/affs/bitmap.c +++ b/fs/affs/bitmap.c @@ -99,7 +99,6 @@ err_bh_read: err_range: affs_error(sb, "affs_free_block","Block %u outside partition", block); - return; } /* diff --git a/fs/affs/dir.c b/fs/affs/dir.c index 59f07bec92a6..ac4f318aafba 100644 --- a/fs/affs/dir.c +++ b/fs/affs/dir.c @@ -54,8 +54,7 @@ affs_readdir(struct file *file, struct dir_context *ctx) u32 ino; int error = 0; - pr_debug("%s(ino=%lu,f_pos=%lx)\n", - __func__, inode->i_ino, (unsigned long)ctx->pos); + pr_debug("%s(ino=%lu,f_pos=%llx)\n", __func__, inode->i_ino, ctx->pos); if (ctx->pos < 2) { file->private_data = (void *)0; @@ -115,11 +114,11 @@ inside: break; } - namelen = min(AFFS_TAIL(sb, fh_bh)->name[0], (u8)30); + namelen = min(AFFS_TAIL(sb, fh_bh)->name[0], + (u8)AFFSNAMEMAX); name = AFFS_TAIL(sb, fh_bh)->name + 1; - pr_debug("readdir(): dir_emit(\"%.*s\", " - "ino=%u), hash=%d, f_pos=%x\n", - namelen, name, ino, hash_pos, (u32)ctx->pos); + pr_debug("readdir(): dir_emit(\"%.*s\", ino=%u), hash=%d, f_pos=%llx\n", + namelen, name, ino, hash_pos, ctx->pos); if (!dir_emit(ctx, name, namelen, ino, DT_UNKNOWN)) goto done; diff --git a/fs/affs/file.c b/fs/affs/file.c index 8faa6593ca6d..d2468bf95669 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -180,8 +180,7 @@ affs_get_extblock_slow(struct inode *inode, u32 ext) ext_key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension); if (ext < AFFS_I(inode)->i_extcnt) goto read_ext; - if (ext > AFFS_I(inode)->i_extcnt) - BUG(); + BUG_ON(ext > AFFS_I(inode)->i_extcnt); bh = affs_alloc_extblock(inode, bh, ext); if (IS_ERR(bh)) return bh; @@ -198,8 +197,7 @@ affs_get_extblock_slow(struct inode *inode, u32 ext) struct buffer_head *prev_bh; /* allocate a new extended block */ - if (ext > AFFS_I(inode)->i_extcnt) - BUG(); + BUG_ON(ext > AFFS_I(inode)->i_extcnt); /* get previous extended block */ prev_bh = affs_get_extblock(inode, ext - 1); @@ -299,8 +297,8 @@ affs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_resul struct buffer_head *ext_bh; u32 ext; - pr_debug("%s(%u, %lu)\n", - __func__, (u32)inode->i_ino, (unsigned long)block); + pr_debug("%s(%lu, %llu)\n", __func__, inode->i_ino, + (unsigned long long)block); BUG_ON(block > (sector_t)0x7fffffffUL); @@ -330,8 +328,9 @@ affs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_resul /* store new block */ if (bh_result->b_blocknr) - affs_warning(sb, "get_block", "block already set (%lx)", - (unsigned long)bh_result->b_blocknr); + affs_warning(sb, "get_block", + "block already set (%llx)", + (unsigned long long)bh_result->b_blocknr); AFFS_BLOCK(sb, ext_bh, block) = cpu_to_be32(blocknr); AFFS_HEAD(ext_bh)->block_count = cpu_to_be32(block + 1); affs_adjust_checksum(ext_bh, blocknr - bh_result->b_blocknr + 1); @@ -353,8 +352,8 @@ affs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_resul return 0; err_big: - affs_error(inode->i_sb, "get_block", "strange block request %d", - (int)block); + affs_error(inode->i_sb, "get_block", "strange block request %llu", + (unsigned long long)block); return -EIO; err_ext: // unlock cache @@ -399,6 +398,13 @@ affs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, size_t count = iov_iter_count(iter); ssize_t ret; + if (rw == WRITE) { + loff_t size = offset + count; + + if (AFFS_I(inode)->mmu_private < size) + return 0; + } + ret = blockdev_direct_IO(rw, iocb, inode, iter, offset, affs_get_block); if (ret < 0 && (rw & WRITE)) affs_write_failed(mapping, offset + count); @@ -503,7 +509,7 @@ affs_do_readpage_ofs(struct page *page, unsigned to) u32 bidx, boff, bsize; u32 tmp; - pr_debug("%s(%u, %ld, 0, %d)\n", __func__, (u32)inode->i_ino, + pr_debug("%s(%lu, %ld, 0, %d)\n", __func__, inode->i_ino, page->index, to); BUG_ON(to > PAGE_CACHE_SIZE); kmap(page); @@ -539,7 +545,7 @@ affs_extent_file_ofs(struct inode *inode, u32 newsize) u32 size, bsize; u32 tmp; - pr_debug("%s(%u, %d)\n", __func__, (u32)inode->i_ino, newsize); + pr_debug("%s(%lu, %d)\n", __func__, inode->i_ino, newsize); bsize = AFFS_SB(sb)->s_data_blksize; bh = NULL; size = AFFS_I(inode)->mmu_private; @@ -608,7 +614,7 @@ affs_readpage_ofs(struct file *file, struct page *page) u32 to; int err; - pr_debug("%s(%u, %ld)\n", __func__, (u32)inode->i_ino, page->index); + pr_debug("%s(%lu, %ld)\n", __func__, inode->i_ino, page->index); to = PAGE_CACHE_SIZE; if (((page->index + 1) << PAGE_CACHE_SHIFT) > inode->i_size) { to = inode->i_size & ~PAGE_CACHE_MASK; @@ -631,8 +637,8 @@ static int affs_write_begin_ofs(struct file *file, struct address_space *mapping pgoff_t index; int err = 0; - pr_debug("%s(%u, %llu, %llu)\n", __func__, (u32)inode->i_ino, - (unsigned long long)pos, (unsigned long long)pos + len); + pr_debug("%s(%lu, %llu, %llu)\n", __func__, inode->i_ino, pos, + pos + len); if (pos > AFFS_I(inode)->mmu_private) { /* XXX: this probably leaves a too-big i_size in case of * failure. Should really be updating i_size at write_end time @@ -681,9 +687,8 @@ static int affs_write_end_ofs(struct file *file, struct address_space *mapping, * due to write_begin. */ - pr_debug("%s(%u, %llu, %llu)\n", - __func__, (u32)inode->i_ino, (unsigned long long)pos, - (unsigned long long)pos + len); + pr_debug("%s(%lu, %llu, %llu)\n", __func__, inode->i_ino, pos, + pos + len); bsize = AFFS_SB(sb)->s_data_blksize; data = page_address(page); @@ -831,8 +836,8 @@ affs_truncate(struct inode *inode) struct buffer_head *ext_bh; int i; - pr_debug("truncate(inode=%d, oldsize=%u, newsize=%u)\n", - (u32)inode->i_ino, (u32)AFFS_I(inode)->mmu_private, (u32)inode->i_size); + pr_debug("truncate(inode=%lu, oldsize=%llu, newsize=%llu)\n", + inode->i_ino, AFFS_I(inode)->mmu_private, inode->i_size); last_blk = 0; ext = 0; @@ -863,7 +868,7 @@ affs_truncate(struct inode *inode) if (IS_ERR(ext_bh)) { affs_warning(sb, "truncate", "unexpected read error for ext block %u (%ld)", - (unsigned int)ext, PTR_ERR(ext_bh)); + ext, PTR_ERR(ext_bh)); return; } if (AFFS_I(inode)->i_lc) { @@ -911,7 +916,7 @@ affs_truncate(struct inode *inode) if (IS_ERR(bh)) { affs_warning(sb, "truncate", "unexpected read error for last block %u (%ld)", - (unsigned int)ext, PTR_ERR(bh)); + ext, PTR_ERR(bh)); return; } tmp = be32_to_cpu(AFFS_DATA_HEAD(bh)->next); diff --git a/fs/affs/inode.c b/fs/affs/inode.c index d0609a282e1d..6f34510449e8 100644 --- a/fs/affs/inode.c +++ b/fs/affs/inode.c @@ -13,8 +13,6 @@ #include <linux/gfp.h> #include "affs.h" -extern const struct inode_operations affs_symlink_inode_operations; - struct inode *affs_iget(struct super_block *sb, unsigned long ino) { struct affs_sb_info *sbi = AFFS_SB(sb); @@ -348,9 +346,8 @@ affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s3 u32 block = 0; int retval; - pr_debug("%s(dir=%u, inode=%u, \"%pd\", type=%d)\n", - __func__, (u32)dir->i_ino, - (u32)inode->i_ino, dentry, type); + pr_debug("%s(dir=%lu, inode=%lu, \"%pd\", type=%d)\n", __func__, + dir->i_ino, inode->i_ino, dentry, type); retval = -EIO; bh = affs_bread(sb, inode->i_ino); diff --git a/fs/affs/namei.c b/fs/affs/namei.c index bbc38530e924..ffb7bd82c2a5 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -64,15 +64,16 @@ __affs_hash_dentry(struct qstr *qstr, toupper_t toupper, bool notruncate) { const u8 *name = qstr->name; unsigned long hash; - int i; + int retval; + u32 len; - i = affs_check_name(qstr->name, qstr->len, notruncate); - if (i) - return i; + retval = affs_check_name(qstr->name, qstr->len, notruncate); + if (retval) + return retval; hash = init_name_hash(); - i = min(qstr->len, 30u); - for (; i > 0; name++, i--) + len = min(qstr->len, AFFSNAMEMAX); + for (; len > 0; name++, len--) hash = partial_name_hash(toupper(*name), hash); qstr->hash = end_name_hash(hash); @@ -114,10 +115,10 @@ static inline int __affs_compare_dentry(unsigned int len, * If the names are longer than the allowed 30 chars, * the excess is ignored, so their length may differ. */ - if (len >= 30) { - if (name->len < 30) + if (len >= AFFSNAMEMAX) { + if (name->len < AFFSNAMEMAX) return 1; - len = 30; + len = AFFSNAMEMAX; } else if (len != name->len) return 1; @@ -156,10 +157,10 @@ affs_match(struct dentry *dentry, const u8 *name2, toupper_t toupper) const u8 *name = dentry->d_name.name; int len = dentry->d_name.len; - if (len >= 30) { - if (*name2 < 30) + if (len >= AFFSNAMEMAX) { + if (*name2 < AFFSNAMEMAX) return 0; - len = 30; + len = AFFSNAMEMAX; } else if (len != *name2) return 0; @@ -173,9 +174,9 @@ int affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len) { toupper_t toupper = affs_get_toupper(sb); - int hash; + u32 hash; - hash = len = min(len, 30u); + hash = len = min(len, AFFSNAMEMAX); for (; len > 0; len--) hash = (hash * 13 + toupper(*name++)) & 0x7ff; @@ -248,9 +249,8 @@ affs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) int affs_unlink(struct inode *dir, struct dentry *dentry) { - pr_debug("%s(dir=%d, %lu \"%pd\")\n", - __func__, (u32)dir->i_ino, dentry->d_inode->i_ino, - dentry); + pr_debug("%s(dir=%lu, %lu \"%pd\")\n", __func__, dir->i_ino, + dentry->d_inode->i_ino, dentry); return affs_remove_header(dentry); } @@ -317,9 +317,8 @@ affs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) int affs_rmdir(struct inode *dir, struct dentry *dentry) { - pr_debug("%s(dir=%u, %lu \"%pd\")\n", - __func__, (u32)dir->i_ino, dentry->d_inode->i_ino, - dentry); + pr_debug("%s(dir=%lu, %lu \"%pd\")\n", __func__, dir->i_ino, + dentry->d_inode->i_ino, dentry); return affs_remove_header(dentry); } @@ -404,8 +403,7 @@ affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) { struct inode *inode = old_dentry->d_inode; - pr_debug("%s(%u, %u, \"%pd\")\n", - __func__, (u32)inode->i_ino, (u32)dir->i_ino, + pr_debug("%s(%lu, %lu, \"%pd\")\n", __func__, inode->i_ino, dir->i_ino, dentry); return affs_add_entry(dir, inode, dentry, ST_LINKFILE); @@ -419,9 +417,8 @@ affs_rename(struct inode *old_dir, struct dentry *old_dentry, struct buffer_head *bh = NULL; int retval; - pr_debug("%s(old=%u,\"%pd\" to new=%u,\"%pd\")\n", - __func__, (u32)old_dir->i_ino, old_dentry, - (u32)new_dir->i_ino, new_dentry); + pr_debug("%s(old=%lu,\"%pd\" to new=%lu,\"%pd\")\n", __func__, + old_dir->i_ino, old_dentry, new_dir->i_ino, new_dentry); retval = affs_check_name(new_dentry->d_name.name, new_dentry->d_name.len, diff --git a/fs/affs/super.c b/fs/affs/super.c index f754ab68a840..4cf0e9113fb6 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -432,39 +432,39 @@ got_root: sb->s_flags |= MS_RDONLY; } switch (chksum) { - case MUFS_FS: - case MUFS_INTLFFS: - case MUFS_DCFFS: - sbi->s_flags |= SF_MUFS; - /* fall thru */ - case FS_INTLFFS: - case FS_DCFFS: - sbi->s_flags |= SF_INTL; - break; - case MUFS_FFS: - sbi->s_flags |= SF_MUFS; - break; - case FS_FFS: - break; - case MUFS_OFS: - sbi->s_flags |= SF_MUFS; - /* fall thru */ - case FS_OFS: - sbi->s_flags |= SF_OFS; - sb->s_flags |= MS_NOEXEC; - break; - case MUFS_DCOFS: - case MUFS_INTLOFS: - sbi->s_flags |= SF_MUFS; - case FS_DCOFS: - case FS_INTLOFS: - sbi->s_flags |= SF_INTL | SF_OFS; - sb->s_flags |= MS_NOEXEC; - break; - default: - pr_err("Unknown filesystem on device %s: %08X\n", - sb->s_id, chksum); - return -EINVAL; + case MUFS_FS: + case MUFS_INTLFFS: + case MUFS_DCFFS: + sbi->s_flags |= SF_MUFS; + /* fall thru */ + case FS_INTLFFS: + case FS_DCFFS: + sbi->s_flags |= SF_INTL; + break; + case MUFS_FFS: + sbi->s_flags |= SF_MUFS; + break; + case FS_FFS: + break; + case MUFS_OFS: + sbi->s_flags |= SF_MUFS; + /* fall thru */ + case FS_OFS: + sbi->s_flags |= SF_OFS; + sb->s_flags |= MS_NOEXEC; + break; + case MUFS_DCOFS: + case MUFS_INTLOFS: + sbi->s_flags |= SF_MUFS; + case FS_DCOFS: + case FS_INTLOFS: + sbi->s_flags |= SF_INTL | SF_OFS; + sb->s_flags |= MS_NOEXEC; + break; + default: + pr_err("Unknown filesystem on device %s: %08X\n", + sb->s_id, chksum); + return -EINVAL; } if (mount_flags & SF_VERBOSE) { @@ -584,7 +584,7 @@ affs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_bavail = free; buf->f_fsid.val[0] = (u32)id; buf->f_fsid.val[1] = (u32)(id >> 32); - buf->f_namelen = 30; + buf->f_namelen = AFFSNAMEMAX; return 0; } @@ -602,6 +602,7 @@ static void affs_kill_sb(struct super_block *sb) affs_free_bitmap(sb); affs_brelse(sbi->s_root_bh); kfree(sbi->s_prefix); + mutex_destroy(&sbi->s_bmlock); kfree(sbi); } } diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c index edf47774b03d..e089f1985fca 100644 --- a/fs/befs/linuxvfs.c +++ b/fs/befs/linuxvfs.c @@ -274,9 +274,9 @@ more: static struct inode * befs_alloc_inode(struct super_block *sb) { - struct befs_inode_info *bi; - bi = (struct befs_inode_info *)kmem_cache_alloc(befs_inode_cachep, - GFP_KERNEL); + struct befs_inode_info *bi; + + bi = kmem_cache_alloc(befs_inode_cachep, GFP_KERNEL); if (!bi) return NULL; return &bi->vfs_inode; diff --git a/fs/binfmt_som.c b/fs/binfmt_som.c deleted file mode 100644 index 4e00ed68d4a6..000000000000 --- a/fs/binfmt_som.c +++ /dev/null @@ -1,299 +0,0 @@ -/* - * linux/fs/binfmt_som.c - * - * These are the functions used to load SOM format executables as used - * by HP-UX. - * - * Copyright 1999 Matthew Wilcox <willy@bofh.ai> - * based on binfmt_elf which is - * Copyright 1993, 1994: Eric Youngdale (ericy@cais.com). - */ - -#include <linux/module.h> - -#include <linux/fs.h> -#include <linux/stat.h> -#include <linux/sched.h> -#include <linux/mm.h> -#include <linux/mman.h> -#include <linux/errno.h> -#include <linux/signal.h> -#include <linux/binfmts.h> -#include <linux/som.h> -#include <linux/string.h> -#include <linux/file.h> -#include <linux/fcntl.h> -#include <linux/ptrace.h> -#include <linux/slab.h> -#include <linux/shm.h> -#include <linux/personality.h> -#include <linux/init.h> - -#include <asm/uaccess.h> -#include <asm/pgtable.h> - - -#include <linux/elf.h> - -static int load_som_binary(struct linux_binprm * bprm); -static int load_som_library(struct file *); - -/* - * If we don't support core dumping, then supply a NULL so we - * don't even try. - */ -#if 0 -static int som_core_dump(struct coredump_params *cprm); -#else -#define som_core_dump NULL -#endif - -#define SOM_PAGESTART(_v) ((_v) & ~(unsigned long)(SOM_PAGESIZE-1)) -#define SOM_PAGEOFFSET(_v) ((_v) & (SOM_PAGESIZE-1)) -#define SOM_PAGEALIGN(_v) (((_v) + SOM_PAGESIZE - 1) & ~(SOM_PAGESIZE - 1)) - -static struct linux_binfmt som_format = { - .module = THIS_MODULE, - .load_binary = load_som_binary, - .load_shlib = load_som_library, - .core_dump = som_core_dump, - .min_coredump = SOM_PAGESIZE -}; - -/* - * create_som_tables() parses the env- and arg-strings in new user - * memory and creates the pointer tables from them, and puts their - * addresses on the "stack", returning the new stack pointer value. - */ -static void create_som_tables(struct linux_binprm *bprm) -{ - char **argv, **envp; - int argc = bprm->argc; - int envc = bprm->envc; - unsigned long p; - unsigned long *sp; - - /* Word-align the stack pointer */ - sp = (unsigned long *)((bprm->p + 3) & ~3); - - envp = (char **) sp; - sp += envc + 1; - argv = (char **) sp; - sp += argc + 1; - - __put_user((unsigned long) envp,++sp); - __put_user((unsigned long) argv,++sp); - - __put_user(argc, ++sp); - - bprm->p = (unsigned long) sp; - - p = current->mm->arg_start; - while (argc-- > 0) { - __put_user((char *)p,argv++); - p += strlen_user((char *)p); - } - __put_user(NULL, argv); - current->mm->arg_end = current->mm->env_start = p; - while (envc-- > 0) { - __put_user((char *)p,envp++); - p += strlen_user((char *)p); - } - __put_user(NULL, envp); - current->mm->env_end = p; -} - -static int check_som_header(struct som_hdr *som_ex) -{ - int *buf = (int *)som_ex; - int i, ck; - - if (som_ex->system_id != SOM_SID_PARISC_1_0 && - som_ex->system_id != SOM_SID_PARISC_1_1 && - som_ex->system_id != SOM_SID_PARISC_2_0) - return -ENOEXEC; - - if (som_ex->a_magic != SOM_EXEC_NONSHARE && - som_ex->a_magic != SOM_EXEC_SHARE && - som_ex->a_magic != SOM_EXEC_DEMAND) - return -ENOEXEC; - - if (som_ex->version_id != SOM_ID_OLD && - som_ex->version_id != SOM_ID_NEW) - return -ENOEXEC; - - ck = 0; - for (i=0; i<32; i++) - ck ^= buf[i]; - if (ck != 0) - return -ENOEXEC; - - return 0; -} - -static int map_som_binary(struct file *file, - const struct som_exec_auxhdr *hpuxhdr) -{ - unsigned long code_start, code_size, data_start, data_size; - unsigned long bss_start, som_brk; - int retval; - int prot = PROT_READ | PROT_EXEC; - int flags = MAP_FIXED|MAP_PRIVATE|MAP_DENYWRITE|MAP_EXECUTABLE; - - mm_segment_t old_fs = get_fs(); - set_fs(get_ds()); - - code_start = SOM_PAGESTART(hpuxhdr->exec_tmem); - code_size = SOM_PAGEALIGN(hpuxhdr->exec_tsize); - current->mm->start_code = code_start; - current->mm->end_code = code_start + code_size; - retval = vm_mmap(file, code_start, code_size, prot, - flags, SOM_PAGESTART(hpuxhdr->exec_tfile)); - if (retval < 0 && retval > -1024) - goto out; - - data_start = SOM_PAGESTART(hpuxhdr->exec_dmem); - data_size = SOM_PAGEALIGN(hpuxhdr->exec_dsize); - current->mm->start_data = data_start; - current->mm->end_data = bss_start = data_start + data_size; - retval = vm_mmap(file, data_start, data_size, - prot | PROT_WRITE, flags, - SOM_PAGESTART(hpuxhdr->exec_dfile)); - if (retval < 0 && retval > -1024) - goto out; - - som_brk = bss_start + SOM_PAGEALIGN(hpuxhdr->exec_bsize); - current->mm->start_brk = current->mm->brk = som_brk; - retval = vm_mmap(NULL, bss_start, som_brk - bss_start, - prot | PROT_WRITE, MAP_FIXED | MAP_PRIVATE, 0); - if (retval > 0 || retval < -1024) - retval = 0; -out: - set_fs(old_fs); - return retval; -} - - -/* - * These are the functions used to load SOM executables and shared - * libraries. There is no binary dependent code anywhere else. - */ - -static int -load_som_binary(struct linux_binprm * bprm) -{ - int retval; - unsigned int size; - unsigned long som_entry; - struct som_hdr *som_ex; - struct som_exec_auxhdr *hpuxhdr; - struct pt_regs *regs = current_pt_regs(); - - /* Get the exec-header */ - som_ex = (struct som_hdr *) bprm->buf; - - retval = check_som_header(som_ex); - if (retval != 0) - goto out; - - /* Now read in the auxiliary header information */ - - retval = -ENOMEM; - size = som_ex->aux_header_size; - if (size > SOM_PAGESIZE) - goto out; - hpuxhdr = kmalloc(size, GFP_KERNEL); - if (!hpuxhdr) - goto out; - - retval = kernel_read(bprm->file, som_ex->aux_header_location, - (char *) hpuxhdr, size); - if (retval != size) { - if (retval >= 0) - retval = -EIO; - goto out_free; - } - - /* Flush all traces of the currently running executable */ - retval = flush_old_exec(bprm); - if (retval) - goto out_free; - - /* OK, This is the point of no return */ - current->personality = PER_HPUX; - setup_new_exec(bprm); - - /* Set the task size for HP-UX processes such that - * the gateway page is outside the address space. - * This can be fixed later, but for now, this is much - * easier. - */ - - current->thread.task_size = 0xc0000000; - - /* Set map base to allow enough room for hp-ux heap growth */ - - current->thread.map_base = 0x80000000; - - retval = map_som_binary(bprm->file, hpuxhdr); - if (retval < 0) - goto out_free; - - som_entry = hpuxhdr->exec_entry; - kfree(hpuxhdr); - - set_binfmt(&som_format); - install_exec_creds(bprm); - setup_arg_pages(bprm, STACK_TOP, EXSTACK_DEFAULT); - - create_som_tables(bprm); - - current->mm->start_stack = bprm->p; - -#if 0 - printk("(start_brk) %08lx\n" , (unsigned long) current->mm->start_brk); - printk("(end_code) %08lx\n" , (unsigned long) current->mm->end_code); - printk("(start_code) %08lx\n" , (unsigned long) current->mm->start_code); - printk("(end_data) %08lx\n" , (unsigned long) current->mm->end_data); - printk("(start_stack) %08lx\n" , (unsigned long) current->mm->start_stack); - printk("(brk) %08lx\n" , (unsigned long) current->mm->brk); -#endif - - map_hpux_gateway_page(current,current->mm); - - start_thread_som(regs, som_entry, bprm->p); - return 0; - - /* error cleanup */ -out_free: - kfree(hpuxhdr); -out: - return retval; -} - -static int load_som_library(struct file *f) -{ -/* No lib support in SOM yet. gizza chance.. */ - return -ENOEXEC; -} - /* Install the SOM loader. - * N.B. We *rely* on the table being the right size with the - * right number of free slots... - */ - -static int __init init_som_binfmt(void) -{ - register_binfmt(&som_format); - return 0; -} - -static void __exit exit_som_binfmt(void) -{ - /* Remove the SOM loader. */ - unregister_binfmt(&som_format); -} - -core_initcall(init_som_binfmt); -module_exit(exit_som_binfmt); - -MODULE_LICENSE("GPL"); diff --git a/fs/coda/dir.c b/fs/coda/dir.c index 86c893884eb9..281ee011bb6a 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -28,29 +28,6 @@ #include "coda_int.h" -/* dir inode-ops */ -static int coda_create(struct inode *dir, struct dentry *new, umode_t mode, bool excl); -static struct dentry *coda_lookup(struct inode *dir, struct dentry *target, unsigned int flags); -static int coda_link(struct dentry *old_dentry, struct inode *dir_inode, - struct dentry *entry); -static int coda_unlink(struct inode *dir_inode, struct dentry *entry); -static int coda_symlink(struct inode *dir_inode, struct dentry *entry, - const char *symname); -static int coda_mkdir(struct inode *dir_inode, struct dentry *entry, umode_t mode); -static int coda_rmdir(struct inode *dir_inode, struct dentry *entry); -static int coda_rename(struct inode *old_inode, struct dentry *old_dentry, - struct inode *new_inode, struct dentry *new_dentry); - -/* dir file-ops */ -static int coda_readdir(struct file *file, struct dir_context *ctx); - -/* dentry ops */ -static int coda_dentry_revalidate(struct dentry *de, unsigned int flags); -static int coda_dentry_delete(const struct dentry *); - -/* support routines */ -static int coda_venus_readdir(struct file *, struct dir_context *); - /* same as fs/bad_inode.c */ static int coda_return_EIO(void) { @@ -58,38 +35,6 @@ static int coda_return_EIO(void) } #define CODA_EIO_ERROR ((void *) (coda_return_EIO)) -const struct dentry_operations coda_dentry_operations = -{ - .d_revalidate = coda_dentry_revalidate, - .d_delete = coda_dentry_delete, -}; - -const struct inode_operations coda_dir_inode_operations = -{ - .create = coda_create, - .lookup = coda_lookup, - .link = coda_link, - .unlink = coda_unlink, - .symlink = coda_symlink, - .mkdir = coda_mkdir, - .rmdir = coda_rmdir, - .mknod = CODA_EIO_ERROR, - .rename = coda_rename, - .permission = coda_permission, - .getattr = coda_getattr, - .setattr = coda_setattr, -}; - -const struct file_operations coda_dir_operations = { - .llseek = generic_file_llseek, - .read = generic_read_dir, - .iterate = coda_readdir, - .open = coda_open, - .release = coda_release, - .fsync = coda_fsync, -}; - - /* inode operations for directories */ /* access routines: lookup, readlink, permission */ static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry, unsigned int flags) @@ -374,33 +319,6 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, return error; } - -/* file operations for directories */ -static int coda_readdir(struct file *coda_file, struct dir_context *ctx) -{ - struct coda_file_info *cfi; - struct file *host_file; - int ret; - - cfi = CODA_FTOC(coda_file); - BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); - host_file = cfi->cfi_container; - - if (host_file->f_op->iterate) { - struct inode *host_inode = file_inode(host_file); - mutex_lock(&host_inode->i_mutex); - ret = -ENOENT; - if (!IS_DEADDIR(host_inode)) { - ret = host_file->f_op->iterate(host_file, ctx); - file_accessed(host_file); - } - mutex_unlock(&host_inode->i_mutex); - return ret; - } - /* Venus: we must read Venus dirents from a file */ - return coda_venus_readdir(coda_file, ctx); -} - static inline unsigned int CDT2DT(unsigned char cdt) { unsigned int dt; @@ -495,6 +413,33 @@ out: return 0; } +/* file operations for directories */ +static int coda_readdir(struct file *coda_file, struct dir_context *ctx) +{ + struct coda_file_info *cfi; + struct file *host_file; + int ret; + + cfi = CODA_FTOC(coda_file); + BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); + host_file = cfi->cfi_container; + + if (host_file->f_op->iterate) { + struct inode *host_inode = file_inode(host_file); + + mutex_lock(&host_inode->i_mutex); + ret = -ENOENT; + if (!IS_DEADDIR(host_inode)) { + ret = host_file->f_op->iterate(host_file, ctx); + file_accessed(host_file); + } + mutex_unlock(&host_inode->i_mutex); + return ret; + } + /* Venus: we must read Venus dirents from a file */ + return coda_venus_readdir(coda_file, ctx); +} + /* called when a cache lookup succeeds */ static int coda_dentry_revalidate(struct dentry *de, unsigned int flags) { @@ -603,3 +548,32 @@ int coda_revalidate_inode(struct inode *inode) } return 0; } + +const struct dentry_operations coda_dentry_operations = { + .d_revalidate = coda_dentry_revalidate, + .d_delete = coda_dentry_delete, +}; + +const struct inode_operations coda_dir_inode_operations = { + .create = coda_create, + .lookup = coda_lookup, + .link = coda_link, + .unlink = coda_unlink, + .symlink = coda_symlink, + .mkdir = coda_mkdir, + .rmdir = coda_rmdir, + .mknod = CODA_EIO_ERROR, + .rename = coda_rename, + .permission = coda_permission, + .getattr = coda_getattr, + .setattr = coda_setattr, +}; + +const struct file_operations coda_dir_operations = { + .llseek = generic_file_llseek, + .read = generic_read_dir, + .iterate = coda_readdir, + .open = coda_open, + .release = coda_release, + .fsync = coda_fsync, +}; diff --git a/fs/dcache.c b/fs/dcache.c index 7d34f04ec7aa..dc400fd29f4d 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -511,7 +511,7 @@ static void __dentry_kill(struct dentry *dentry) * dentry_iput drops the locks, at which point nobody (except * transient RCU lookups) can reach this dentry. */ - BUG_ON((int)dentry->d_lockref.count > 0); + BUG_ON(dentry->d_lockref.count > 0); this_cpu_dec(nr_dentry); if (dentry->d_op && dentry->d_op->d_release) dentry->d_op->d_release(dentry); @@ -564,7 +564,7 @@ static inline struct dentry *lock_parent(struct dentry *dentry) struct dentry *parent = dentry->d_parent; if (IS_ROOT(dentry)) return NULL; - if (unlikely((int)dentry->d_lockref.count < 0)) + if (unlikely(dentry->d_lockref.count < 0)) return NULL; if (likely(spin_trylock(&parent->d_lock))) return parent; @@ -593,6 +593,110 @@ again: return parent; } +/* + * Try to do a lockless dput(), and return whether that was successful. + * + * If unsuccessful, we return false, having already taken the dentry lock. + * + * The caller needs to hold the RCU read lock, so that the dentry is + * guaranteed to stay around even if the refcount goes down to zero! + */ +static inline bool fast_dput(struct dentry *dentry) +{ + int ret; + unsigned int d_flags; + + /* + * If we have a d_op->d_delete() operation, we sould not + * let the dentry count go to zero, so use "put__or_lock". + */ + if (unlikely(dentry->d_flags & DCACHE_OP_DELETE)) + return lockref_put_or_lock(&dentry->d_lockref); + + /* + * .. otherwise, we can try to just decrement the + * lockref optimistically. + */ + ret = lockref_put_return(&dentry->d_lockref); + + /* + * If the lockref_put_return() failed due to the lock being held + * by somebody else, the fast path has failed. We will need to + * get the lock, and then check the count again. + */ + if (unlikely(ret < 0)) { + spin_lock(&dentry->d_lock); + if (dentry->d_lockref.count > 1) { + dentry->d_lockref.count--; + spin_unlock(&dentry->d_lock); + return 1; + } + return 0; + } + + /* + * If we weren't the last ref, we're done. + */ + if (ret) + return 1; + + /* + * Careful, careful. The reference count went down + * to zero, but we don't hold the dentry lock, so + * somebody else could get it again, and do another + * dput(), and we need to not race with that. + * + * However, there is a very special and common case + * where we don't care, because there is nothing to + * do: the dentry is still hashed, it does not have + * a 'delete' op, and it's referenced and already on + * the LRU list. + * + * NOTE! Since we aren't locked, these values are + * not "stable". However, it is sufficient that at + * some point after we dropped the reference the + * dentry was hashed and the flags had the proper + * value. Other dentry users may have re-gotten + * a reference to the dentry and change that, but + * our work is done - we can leave the dentry + * around with a zero refcount. + */ + smp_rmb(); + d_flags = ACCESS_ONCE(dentry->d_flags); + d_flags &= DCACHE_REFERENCED | DCACHE_LRU_LIST; + + /* Nothing to do? Dropping the reference was all we needed? */ + if (d_flags == (DCACHE_REFERENCED | DCACHE_LRU_LIST) && !d_unhashed(dentry)) + return 1; + + /* + * Not the fast normal case? Get the lock. We've already decremented + * the refcount, but we'll need to re-check the situation after + * getting the lock. + */ + spin_lock(&dentry->d_lock); + + /* + * Did somebody else grab a reference to it in the meantime, and + * we're no longer the last user after all? Alternatively, somebody + * else could have killed it and marked it dead. Either way, we + * don't need to do anything else. + */ + if (dentry->d_lockref.count) { + spin_unlock(&dentry->d_lock); + return 1; + } + + /* + * Re-get the reference we optimistically dropped. We hold the + * lock, and we just tested that it was zero, so we can just + * set it to 1. + */ + dentry->d_lockref.count = 1; + return 0; +} + + /* * This is dput * @@ -625,8 +729,14 @@ void dput(struct dentry *dentry) return; repeat: - if (lockref_put_or_lock(&dentry->d_lockref)) + rcu_read_lock(); + if (likely(fast_dput(dentry))) { + rcu_read_unlock(); return; + } + + /* Slow case: now with the dentry lock held */ + rcu_read_unlock(); /* Unreachable? Get rid of it */ if (unlikely(d_unhashed(dentry))) @@ -813,7 +923,7 @@ static void shrink_dentry_list(struct list_head *list) * We found an inuse dentry which was not removed from * the LRU because of laziness during lookup. Do not free it. */ - if ((int)dentry->d_lockref.count > 0) { + if (dentry->d_lockref.count > 0) { spin_unlock(&dentry->d_lock); if (parent) spin_unlock(&parent->d_lock); @@ -2191,37 +2301,6 @@ struct dentry *d_hash_and_lookup(struct dentry *dir, struct qstr *name) } EXPORT_SYMBOL(d_hash_and_lookup); -/** - * d_validate - verify dentry provided from insecure source (deprecated) - * @dentry: The dentry alleged to be valid child of @dparent - * @dparent: The parent dentry (known to be valid) - * - * An insecure source has sent us a dentry, here we verify it and dget() it. - * This is used by ncpfs in its readdir implementation. - * Zero is returned in the dentry is invalid. - * - * This function is slow for big directories, and deprecated, do not use it. - */ -int d_validate(struct dentry *dentry, struct dentry *dparent) -{ - struct dentry *child; - - spin_lock(&dparent->d_lock); - list_for_each_entry(child, &dparent->d_subdirs, d_child) { - if (dentry == child) { - spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); - __dget_dlock(dentry); - spin_unlock(&dentry->d_lock); - spin_unlock(&dparent->d_lock); - return 1; - } - } - spin_unlock(&dparent->d_lock); - - return 0; -} -EXPORT_SYMBOL(d_validate); - /* * When a file is deleted, we have two options: * - turn this dentry into a negative dentry diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 05f2960ed7c3..45b18a5e225c 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -34,93 +34,16 @@ static struct vfsmount *debugfs_mount; static int debugfs_mount_count; static bool debugfs_registered; -static struct inode *debugfs_get_inode(struct super_block *sb, umode_t mode, dev_t dev, - void *data, const struct file_operations *fops) - +static struct inode *debugfs_get_inode(struct super_block *sb) { struct inode *inode = new_inode(sb); - if (inode) { inode->i_ino = get_next_ino(); - inode->i_mode = mode; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; - switch (mode & S_IFMT) { - default: - init_special_inode(inode, mode, dev); - break; - case S_IFREG: - inode->i_fop = fops ? fops : &debugfs_file_operations; - inode->i_private = data; - break; - case S_IFLNK: - inode->i_op = &debugfs_link_operations; - inode->i_private = data; - break; - case S_IFDIR: - inode->i_op = &simple_dir_inode_operations; - inode->i_fop = &simple_dir_operations; - - /* directory inodes start off with i_nlink == 2 - * (for "." entry) */ - inc_nlink(inode); - break; - } } return inode; } -/* SMP-safe */ -static int debugfs_mknod(struct inode *dir, struct dentry *dentry, - umode_t mode, dev_t dev, void *data, - const struct file_operations *fops) -{ - struct inode *inode; - int error = -EPERM; - - if (dentry->d_inode) - return -EEXIST; - - inode = debugfs_get_inode(dir->i_sb, mode, dev, data, fops); - if (inode) { - d_instantiate(dentry, inode); - dget(dentry); - error = 0; - } - return error; -} - -static int debugfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) -{ - int res; - - mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR; - res = debugfs_mknod(dir, dentry, mode, 0, NULL, NULL); - if (!res) { - inc_nlink(dir); - fsnotify_mkdir(dir, dentry); - } - return res; -} - -static int debugfs_link(struct inode *dir, struct dentry *dentry, umode_t mode, - void *data) -{ - mode = (mode & S_IALLUGO) | S_IFLNK; - return debugfs_mknod(dir, dentry, mode, 0, data, NULL); -} - -static int debugfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, - void *data, const struct file_operations *fops) -{ - int res; - - mode = (mode & S_IALLUGO) | S_IFREG; - res = debugfs_mknod(dir, dentry, mode, 0, data, fops); - if (!res) - fsnotify_create(dir, dentry); - return res; -} - static inline int debugfs_positive(struct dentry *dentry) { return dentry->d_inode && !d_unhashed(dentry); @@ -252,6 +175,18 @@ static const struct super_operations debugfs_super_operations = { .show_options = debugfs_show_options, }; +static struct vfsmount *debugfs_automount(struct path *path) +{ + struct vfsmount *(*f)(void *); + f = (struct vfsmount *(*)(void *))path->dentry->d_fsdata; + return f(path->dentry->d_inode->i_private); +} + +static const struct dentry_operations debugfs_dops = { + .d_delete = always_delete_dentry, + .d_automount = debugfs_automount, +}; + static int debug_fill_super(struct super_block *sb, void *data, int silent) { static struct tree_descr debug_files[] = {{""}}; @@ -276,6 +211,7 @@ static int debug_fill_super(struct super_block *sb, void *data, int silent) goto fail; sb->s_op = &debugfs_super_operations; + sb->s_d_op = &debugfs_dops; debugfs_apply_options(sb); @@ -302,11 +238,9 @@ static struct file_system_type debug_fs_type = { }; MODULE_ALIAS_FS("debugfs"); -static struct dentry *__create_file(const char *name, umode_t mode, - struct dentry *parent, void *data, - const struct file_operations *fops) +static struct dentry *start_creating(const char *name, struct dentry *parent) { - struct dentry *dentry = NULL; + struct dentry *dentry; int error; pr_debug("debugfs: creating file '%s'\n",name); @@ -314,7 +248,7 @@ static struct dentry *__create_file(const char *name, umode_t mode, error = simple_pin_fs(&debug_fs_type, &debugfs_mount, &debugfs_mount_count); if (error) - goto exit; + return ERR_PTR(error); /* If the parent is not specified, we create it in the root. * We need the root dentry to do this, which is in the super @@ -326,31 +260,26 @@ static struct dentry *__create_file(const char *name, umode_t mode, mutex_lock(&parent->d_inode->i_mutex); dentry = lookup_one_len(name, parent, strlen(name)); - if (!IS_ERR(dentry)) { - switch (mode & S_IFMT) { - case S_IFDIR: - error = debugfs_mkdir(parent->d_inode, dentry, mode); - - break; - case S_IFLNK: - error = debugfs_link(parent->d_inode, dentry, mode, - data); - break; - default: - error = debugfs_create(parent->d_inode, dentry, mode, - data, fops); - break; - } + if (!IS_ERR(dentry) && dentry->d_inode) { dput(dentry); - } else - error = PTR_ERR(dentry); - mutex_unlock(&parent->d_inode->i_mutex); - - if (error) { - dentry = NULL; - simple_release_fs(&debugfs_mount, &debugfs_mount_count); + dentry = ERR_PTR(-EEXIST); } -exit: + if (IS_ERR(dentry)) + mutex_unlock(&parent->d_inode->i_mutex); + return dentry; +} + +static struct dentry *failed_creating(struct dentry *dentry) +{ + mutex_unlock(&dentry->d_parent->d_inode->i_mutex); + dput(dentry); + simple_release_fs(&debugfs_mount, &debugfs_mount_count); + return NULL; +} + +static struct dentry *end_creating(struct dentry *dentry) +{ + mutex_unlock(&dentry->d_parent->d_inode->i_mutex); return dentry; } @@ -384,19 +313,71 @@ struct dentry *debugfs_create_file(const char *name, umode_t mode, struct dentry *parent, void *data, const struct file_operations *fops) { - switch (mode & S_IFMT) { - case S_IFREG: - case 0: - break; - default: - BUG(); - } + struct dentry *dentry; + struct inode *inode; + + if (!(mode & S_IFMT)) + mode |= S_IFREG; + BUG_ON(!S_ISREG(mode)); + dentry = start_creating(name, parent); + + if (IS_ERR(dentry)) + return NULL; - return __create_file(name, mode, parent, data, fops); + inode = debugfs_get_inode(dentry->d_sb); + if (unlikely(!inode)) + return failed_creating(dentry); + + inode->i_mode = mode; + inode->i_fop = fops ? fops : &debugfs_file_operations; + inode->i_private = data; + d_instantiate(dentry, inode); + fsnotify_create(dentry->d_parent->d_inode, dentry); + return end_creating(dentry); } EXPORT_SYMBOL_GPL(debugfs_create_file); /** + * debugfs_create_file_size - create a file in the debugfs filesystem + * @name: a pointer to a string containing the name of the file to create. + * @mode: the permission that the file should have. + * @parent: a pointer to the parent dentry for this file. This should be a + * directory dentry if set. If this parameter is NULL, then the + * file will be created in the root of the debugfs filesystem. + * @data: a pointer to something that the caller will want to get to later + * on. The inode.i_private pointer will point to this value on + * the open() call. + * @fops: a pointer to a struct file_operations that should be used for + * this file. + * @file_size: initial file size + * + * This is the basic "create a file" function for debugfs. It allows for a + * wide range of flexibility in creating a file, or a directory (if you want + * to create a directory, the debugfs_create_dir() function is + * recommended to be used instead.) + * + * This function will return a pointer to a dentry if it succeeds. This + * pointer must be passed to the debugfs_remove() function when the file is + * to be removed (no automatic cleanup happens if your module is unloaded, + * you are responsible here.) If an error occurs, %NULL will be returned. + * + * If debugfs is not enabled in the kernel, the value -%ENODEV will be + * returned. + */ +struct dentry *debugfs_create_file_size(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *fops, + loff_t file_size) +{ + struct dentry *de = debugfs_create_file(name, mode, parent, data, fops); + + if (de) + de->d_inode->i_size = file_size; + return de; +} +EXPORT_SYMBOL_GPL(debugfs_create_file_size); + +/** * debugfs_create_dir - create a directory in the debugfs filesystem * @name: a pointer to a string containing the name of the directory to * create. @@ -416,12 +397,65 @@ EXPORT_SYMBOL_GPL(debugfs_create_file); */ struct dentry *debugfs_create_dir(const char *name, struct dentry *parent) { - return __create_file(name, S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, - parent, NULL, NULL); + struct dentry *dentry = start_creating(name, parent); + struct inode *inode; + + if (IS_ERR(dentry)) + return NULL; + + inode = debugfs_get_inode(dentry->d_sb); + if (unlikely(!inode)) + return failed_creating(dentry); + + inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; + inode->i_op = &simple_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + + /* directory inodes start off with i_nlink == 2 (for "." entry) */ + inc_nlink(inode); + d_instantiate(dentry, inode); + inc_nlink(dentry->d_parent->d_inode); + fsnotify_mkdir(dentry->d_parent->d_inode, dentry); + return end_creating(dentry); } EXPORT_SYMBOL_GPL(debugfs_create_dir); /** + * debugfs_create_automount - create automount point in the debugfs filesystem + * @name: a pointer to a string containing the name of the file to create. + * @parent: a pointer to the parent dentry for this file. This should be a + * directory dentry if set. If this parameter is NULL, then the + * file will be created in the root of the debugfs filesystem. + * @f: function to be called when pathname resolution steps on that one. + * @data: opaque argument to pass to f(). + * + * @f should return what ->d_automount() would. + */ +struct dentry *debugfs_create_automount(const char *name, + struct dentry *parent, + struct vfsmount *(*f)(void *), + void *data) +{ + struct dentry *dentry = start_creating(name, parent); + struct inode *inode; + + if (IS_ERR(dentry)) + return NULL; + + inode = debugfs_get_inode(dentry->d_sb); + if (unlikely(!inode)) + return failed_creating(dentry); + + inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; + inode->i_flags |= S_AUTOMOUNT; + inode->i_private = data; + dentry->d_fsdata = (void *)f; + d_instantiate(dentry, inode); + return end_creating(dentry); +} +EXPORT_SYMBOL(debugfs_create_automount); + +/** * debugfs_create_symlink- create a symbolic link in the debugfs filesystem * @name: a pointer to a string containing the name of the symbolic link to * create. @@ -447,17 +481,28 @@ EXPORT_SYMBOL_GPL(debugfs_create_dir); struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent, const char *target) { - struct dentry *result; - char *link; - - link = kstrdup(target, GFP_KERNEL); + struct dentry *dentry; + struct inode *inode; + char *link = kstrdup(target, GFP_KERNEL); if (!link) return NULL; - result = __create_file(name, S_IFLNK | S_IRWXUGO, parent, link, NULL); - if (!result) + dentry = start_creating(name, parent); + if (IS_ERR(dentry)) { kfree(link); - return result; + return NULL; + } + + inode = debugfs_get_inode(dentry->d_sb); + if (unlikely(!inode)) { + kfree(link); + return failed_creating(dentry); + } + inode->i_mode = S_IFLNK | S_IRWXUGO; + inode->i_op = &debugfs_link_operations; + inode->i_private = link; + d_instantiate(dentry, inode); + return end_creating(dentry); } EXPORT_SYMBOL_GPL(debugfs_create_symlink); diff --git a/fs/eventfd.c b/fs/eventfd.c index 4b0a226024fa..8d0c0df01854 100644 --- a/fs/eventfd.c +++ b/fs/eventfd.c @@ -118,18 +118,18 @@ static unsigned int eventfd_poll(struct file *file, poll_table *wait) { struct eventfd_ctx *ctx = file->private_data; unsigned int events = 0; - unsigned long flags; + u64 count; poll_wait(file, &ctx->wqh, wait); + smp_rmb(); + count = ctx->count; - spin_lock_irqsave(&ctx->wqh.lock, flags); - if (ctx->count > 0) + if (count > 0) events |= POLLIN; - if (ctx->count == ULLONG_MAX) + if (count == ULLONG_MAX) events |= POLLERR; - if (ULLONG_MAX - 1 > ctx->count) + if (ULLONG_MAX - 1 > count) events |= POLLOUT; - spin_unlock_irqrestore(&ctx->wqh.lock, flags); return events; } diff --git a/fs/exec.c b/fs/exec.c index ad8798e26be9..c7f9b733406d 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -794,8 +794,14 @@ exit: struct file *open_exec(const char *name) { - struct filename tmp = { .name = name }; - return do_open_execat(AT_FDCWD, &tmp, 0); + struct filename *filename = getname_kernel(name); + struct file *f = ERR_CAST(filename); + + if (!IS_ERR(filename)) { + f = do_open_execat(AT_FDCWD, filename, 0); + putname(filename); + } + return f; } EXPORT_SYMBOL(open_exec); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 28555f191b62..85404f15e53a 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4174,6 +4174,65 @@ static int ext4_inode_blocks_set(handle_t *handle, return 0; } +struct other_inode { + unsigned long orig_ino; + struct ext4_inode *raw_inode; +}; + +static int other_inode_match(struct inode * inode, unsigned long ino, + void *data) +{ + struct other_inode *oi = (struct other_inode *) data; + + if ((inode->i_ino != ino) || + (inode->i_state & (I_FREEING | I_WILL_FREE | I_NEW | + I_DIRTY_SYNC | I_DIRTY_DATASYNC)) || + ((inode->i_state & I_DIRTY_TIME) == 0)) + return 0; + spin_lock(&inode->i_lock); + if (((inode->i_state & (I_FREEING | I_WILL_FREE | I_NEW | + I_DIRTY_SYNC | I_DIRTY_DATASYNC)) == 0) && + (inode->i_state & I_DIRTY_TIME)) { + struct ext4_inode_info *ei = EXT4_I(inode); + + inode->i_state &= ~(I_DIRTY_TIME | I_DIRTY_TIME_EXPIRED); + spin_unlock(&inode->i_lock); + + spin_lock(&ei->i_raw_lock); + EXT4_INODE_SET_XTIME(i_ctime, inode, oi->raw_inode); + EXT4_INODE_SET_XTIME(i_mtime, inode, oi->raw_inode); + EXT4_INODE_SET_XTIME(i_atime, inode, oi->raw_inode); + ext4_inode_csum_set(inode, oi->raw_inode, ei); + spin_unlock(&ei->i_raw_lock); + trace_ext4_other_inode_update_time(inode, oi->orig_ino); + return -1; + } + spin_unlock(&inode->i_lock); + return -1; +} + +/* + * Opportunistically update the other time fields for other inodes in + * the same inode table block. + */ +static void ext4_update_other_inodes_time(struct super_block *sb, + unsigned long orig_ino, char *buf) +{ + struct other_inode oi; + unsigned long ino; + int i, inodes_per_block = EXT4_SB(sb)->s_inodes_per_block; + int inode_size = EXT4_INODE_SIZE(sb); + + oi.orig_ino = orig_ino; + ino = orig_ino & ~(inodes_per_block - 1); + for (i = 0; i < inodes_per_block; i++, ino++, buf += inode_size) { + if (ino == orig_ino) + continue; + oi.raw_inode = (struct ext4_inode *) buf; + (void) find_inode_nowait(sb, ino, other_inode_match, &oi); + } +} + /* * Post the struct inode info into an on-disk inode location in the * buffer-cache. This gobbles the caller's reference to the @@ -4283,10 +4342,11 @@ static int ext4_do_update_inode(handle_t *handle, cpu_to_le16(ei->i_extra_isize); } } - ext4_inode_csum_set(inode, raw_inode, ei); - spin_unlock(&ei->i_raw_lock); + if (inode->i_sb->s_flags & MS_LAZYTIME) + ext4_update_other_inodes_time(inode->i_sb, inode->i_ino, + bh->b_data); BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata"); rc = ext4_handle_dirty_metadata(handle, NULL, bh); @@ -4875,11 +4935,17 @@ int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode) * If the inode is marked synchronous, we don't honour that here - doing * so would cause a commit on atime updates, which we don't bother doing. * We handle synchronous inodes at the highest possible level. + * + * If only the I_DIRTY_TIME flag is set, we can skip everything. If + * I_DIRTY_TIME and I_DIRTY_SYNC is set, the only inode fields we need + * to copy into the on-disk inode structure are the timestamp files. */ void ext4_dirty_inode(struct inode *inode, int flags) { handle_t *handle; + if (flags == I_DIRTY_TIME) + return; handle = ext4_journal_start(inode, EXT4_HT_INODE, 2); if (IS_ERR(handle)) goto out; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 10e8c6b7ca08..1adac6868e6f 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1126,6 +1126,7 @@ enum { Opt_noquota, Opt_barrier, Opt_nobarrier, Opt_err, Opt_usrquota, Opt_grpquota, Opt_i_version, Opt_dax, Opt_stripe, Opt_delalloc, Opt_nodelalloc, Opt_mblk_io_submit, + Opt_lazytime, Opt_nolazytime, Opt_nomblk_io_submit, Opt_block_validity, Opt_noblock_validity, Opt_inode_readahead_blks, Opt_journal_ioprio, Opt_dioread_nolock, Opt_dioread_lock, @@ -1190,6 +1191,8 @@ static const match_table_t tokens = { {Opt_dax, "dax"}, {Opt_stripe, "stripe=%u"}, {Opt_delalloc, "delalloc"}, + {Opt_lazytime, "lazytime"}, + {Opt_nolazytime, "nolazytime"}, {Opt_nodelalloc, "nodelalloc"}, {Opt_removed, "mblk_io_submit"}, {Opt_removed, "nomblk_io_submit"}, @@ -1448,6 +1451,12 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token, case Opt_i_version: sb->s_flags |= MS_I_VERSION; return 1; + case Opt_lazytime: + sb->s_flags |= MS_LAZYTIME; + return 1; + case Opt_nolazytime: + sb->s_flags &= ~MS_LAZYTIME; + return 1; } for (m = ext4_mount_opts; m->token != Opt_err; m++) @@ -5044,6 +5053,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) } #endif + *flags = (*flags & ~MS_LAZYTIME) | (sb->s_flags & MS_LAZYTIME); ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s", orig_data); kfree(orig_data); return 0; diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 7b41a2dcdd76..497c7c5263c7 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -580,7 +580,7 @@ static void fat_set_state(struct super_block *sb, { struct buffer_head *bh; struct fat_boot_sector *b; - struct msdos_sb_info *sbi = sb->s_fs_info; + struct msdos_sb_info *sbi = MSDOS_SB(sb); /* do not change any thing if mounted read only */ if ((sb->s_flags & MS_RDONLY) && !force) diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index c399152de397..073657f755d4 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -253,14 +253,19 @@ static bool inode_dirtied_after(struct inode *inode, unsigned long t) return ret; } +#define EXPIRE_DIRTY_ATIME 0x0001 + /* * Move expired (dirtied before work->older_than_this) dirty inodes from * @delaying_queue to @dispatch_queue. */ static int move_expired_inodes(struct list_head *delaying_queue, struct list_head *dispatch_queue, + int flags, struct wb_writeback_work *work) { + unsigned long *older_than_this = NULL; + unsigned long expire_time; LIST_HEAD(tmp); struct list_head *pos, *node; struct super_block *sb = NULL; @@ -268,13 +273,21 @@ static int move_expired_inodes(struct list_head *delaying_queue, int do_sb_sort = 0; int moved = 0; + if ((flags & EXPIRE_DIRTY_ATIME) == 0) + older_than_this = work->older_than_this; + else if ((work->reason == WB_REASON_SYNC) == 0) { + expire_time = jiffies - (HZ * 86400); + older_than_this = &expire_time; + } while (!list_empty(delaying_queue)) { inode = wb_inode(delaying_queue->prev); - if (work->older_than_this && - inode_dirtied_after(inode, *work->older_than_this)) + if (older_than_this && + inode_dirtied_after(inode, *older_than_this)) break; list_move(&inode->i_wb_list, &tmp); moved++; + if (flags & EXPIRE_DIRTY_ATIME) + set_bit(__I_DIRTY_TIME_EXPIRED, &inode->i_state); if (sb_is_blkdev_sb(inode->i_sb)) continue; if (sb && sb != inode->i_sb) @@ -315,9 +328,12 @@ out: static void queue_io(struct bdi_writeback *wb, struct wb_writeback_work *work) { int moved; + assert_spin_locked(&wb->list_lock); list_splice_init(&wb->b_more_io, &wb->b_io); - moved = move_expired_inodes(&wb->b_dirty, &wb->b_io, work); + moved = move_expired_inodes(&wb->b_dirty, &wb->b_io, 0, work); + moved += move_expired_inodes(&wb->b_dirty_time, &wb->b_io, + EXPIRE_DIRTY_ATIME, work); trace_writeback_queue_io(wb, work, moved); } @@ -441,6 +457,8 @@ static void requeue_inode(struct inode *inode, struct bdi_writeback *wb, * updates after data IO completion. */ redirty_tail(inode, wb); + } else if (inode->i_state & I_DIRTY_TIME) { + list_move(&inode->i_wb_list, &wb->b_dirty_time); } else { /* The inode is clean. Remove from writeback lists. */ list_del_init(&inode->i_wb_list); @@ -487,7 +505,13 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) spin_lock(&inode->i_lock); dirty = inode->i_state & I_DIRTY; - inode->i_state &= ~I_DIRTY; + if (((dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) && + (inode->i_state & I_DIRTY_TIME)) || + (inode->i_state & I_DIRTY_TIME_EXPIRED)) { + dirty |= I_DIRTY_TIME | I_DIRTY_TIME_EXPIRED; + trace_writeback_lazytime(inode); + } + inode->i_state &= ~dirty; /* * Paired with smp_mb() in __mark_inode_dirty(). This allows @@ -507,8 +531,10 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) spin_unlock(&inode->i_lock); + if (dirty & I_DIRTY_TIME) + mark_inode_dirty_sync(inode); /* Don't write the inode if only I_DIRTY_PAGES was set */ - if (dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) { + if (dirty & ~I_DIRTY_PAGES) { int err = write_inode(inode, wbc); if (ret == 0) ret = err; @@ -556,7 +582,7 @@ writeback_single_inode(struct inode *inode, struct bdi_writeback *wb, * make sure inode is on some writeback list and leave it there unless * we have completely cleaned the inode. */ - if (!(inode->i_state & I_DIRTY) && + if (!(inode->i_state & I_DIRTY_ALL) && (wbc->sync_mode != WB_SYNC_ALL || !mapping_tagged(inode->i_mapping, PAGECACHE_TAG_WRITEBACK))) goto out; @@ -571,7 +597,7 @@ writeback_single_inode(struct inode *inode, struct bdi_writeback *wb, * If inode is clean, remove it from writeback lists. Otherwise don't * touch it. See comment above for explanation. */ - if (!(inode->i_state & I_DIRTY)) + if (!(inode->i_state & I_DIRTY_ALL)) list_del_init(&inode->i_wb_list); spin_unlock(&wb->list_lock); inode_sync_complete(inode); @@ -713,7 +739,7 @@ static long writeback_sb_inodes(struct super_block *sb, wrote += write_chunk - wbc.nr_to_write; spin_lock(&wb->list_lock); spin_lock(&inode->i_lock); - if (!(inode->i_state & I_DIRTY)) + if (!(inode->i_state & I_DIRTY_ALL)) wrote++; requeue_inode(inode, wb, &wbc); inode_sync_complete(inode); @@ -1151,16 +1177,20 @@ static noinline void block_dump___mark_inode_dirty(struct inode *inode) * page->mapping->host, so the page-dirtying time is recorded in the internal * blockdev inode. */ +#define I_DIRTY_INODE (I_DIRTY_SYNC | I_DIRTY_DATASYNC) void __mark_inode_dirty(struct inode *inode, int flags) { struct super_block *sb = inode->i_sb; struct backing_dev_info *bdi = NULL; + int dirtytime; + + trace_writeback_mark_inode_dirty(inode, flags); /* * Don't do this for I_DIRTY_PAGES - that doesn't actually * dirty the inode itself */ - if (flags & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) { + if (flags & (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_TIME)) { trace_writeback_dirty_inode_start(inode, flags); if (sb->s_op->dirty_inode) @@ -1168,6 +1198,9 @@ void __mark_inode_dirty(struct inode *inode, int flags) trace_writeback_dirty_inode(inode, flags); } + if (flags & I_DIRTY_INODE) + flags &= ~I_DIRTY_TIME; + dirtytime = flags & I_DIRTY_TIME; /* * Paired with smp_mb() in __writeback_single_inode() for the @@ -1175,16 +1208,21 @@ void __mark_inode_dirty(struct inode *inode, int flags) */ smp_mb(); - if ((inode->i_state & flags) == flags) + if (((inode->i_state & flags) == flags) || + (dirtytime && (inode->i_state & I_DIRTY_INODE))) return; if (unlikely(block_dump)) block_dump___mark_inode_dirty(inode); spin_lock(&inode->i_lock); + if (dirtytime && (inode->i_state & I_DIRTY_INODE)) + goto out_unlock_inode; if ((inode->i_state & flags) != flags) { const int was_dirty = inode->i_state & I_DIRTY; + if (flags & I_DIRTY_INODE) + inode->i_state &= ~I_DIRTY_TIME; inode->i_state |= flags; /* @@ -1231,8 +1269,10 @@ void __mark_inode_dirty(struct inode *inode, int flags) } inode->dirtied_when = jiffies; - list_move(&inode->i_wb_list, &bdi->wb.b_dirty); + list_move(&inode->i_wb_list, dirtytime ? + &bdi->wb.b_dirty_time : &bdi->wb.b_dirty); spin_unlock(&bdi->wb.list_lock); + trace_writeback_dirty_inode_enqueue(inode); if (wakeup_bdi) bdi_wakeup_thread_delayed(bdi); diff --git a/fs/fs_pin.c b/fs/fs_pin.c index 9368236ca100..b06c98796afb 100644 --- a/fs/fs_pin.c +++ b/fs/fs_pin.c @@ -1,78 +1,102 @@ #include <linux/fs.h> +#include <linux/sched.h> #include <linux/slab.h> -#include <linux/fs_pin.h> #include "internal.h" #include "mount.h" -static void pin_free_rcu(struct rcu_head *head) -{ - kfree(container_of(head, struct fs_pin, rcu)); -} - static DEFINE_SPINLOCK(pin_lock); -void pin_put(struct fs_pin *p) -{ - if (atomic_long_dec_and_test(&p->count)) - call_rcu(&p->rcu, pin_free_rcu); -} - void pin_remove(struct fs_pin *pin) { spin_lock(&pin_lock); hlist_del(&pin->m_list); hlist_del(&pin->s_list); spin_unlock(&pin_lock); + spin_lock_irq(&pin->wait.lock); + pin->done = 1; + wake_up_locked(&pin->wait); + spin_unlock_irq(&pin->wait.lock); } -void pin_insert(struct fs_pin *pin, struct vfsmount *m) +void pin_insert_group(struct fs_pin *pin, struct vfsmount *m, struct hlist_head *p) { spin_lock(&pin_lock); - hlist_add_head(&pin->s_list, &m->mnt_sb->s_pins); + if (p) + hlist_add_head(&pin->s_list, p); hlist_add_head(&pin->m_list, &real_mount(m)->mnt_pins); spin_unlock(&pin_lock); } +void pin_insert(struct fs_pin *pin, struct vfsmount *m) +{ + pin_insert_group(pin, m, &m->mnt_sb->s_pins); +} + +void pin_kill(struct fs_pin *p) +{ + wait_queue_t wait; + + if (!p) { + rcu_read_unlock(); + return; + } + init_wait(&wait); + spin_lock_irq(&p->wait.lock); + if (likely(!p->done)) { + p->done = -1; + spin_unlock_irq(&p->wait.lock); + rcu_read_unlock(); + p->kill(p); + return; + } + if (p->done > 0) { + spin_unlock_irq(&p->wait.lock); + rcu_read_unlock(); + return; + } + __add_wait_queue(&p->wait, &wait); + while (1) { + set_current_state(TASK_UNINTERRUPTIBLE); + spin_unlock_irq(&p->wait.lock); + rcu_read_unlock(); + schedule(); + rcu_read_lock(); + if (likely(list_empty(&wait.task_list))) + break; + /* OK, we know p couldn't have been freed yet */ + spin_lock_irq(&p->wait.lock); + if (p->done > 0) { + spin_unlock_irq(&p->wait.lock); + break; + } + } + rcu_read_unlock(); +} + void mnt_pin_kill(struct mount *m) { while (1) { struct hlist_node *p; - struct fs_pin *pin; rcu_read_lock(); p = ACCESS_ONCE(m->mnt_pins.first); if (!p) { rcu_read_unlock(); break; } - pin = hlist_entry(p, struct fs_pin, m_list); - if (!atomic_long_inc_not_zero(&pin->count)) { - rcu_read_unlock(); - cpu_relax(); - continue; - } - rcu_read_unlock(); - pin->kill(pin); + pin_kill(hlist_entry(p, struct fs_pin, m_list)); } } -void sb_pin_kill(struct super_block *sb) +void group_pin_kill(struct hlist_head *p) { while (1) { - struct hlist_node *p; - struct fs_pin *pin; + struct hlist_node *q; rcu_read_lock(); - p = ACCESS_ONCE(sb->s_pins.first); - if (!p) { + q = ACCESS_ONCE(p->first); + if (!q) { rcu_read_unlock(); break; } - pin = hlist_entry(p, struct fs_pin, s_list); - if (!atomic_long_inc_not_zero(&pin->count)) { - rcu_read_unlock(); - cpu_relax(); - continue; - } - rcu_read_unlock(); - pin->kill(pin); + pin_kill(hlist_entry(q, struct fs_pin, s_list)); } } diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index ec9c2d33477a..3e32bb8e2d7e 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -654,7 +654,7 @@ static int gfs2_fsync(struct file *file, loff_t start, loff_t end, { struct address_space *mapping = file->f_mapping; struct inode *inode = mapping->host; - int sync_state = inode->i_state & I_DIRTY; + int sync_state = inode->i_state & I_DIRTY_ALL; struct gfs2_inode *ip = GFS2_I(inode); int ret = 0, ret1 = 0; @@ -667,7 +667,7 @@ static int gfs2_fsync(struct file *file, loff_t start, loff_t end, if (!gfs2_is_jdata(ip)) sync_state &= ~I_DIRTY_PAGES; if (datasync) - sync_state &= ~I_DIRTY_SYNC; + sync_state &= ~(I_DIRTY_SYNC | I_DIRTY_TIME); if (sync_state) { ret = sync_inode_metadata(inode, 1); diff --git a/fs/inode.c b/fs/inode.c index 86bfaca724db..f00b16f45507 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -18,6 +18,7 @@ #include <linux/buffer_head.h> /* for inode_has_buffers */ #include <linux/ratelimit.h> #include <linux/list_lru.h> +#include <trace/events/writeback.h> #include "internal.h" /* @@ -30,7 +31,7 @@ * inode_sb_list_lock protects: * sb->s_inodes, inode->i_sb_list * bdi->wb.list_lock protects: - * bdi->wb.b_{dirty,io,more_io}, inode->i_wb_list + * bdi->wb.b_{dirty,io,more_io,dirty_time}, inode->i_wb_list * inode_hash_lock protects: * inode_hashtable, inode->i_hash * @@ -403,7 +404,8 @@ static void inode_lru_list_add(struct inode *inode) */ void inode_add_lru(struct inode *inode) { - if (!(inode->i_state & (I_DIRTY | I_SYNC | I_FREEING | I_WILL_FREE)) && + if (!(inode->i_state & (I_DIRTY_ALL | I_SYNC | + I_FREEING | I_WILL_FREE)) && !atomic_read(&inode->i_count) && inode->i_sb->s_flags & MS_ACTIVE) inode_lru_list_add(inode); } @@ -634,7 +636,7 @@ int invalidate_inodes(struct super_block *sb, bool kill_dirty) spin_unlock(&inode->i_lock); continue; } - if (inode->i_state & I_DIRTY && !kill_dirty) { + if (inode->i_state & I_DIRTY_ALL && !kill_dirty) { spin_unlock(&inode->i_lock); busy = 1; continue; @@ -1268,6 +1270,56 @@ struct inode *ilookup(struct super_block *sb, unsigned long ino) } EXPORT_SYMBOL(ilookup); +/** + * find_inode_nowait - find an inode in the inode cache + * @sb: super block of file system to search + * @hashval: hash value (usually inode number) to search for + * @match: callback used for comparisons between inodes + * @data: opaque data pointer to pass to @match + * + * Search for the inode specified by @hashval and @data in the inode + * cache, where the helper function @match will return 0 if the inode + * does not match, 1 if the inode does match, and -1 if the search + * should be stopped. The @match function must be responsible for + * taking the i_lock spin_lock and checking i_state for an inode being + * freed or being initialized, and incrementing the reference count + * before returning 1. It also must not sleep, since it is called with + * the inode_hash_lock spinlock held. + * + * This is a even more generalized version of ilookup5() when the + * function must never block --- find_inode() can block in + * __wait_on_freeing_inode() --- or when the caller can not increment + * the reference count because the resulting iput() might cause an + * inode eviction. The tradeoff is that the @match funtion must be + * very carefully implemented. + */ +struct inode *find_inode_nowait(struct super_block *sb, + unsigned long hashval, + int (*match)(struct inode *, unsigned long, + void *), + void *data) +{ + struct hlist_head *head = inode_hashtable + hash(sb, hashval); + struct inode *inode, *ret_inode = NULL; + int mval; + + spin_lock(&inode_hash_lock); + hlist_for_each_entry(inode, head, i_hash) { + if (inode->i_sb != sb) + continue; + mval = match(inode, hashval, data); + if (mval == 0) + continue; + if (mval == 1) + ret_inode = inode; + goto out; + } +out: + spin_unlock(&inode_hash_lock); + return ret_inode; +} +EXPORT_SYMBOL(find_inode_nowait); + int insert_inode_locked(struct inode *inode) { struct super_block *sb = inode->i_sb; @@ -1418,11 +1470,20 @@ static void iput_final(struct inode *inode) */ void iput(struct inode *inode) { - if (inode) { - BUG_ON(inode->i_state & I_CLEAR); - - if (atomic_dec_and_lock(&inode->i_count, &inode->i_lock)) - iput_final(inode); + if (!inode) + return; + BUG_ON(inode->i_state & I_CLEAR); +retry: + if (atomic_dec_and_lock(&inode->i_count, &inode->i_lock)) { + if (inode->i_nlink && (inode->i_state & I_DIRTY_TIME)) { + atomic_inc(&inode->i_count); + inode->i_state &= ~I_DIRTY_TIME; + spin_unlock(&inode->i_lock); + trace_writeback_lazytime_iput(inode); + mark_inode_dirty_sync(inode); + goto retry; + } + iput_final(inode); } } EXPORT_SYMBOL(iput); @@ -1481,14 +1542,9 @@ static int relatime_need_update(struct vfsmount *mnt, struct inode *inode, return 0; } -/* - * This does the actual work of updating an inodes time or version. Must have - * had called mnt_want_write() before calling this. - */ -static int update_time(struct inode *inode, struct timespec *time, int flags) +int generic_update_time(struct inode *inode, struct timespec *time, int flags) { - if (inode->i_op->update_time) - return inode->i_op->update_time(inode, time, flags); + int iflags = I_DIRTY_TIME; if (flags & S_ATIME) inode->i_atime = *time; @@ -1498,9 +1554,27 @@ static int update_time(struct inode *inode, struct timespec *time, int flags) inode->i_ctime = *time; if (flags & S_MTIME) inode->i_mtime = *time; - mark_inode_dirty_sync(inode); + + if (!(inode->i_sb->s_flags & MS_LAZYTIME) || (flags & S_VERSION)) + iflags |= I_DIRTY_SYNC; + __mark_inode_dirty(inode, iflags); return 0; } +EXPORT_SYMBOL(generic_update_time); + +/* + * This does the actual work of updating an inodes time or version. Must have + * had called mnt_want_write() before calling this. + */ +static int update_time(struct inode *inode, struct timespec *time, int flags) +{ + int (*update_time)(struct inode *, struct timespec *, int); + + update_time = inode->i_op->update_time ? inode->i_op->update_time : + generic_update_time; + + return update_time(inode, time, flags); +} /** * touch_atime - update the access time diff --git a/fs/internal.h b/fs/internal.h index d92c346a793d..30459dab409d 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -144,7 +144,7 @@ extern const struct file_operations pipefifo_fops; /* * fs_pin.c */ -extern void sb_pin_kill(struct super_block *sb); +extern void group_pin_kill(struct hlist_head *p); extern void mnt_pin_kill(struct mount *m); /* diff --git a/fs/jffs2/compr_rubin.c b/fs/jffs2/compr_rubin.c index 92e0644bf867..556de100ebd5 100644 --- a/fs/jffs2/compr_rubin.c +++ b/fs/jffs2/compr_rubin.c @@ -84,11 +84,6 @@ static inline int pullbit(struct pushpull *pp) return bit; } -static inline int pulledbits(struct pushpull *pp) -{ - return pp->ofs; -} - static void init_rubin(struct rubin_state *rs, int div, int *bits) { diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index 7654e87b0428..9ad5ba4b299b 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -510,6 +510,10 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo sumlen = c->sector_size - je32_to_cpu(sm->offset); sumptr = buf + buf_size - sumlen; + /* sm->offset maybe wrong but MAGIC maybe right */ + if (sumlen > c->sector_size) + goto full_scan; + /* Now, make sure the summary itself is available */ if (sumlen > buf_size) { /* Need to kmalloc for this. */ @@ -544,6 +548,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo } } +full_scan: buf_ofs = jeb->offset; if (!buf_size) { diff --git a/fs/jfs/file.c b/fs/jfs/file.c index 33aa0cc1f8b8..10815f8dfd8b 100644 --- a/fs/jfs/file.c +++ b/fs/jfs/file.c @@ -39,7 +39,7 @@ int jfs_fsync(struct file *file, loff_t start, loff_t end, int datasync) return rc; mutex_lock(&inode->i_mutex); - if (!(inode->i_state & I_DIRTY) || + if (!(inode->i_state & I_DIRTY_ALL) || (datasync && !(inode->i_state & I_DIRTY_DATASYNC))) { /* Make sure committed changes hit the disk */ jfs_flush_journal(JFS_SBI(inode->i_sb)->log, 1); diff --git a/fs/libfs.c b/fs/libfs.c index 005843ce5dbd..b2ffdb045be4 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -948,7 +948,7 @@ int __generic_file_fsync(struct file *file, loff_t start, loff_t end, mutex_lock(&inode->i_mutex); ret = sync_mapping_buffers(inode->i_mapping); - if (!(inode->i_state & I_DIRTY)) + if (!(inode->i_state & I_DIRTY_ALL)) goto out; if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) goto out; diff --git a/fs/mount.h b/fs/mount.h index 0ad6f760ce52..6a61c2b3e385 100644 --- a/fs/mount.h +++ b/fs/mount.h @@ -2,6 +2,7 @@ #include <linux/seq_file.h> #include <linux/poll.h> #include <linux/ns_common.h> +#include <linux/fs_pin.h> struct mnt_namespace { atomic_t count; @@ -62,7 +63,8 @@ struct mount { int mnt_group_id; /* peer group identifier */ int mnt_expiry_mark; /* true if marked for expiry */ struct hlist_head mnt_pins; - struct path mnt_ex_mountpoint; + struct fs_pin mnt_umount; + struct dentry *mnt_ex_mountpoint; }; #define MNT_NS_INTERNAL ERR_PTR(-EINVAL) /* distinct from any mnt_namespace */ diff --git a/fs/namei.c b/fs/namei.c index bc35b02883bb..96ca11dea4a2 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -118,15 +118,6 @@ * POSIX.1 2.4: an empty pathname is invalid (ENOENT). * PATH_MAX includes the nul terminator --RR. */ -void final_putname(struct filename *name) -{ - if (name->separate) { - __putname(name->name); - kfree(name); - } else { - __putname(name); - } -} #define EMBEDDED_NAME_MAX (PATH_MAX - sizeof(struct filename)) @@ -145,6 +136,7 @@ getname_flags(const char __user *filename, int flags, int *empty) result = __getname(); if (unlikely(!result)) return ERR_PTR(-ENOMEM); + result->refcnt = 1; /* * First, try to embed the struct filename inside the names_cache @@ -179,6 +171,7 @@ recopy: } result->name = kname; result->separate = true; + result->refcnt = 1; max = PATH_MAX; goto recopy; } @@ -202,7 +195,7 @@ recopy: return result; error: - final_putname(result); + putname(result); return err; } @@ -212,43 +205,56 @@ getname(const char __user * filename) return getname_flags(filename, 0, NULL); } -/* - * The "getname_kernel()" interface doesn't do pathnames longer - * than EMBEDDED_NAME_MAX. Deal with it - you're a kernel user. - */ struct filename * getname_kernel(const char * filename) { struct filename *result; - char *kname; - int len; - - len = strlen(filename); - if (len >= EMBEDDED_NAME_MAX) - return ERR_PTR(-ENAMETOOLONG); + int len = strlen(filename) + 1; result = __getname(); if (unlikely(!result)) return ERR_PTR(-ENOMEM); - kname = (char *)result + sizeof(*result); - result->name = kname; + if (len <= EMBEDDED_NAME_MAX) { + result->name = (char *)(result) + sizeof(*result); + result->separate = false; + } else if (len <= PATH_MAX) { + struct filename *tmp; + + tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); + if (unlikely(!tmp)) { + __putname(result); + return ERR_PTR(-ENOMEM); + } + tmp->name = (char *)result; + tmp->separate = true; + result = tmp; + } else { + __putname(result); + return ERR_PTR(-ENAMETOOLONG); + } + memcpy((char *)result->name, filename, len); result->uptr = NULL; result->aname = NULL; - result->separate = false; + result->refcnt = 1; + audit_getname(result); - strlcpy(kname, filename, EMBEDDED_NAME_MAX); return result; } -#ifdef CONFIG_AUDITSYSCALL void putname(struct filename *name) { - if (unlikely(!audit_dummy_context())) - return audit_putname(name); - final_putname(name); + BUG_ON(name->refcnt <= 0); + + if (--name->refcnt > 0) + return; + + if (name->separate) { + __putname(name->name); + kfree(name); + } else + __putname(name); } -#endif static int check_acl(struct inode *inode, int mask) { @@ -2036,31 +2042,47 @@ static int filename_lookup(int dfd, struct filename *name, static int do_path_lookup(int dfd, const char *name, unsigned int flags, struct nameidata *nd) { - struct filename filename = { .name = name }; + struct filename *filename = getname_kernel(name); + int retval = PTR_ERR(filename); - return filename_lookup(dfd, &filename, flags, nd); + if (!IS_ERR(filename)) { + retval = filename_lookup(dfd, filename, flags, nd); + putname(filename); + } + return retval; } /* does lookup, returns the object with parent locked */ struct dentry *kern_path_locked(const char *name, struct path *path) { + struct filename *filename = getname_kernel(name); struct nameidata nd; struct dentry *d; - int err = do_path_lookup(AT_FDCWD, name, LOOKUP_PARENT, &nd); - if (err) - return ERR_PTR(err); + int err; + + if (IS_ERR(filename)) + return ERR_CAST(filename); + + err = filename_lookup(AT_FDCWD, filename, LOOKUP_PARENT, &nd); + if (err) { + d = ERR_PTR(err); + goto out; + } if (nd.last_type != LAST_NORM) { path_put(&nd.path); - return ERR_PTR(-EINVAL); + d = ERR_PTR(-EINVAL); + goto out; } mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); d = __lookup_hash(&nd.last, nd.path.dentry, 0); if (IS_ERR(d)) { mutex_unlock(&nd.path.dentry->d_inode->i_mutex); path_put(&nd.path); - return d; + goto out; } *path = nd.path; +out: + putname(filename); return d; } @@ -2351,13 +2373,17 @@ static int filename_mountpoint(int dfd, struct filename *s, struct path *path, unsigned int flags) { - int error = path_mountpoint(dfd, s->name, path, flags | LOOKUP_RCU); + int error; + if (IS_ERR(s)) + return PTR_ERR(s); + error = path_mountpoint(dfd, s->name, path, flags | LOOKUP_RCU); if (unlikely(error == -ECHILD)) error = path_mountpoint(dfd, s->name, path, flags); if (unlikely(error == -ESTALE)) error = path_mountpoint(dfd, s->name, path, flags | LOOKUP_REVAL); if (likely(!error)) audit_inode(s, path->dentry, 0); + putname(s); return error; } @@ -2379,21 +2405,14 @@ int user_path_mountpoint_at(int dfd, const char __user *name, unsigned int flags, struct path *path) { - struct filename *s = getname(name); - int error; - if (IS_ERR(s)) - return PTR_ERR(s); - error = filename_mountpoint(dfd, s, path, flags); - putname(s); - return error; + return filename_mountpoint(dfd, getname(name), path, flags); } int kern_path_mountpoint(int dfd, const char *name, struct path *path, unsigned int flags) { - struct filename s = {.name = name}; - return filename_mountpoint(dfd, &s, path, flags); + return filename_mountpoint(dfd, getname_kernel(name), path, flags); } EXPORT_SYMBOL(kern_path_mountpoint); @@ -3273,7 +3292,7 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt, { struct nameidata nd; struct file *file; - struct filename filename = { .name = name }; + struct filename *filename; int flags = op->lookup_flags | LOOKUP_ROOT; nd.root.mnt = mnt; @@ -3282,15 +3301,20 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt, if (d_is_symlink(dentry) && op->intent & LOOKUP_OPEN) return ERR_PTR(-ELOOP); - file = path_openat(-1, &filename, &nd, op, flags | LOOKUP_RCU); + filename = getname_kernel(name); + if (unlikely(IS_ERR(filename))) + return ERR_CAST(filename); + + file = path_openat(-1, filename, &nd, op, flags | LOOKUP_RCU); if (unlikely(file == ERR_PTR(-ECHILD))) - file = path_openat(-1, &filename, &nd, op, flags); + file = path_openat(-1, filename, &nd, op, flags); if (unlikely(file == ERR_PTR(-ESTALE))) - file = path_openat(-1, &filename, &nd, op, flags | LOOKUP_REVAL); + file = path_openat(-1, filename, &nd, op, flags | LOOKUP_REVAL); + putname(filename); return file; } -struct dentry *kern_path_create(int dfd, const char *pathname, +static struct dentry *filename_create(int dfd, struct filename *name, struct path *path, unsigned int lookup_flags) { struct dentry *dentry = ERR_PTR(-EEXIST); @@ -3305,7 +3329,7 @@ struct dentry *kern_path_create(int dfd, const char *pathname, */ lookup_flags &= LOOKUP_REVAL; - error = do_path_lookup(dfd, pathname, LOOKUP_PARENT|lookup_flags, &nd); + error = filename_lookup(dfd, name, LOOKUP_PARENT|lookup_flags, &nd); if (error) return ERR_PTR(error); @@ -3359,6 +3383,19 @@ out: path_put(&nd.path); return dentry; } + +struct dentry *kern_path_create(int dfd, const char *pathname, + struct path *path, unsigned int lookup_flags) +{ + struct filename *filename = getname_kernel(pathname); + struct dentry *res; + + if (IS_ERR(filename)) + return ERR_CAST(filename); + res = filename_create(dfd, filename, path, lookup_flags); + putname(filename); + return res; +} EXPORT_SYMBOL(kern_path_create); void done_path_create(struct path *path, struct dentry *dentry) @@ -3377,7 +3414,7 @@ struct dentry *user_path_create(int dfd, const char __user *pathname, struct dentry *res; if (IS_ERR(tmp)) return ERR_CAST(tmp); - res = kern_path_create(dfd, tmp->name, path, lookup_flags); + res = filename_create(dfd, tmp, path, lookup_flags); putname(tmp); return res; } diff --git a/fs/namespace.c b/fs/namespace.c index 6dae553dd69c..72a286e0d33e 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -190,6 +190,14 @@ unsigned int mnt_get_count(struct mount *mnt) #endif } +static void drop_mountpoint(struct fs_pin *p) +{ + struct mount *m = container_of(p, struct mount, mnt_umount); + dput(m->mnt_ex_mountpoint); + pin_remove(p); + mntput(&m->mnt); +} + static struct mount *alloc_vfsmnt(const char *name) { struct mount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL); @@ -229,6 +237,7 @@ static struct mount *alloc_vfsmnt(const char *name) #ifdef CONFIG_FSNOTIFY INIT_HLIST_HEAD(&mnt->mnt_fsnotify_marks); #endif + init_fs_pin(&mnt->mnt_umount, drop_mountpoint); } return mnt; @@ -1289,7 +1298,6 @@ static HLIST_HEAD(unmounted); /* protected by namespace_sem */ static void namespace_unlock(void) { - struct mount *mnt; struct hlist_head head = unmounted; if (likely(hlist_empty(&head))) { @@ -1299,23 +1307,11 @@ static void namespace_unlock(void) head.first->pprev = &head.first; INIT_HLIST_HEAD(&unmounted); - - /* undo decrements we'd done in umount_tree() */ - hlist_for_each_entry(mnt, &head, mnt_hash) - if (mnt->mnt_ex_mountpoint.mnt) - mntget(mnt->mnt_ex_mountpoint.mnt); - up_write(&namespace_sem); synchronize_rcu(); - while (!hlist_empty(&head)) { - mnt = hlist_entry(head.first, struct mount, mnt_hash); - hlist_del_init(&mnt->mnt_hash); - if (mnt->mnt_ex_mountpoint.mnt) - path_put(&mnt->mnt_ex_mountpoint); - mntput(&mnt->mnt); - } + group_pin_kill(&head); } static inline void namespace_lock(void) @@ -1334,7 +1330,6 @@ void umount_tree(struct mount *mnt, int how) { HLIST_HEAD(tmp_list); struct mount *p; - struct mount *last = NULL; for (p = mnt; p; p = next_mnt(p, mnt)) { hlist_del_init_rcu(&p->mnt_hash); @@ -1347,33 +1342,28 @@ void umount_tree(struct mount *mnt, int how) if (how) propagate_umount(&tmp_list); - hlist_for_each_entry(p, &tmp_list, mnt_hash) { + while (!hlist_empty(&tmp_list)) { + p = hlist_entry(tmp_list.first, struct mount, mnt_hash); + hlist_del_init_rcu(&p->mnt_hash); list_del_init(&p->mnt_expire); list_del_init(&p->mnt_list); __touch_mnt_namespace(p->mnt_ns); p->mnt_ns = NULL; if (how < 2) p->mnt.mnt_flags |= MNT_SYNC_UMOUNT; + + pin_insert_group(&p->mnt_umount, &p->mnt_parent->mnt, &unmounted); if (mnt_has_parent(p)) { hlist_del_init(&p->mnt_mp_list); put_mountpoint(p->mnt_mp); mnt_add_count(p->mnt_parent, -1); - /* move the reference to mountpoint into ->mnt_ex_mountpoint */ - p->mnt_ex_mountpoint.dentry = p->mnt_mountpoint; - p->mnt_ex_mountpoint.mnt = &p->mnt_parent->mnt; + /* old mountpoint will be dropped when we can do that */ + p->mnt_ex_mountpoint = p->mnt_mountpoint; p->mnt_mountpoint = p->mnt.mnt_root; p->mnt_parent = p; p->mnt_mp = NULL; } change_mnt_propagation(p, MS_PRIVATE); - last = p; - } - if (last) { - last->mnt_hash.next = unmounted.first; - if (unmounted.first) - unmounted.first->pprev = &last->mnt_hash.next; - unmounted.first = tmp_list.first; - unmounted.first->pprev = &unmounted.first; } } diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index 008960101520..e7ca827d7694 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -77,6 +77,7 @@ static int ncp_hash_dentry(const struct dentry *, struct qstr *); static int ncp_compare_dentry(const struct dentry *, const struct dentry *, unsigned int, const char *, const struct qstr *); static int ncp_delete_dentry(const struct dentry *); +static void ncp_d_prune(struct dentry *dentry); const struct dentry_operations ncp_dentry_operations = { @@ -84,6 +85,7 @@ const struct dentry_operations ncp_dentry_operations = .d_hash = ncp_hash_dentry, .d_compare = ncp_compare_dentry, .d_delete = ncp_delete_dentry, + .d_prune = ncp_d_prune, }; #define ncp_namespace(i) (NCP_SERVER(i)->name_space[NCP_FINFO(i)->volNumber]) @@ -384,42 +386,6 @@ finished: return val; } -static struct dentry * -ncp_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos) -{ - struct dentry *dent = dentry; - - if (d_validate(dent, parent)) { - if (dent->d_name.len <= NCP_MAXPATHLEN && - (unsigned long)dent->d_fsdata == fpos) { - if (!dent->d_inode) { - dput(dent); - dent = NULL; - } - return dent; - } - dput(dent); - } - - /* If a pointer is invalid, we search the dentry. */ - spin_lock(&parent->d_lock); - list_for_each_entry(dent, &parent->d_subdirs, d_child) { - if ((unsigned long)dent->d_fsdata == fpos) { - if (dent->d_inode) - dget(dent); - else - dent = NULL; - spin_unlock(&parent->d_lock); - goto out; - } - } - spin_unlock(&parent->d_lock); - return NULL; - -out: - return dent; -} - static time_t ncp_obtain_mtime(struct dentry *dentry) { struct inode *inode = dentry->d_inode; @@ -435,6 +401,20 @@ static time_t ncp_obtain_mtime(struct dentry *dentry) return ncp_date_dos2unix(i.modifyTime, i.modifyDate); } +static inline void +ncp_invalidate_dircache_entries(struct dentry *parent) +{ + struct ncp_server *server = NCP_SERVER(parent->d_inode); + struct dentry *dentry; + + spin_lock(&parent->d_lock); + list_for_each_entry(dentry, &parent->d_subdirs, d_child) { + dentry->d_fsdata = NULL; + ncp_age_dentry(server, dentry); + } + spin_unlock(&parent->d_lock); +} + static int ncp_readdir(struct file *file, struct dir_context *ctx) { struct dentry *dentry = file->f_path.dentry; @@ -500,10 +480,21 @@ static int ncp_readdir(struct file *file, struct dir_context *ctx) struct dentry *dent; bool over; - dent = ncp_dget_fpos(ctl.cache->dentry[ctl.idx], - dentry, ctx->pos); - if (!dent) + spin_lock(&dentry->d_lock); + if (!(NCP_FINFO(inode)->flags & NCPI_DIR_CACHE)) { + spin_unlock(&dentry->d_lock); + goto invalid_cache; + } + dent = ctl.cache->dentry[ctl.idx]; + if (unlikely(!lockref_get_not_dead(&dent->d_lockref))) { + spin_unlock(&dentry->d_lock); + goto invalid_cache; + } + spin_unlock(&dentry->d_lock); + if (!dent->d_inode) { + dput(dent); goto invalid_cache; + } over = !dir_emit(ctx, dent->d_name.name, dent->d_name.len, dent->d_inode->i_ino, DT_UNKNOWN); @@ -548,6 +539,9 @@ init_cache: ctl.filled = 0; ctl.valid = 1; read_really: + spin_lock(&dentry->d_lock); + NCP_FINFO(inode)->flags |= NCPI_DIR_CACHE; + spin_unlock(&dentry->d_lock); if (ncp_is_server_root(inode)) { ncp_read_volume_list(file, ctx, &ctl); } else { @@ -573,6 +567,13 @@ out: return result; } +static void ncp_d_prune(struct dentry *dentry) +{ + if (!dentry->d_fsdata) /* not referenced from page cache */ + return; + NCP_FINFO(dentry->d_parent->d_inode)->flags &= ~NCPI_DIR_CACHE; +} + static int ncp_fill_cache(struct file *file, struct dir_context *ctx, struct ncp_cache_control *ctrl, struct ncp_entry_info *entry, @@ -630,6 +631,10 @@ ncp_fill_cache(struct file *file, struct dir_context *ctx, d_instantiate(newdent, inode); if (!hashed) d_rehash(newdent); + } else { + spin_lock(&dentry->d_lock); + NCP_FINFO(inode)->flags &= ~NCPI_DIR_CACHE; + spin_unlock(&dentry->d_lock); } } else { struct inode *inode = newdent->d_inode; @@ -639,12 +644,6 @@ ncp_fill_cache(struct file *file, struct dir_context *ctx, mutex_unlock(&inode->i_mutex); } - if (newdent->d_inode) { - ino = newdent->d_inode->i_ino; - newdent->d_fsdata = (void *) ctl.fpos; - ncp_new_dentry(newdent); - } - if (ctl.idx >= NCP_DIRCACHE_SIZE) { if (ctl.page) { kunmap(ctl.page); @@ -660,8 +659,13 @@ ncp_fill_cache(struct file *file, struct dir_context *ctx, ctl.cache = kmap(ctl.page); } if (ctl.cache) { - ctl.cache->dentry[ctl.idx] = newdent; - valid = 1; + if (newdent->d_inode) { + newdent->d_fsdata = newdent; + ctl.cache->dentry[ctl.idx] = newdent; + ino = newdent->d_inode->i_ino; + ncp_new_dentry(newdent); + } + valid = 1; } dput(newdent); end_advance: diff --git a/fs/ncpfs/ncp_fs_i.h b/fs/ncpfs/ncp_fs_i.h index 4b0bec477846..c4794504f843 100644 --- a/fs/ncpfs/ncp_fs_i.h +++ b/fs/ncpfs/ncp_fs_i.h @@ -22,6 +22,7 @@ struct ncp_inode_info { int access; int flags; #define NCPI_KLUDGE_SYMLINK 0x0001 +#define NCPI_DIR_CACHE 0x0002 __u8 file_handle[6]; struct inode vfs_inode; }; diff --git a/fs/ncpfs/ncplib_kernel.h b/fs/ncpfs/ncplib_kernel.h index b785f74bfe3c..250e443a07f3 100644 --- a/fs/ncpfs/ncplib_kernel.h +++ b/fs/ncpfs/ncplib_kernel.h @@ -184,36 +184,6 @@ ncp_new_dentry(struct dentry* dentry) dentry->d_time = jiffies; } -static inline void -ncp_renew_dentries(struct dentry *parent) -{ - struct ncp_server *server = NCP_SERVER(parent->d_inode); - struct dentry *dentry; - - spin_lock(&parent->d_lock); - list_for_each_entry(dentry, &parent->d_subdirs, d_child) { - if (dentry->d_fsdata == NULL) - ncp_age_dentry(server, dentry); - else - ncp_new_dentry(dentry); - } - spin_unlock(&parent->d_lock); -} - -static inline void -ncp_invalidate_dircache_entries(struct dentry *parent) -{ - struct ncp_server *server = NCP_SERVER(parent->d_inode); - struct dentry *dentry; - - spin_lock(&parent->d_lock); - list_for_each_entry(dentry, &parent->d_subdirs, d_child) { - dentry->d_fsdata = NULL; - ncp_age_dentry(server, dentry); - } - spin_unlock(&parent->d_lock); -} - struct ncp_cache_head { time_t mtime; unsigned long time; /* cache age */ diff --git a/fs/nfsd/nfsfh.h b/fs/nfsd/nfsfh.h index 84cae2079d21..f22920442172 100644 --- a/fs/nfsd/nfsfh.h +++ b/fs/nfsd/nfsfh.h @@ -200,7 +200,7 @@ static inline bool fh_fsid_match(struct knfsd_fh *fh1, struct knfsd_fh *fh2) { if (fh1->fh_fsid_type != fh2->fh_fsid_type) return false; - if (memcmp(fh1->fh_fsid, fh2->fh_fsid, key_len(fh1->fh_fsid_type) != 0)) + if (memcmp(fh1->fh_fsid, fh2->fh_fsid, key_len(fh1->fh_fsid_type)) != 0) return false; return true; } diff --git a/fs/nfsd/pnfs.h b/fs/nfsd/pnfs.h index fedb4d620a81..d4c4453674c6 100644 --- a/fs/nfsd/pnfs.h +++ b/fs/nfsd/pnfs.h @@ -1,6 +1,7 @@ #ifndef _FS_NFSD_PNFS_H #define _FS_NFSD_PNFS_H 1 +#ifdef CONFIG_NFSD_V4 #include <linux/exportfs.h> #include <linux/nfsd/export.h> @@ -50,6 +51,7 @@ __be32 nfsd4_return_client_layouts(struct svc_rqst *rqstp, int nfsd4_set_deviceid(struct nfsd4_deviceid *id, const struct svc_fh *fhp, u32 device_generation); struct nfsd4_deviceid_map *nfsd4_find_devid_map(int idx); +#endif /* CONFIG_NFSD_V4 */ #ifdef CONFIG_NFSD_PNFS void nfsd4_setup_layout_type(struct svc_export *exp); @@ -59,6 +61,9 @@ void nfsd4_return_all_file_layouts(struct nfs4_client *clp, int nfsd4_init_pnfs(void); void nfsd4_exit_pnfs(void); #else +struct nfs4_client; +struct nfs4_file; + static inline void nfsd4_setup_layout_type(struct svc_export *exp) { } diff --git a/fs/open.c b/fs/open.c index a293c2020676..33f9cbf2610b 100644 --- a/fs/open.c +++ b/fs/open.c @@ -968,8 +968,14 @@ struct file *file_open_name(struct filename *name, int flags, umode_t mode) */ struct file *filp_open(const char *filename, int flags, umode_t mode) { - struct filename name = {.name = filename}; - return file_open_name(&name, flags, mode); + struct filename *name = getname_kernel(filename); + struct file *file = ERR_CAST(name); + + if (!IS_ERR(name)) { + file = file_open_name(name, flags, mode); + putname(name); + } + return file; } EXPORT_SYMBOL(filp_open); diff --git a/fs/proc/generic.c b/fs/proc/generic.c index de14e46fd807..3309f59d421b 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -350,29 +350,12 @@ static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp if (ret) return ret; - if (S_ISDIR(dp->mode)) { - dp->proc_fops = &proc_dir_operations; - dp->proc_iops = &proc_dir_inode_operations; - dir->nlink++; - } else if (S_ISLNK(dp->mode)) { - dp->proc_iops = &proc_link_inode_operations; - } else if (S_ISREG(dp->mode)) { - BUG_ON(dp->proc_fops == NULL); - dp->proc_iops = &proc_file_inode_operations; - } else { - WARN_ON(1); - proc_free_inum(dp->low_ino); - return -EINVAL; - } - spin_lock(&proc_subdir_lock); dp->parent = dir; if (pde_subdir_insert(dir, dp) == false) { WARN(1, "proc_dir_entry '%s/%s' already registered\n", dir->name, dp->name); spin_unlock(&proc_subdir_lock); - if (S_ISDIR(dp->mode)) - dir->nlink--; proc_free_inum(dp->low_ino); return -EEXIST; } @@ -431,6 +414,7 @@ struct proc_dir_entry *proc_symlink(const char *name, ent->data = kmalloc((ent->size=strlen(dest))+1, GFP_KERNEL); if (ent->data) { strcpy((char*)ent->data,dest); + ent->proc_iops = &proc_link_inode_operations; if (proc_register(parent, ent) < 0) { kfree(ent->data); kfree(ent); @@ -456,8 +440,12 @@ struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode, ent = __proc_create(&parent, name, S_IFDIR | mode, 2); if (ent) { ent->data = data; + ent->proc_fops = &proc_dir_operations; + ent->proc_iops = &proc_dir_inode_operations; + parent->nlink++; if (proc_register(parent, ent) < 0) { kfree(ent); + parent->nlink--; ent = NULL; } } @@ -493,6 +481,8 @@ struct proc_dir_entry *proc_create_data(const char *name, umode_t mode, return NULL; } + BUG_ON(proc_fops == NULL); + if ((mode & S_IALLUGO) == 0) mode |= S_IRUGO; pde = __proc_create(&parent, name, mode, 1); @@ -500,6 +490,7 @@ struct proc_dir_entry *proc_create_data(const char *name, umode_t mode, goto out; pde->proc_fops = proc_fops; pde->data = data; + pde->proc_iops = &proc_file_inode_operations; if (proc_register(parent, pde) < 0) goto out_free; return pde; diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c index a90d6d354199..4e61388ec03d 100644 --- a/fs/proc/vmcore.c +++ b/fs/proc/vmcore.c @@ -546,8 +546,8 @@ static int __init update_note_header_size_elf64(const Elf64_Ehdr *ehdr_ptr) nhdr_ptr = notes_section; while (nhdr_ptr->n_namesz != 0) { sz = sizeof(Elf64_Nhdr) + - ((nhdr_ptr->n_namesz + 3) & ~3) + - ((nhdr_ptr->n_descsz + 3) & ~3); + (((u64)nhdr_ptr->n_namesz + 3) & ~3) + + (((u64)nhdr_ptr->n_descsz + 3) & ~3); if ((real_sz + sz) > max_sz) { pr_warn("Warning: Exceeded p_memsz, dropping PT_NOTE entry n_namesz=0x%x, n_descsz=0x%x\n", nhdr_ptr->n_namesz, nhdr_ptr->n_descsz); @@ -732,8 +732,8 @@ static int __init update_note_header_size_elf32(const Elf32_Ehdr *ehdr_ptr) nhdr_ptr = notes_section; while (nhdr_ptr->n_namesz != 0) { sz = sizeof(Elf32_Nhdr) + - ((nhdr_ptr->n_namesz + 3) & ~3) + - ((nhdr_ptr->n_descsz + 3) & ~3); + (((u64)nhdr_ptr->n_namesz + 3) & ~3) + + (((u64)nhdr_ptr->n_descsz + 3) & ~3); if ((real_sz + sz) > max_sz) { pr_warn("Warning: Exceeded p_memsz, dropping PT_NOTE entry n_namesz=0x%x, n_descsz=0x%x\n", nhdr_ptr->n_namesz, nhdr_ptr->n_descsz); diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c index 0f96f71ab32b..8db932da4009 100644 --- a/fs/proc_namespace.c +++ b/fs/proc_namespace.c @@ -44,6 +44,7 @@ static int show_sb_opts(struct seq_file *m, struct super_block *sb) { MS_SYNCHRONOUS, ",sync" }, { MS_DIRSYNC, ",dirsync" }, { MS_MANDLOCK, ",mand" }, + { MS_LAZYTIME, ",lazytime" }, { 0, NULL } }; const struct proc_fs_info *fs_infop; diff --git a/fs/read_write.c b/fs/read_write.c index 4060691e78f7..8e1b68786d66 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -333,6 +333,52 @@ out_putf: } #endif +ssize_t vfs_iter_read(struct file *file, struct iov_iter *iter, loff_t *ppos) +{ + struct kiocb kiocb; + ssize_t ret; + + if (!file->f_op->read_iter) + return -EINVAL; + + init_sync_kiocb(&kiocb, file); + kiocb.ki_pos = *ppos; + kiocb.ki_nbytes = iov_iter_count(iter); + + iter->type |= READ; + ret = file->f_op->read_iter(&kiocb, iter); + if (ret == -EIOCBQUEUED) + ret = wait_on_sync_kiocb(&kiocb); + + if (ret > 0) + *ppos = kiocb.ki_pos; + return ret; +} +EXPORT_SYMBOL(vfs_iter_read); + +ssize_t vfs_iter_write(struct file *file, struct iov_iter *iter, loff_t *ppos) +{ + struct kiocb kiocb; + ssize_t ret; + + if (!file->f_op->write_iter) + return -EINVAL; + + init_sync_kiocb(&kiocb, file); + kiocb.ki_pos = *ppos; + kiocb.ki_nbytes = iov_iter_count(iter); + + iter->type |= WRITE; + ret = file->f_op->write_iter(&kiocb, iter); + if (ret == -EIOCBQUEUED) + ret = wait_on_sync_kiocb(&kiocb); + + if (ret > 0) + *ppos = kiocb.ki_pos; + return ret; +} +EXPORT_SYMBOL(vfs_iter_write); + /* * rw_verify_area doesn't like huge counts. We limit * them to something that fits in "int" so that others diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index a7eec9888f10..e72401e1f995 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -2766,7 +2766,7 @@ static int reiserfs_write_begin(struct file *file, int old_ref = 0; inode = mapping->host; - *fsdata = 0; + *fsdata = NULL; if (flags & AOP_FLAG_CONT_EXPAND && (pos & (inode->i_sb->s_blocksize - 1)) == 0) { pos ++; diff --git a/fs/splice.c b/fs/splice.c index 75c6058eabf2..7968da96bebb 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -961,7 +961,6 @@ iter_file_splice_write(struct pipe_inode_info *pipe, struct file *out, splice_from_pipe_begin(&sd); while (sd.total_len) { struct iov_iter from; - struct kiocb kiocb; size_t left; int n, idx; @@ -1005,29 +1004,15 @@ iter_file_splice_write(struct pipe_inode_info *pipe, struct file *out, left -= this_len; } - /* ... iov_iter */ - from.type = ITER_BVEC | WRITE; - from.bvec = array; - from.nr_segs = n; - from.count = sd.total_len - left; - from.iov_offset = 0; - - /* ... and iocb */ - init_sync_kiocb(&kiocb, out); - kiocb.ki_pos = sd.pos; - kiocb.ki_nbytes = sd.total_len - left; - - /* now, send it */ - ret = out->f_op->write_iter(&kiocb, &from); - if (-EIOCBQUEUED == ret) - ret = wait_on_sync_kiocb(&kiocb); - + iov_iter_bvec(&from, ITER_BVEC | WRITE, array, n, + sd.total_len - left); + ret = vfs_iter_write(out, &from, &sd.pos); if (ret <= 0) break; sd.num_spliced += ret; sd.total_len -= ret; - *ppos = sd.pos = kiocb.ki_pos; + *ppos = sd.pos; /* dismiss the fully eaten buffers, adjust the partial one */ while (ret) { diff --git a/fs/super.c b/fs/super.c index 1facd2c282e5..65a53efc1cf4 100644 --- a/fs/super.c +++ b/fs/super.c @@ -715,9 +715,9 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force) remount_ro = (flags & MS_RDONLY) && !(sb->s_flags & MS_RDONLY); if (remount_ro) { - if (sb->s_pins.first) { + if (!hlist_empty(&sb->s_pins)) { up_write(&sb->s_umount); - sb_pin_kill(sb); + group_pin_kill(&sb->s_pins); down_write(&sb->s_umount); if (!sb->s_root) return 0; diff --git a/fs/sync.c b/fs/sync.c index 01d9f18a70b5..fbc98ee62044 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -177,8 +177,16 @@ SYSCALL_DEFINE1(syncfs, int, fd) */ int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync) { + struct inode *inode = file->f_mapping->host; + if (!file->f_op->fsync) return -EINVAL; + if (!datasync && (inode->i_state & I_DIRTY_TIME)) { + spin_lock(&inode->i_lock); + inode->i_state &= ~I_DIRTY_TIME; + spin_unlock(&inode->i_lock); + mark_inode_dirty_sync(inode); + } return file->f_op->fsync(file, start, end, datasync); } EXPORT_SYMBOL(vfs_fsync_range); diff --git a/fs/ufs/super.c b/fs/ufs/super.c index da73801301d5..8092d3759a5e 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -95,22 +95,18 @@ void lock_ufs(struct super_block *sb) { -#if defined(CONFIG_SMP) || defined (CONFIG_PREEMPT) struct ufs_sb_info *sbi = UFS_SB(sb); mutex_lock(&sbi->mutex); sbi->mutex_owner = current; -#endif } void unlock_ufs(struct super_block *sb) { -#if defined(CONFIG_SMP) || defined (CONFIG_PREEMPT) struct ufs_sb_info *sbi = UFS_SB(sb); sbi->mutex_owner = NULL; mutex_unlock(&sbi->mutex); -#endif } static struct inode *ufs_nfs_get_inode(struct super_block *sb, u64 ino, u32 generation) @@ -1415,9 +1411,11 @@ static struct kmem_cache * ufs_inode_cachep; static struct inode *ufs_alloc_inode(struct super_block *sb) { struct ufs_inode_info *ei; - ei = (struct ufs_inode_info *)kmem_cache_alloc(ufs_inode_cachep, GFP_NOFS); + + ei = kmem_cache_alloc(ufs_inode_cachep, GFP_NOFS); if (!ei) return NULL; + ei->vfs_inode.i_version = 1; return &ei->vfs_inode; } diff --git a/include/linux/audit.h b/include/linux/audit.h index 599f3bd2d6c5..c2e7e3a83965 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -127,7 +127,6 @@ extern void __audit_syscall_entry(int major, unsigned long a0, unsigned long a1, extern void __audit_syscall_exit(int ret_success, long ret_value); extern struct filename *__audit_reusename(const __user char *uptr); extern void __audit_getname(struct filename *name); -extern void audit_putname(struct filename *name); #define AUDIT_INODE_PARENT 1 /* dentry represents the parent */ #define AUDIT_INODE_HIDDEN 2 /* audit record should be hidden */ @@ -352,8 +351,6 @@ static inline struct filename *audit_reusename(const __user char *name) } static inline void audit_getname(struct filename *name) { } -static inline void audit_putname(struct filename *name) -{ } static inline void __audit_inode(struct filename *name, const struct dentry *dentry, unsigned int flags) diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index d94077fea1f8..aff923ae8c4b 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -55,6 +55,7 @@ struct bdi_writeback { struct list_head b_dirty; /* dirty inodes */ struct list_head b_io; /* parked for writeback */ struct list_head b_more_io; /* parked for more writeback */ + struct list_head b_dirty_time; /* time stamps are dirty */ spinlock_t list_lock; /* protects the b_* lists */ }; diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index ab70f3bc44ad..f551a9299ac9 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -50,6 +50,15 @@ struct cpuidle_state { int index); int (*enter_dead) (struct cpuidle_device *dev, int index); + + /* + * CPUs execute ->enter_freeze with the local tick or entire timekeeping + * suspended, so it must not re-enable interrupts at any point (even + * temporarily) or attempt to change states of clock event devices. + */ + void (*enter_freeze) (struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index); }; /* Idle State Flags */ @@ -141,7 +150,7 @@ extern void cpuidle_resume(void); extern int cpuidle_enable_device(struct cpuidle_device *dev); extern void cpuidle_disable_device(struct cpuidle_device *dev); extern int cpuidle_play_dead(void); -extern void cpuidle_use_deepest_state(bool enable); +extern void cpuidle_enter_freeze(void); extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev); #else @@ -174,7 +183,7 @@ static inline int cpuidle_enable_device(struct cpuidle_device *dev) {return -ENODEV; } static inline void cpuidle_disable_device(struct cpuidle_device *dev) { } static inline int cpuidle_play_dead(void) {return -ENODEV; } -static inline void cpuidle_use_deepest_state(bool enable) {} +static inline void cpuidle_enter_freeze(void) { } static inline struct cpuidle_driver *cpuidle_get_cpu_driver( struct cpuidle_device *dev) {return NULL; } #endif diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 5a813988e6d4..92c08cf7670e 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -319,9 +319,6 @@ static inline unsigned d_count(const struct dentry *dentry) return dentry->d_lockref.count; } -/* validate "insecure" dentry pointer */ -extern int d_validate(struct dentry *, struct dentry *); - /* * helper function for dentry_operations.d_dname() members */ diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index da4c4983adbe..cb25af461054 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -51,11 +51,21 @@ struct dentry *debugfs_create_file(const char *name, umode_t mode, struct dentry *parent, void *data, const struct file_operations *fops); +struct dentry *debugfs_create_file_size(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *fops, + loff_t file_size); + struct dentry *debugfs_create_dir(const char *name, struct dentry *parent); struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent, const char *dest); +struct dentry *debugfs_create_automount(const char *name, + struct dentry *parent, + struct vfsmount *(*f)(void *), + void *data); + void debugfs_remove(struct dentry *dentry); void debugfs_remove_recursive(struct dentry *dentry); @@ -124,6 +134,14 @@ static inline struct dentry *debugfs_create_file(const char *name, umode_t mode, return ERR_PTR(-ENODEV); } +static inline struct dentry *debugfs_create_file_size(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *fops, + loff_t file_size) +{ + return ERR_PTR(-ENODEV); +} + static inline struct dentry *debugfs_create_dir(const char *name, struct dentry *parent) { diff --git a/include/linux/fs.h b/include/linux/fs.h index ed5a0900b94d..447932aed1e1 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1790,8 +1790,12 @@ struct super_operations { #define __I_DIO_WAKEUP 9 #define I_DIO_WAKEUP (1 << I_DIO_WAKEUP) #define I_LINKABLE (1 << 10) +#define I_DIRTY_TIME (1 << 11) +#define __I_DIRTY_TIME_EXPIRED 12 +#define I_DIRTY_TIME_EXPIRED (1 << __I_DIRTY_TIME_EXPIRED) #define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES) +#define I_DIRTY_ALL (I_DIRTY | I_DIRTY_TIME) extern void __mark_inode_dirty(struct inode *, int); static inline void mark_inode_dirty(struct inode *inode) @@ -1954,6 +1958,7 @@ extern int current_umask(void); extern void ihold(struct inode * inode); extern void iput(struct inode *); +extern int generic_update_time(struct inode *, struct timespec *, int); static inline struct inode *file_inode(const struct file *f) { @@ -2141,6 +2146,7 @@ struct filename { const char *name; /* pointer to actual string */ const __user char *uptr; /* original userland pointer */ struct audit_names *aname; + int refcnt; bool separate; /* should "name" be freed? */ }; @@ -2162,6 +2168,7 @@ extern int filp_close(struct file *, fl_owner_t id); extern struct filename *getname_flags(const char __user *, int, int *); extern struct filename *getname(const char __user *); extern struct filename *getname_kernel(const char *); +extern void putname(struct filename *name); enum { FILE_CREATED = 1, @@ -2182,15 +2189,8 @@ extern void __init vfs_caches_init(unsigned long); extern struct kmem_cache *names_cachep; -extern void final_putname(struct filename *name); - #define __getname() kmem_cache_alloc(names_cachep, GFP_KERNEL) #define __putname(name) kmem_cache_free(names_cachep, (void *)(name)) -#ifndef CONFIG_AUDITSYSCALL -#define putname(name) final_putname(name) -#else -extern void putname(struct filename *name); -#endif #ifdef CONFIG_BLOCK extern int register_blkdev(unsigned int, const char *); @@ -2497,6 +2497,11 @@ extern struct inode *ilookup(struct super_block *sb, unsigned long ino); extern struct inode * iget5_locked(struct super_block *, unsigned long, int (*test)(struct inode *, void *), int (*set)(struct inode *, void *), void *); extern struct inode * iget_locked(struct super_block *, unsigned long); +extern struct inode *find_inode_nowait(struct super_block *, + unsigned long, + int (*match)(struct inode *, + unsigned long, void *), + void *data); extern int insert_inode_locked4(struct inode *, unsigned long, int (*test)(struct inode *, void *), void *); extern int insert_inode_locked(struct inode *); #ifdef CONFIG_DEBUG_LOCK_ALLOC @@ -2553,6 +2558,9 @@ extern ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t l extern ssize_t new_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos); extern ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos); +ssize_t vfs_iter_read(struct file *file, struct iov_iter *iter, loff_t *ppos); +ssize_t vfs_iter_write(struct file *file, struct iov_iter *iter, loff_t *ppos); + /* fs/block_dev.c */ extern ssize_t blkdev_read_iter(struct kiocb *iocb, struct iov_iter *to); extern ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from); diff --git a/include/linux/fs_pin.h b/include/linux/fs_pin.h index f66525e72ccf..9dc4e0384bfb 100644 --- a/include/linux/fs_pin.h +++ b/include/linux/fs_pin.h @@ -1,17 +1,22 @@ -#include <linux/fs.h> +#include <linux/wait.h> struct fs_pin { - atomic_long_t count; - union { - struct { - struct hlist_node s_list; - struct hlist_node m_list; - }; - struct rcu_head rcu; - }; + wait_queue_head_t wait; + int done; + struct hlist_node s_list; + struct hlist_node m_list; void (*kill)(struct fs_pin *); }; -void pin_put(struct fs_pin *); +struct vfsmount; + +static inline void init_fs_pin(struct fs_pin *p, void (*kill)(struct fs_pin *)) +{ + init_waitqueue_head(&p->wait); + p->kill = kill; +} + void pin_remove(struct fs_pin *); +void pin_insert_group(struct fs_pin *, struct vfsmount *, struct hlist_head *); void pin_insert(struct fs_pin *, struct vfsmount *); +void pin_kill(struct fs_pin *); diff --git a/include/linux/kexec.h b/include/linux/kexec.h index 9d957b7ae095..e60a745ac198 100644 --- a/include/linux/kexec.h +++ b/include/linux/kexec.h @@ -1,6 +1,19 @@ #ifndef LINUX_KEXEC_H #define LINUX_KEXEC_H +#define IND_DESTINATION_BIT 0 +#define IND_INDIRECTION_BIT 1 +#define IND_DONE_BIT 2 +#define IND_SOURCE_BIT 3 + +#define IND_DESTINATION (1 << IND_DESTINATION_BIT) +#define IND_INDIRECTION (1 << IND_INDIRECTION_BIT) +#define IND_DONE (1 << IND_DONE_BIT) +#define IND_SOURCE (1 << IND_SOURCE_BIT) +#define IND_FLAGS (IND_DESTINATION | IND_INDIRECTION | IND_DONE | IND_SOURCE) + +#if !defined(__ASSEMBLY__) + #include <uapi/linux/kexec.h> #ifdef CONFIG_KEXEC @@ -64,10 +77,6 @@ */ typedef unsigned long kimage_entry_t; -#define IND_DESTINATION 0x1 -#define IND_INDIRECTION 0x2 -#define IND_DONE 0x4 -#define IND_SOURCE 0x8 struct kexec_segment { /* @@ -122,8 +131,6 @@ struct kimage { kimage_entry_t *entry; kimage_entry_t *last_entry; - unsigned long destination; - unsigned long start; struct page *control_code_page; struct page *swap_page; @@ -313,4 +320,7 @@ struct task_struct; static inline void crash_kexec(struct pt_regs *regs) { } static inline int kexec_should_crash(struct task_struct *p) { return 0; } #endif /* CONFIG_KEXEC */ + +#endif /* !defined(__ASSEBMLY__) */ + #endif /* LINUX_KEXEC_H */ diff --git a/include/linux/lockref.h b/include/linux/lockref.h index 4bfde0e99ed5..b10b122dd099 100644 --- a/include/linux/lockref.h +++ b/include/linux/lockref.h @@ -28,12 +28,13 @@ struct lockref { #endif struct { spinlock_t lock; - unsigned int count; + int count; }; }; }; extern void lockref_get(struct lockref *); +extern int lockref_put_return(struct lockref *); extern int lockref_get_not_zero(struct lockref *); extern int lockref_get_or_lock(struct lockref *); extern int lockref_put_or_lock(struct lockref *); diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 2e75ab00dbf2..e530533b94be 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -364,8 +364,6 @@ struct ssb_device_id { } __attribute__((packed, aligned(2))); #define SSB_DEVICE(_vendor, _coreid, _revision) \ { .vendor = _vendor, .coreid = _coreid, .revision = _revision, } -#define SSB_DEVTABLE_END \ - { 0, }, #define SSB_ANY_VENDOR 0xFFFF #define SSB_ANY_ID 0xFFFF @@ -380,8 +378,6 @@ struct bcma_device_id { } __attribute__((packed,aligned(2))); #define BCMA_CORE(_manuf, _id, _rev, _class) \ { .manuf = _manuf, .id = _id, .rev = _rev, .class = _class, } -#define BCMA_CORETABLE_END \ - { 0, }, #define BCMA_ANY_MANUF 0xFFFF #define BCMA_ANY_ID 0xFFFF diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 3301c4c289d6..f17fa75809aa 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -227,6 +227,7 @@ struct mtd_info { int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs); int (*_suspend) (struct mtd_info *mtd); void (*_resume) (struct mtd_info *mtd); + void (*_reboot) (struct mtd_info *mtd); /* * If the driver is something smart, like UBI, it may need to maintain * its own reference counting. The below functions are only for driver. diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 63aeccf9ddc8..4720b86ee73d 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -56,6 +56,10 @@ /* Used for Spansion flashes only. */ #define SPINOR_OP_BRWR 0x17 /* Bank register write */ +/* Used for Micron flashes only. */ +#define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */ +#define SPINOR_OP_WD_EVCR 0x61 /* Write EVCR register */ + /* Status Register bits. */ #define SR_WIP 1 /* Write in progress */ #define SR_WEL 2 /* Write enable latch */ @@ -67,6 +71,9 @@ #define SR_QUAD_EN_MX 0x40 /* Macronix Quad I/O */ +/* Enhanced Volatile Configuration Register bits */ +#define EVCR_QUAD_EN_MICRON 0x80 /* Micron Quad I/O */ + /* Flag Status Register bits */ #define FSR_READY 0x80 diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index d115256ed5a2..5897b4ea5a3f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1923,13 +1923,8 @@ struct napi_gro_cb { /* Number of segments aggregated. */ u16 count; - /* This is non-zero if the packet may be of the same flow. */ - u8 same_flow; - - /* Free the skb? */ - u8 free; -#define NAPI_GRO_FREE 1 -#define NAPI_GRO_FREE_STOLEN_HEAD 2 + /* Start offset for remote checksum offload */ + u16 gro_remcsum_start; /* jiffies when first packet was created/queued */ unsigned long age; @@ -1937,6 +1932,9 @@ struct napi_gro_cb { /* Used in ipv6_gro_receive() and foo-over-udp */ u16 proto; + /* This is non-zero if the packet may be of the same flow. */ + u8 same_flow:1; + /* Used in udp_gro_receive */ u8 udp_mark:1; @@ -1946,9 +1944,16 @@ struct napi_gro_cb { /* Number of checksums via CHECKSUM_UNNECESSARY */ u8 csum_cnt:3; + /* Free the skb? */ + u8 free:2; +#define NAPI_GRO_FREE 1 +#define NAPI_GRO_FREE_STOLEN_HEAD 2 + /* Used in foo-over-udp, set in udp[46]_gro_receive */ u8 is_ipv6:1; + /* 7 bit hole */ + /* used to support CHECKSUM_COMPLETE for tunneling protocols */ __wsum csum; @@ -2242,11 +2247,20 @@ static inline void skb_gro_postpull_rcsum(struct sk_buff *skb, __sum16 __skb_gro_checksum_complete(struct sk_buff *skb); +static inline bool skb_at_gro_remcsum_start(struct sk_buff *skb) +{ + return (NAPI_GRO_CB(skb)->gro_remcsum_start - skb_headroom(skb) == + skb_gro_offset(skb)); +} + static inline bool __skb_gro_checksum_validate_needed(struct sk_buff *skb, bool zero_okay, __sum16 check) { - return (skb->ip_summed != CHECKSUM_PARTIAL && + return ((skb->ip_summed != CHECKSUM_PARTIAL || + skb_checksum_start_offset(skb) < + skb_gro_offset(skb)) && + !skb_at_gro_remcsum_start(skb) && NAPI_GRO_CB(skb)->csum_cnt == 0 && (!zero_okay || check)); } @@ -2321,20 +2335,48 @@ do { \ compute_pseudo(skb, proto)); \ } while (0) +struct gro_remcsum { + int offset; + __wsum delta; +}; + +static inline void skb_gro_remcsum_init(struct gro_remcsum *grc) +{ + grc->delta = 0; +} + static inline void skb_gro_remcsum_process(struct sk_buff *skb, void *ptr, - int start, int offset) + int start, int offset, + struct gro_remcsum *grc, + bool nopartial) { __wsum delta; BUG_ON(!NAPI_GRO_CB(skb)->csum_valid); + if (!nopartial) { + NAPI_GRO_CB(skb)->gro_remcsum_start = + ((unsigned char *)ptr + start) - skb->head; + return; + } + delta = remcsum_adjust(ptr, NAPI_GRO_CB(skb)->csum, start, offset); /* Adjust skb->csum since we changed the packet */ - skb->csum = csum_add(skb->csum, delta); NAPI_GRO_CB(skb)->csum = csum_add(NAPI_GRO_CB(skb)->csum, delta); + + grc->offset = (ptr + offset) - (void *)skb->head; + grc->delta = delta; } +static inline void skb_gro_remcsum_cleanup(struct sk_buff *skb, + struct gro_remcsum *grc) +{ + if (!grc->delta) + return; + + remcsum_unadjust((__sum16 *)(skb->head + grc->offset), grc->delta); +} static inline int dev_hard_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, diff --git a/include/linux/pid_namespace.h b/include/linux/pid_namespace.h index b9cf6c51b181..918b117a7cd3 100644 --- a/include/linux/pid_namespace.h +++ b/include/linux/pid_namespace.h @@ -19,7 +19,7 @@ struct pidmap { #define BITS_PER_PAGE_MASK (BITS_PER_PAGE-1) #define PIDMAP_ENTRIES ((PID_MAX_LIMIT+BITS_PER_PAGE-1)/BITS_PER_PAGE) -struct bsd_acct_struct; +struct fs_pin; struct pid_namespace { struct kref kref; @@ -37,7 +37,7 @@ struct pid_namespace { struct dentry *proc_thread_self; #endif #ifdef CONFIG_BSD_PROCESS_ACCT - struct bsd_acct_struct *bacct; + struct fs_pin *bacct; #endif struct user_namespace *user_ns; struct work_struct proc_work; diff --git a/include/linux/rbtree.h b/include/linux/rbtree.h index 57e75ae9910f..fb31765e935a 100644 --- a/include/linux/rbtree.h +++ b/include/linux/rbtree.h @@ -51,7 +51,7 @@ struct rb_root { #define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) -/* 'empty' nodes are nodes that are known not to be inserted in an rbree */ +/* 'empty' nodes are nodes that are known not to be inserted in an rbtree */ #define RB_EMPTY_NODE(node) \ ((node)->__rb_parent_color == (unsigned long)(node)) #define RB_CLEAR_NODE(node) \ diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 1bb36edb66b9..30007afe70b3 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -83,11 +83,15 @@ * * CHECKSUM_PARTIAL: * - * This is identical to the case for output below. This may occur on a packet + * A checksum is set up to be offloaded to a device as described in the + * output description for CHECKSUM_PARTIAL. This may occur on a packet * received directly from another Linux OS, e.g., a virtualized Linux kernel - * on the same host. The packet can be treated in the same way as - * CHECKSUM_UNNECESSARY, except that on output (i.e., forwarding) the - * checksum must be filled in by the OS or the hardware. + * on the same host, or it may be set in the input path in GRO or remote + * checksum offload. For the purposes of checksum verification, the checksum + * referred to by skb->csum_start + skb->csum_offset and any preceding + * checksums in the packet are considered verified. Any checksums in the + * packet that are after the checksum being offloaded are not considered to + * be verified. * * B. Checksumming on output. * @@ -2915,7 +2919,10 @@ __sum16 __skb_checksum_complete(struct sk_buff *skb); static inline int skb_csum_unnecessary(const struct sk_buff *skb) { - return ((skb->ip_summed & CHECKSUM_UNNECESSARY) || skb->csum_valid); + return ((skb->ip_summed == CHECKSUM_UNNECESSARY) || + skb->csum_valid || + (skb->ip_summed == CHECKSUM_PARTIAL && + skb_checksum_start_offset(skb) >= 0)); } /** @@ -3097,16 +3104,29 @@ do { \ compute_pseudo(skb, proto)); \ } while (0) +static inline void skb_remcsum_adjust_partial(struct sk_buff *skb, void *ptr, + u16 start, u16 offset) +{ + skb->ip_summed = CHECKSUM_PARTIAL; + skb->csum_start = ((unsigned char *)ptr + start) - skb->head; + skb->csum_offset = offset - start; +} + /* Update skbuf and packet to reflect the remote checksum offload operation. * When called, ptr indicates the starting point for skb->csum when * ip_summed is CHECKSUM_COMPLETE. If we need create checksum complete * here, skb_postpull_rcsum is done so skb->csum start is ptr. */ static inline void skb_remcsum_process(struct sk_buff *skb, void *ptr, - int start, int offset) + int start, int offset, bool nopartial) { __wsum delta; + if (!nopartial) { + skb_remcsum_adjust_partial(skb, ptr, start, offset); + return; + } + if (unlikely(skb->ip_summed != CHECKSUM_COMPLETE)) { __skb_checksum_complete(skb); skb_postpull_rcsum(skb, skb->data, ptr - (void *)skb->data); diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 3388c1b6f7d8..5efe743ce1e8 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -201,6 +201,21 @@ struct platform_freeze_ops { */ extern void suspend_set_ops(const struct platform_suspend_ops *ops); extern int suspend_valid_only_mem(suspend_state_t state); + +/* Suspend-to-idle state machnine. */ +enum freeze_state { + FREEZE_STATE_NONE, /* Not suspended/suspending. */ + FREEZE_STATE_ENTER, /* Enter suspend-to-idle. */ + FREEZE_STATE_WAKE, /* Wake up from suspend-to-idle. */ +}; + +extern enum freeze_state __read_mostly suspend_freeze_state; + +static inline bool idle_should_freeze(void) +{ + return unlikely(suspend_freeze_state == FREEZE_STATE_ENTER); +} + extern void freeze_set_ops(const struct platform_freeze_ops *ops); extern void freeze_wake(void); @@ -228,6 +243,7 @@ extern int pm_suspend(suspend_state_t state); static inline void suspend_set_ops(const struct platform_suspend_ops *ops) {} static inline int pm_suspend(suspend_state_t state) { return -ENOSYS; } +static inline bool idle_should_freeze(void) { return false; } static inline void freeze_set_ops(const struct platform_freeze_ops *ops) {} static inline void freeze_wake(void) {} #endif /* !CONFIG_SUSPEND */ diff --git a/include/linux/tick.h b/include/linux/tick.h index eda850ca757a..9c085dc12ae9 100644 --- a/include/linux/tick.h +++ b/include/linux/tick.h @@ -79,6 +79,9 @@ extern void __init tick_init(void); extern int tick_is_oneshot_available(void); extern struct tick_device *tick_get_device(int cpu); +extern void tick_freeze(void); +extern void tick_unfreeze(void); + # ifdef CONFIG_HIGH_RES_TIMERS extern int tick_init_highres(void); extern int tick_program_event(ktime_t expires, int force); @@ -119,6 +122,8 @@ static inline int tick_oneshot_mode_active(void) { return 0; } #else /* CONFIG_GENERIC_CLOCKEVENTS */ static inline void tick_init(void) { } +static inline void tick_freeze(void) { } +static inline void tick_unfreeze(void) { } static inline void tick_cancel_sched_timer(int cpu) { } static inline void tick_clock_notify(void) { } static inline int tick_check_oneshot_change(int allow_nohz) { return 0; } @@ -226,5 +231,4 @@ static inline void tick_nohz_task_switch(struct task_struct *tsk) __tick_nohz_task_switch(tsk); } - #endif diff --git a/include/linux/uio.h b/include/linux/uio.h index 3e0cb4ea3905..07a022641996 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -88,7 +88,9 @@ size_t iov_iter_zero(size_t bytes, struct iov_iter *); unsigned long iov_iter_alignment(const struct iov_iter *i); void iov_iter_init(struct iov_iter *i, int direction, const struct iovec *iov, unsigned long nr_segs, size_t count); -void iov_iter_kvec(struct iov_iter *i, int direction, const struct kvec *iov, +void iov_iter_kvec(struct iov_iter *i, int direction, const struct kvec *kvec, + unsigned long nr_segs, size_t count); +void iov_iter_bvec(struct iov_iter *i, int direction, const struct bio_vec *bvec, unsigned long nr_segs, size_t count); ssize_t iov_iter_get_pages(struct iov_iter *i, struct page **pages, size_t maxsize, unsigned maxpages, size_t *start); diff --git a/include/net/checksum.h b/include/net/checksum.h index e339a9513e29..0a55ac715077 100644 --- a/include/net/checksum.h +++ b/include/net/checksum.h @@ -167,4 +167,9 @@ static inline __wsum remcsum_adjust(void *ptr, __wsum csum, return delta; } +static inline void remcsum_unadjust(__sum16 *psum, __wsum delta) +{ + *psum = csum_fold(csum_sub(delta, *psum)); +} + #endif diff --git a/include/net/vxlan.h b/include/net/vxlan.h index 2927d6244481..eabd3a038674 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -128,13 +128,15 @@ struct vxlan_sock { #define VXLAN_F_REMCSUM_TX 0x200 #define VXLAN_F_REMCSUM_RX 0x400 #define VXLAN_F_GBP 0x800 +#define VXLAN_F_REMCSUM_NOPARTIAL 0x1000 /* Flags that are used in the receive patch. These flags must match in * order for a socket to be shareable */ #define VXLAN_F_RCV_FLAGS (VXLAN_F_GBP | \ VXLAN_F_UDP_ZERO_CSUM6_RX | \ - VXLAN_F_REMCSUM_RX) + VXLAN_F_REMCSUM_RX | \ + VXLAN_F_REMCSUM_NOPARTIAL) struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port, vxlan_rcv_t *rcv, void *data, diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index 6cfb841fea7c..6e5abd6d38a2 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h @@ -73,6 +73,36 @@ struct extent_status; { FALLOC_FL_ZERO_RANGE, "ZERO_RANGE"}) +TRACE_EVENT(ext4_other_inode_update_time, + TP_PROTO(struct inode *inode, ino_t orig_ino), + + TP_ARGS(inode, orig_ino), + + TP_STRUCT__entry( + __field( dev_t, dev ) + __field( ino_t, ino ) + __field( ino_t, orig_ino ) + __field( uid_t, uid ) + __field( gid_t, gid ) + __field( __u16, mode ) + ), + + TP_fast_assign( + __entry->orig_ino = orig_ino; + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->uid = i_uid_read(inode); + __entry->gid = i_gid_read(inode); + __entry->mode = inode->i_mode; + ), + + TP_printk("dev %d,%d orig_ino %lu ino %lu mode 0%o uid %u gid %u", + MAJOR(__entry->dev), MINOR(__entry->dev), + (unsigned long) __entry->orig_ino, + (unsigned long) __entry->ino, __entry->mode, + __entry->uid, __entry->gid) +); + TRACE_EVENT(ext4_free_inode, TP_PROTO(struct inode *inode), diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h index 0e9310905413..5a14ead59696 100644 --- a/include/trace/events/writeback.h +++ b/include/trace/events/writeback.h @@ -18,6 +18,8 @@ {I_FREEING, "I_FREEING"}, \ {I_CLEAR, "I_CLEAR"}, \ {I_SYNC, "I_SYNC"}, \ + {I_DIRTY_TIME, "I_DIRTY_TIME"}, \ + {I_DIRTY_TIME_EXPIRED, "I_DIRTY_TIME_EXPIRED"}, \ {I_REFERENCED, "I_REFERENCED"} \ ) @@ -68,6 +70,7 @@ DECLARE_EVENT_CLASS(writeback_dirty_inode_template, TP_STRUCT__entry ( __array(char, name, 32) __field(unsigned long, ino) + __field(unsigned long, state) __field(unsigned long, flags) ), @@ -78,16 +81,25 @@ DECLARE_EVENT_CLASS(writeback_dirty_inode_template, strncpy(__entry->name, bdi->dev ? dev_name(bdi->dev) : "(unknown)", 32); __entry->ino = inode->i_ino; + __entry->state = inode->i_state; __entry->flags = flags; ), - TP_printk("bdi %s: ino=%lu flags=%s", + TP_printk("bdi %s: ino=%lu state=%s flags=%s", __entry->name, __entry->ino, + show_inode_state(__entry->state), show_inode_state(__entry->flags) ) ); +DEFINE_EVENT(writeback_dirty_inode_template, writeback_mark_inode_dirty, + + TP_PROTO(struct inode *inode, int flags), + + TP_ARGS(inode, flags) +); + DEFINE_EVENT(writeback_dirty_inode_template, writeback_dirty_inode_start, TP_PROTO(struct inode *inode, int flags), @@ -596,6 +608,52 @@ DEFINE_EVENT(writeback_single_inode_template, writeback_single_inode, TP_ARGS(inode, wbc, nr_to_write) ); +DECLARE_EVENT_CLASS(writeback_lazytime_template, + TP_PROTO(struct inode *inode), + + TP_ARGS(inode), + + TP_STRUCT__entry( + __field( dev_t, dev ) + __field(unsigned long, ino ) + __field(unsigned long, state ) + __field( __u16, mode ) + __field(unsigned long, dirtied_when ) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->state = inode->i_state; + __entry->mode = inode->i_mode; + __entry->dirtied_when = inode->dirtied_when; + ), + + TP_printk("dev %d,%d ino %lu dirtied %lu state %s mode 0%o", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->ino, __entry->dirtied_when, + show_inode_state(__entry->state), __entry->mode) +); + +DEFINE_EVENT(writeback_lazytime_template, writeback_lazytime, + TP_PROTO(struct inode *inode), + + TP_ARGS(inode) +); + +DEFINE_EVENT(writeback_lazytime_template, writeback_lazytime_iput, + TP_PROTO(struct inode *inode), + + TP_ARGS(inode) +); + +DEFINE_EVENT(writeback_lazytime_template, writeback_dirty_inode_enqueue, + + TP_PROTO(struct inode *inode), + + TP_ARGS(inode) +); + #endif /* _TRACE_WRITEBACK_H */ /* This part must be outside protection */ diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 7b8141bf59a7..68ceb97c458c 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -370,7 +370,6 @@ header-y += snmp.h header-y += sock_diag.h header-y += socket.h header-y += sockios.h -header-y += som.h header-y += sonet.h header-y += sonypi.h header-y += soundcard.h diff --git a/include/uapi/linux/fou.h b/include/uapi/linux/fou.h index 8df06894da23..c303588bb767 100644 --- a/include/uapi/linux/fou.h +++ b/include/uapi/linux/fou.h @@ -14,6 +14,7 @@ enum { FOU_ATTR_AF, /* u8 */ FOU_ATTR_IPPROTO, /* u8 */ FOU_ATTR_TYPE, /* u8 */ + FOU_ATTR_REMCSUM_NOPARTIAL, /* flag */ __FOU_ATTR_MAX, }; diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h index 3735fa0a6784..9b964a5920af 100644 --- a/include/uapi/linux/fs.h +++ b/include/uapi/linux/fs.h @@ -90,6 +90,7 @@ struct inodes_stat_t { #define MS_KERNMOUNT (1<<22) /* this is a kern_mount call */ #define MS_I_VERSION (1<<23) /* Update inode I_version field */ #define MS_STRICTATIME (1<<24) /* Always perform atime updates */ +#define MS_LAZYTIME (1<<25) /* Update the on-disk [acm]times lazily */ /* These sb flags are internal to the kernel */ #define MS_NOSEC (1<<28) @@ -100,7 +101,8 @@ struct inodes_stat_t { /* * Superblock flags that can be altered by MS_REMOUNT */ -#define MS_RMT_MASK (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION) +#define MS_RMT_MASK (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION|\ + MS_LAZYTIME) /* * Old magic mount flag and mask diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 0deee3eeddbf..dfd0bb22e554 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -374,6 +374,7 @@ enum { IFLA_VXLAN_REMCSUM_TX, IFLA_VXLAN_REMCSUM_RX, IFLA_VXLAN_GBP, + IFLA_VXLAN_REMCSUM_NOPARTIAL, __IFLA_VXLAN_MAX }; #define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1) diff --git a/include/uapi/linux/kexec.h b/include/uapi/linux/kexec.h index 6925f5b42f89..99048e501b88 100644 --- a/include/uapi/linux/kexec.h +++ b/include/uapi/linux/kexec.h @@ -55,12 +55,6 @@ struct kexec_segment { size_t memsz; }; -/* Load a new kernel image as described by the kexec_segment array - * consisting of passed number of segments at the entry-point address. - * The flags allow different useage types. - */ -extern int kexec_load(void *, size_t, struct kexec_segment *, - unsigned long int); #endif /* __KERNEL__ */ #endif /* _UAPILINUX_KEXEC_H */ diff --git a/include/uapi/linux/som.h b/include/uapi/linux/som.h deleted file mode 100644 index 166594e4e7be..000000000000 --- a/include/uapi/linux/som.h +++ /dev/null @@ -1,154 +0,0 @@ -#ifndef _LINUX_SOM_H -#define _LINUX_SOM_H - -/* File format definition for SOM executables / shared libraries */ - -/* we need struct timespec */ -#include <linux/time.h> - -#define SOM_PAGESIZE 4096 - -/* this is the SOM header */ -struct som_hdr { - short system_id; /* magic number - system */ - short a_magic; /* magic number - file type */ - unsigned int version_id; /* versiod ID: YYMMDDHH */ - struct timespec file_time; /* system clock */ - unsigned int entry_space; /* space for entry point */ - unsigned int entry_subspace; /* subspace for entry point */ - unsigned int entry_offset; /* offset of entry point */ - unsigned int aux_header_location; /* auxiliary header location */ - unsigned int aux_header_size; /* auxiliary header size */ - unsigned int som_length; /* length of entire SOM */ - unsigned int presumed_dp; /* compiler's DP value */ - unsigned int space_location; /* space dictionary location */ - unsigned int space_total; /* number of space entries */ - unsigned int subspace_location; /* subspace entries location */ - unsigned int subspace_total; /* number of subspace entries */ - unsigned int loader_fixup_location; /* MPE/iX loader fixup */ - unsigned int loader_fixup_total; /* number of fixup records */ - unsigned int space_strings_location; /* (sub)space names */ - unsigned int space_strings_size; /* size of strings area */ - unsigned int init_array_location; /* reserved */ - unsigned int init_array_total; /* reserved */ - unsigned int compiler_location; /* module dictionary */ - unsigned int compiler_total; /* number of modules */ - unsigned int symbol_location; /* symbol dictionary */ - unsigned int symbol_total; /* number of symbols */ - unsigned int fixup_request_location; /* fixup requests */ - unsigned int fixup_request_total; /* number of fixup requests */ - unsigned int symbol_strings_location;/* module & symbol names area */ - unsigned int symbol_strings_size; /* size of strings area */ - unsigned int unloadable_sp_location; /* unloadable spaces location */ - unsigned int unloadable_sp_size; /* size of data */ - unsigned int checksum; -}; - -/* values for system_id */ - -#define SOM_SID_PARISC_1_0 0x020b -#define SOM_SID_PARISC_1_1 0x0210 -#define SOM_SID_PARISC_2_0 0x0214 - -/* values for a_magic */ - -#define SOM_LIB_EXEC 0x0104 -#define SOM_RELOCATABLE 0x0106 -#define SOM_EXEC_NONSHARE 0x0107 -#define SOM_EXEC_SHARE 0x0108 -#define SOM_EXEC_DEMAND 0x010B -#define SOM_LIB_DYN 0x010D -#define SOM_LIB_SHARE 0x010E -#define SOM_LIB_RELOC 0x0619 - -/* values for version_id. Decimal not hex, yes. Grr. */ - -#define SOM_ID_OLD 85082112 -#define SOM_ID_NEW 87102412 - -struct aux_id { - unsigned int mandatory :1; /* the linker must understand this */ - unsigned int copy :1; /* Must be copied by the linker */ - unsigned int append :1; /* Must be merged by the linker */ - unsigned int ignore :1; /* Discard section if unknown */ - unsigned int reserved :12; - unsigned int type :16; /* Header type */ - unsigned int length; /* length of _following_ data */ -}; - -/* The Exec Auxiliary Header. Called The HP-UX Header within HP apparently. */ -struct som_exec_auxhdr { - struct aux_id som_auxhdr; - int exec_tsize; /* Text size in bytes */ - int exec_tmem; /* Address to load text at */ - int exec_tfile; /* Location of text in file */ - int exec_dsize; /* Data size in bytes */ - int exec_dmem; /* Address to load data at */ - int exec_dfile; /* Location of data in file */ - int exec_bsize; /* Uninitialised data (bss) */ - int exec_entry; /* Address to start executing */ - int exec_flags; /* loader flags */ - int exec_bfill; /* initialisation value for bss */ -}; - -/* Oh, the things people do to avoid casts. Shame it'll break with gcc's - * new aliasing rules really. - */ -union name_pt { - char * n_name; - unsigned int n_strx; -}; - -/* The Space Dictionary */ -struct space_dictionary_record { - union name_pt name; /* index to subspace name */ - unsigned int is_loadable :1; /* loadable */ - unsigned int is_defined :1; /* defined within file */ - unsigned int is_private :1; /* not sharable */ - unsigned int has_intermediate_code :1; /* contains intermediate code */ - unsigned int is_tspecific :1; /* thread specific */ - unsigned int reserved :11; /* for future expansion */ - unsigned int sort_key :8; /* for linker */ - unsigned int reserved2 :8; /* for future expansion */ - - int space_number; /* index */ - int subspace_index; /* index into subspace dict */ - unsigned int subspace_quantity; /* number of subspaces */ - int loader_fix_index; /* for loader */ - unsigned int loader_fix_quantity; /* for loader */ - int init_pointer_index; /* data pointer array index */ - unsigned int init_pointer_quantity; /* number of data pointers */ -}; - -/* The Subspace Dictionary */ -struct subspace_dictionary_record { - int space_index; - unsigned int access_control_bits :7; - unsigned int memory_resident :1; - unsigned int dup_common :1; - unsigned int is_common :1; - unsigned int quadrant :2; - unsigned int initially_frozen :1; - unsigned int is_first :1; - unsigned int code_only :1; - unsigned int sort_key :8; - unsigned int replicate_init :1; - unsigned int continuation :1; - unsigned int is_tspecific :1; - unsigned int is_comdat :1; - unsigned int reserved :4; - - int file_loc_init_value; - unsigned int initialization_length; - unsigned int subspace_start; - unsigned int subspace_length; - - unsigned int reserved2 :5; - unsigned int alignment :27; - - union name_pt name; - int fixup_request_index; - unsigned int fixup_request_quantity; -}; - -#endif /* _LINUX_SOM_H */ diff --git a/ipc/sem.c b/ipc/sem.c index 6115146563f9..92842113c6a9 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -1941,7 +1941,7 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, queue.sleeper = current; sleep_again: - current->state = TASK_INTERRUPTIBLE; + __set_current_state(TASK_INTERRUPTIBLE); sem_unlock(sma, locknum); rcu_read_unlock(); diff --git a/kernel/acct.c b/kernel/acct.c index 33738ef972f3..e6c10d1a4058 100644 --- a/kernel/acct.c +++ b/kernel/acct.c @@ -76,10 +76,11 @@ int acct_parm[3] = {4, 2, 30}; /* * External references and all of the globals. */ -static void do_acct_process(struct bsd_acct_struct *acct); struct bsd_acct_struct { struct fs_pin pin; + atomic_long_t count; + struct rcu_head rcu; struct mutex lock; int active; unsigned long needcheck; @@ -89,6 +90,8 @@ struct bsd_acct_struct { struct completion done; }; +static void do_acct_process(struct bsd_acct_struct *acct); + /* * Check the amount of free space and suspend/resume accordingly. */ @@ -124,32 +127,56 @@ out: return acct->active; } +static void acct_put(struct bsd_acct_struct *p) +{ + if (atomic_long_dec_and_test(&p->count)) + kfree_rcu(p, rcu); +} + +static inline struct bsd_acct_struct *to_acct(struct fs_pin *p) +{ + return p ? container_of(p, struct bsd_acct_struct, pin) : NULL; +} + static struct bsd_acct_struct *acct_get(struct pid_namespace *ns) { struct bsd_acct_struct *res; again: smp_rmb(); rcu_read_lock(); - res = ACCESS_ONCE(ns->bacct); + res = to_acct(ACCESS_ONCE(ns->bacct)); if (!res) { rcu_read_unlock(); return NULL; } - if (!atomic_long_inc_not_zero(&res->pin.count)) { + if (!atomic_long_inc_not_zero(&res->count)) { rcu_read_unlock(); cpu_relax(); goto again; } rcu_read_unlock(); mutex_lock(&res->lock); - if (!res->ns) { + if (res != to_acct(ACCESS_ONCE(ns->bacct))) { mutex_unlock(&res->lock); - pin_put(&res->pin); + acct_put(res); goto again; } return res; } +static void acct_pin_kill(struct fs_pin *pin) +{ + struct bsd_acct_struct *acct = to_acct(pin); + mutex_lock(&acct->lock); + do_acct_process(acct); + schedule_work(&acct->work); + wait_for_completion(&acct->done); + cmpxchg(&acct->ns->bacct, pin, NULL); + mutex_unlock(&acct->lock); + pin_remove(pin); + acct_put(acct); +} + static void close_work(struct work_struct *work) { struct bsd_acct_struct *acct = container_of(work, struct bsd_acct_struct, work); @@ -160,44 +187,13 @@ static void close_work(struct work_struct *work) complete(&acct->done); } -static void acct_kill(struct bsd_acct_struct *acct, - struct bsd_acct_struct *new) -{ - if (acct) { - struct pid_namespace *ns = acct->ns; - do_acct_process(acct); - INIT_WORK(&acct->work, close_work); - init_completion(&acct->done); - schedule_work(&acct->work); - wait_for_completion(&acct->done); - pin_remove(&acct->pin); - ns->bacct = new; - acct->ns = NULL; - atomic_long_dec(&acct->pin.count); - mutex_unlock(&acct->lock); - pin_put(&acct->pin); - } -} - -static void acct_pin_kill(struct fs_pin *pin) -{ - struct bsd_acct_struct *acct; - acct = container_of(pin, struct bsd_acct_struct, pin); - mutex_lock(&acct->lock); - if (!acct->ns) { - mutex_unlock(&acct->lock); - pin_put(pin); - acct = NULL; - } - acct_kill(acct, NULL); -} - static int acct_on(struct filename *pathname) { struct file *file; struct vfsmount *mnt, *internal; struct pid_namespace *ns = task_active_pid_ns(current); - struct bsd_acct_struct *acct, *old; + struct bsd_acct_struct *acct; + struct fs_pin *old; int err; acct = kzalloc(sizeof(struct bsd_acct_struct), GFP_KERNEL); @@ -238,21 +234,21 @@ static int acct_on(struct filename *pathname) mnt = file->f_path.mnt; file->f_path.mnt = internal; - atomic_long_set(&acct->pin.count, 1); - acct->pin.kill = acct_pin_kill; + atomic_long_set(&acct->count, 1); + init_fs_pin(&acct->pin, acct_pin_kill); acct->file = file; acct->needcheck = jiffies; acct->ns = ns; mutex_init(&acct->lock); + INIT_WORK(&acct->work, close_work); + init_completion(&acct->done); mutex_lock_nested(&acct->lock, 1); /* nobody has seen it yet */ pin_insert(&acct->pin, mnt); - old = acct_get(ns); - if (old) - acct_kill(old, acct); - else - ns->bacct = acct; + rcu_read_lock(); + old = xchg(&ns->bacct, &acct->pin); mutex_unlock(&acct->lock); + pin_kill(old); mnt_drop_write(mnt); mntput(mnt); return 0; @@ -288,7 +284,8 @@ SYSCALL_DEFINE1(acct, const char __user *, name) mutex_unlock(&acct_on_mutex); putname(tmp); } else { - acct_kill(acct_get(task_active_pid_ns(current)), NULL); + rcu_read_lock(); + pin_kill(task_active_pid_ns(current)->bacct); } return error; @@ -296,7 +293,8 @@ SYSCALL_DEFINE1(acct, const char __user *, name) void acct_exit_ns(struct pid_namespace *ns) { - acct_kill(acct_get(ns), NULL); + rcu_read_lock(); + pin_kill(ns->bacct); } /* @@ -576,7 +574,7 @@ static void slow_acct_process(struct pid_namespace *ns) if (acct) { do_acct_process(acct); mutex_unlock(&acct->lock); - pin_put(&acct->pin); + acct_put(acct); } } } diff --git a/kernel/audit.h b/kernel/audit.h index 3cdffad5a1d9..1caa0d345d90 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -24,12 +24,6 @@ #include <linux/skbuff.h> #include <uapi/linux/mqueue.h> -/* 0 = no checking - 1 = put_count checking - 2 = verbose put_count checking -*/ -#define AUDIT_DEBUG 0 - /* AUDIT_NAMES is the number of slots we reserve in the audit_context * for saving names from getname(). If we get more names we will allocate * a name dynamically and also add those to the list anchored by names_list. */ @@ -74,9 +68,8 @@ struct audit_cap_data { }; }; -/* When fs/namei.c:getname() is called, we store the pointer in name and - * we don't let putname() free it (instead we free all of the saved - * pointers at syscall exit time). +/* When fs/namei.c:getname() is called, we store the pointer in name and bump + * the refcnt in the associated filename struct. * * Further, in fs/namei.c:path_lookup() we store the inode and device. */ @@ -86,7 +79,6 @@ struct audit_names { struct filename *name; int name_len; /* number of chars to log */ bool hidden; /* don't log this record */ - bool name_put; /* call __putname()? */ unsigned long ino; dev_t dev; @@ -208,11 +200,6 @@ struct audit_context { }; int fds[2]; struct audit_proctitle proctitle; - -#if AUDIT_DEBUG - int put_count; - int ino_count; -#endif }; extern u32 audit_ever_enabled; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 072566dd0caf..dc4ae70a7413 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -866,33 +866,10 @@ static inline void audit_free_names(struct audit_context *context) { struct audit_names *n, *next; -#if AUDIT_DEBUG == 2 - if (context->put_count + context->ino_count != context->name_count) { - int i = 0; - - pr_err("%s:%d(:%d): major=%d in_syscall=%d" - " name_count=%d put_count=%d ino_count=%d" - " [NOT freeing]\n", __FILE__, __LINE__, - context->serial, context->major, context->in_syscall, - context->name_count, context->put_count, - context->ino_count); - list_for_each_entry(n, &context->names_list, list) { - pr_err("names[%d] = %p = %s\n", i++, n->name, - n->name->name ?: "(null)"); - } - dump_stack(); - return; - } -#endif -#if AUDIT_DEBUG - context->put_count = 0; - context->ino_count = 0; -#endif - list_for_each_entry_safe(n, next, &context->names_list, list) { list_del(&n->list); - if (n->name && n->name_put) - final_putname(n->name); + if (n->name) + putname(n->name); if (n->should_free) kfree(n); } @@ -1711,9 +1688,6 @@ static struct audit_names *audit_alloc_name(struct audit_context *context, list_add_tail(&aname->list, &context->names_list); context->name_count++; -#if AUDIT_DEBUG - context->ino_count++; -#endif return aname; } @@ -1734,8 +1708,10 @@ __audit_reusename(const __user char *uptr) list_for_each_entry(n, &context->names_list, list) { if (!n->name) continue; - if (n->name->uptr == uptr) + if (n->name->uptr == uptr) { + n->name->refcnt++; return n->name; + } } return NULL; } @@ -1752,19 +1728,8 @@ void __audit_getname(struct filename *name) struct audit_context *context = current->audit_context; struct audit_names *n; - if (!context->in_syscall) { -#if AUDIT_DEBUG == 2 - pr_err("%s:%d(:%d): ignoring getname(%p)\n", - __FILE__, __LINE__, context->serial, name); - dump_stack(); -#endif + if (!context->in_syscall) return; - } - -#if AUDIT_DEBUG - /* The filename _must_ have a populated ->name */ - BUG_ON(!name->name); -#endif n = audit_alloc_name(context, AUDIT_TYPE_UNKNOWN); if (!n) @@ -1772,56 +1737,13 @@ void __audit_getname(struct filename *name) n->name = name; n->name_len = AUDIT_NAME_FULL; - n->name_put = true; name->aname = n; + name->refcnt++; if (!context->pwd.dentry) get_fs_pwd(current->fs, &context->pwd); } -/* audit_putname - intercept a putname request - * @name: name to intercept and delay for putname - * - * If we have stored the name from getname in the audit context, - * then we delay the putname until syscall exit. - * Called from include/linux/fs.h:putname(). - */ -void audit_putname(struct filename *name) -{ - struct audit_context *context = current->audit_context; - - BUG_ON(!context); - if (!name->aname || !context->in_syscall) { -#if AUDIT_DEBUG == 2 - pr_err("%s:%d(:%d): final_putname(%p)\n", - __FILE__, __LINE__, context->serial, name); - if (context->name_count) { - struct audit_names *n; - int i = 0; - - list_for_each_entry(n, &context->names_list, list) - pr_err("name[%d] = %p = %s\n", i++, n->name, - n->name->name ?: "(null)"); - } -#endif - final_putname(name); - } -#if AUDIT_DEBUG - else { - ++context->put_count; - if (context->put_count > context->name_count) { - pr_err("%s:%d(:%d): major=%d in_syscall=%d putname(%p)" - " name_count=%d put_count=%d\n", - __FILE__, __LINE__, - context->serial, context->major, - context->in_syscall, name->name, - context->name_count, context->put_count); - dump_stack(); - } - } -#endif -} - /** * __audit_inode - store the inode and device from a lookup * @name: name being audited @@ -1842,10 +1764,6 @@ void __audit_inode(struct filename *name, const struct dentry *dentry, if (!name) goto out_alloc; -#if AUDIT_DEBUG - /* The struct filename _must_ have a populated ->name */ - BUG_ON(!name->name); -#endif /* * If we have a pointer to an audit_names entry already, then we can * just use it directly if the type is correct. @@ -1863,7 +1781,17 @@ void __audit_inode(struct filename *name, const struct dentry *dentry, } list_for_each_entry_reverse(n, &context->names_list, list) { - if (!n->name || strcmp(n->name->name, name->name)) + if (n->ino) { + /* valid inode number, use that for the comparison */ + if (n->ino != inode->i_ino || + n->dev != inode->i_sb->s_dev) + continue; + } else if (n->name) { + /* inode number has not been set, check the name */ + if (strcmp(n->name->name, name->name)) + continue; + } else + /* no inode and no name (?!) ... this is odd ... */ continue; /* match the correct record type */ @@ -1882,44 +1810,11 @@ out_alloc: n = audit_alloc_name(context, AUDIT_TYPE_UNKNOWN); if (!n) return; - /* unfortunately, while we may have a path name to record with the - * inode, we can't always rely on the string lasting until the end of - * the syscall so we need to create our own copy, it may fail due to - * memory allocation issues, but we do our best */ if (name) { - /* we can't use getname_kernel() due to size limits */ - size_t len = strlen(name->name) + 1; - struct filename *new = __getname(); - - if (unlikely(!new)) - goto out; - - if (len <= (PATH_MAX - sizeof(*new))) { - new->name = (char *)(new) + sizeof(*new); - new->separate = false; - } else if (len <= PATH_MAX) { - /* this looks odd, but is due to final_putname() */ - struct filename *new2; - - new2 = kmalloc(sizeof(*new2), GFP_KERNEL); - if (unlikely(!new2)) { - __putname(new); - goto out; - } - new2->name = (char *)new; - new2->separate = true; - new = new2; - } else { - /* we should never get here, but let's be safe */ - __putname(new); - goto out; - } - strlcpy((char *)new->name, name->name, len); - new->uptr = NULL; - new->aname = n; - n->name = new; - n->name_put = true; + n->name = name; + name->refcnt++; } + out: if (parent) { n->name_len = n->name ? parent_len(n->name->name) : AUDIT_NAME_FULL; @@ -1970,11 +1865,16 @@ void __audit_inode_child(const struct inode *parent, /* look for a parent entry first */ list_for_each_entry(n, &context->names_list, list) { - if (!n->name || n->type != AUDIT_TYPE_PARENT) + if (!n->name || + (n->type != AUDIT_TYPE_PARENT && + n->type != AUDIT_TYPE_UNKNOWN)) continue; - if (n->ino == parent->i_ino && - !audit_compare_dname_path(dname, n->name->name, n->name_len)) { + if (n->ino == parent->i_ino && n->dev == parent->i_sb->s_dev && + !audit_compare_dname_path(dname, + n->name->name, n->name_len)) { + if (n->type == AUDIT_TYPE_UNKNOWN) + n->type = AUDIT_TYPE_PARENT; found_parent = n; break; } @@ -1983,11 +1883,8 @@ void __audit_inode_child(const struct inode *parent, /* is there a matching child entry? */ list_for_each_entry(n, &context->names_list, list) { /* can only match entries that have a name */ - if (!n->name || n->type != type) - continue; - - /* if we found a parent, make sure this one is a child of it */ - if (found_parent && (n->name != found_parent->name)) + if (!n->name || + (n->type != type && n->type != AUDIT_TYPE_UNKNOWN)) continue; if (!strcmp(dname, n->name->name) || @@ -1995,6 +1892,8 @@ void __audit_inode_child(const struct inode *parent, found_parent ? found_parent->name_len : AUDIT_NAME_FULL)) { + if (n->type == AUDIT_TYPE_UNKNOWN) + n->type = type; found_child = n; break; } @@ -2019,10 +1918,10 @@ void __audit_inode_child(const struct inode *parent, if (found_parent) { found_child->name = found_parent->name; found_child->name_len = AUDIT_NAME_FULL; - /* don't call __putname() */ - found_child->name_put = false; + found_child->name->refcnt++; } } + if (inode) audit_copy_inode(found_child, dentry, inode); else @@ -2405,7 +2304,6 @@ int __audit_log_bprm_fcaps(struct linux_binprm *bprm, struct audit_aux_data_bprm_fcaps *ax; struct audit_context *context = current->audit_context; struct cpu_vfs_cap_data vcaps; - struct dentry *dentry; ax = kmalloc(sizeof(*ax), GFP_KERNEL); if (!ax) @@ -2415,9 +2313,7 @@ int __audit_log_bprm_fcaps(struct linux_binprm *bprm, ax->d.next = context->aux; context->aux = (void *)ax; - dentry = dget(bprm->file->f_path.dentry); - get_vfs_caps_from_disk(dentry, &vcaps); - dput(dentry); + get_vfs_caps_from_disk(bprm->file->f_path.dentry, &vcaps); ax->fcap.permitted = vcaps.permitted; ax->fcap.inheritable = vcaps.inheritable; diff --git a/kernel/kexec.c b/kernel/kexec.c index c85277639b34..38c25b1f2fd5 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -444,7 +444,7 @@ arch_kexec_apply_relocations(const Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, } /* - * Free up memory used by kernel, initrd, and comand line. This is temporary + * Free up memory used by kernel, initrd, and command line. This is temporary * memory allocation which is not needed any more after these buffers have * been loaded into separate segments and have been copied elsewhere. */ @@ -856,8 +856,6 @@ static int kimage_set_destination(struct kimage *image, destination &= PAGE_MASK; result = kimage_add_entry(image, destination | IND_DESTINATION); - if (result == 0) - image->destination = destination; return result; } @@ -869,8 +867,6 @@ static int kimage_add_page(struct kimage *image, unsigned long page) page &= PAGE_MASK; result = kimage_add_entry(image, page | IND_SOURCE); - if (result == 0) - image->destination += PAGE_SIZE; return result; } @@ -1288,19 +1284,22 @@ SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments, if (nr_segments > 0) { unsigned long i; - /* Loading another kernel to reboot into */ - if ((flags & KEXEC_ON_CRASH) == 0) - result = kimage_alloc_init(&image, entry, nr_segments, - segments, flags); - /* Loading another kernel to switch to if this one crashes */ - else if (flags & KEXEC_ON_CRASH) { - /* Free any current crash dump kernel before + if (flags & KEXEC_ON_CRASH) { + /* + * Loading another kernel to switch to if this one + * crashes. Free any current crash dump kernel before * we corrupt it. */ + kimage_free(xchg(&kexec_crash_image, NULL)); result = kimage_alloc_init(&image, entry, nr_segments, segments, flags); crash_map_reserved_pages(); + } else { + /* Loading another kernel to reboot into. */ + + result = kimage_alloc_init(&image, entry, nr_segments, + segments, flags); } if (result) goto out; diff --git a/kernel/module.c b/kernel/module.c index 8426ad48362c..b34813f725e9 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -3025,8 +3025,13 @@ static void do_free_init(struct rcu_head *head) kfree(m); } -/* This is where the real work happens */ -static int do_init_module(struct module *mod) +/* + * This is where the real work happens. + * + * Keep it uninlined to provide a reliable breakpoint target, e.g. for the gdb + * helper command 'lx-symbols'. + */ +static noinline int do_init_module(struct module *mod) { int ret = 0; struct mod_initfree *freeinit; diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index c347e3ce3a55..b7d6b3a721b1 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -37,7 +37,9 @@ const char *pm_states[PM_SUSPEND_MAX]; static const struct platform_suspend_ops *suspend_ops; static const struct platform_freeze_ops *freeze_ops; static DECLARE_WAIT_QUEUE_HEAD(suspend_freeze_wait_head); -static bool suspend_freeze_wake; + +enum freeze_state __read_mostly suspend_freeze_state; +static DEFINE_SPINLOCK(suspend_freeze_lock); void freeze_set_ops(const struct platform_freeze_ops *ops) { @@ -48,22 +50,49 @@ void freeze_set_ops(const struct platform_freeze_ops *ops) static void freeze_begin(void) { - suspend_freeze_wake = false; + suspend_freeze_state = FREEZE_STATE_NONE; } static void freeze_enter(void) { - cpuidle_use_deepest_state(true); + spin_lock_irq(&suspend_freeze_lock); + if (pm_wakeup_pending()) + goto out; + + suspend_freeze_state = FREEZE_STATE_ENTER; + spin_unlock_irq(&suspend_freeze_lock); + + get_online_cpus(); cpuidle_resume(); - wait_event(suspend_freeze_wait_head, suspend_freeze_wake); + + /* Push all the CPUs into the idle loop. */ + wake_up_all_idle_cpus(); + pr_debug("PM: suspend-to-idle\n"); + /* Make the current CPU wait so it can enter the idle loop too. */ + wait_event(suspend_freeze_wait_head, + suspend_freeze_state == FREEZE_STATE_WAKE); + pr_debug("PM: resume from suspend-to-idle\n"); + cpuidle_pause(); - cpuidle_use_deepest_state(false); + put_online_cpus(); + + spin_lock_irq(&suspend_freeze_lock); + + out: + suspend_freeze_state = FREEZE_STATE_NONE; + spin_unlock_irq(&suspend_freeze_lock); } void freeze_wake(void) { - suspend_freeze_wake = true; - wake_up(&suspend_freeze_wait_head); + unsigned long flags; + + spin_lock_irqsave(&suspend_freeze_lock, flags); + if (suspend_freeze_state > FREEZE_STATE_NONE) { + suspend_freeze_state = FREEZE_STATE_WAKE; + wake_up(&suspend_freeze_wait_head); + } + spin_unlock_irqrestore(&suspend_freeze_lock, flags); } EXPORT_SYMBOL_GPL(freeze_wake); diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 1eb9d90c3af9..227fec36b12a 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -1077,7 +1077,6 @@ int generic_ptrace_pokedata(struct task_struct *tsk, unsigned long addr, } #if defined CONFIG_COMPAT -#include <linux/compat.h> int compat_ptrace_request(struct task_struct *child, compat_long_t request, compat_ulong_t addr, compat_ulong_t data) diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c index aaf1c1d5cf5d..94b2d7b88a27 100644 --- a/kernel/sched/idle.c +++ b/kernel/sched/idle.c @@ -7,6 +7,7 @@ #include <linux/tick.h> #include <linux/mm.h> #include <linux/stackprotector.h> +#include <linux/suspend.h> #include <asm/tlb.h> @@ -105,6 +106,21 @@ static void cpuidle_idle_call(void) rcu_idle_enter(); /* + * Suspend-to-idle ("freeze") is a system state in which all user space + * has been frozen, all I/O devices have been suspended and the only + * activity happens here and in iterrupts (if any). In that case bypass + * the cpuidle governor and go stratight for the deepest idle state + * available. Possibly also suspend the local tick and the entire + * timekeeping to prevent timer interrupts from kicking us out of idle + * until a proper wakeup interrupt happens. + */ + if (idle_should_freeze()) { + cpuidle_enter_freeze(); + local_irq_enable(); + goto exit_idle; + } + + /* * Ask the cpuidle framework to choose a convenient idle state. * Fall back to the default arch idle method on errors. */ diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 4ef9687ac115..4f44028943e6 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -629,7 +629,9 @@ static u32 __seccomp_phase1_filter(int this_syscall, struct seccomp_data *sd) switch (action) { case SECCOMP_RET_ERRNO: - /* Set the low-order 16-bits as a errno. */ + /* Set low-order bits as an errno, capped at MAX_ERRNO. */ + if (data > MAX_ERRNO) + data = MAX_ERRNO; syscall_set_return_value(current, task_pt_regs(current), -data, 0); goto skip; diff --git a/kernel/signal.c b/kernel/signal.c index 33a52759cc0e..a390499943e4 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -3550,7 +3550,7 @@ SYSCALL_DEFINE2(signal, int, sig, __sighandler_t, handler) SYSCALL_DEFINE0(pause) { while (!signal_pending(current)) { - current->state = TASK_INTERRUPTIBLE; + __set_current_state(TASK_INTERRUPTIBLE); schedule(); } return -ERESTARTNOHAND; @@ -3563,7 +3563,7 @@ int sigsuspend(sigset_t *set) current->saved_sigmask = current->blocked; set_current_blocked(set); - current->state = TASK_INTERRUPTIBLE; + __set_current_state(TASK_INTERRUPTIBLE); schedule(); set_restore_sigmask(); return -ERESTARTNOHAND; diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index 7efeedf53ebd..f7c515595b42 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -394,6 +394,56 @@ void tick_resume(void) } } +static DEFINE_RAW_SPINLOCK(tick_freeze_lock); +static unsigned int tick_freeze_depth; + +/** + * tick_freeze - Suspend the local tick and (possibly) timekeeping. + * + * Check if this is the last online CPU executing the function and if so, + * suspend timekeeping. Otherwise suspend the local tick. + * + * Call with interrupts disabled. Must be balanced with %tick_unfreeze(). + * Interrupts must not be enabled before the subsequent %tick_unfreeze(). + */ +void tick_freeze(void) +{ + raw_spin_lock(&tick_freeze_lock); + + tick_freeze_depth++; + if (tick_freeze_depth == num_online_cpus()) { + timekeeping_suspend(); + } else { + tick_suspend(); + tick_suspend_broadcast(); + } + + raw_spin_unlock(&tick_freeze_lock); +} + +/** + * tick_unfreeze - Resume the local tick and (possibly) timekeeping. + * + * Check if this is the first CPU executing the function and if so, resume + * timekeeping. Otherwise resume the local tick. + * + * Call with interrupts disabled. Must be balanced with %tick_freeze(). + * Interrupts must not be enabled after the preceding %tick_freeze(). + */ +void tick_unfreeze(void) +{ + raw_spin_lock(&tick_freeze_lock); + + if (tick_freeze_depth == num_online_cpus()) + timekeeping_resume(); + else + tick_resume(); + + tick_freeze_depth--; + + raw_spin_unlock(&tick_freeze_lock); +} + /** * tick_init - initialize the tick control */ diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index b124af259800..91db94136c10 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -230,9 +230,7 @@ static inline s64 timekeeping_get_ns_raw(struct timekeeper *tk) /** * update_fast_timekeeper - Update the fast and NMI safe monotonic timekeeper. - * @tk: The timekeeper from which we take the update - * @tkf: The fast timekeeper to update - * @tbase: The time base for the fast timekeeper (mono/raw) + * @tkr: Timekeeping readout base from which we take the update * * We want to use this from any context including NMI and tracing / * instrumenting the timekeeping code itself. @@ -244,11 +242,11 @@ static inline s64 timekeeping_get_ns_raw(struct timekeeper *tk) * smp_wmb(); <- Ensure that the last base[1] update is visible * tkf->seq++; * smp_wmb(); <- Ensure that the seqcount update is visible - * update(tkf->base[0], tk); + * update(tkf->base[0], tkr); * smp_wmb(); <- Ensure that the base[0] update is visible * tkf->seq++; * smp_wmb(); <- Ensure that the seqcount update is visible - * update(tkf->base[1], tk); + * update(tkf->base[1], tkr); * * The reader side does: * @@ -269,7 +267,7 @@ static inline s64 timekeeping_get_ns_raw(struct timekeeper *tk) * slightly wrong timestamp (a few nanoseconds). See * @ktime_get_mono_fast_ns. */ -static void update_fast_timekeeper(struct timekeeper *tk) +static void update_fast_timekeeper(struct tk_read_base *tkr) { struct tk_read_base *base = tk_fast_mono.base; @@ -277,7 +275,7 @@ static void update_fast_timekeeper(struct timekeeper *tk) raw_write_seqcount_latch(&tk_fast_mono.seq); /* Update base[0] */ - memcpy(base, &tk->tkr, sizeof(*base)); + memcpy(base, tkr, sizeof(*base)); /* Force readers back to base[0] */ raw_write_seqcount_latch(&tk_fast_mono.seq); @@ -334,6 +332,35 @@ u64 notrace ktime_get_mono_fast_ns(void) } EXPORT_SYMBOL_GPL(ktime_get_mono_fast_ns); +/* Suspend-time cycles value for halted fast timekeeper. */ +static cycle_t cycles_at_suspend; + +static cycle_t dummy_clock_read(struct clocksource *cs) +{ + return cycles_at_suspend; +} + +/** + * halt_fast_timekeeper - Prevent fast timekeeper from accessing clocksource. + * @tk: Timekeeper to snapshot. + * + * It generally is unsafe to access the clocksource after timekeeping has been + * suspended, so take a snapshot of the readout base of @tk and use it as the + * fast timekeeper's readout base while suspended. It will return the same + * number of cycles every time until timekeeping is resumed at which time the + * proper readout base for the fast timekeeper will be restored automatically. + */ +static void halt_fast_timekeeper(struct timekeeper *tk) +{ + static struct tk_read_base tkr_dummy; + struct tk_read_base *tkr = &tk->tkr; + + memcpy(&tkr_dummy, tkr, sizeof(tkr_dummy)); + cycles_at_suspend = tkr->read(tkr->clock); + tkr_dummy.read = dummy_clock_read; + update_fast_timekeeper(&tkr_dummy); +} + #ifdef CONFIG_GENERIC_TIME_VSYSCALL_OLD static inline void update_vsyscall(struct timekeeper *tk) @@ -462,7 +489,7 @@ static void timekeeping_update(struct timekeeper *tk, unsigned int action) memcpy(&shadow_timekeeper, &tk_core.timekeeper, sizeof(tk_core.timekeeper)); - update_fast_timekeeper(tk); + update_fast_timekeeper(&tk->tkr); } /** @@ -1170,7 +1197,7 @@ void timekeeping_inject_sleeptime64(struct timespec64 *delta) * xtime/wall_to_monotonic/jiffies/etc are * still managed by arch specific suspend/resume code. */ -static void timekeeping_resume(void) +void timekeeping_resume(void) { struct timekeeper *tk = &tk_core.timekeeper; struct clocksource *clock = tk->tkr.clock; @@ -1251,7 +1278,7 @@ static void timekeeping_resume(void) hrtimers_resume(); } -static int timekeeping_suspend(void) +int timekeeping_suspend(void) { struct timekeeper *tk = &tk_core.timekeeper; unsigned long flags; @@ -1296,6 +1323,7 @@ static int timekeeping_suspend(void) } timekeeping_update(tk, TK_MIRROR); + halt_fast_timekeeper(tk); write_seqcount_end(&tk_core.seq); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); diff --git a/kernel/time/timekeeping.h b/kernel/time/timekeeping.h index adc1fc98bde3..1d91416055d5 100644 --- a/kernel/time/timekeeping.h +++ b/kernel/time/timekeeping.h @@ -16,5 +16,7 @@ extern int timekeeping_inject_offset(struct timespec *ts); extern s32 timekeeping_get_tai_offset(void); extern void timekeeping_set_tai_offset(s32 tai_offset); extern void timekeeping_clocktai(struct timespec *ts); +extern int timekeeping_suspend(void); +extern void timekeeping_resume(void); #endif diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index ecb3516f6546..c5cefb3c009c 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -167,6 +167,17 @@ config DEBUG_INFO_DWARF4 But it significantly improves the success of resolving variables in gdb on optimized code. +config GDB_SCRIPTS + bool "Provide GDB scripts for kernel debugging" + depends on DEBUG_INFO + help + This creates the required links to GDB helper scripts in the + build directory. If you load vmlinux into gdb, the helper + scripts will be automatically imported by gdb as well, and + additional functions are available to analyze a Linux kernel + instance. See Documentation/gdb-kernel-debugging.txt for further + details. + config ENABLE_WARN_DEPRECATED bool "Enable __deprecated logic" default y diff --git a/lib/lockref.c b/lib/lockref.c index d2233de9a86e..ecb9a665ec19 100644 --- a/lib/lockref.c +++ b/lib/lockref.c @@ -60,7 +60,7 @@ void lockref_get(struct lockref *lockref) EXPORT_SYMBOL(lockref_get); /** - * lockref_get_not_zero - Increments count unless the count is 0 + * lockref_get_not_zero - Increments count unless the count is 0 or dead * @lockref: pointer to lockref structure * Return: 1 if count updated successfully or 0 if count was zero */ @@ -70,7 +70,7 @@ int lockref_get_not_zero(struct lockref *lockref) CMPXCHG_LOOP( new.count++; - if (!old.count) + if (old.count <= 0) return 0; , return 1; @@ -78,7 +78,7 @@ int lockref_get_not_zero(struct lockref *lockref) spin_lock(&lockref->lock); retval = 0; - if (lockref->count) { + if (lockref->count > 0) { lockref->count++; retval = 1; } @@ -88,7 +88,7 @@ int lockref_get_not_zero(struct lockref *lockref) EXPORT_SYMBOL(lockref_get_not_zero); /** - * lockref_get_or_lock - Increments count unless the count is 0 + * lockref_get_or_lock - Increments count unless the count is 0 or dead * @lockref: pointer to lockref structure * Return: 1 if count updated successfully or 0 if count was zero * and we got the lock instead. @@ -97,14 +97,14 @@ int lockref_get_or_lock(struct lockref *lockref) { CMPXCHG_LOOP( new.count++; - if (!old.count) + if (old.count <= 0) break; , return 1; ); spin_lock(&lockref->lock); - if (!lockref->count) + if (lockref->count <= 0) return 0; lockref->count++; spin_unlock(&lockref->lock); @@ -113,6 +113,26 @@ int lockref_get_or_lock(struct lockref *lockref) EXPORT_SYMBOL(lockref_get_or_lock); /** + * lockref_put_return - Decrement reference count if possible + * @lockref: pointer to lockref structure + * + * Decrement the reference count and return the new value. + * If the lockref was dead or locked, return an error. + */ +int lockref_put_return(struct lockref *lockref) +{ + CMPXCHG_LOOP( + new.count--; + if (old.count <= 0) + return -1; + , + return new.count; + ); + return -1; +} +EXPORT_SYMBOL(lockref_put_return); + +/** * lockref_put_or_lock - decrements count unless count <= 1 before decrement * @lockref: pointer to lockref structure * Return: 1 if count updated successfully or 0 if count <= 1 and lock taken @@ -158,7 +178,7 @@ int lockref_get_not_dead(struct lockref *lockref) CMPXCHG_LOOP( new.count++; - if ((int)old.count < 0) + if (old.count < 0) return 0; , return 1; @@ -166,7 +186,7 @@ int lockref_get_not_dead(struct lockref *lockref) spin_lock(&lockref->lock); retval = 0; - if ((int) lockref->count >= 0) { + if (lockref->count >= 0) { lockref->count++; retval = 1; } diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 7690ec77c722..6dc4580df2af 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -49,10 +49,10 @@ static int bdi_debug_stats_show(struct seq_file *m, void *v) unsigned long background_thresh; unsigned long dirty_thresh; unsigned long bdi_thresh; - unsigned long nr_dirty, nr_io, nr_more_io; + unsigned long nr_dirty, nr_io, nr_more_io, nr_dirty_time; struct inode *inode; - nr_dirty = nr_io = nr_more_io = 0; + nr_dirty = nr_io = nr_more_io = nr_dirty_time = 0; spin_lock(&wb->list_lock); list_for_each_entry(inode, &wb->b_dirty, i_wb_list) nr_dirty++; @@ -60,6 +60,9 @@ static int bdi_debug_stats_show(struct seq_file *m, void *v) nr_io++; list_for_each_entry(inode, &wb->b_more_io, i_wb_list) nr_more_io++; + list_for_each_entry(inode, &wb->b_dirty_time, i_wb_list) + if (inode->i_state & I_DIRTY_TIME) + nr_dirty_time++; spin_unlock(&wb->list_lock); global_dirty_limits(&background_thresh, &dirty_thresh); @@ -78,6 +81,7 @@ static int bdi_debug_stats_show(struct seq_file *m, void *v) "b_dirty: %10lu\n" "b_io: %10lu\n" "b_more_io: %10lu\n" + "b_dirty_time: %10lu\n" "bdi_list: %10u\n" "state: %10lx\n", (unsigned long) K(bdi_stat(bdi, BDI_WRITEBACK)), @@ -91,6 +95,7 @@ static int bdi_debug_stats_show(struct seq_file *m, void *v) nr_dirty, nr_io, nr_more_io, + nr_dirty_time, !list_empty(&bdi->bdi_list), bdi->state); #undef K @@ -380,6 +385,7 @@ static void bdi_wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi) INIT_LIST_HEAD(&wb->b_dirty); INIT_LIST_HEAD(&wb->b_io); INIT_LIST_HEAD(&wb->b_more_io); + INIT_LIST_HEAD(&wb->b_dirty_time); spin_lock_init(&wb->list_lock); INIT_DELAYED_WORK(&wb->dwork, bdi_writeback_workfn); } diff --git a/mm/iov_iter.c b/mm/iov_iter.c index a1599ca4ab0e..827732047da1 100644 --- a/mm/iov_iter.c +++ b/mm/iov_iter.c @@ -501,18 +501,31 @@ size_t iov_iter_single_seg_count(const struct iov_iter *i) EXPORT_SYMBOL(iov_iter_single_seg_count); void iov_iter_kvec(struct iov_iter *i, int direction, - const struct kvec *iov, unsigned long nr_segs, + const struct kvec *kvec, unsigned long nr_segs, size_t count) { BUG_ON(!(direction & ITER_KVEC)); i->type = direction; - i->kvec = (struct kvec *)iov; + i->kvec = kvec; i->nr_segs = nr_segs; i->iov_offset = 0; i->count = count; } EXPORT_SYMBOL(iov_iter_kvec); +void iov_iter_bvec(struct iov_iter *i, int direction, + const struct bio_vec *bvec, unsigned long nr_segs, + size_t count) +{ + BUG_ON(!(direction & ITER_BVEC)); + i->type = direction; + i->bvec = bvec; + i->nr_segs = nr_segs; + i->iov_offset = 0; + i->count = count; +} +EXPORT_SYMBOL(iov_iter_bvec); + unsigned long iov_iter_alignment(const struct iov_iter *i) { unsigned long res = 0; diff --git a/mm/page_io.c b/mm/page_io.c index 955db8b0d497..e6045804c8d8 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -269,14 +269,9 @@ int __swap_writepage(struct page *page, struct writeback_control *wbc, .bv_len = PAGE_SIZE, .bv_offset = 0 }; - struct iov_iter from = { - .type = ITER_BVEC | WRITE, - .count = PAGE_SIZE, - .iov_offset = 0, - .nr_segs = 1, - }; - from.bvec = &bv; /* older gcc versions are broken */ + struct iov_iter from; + iov_iter_bvec(&from, ITER_BVEC | WRITE, &bv, 1, PAGE_SIZE); init_sync_kiocb(&kiocb, swap_file); kiocb.ki_pos = page_file_offset(page); kiocb.ki_nbytes = PAGE_SIZE; diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 65728e0dc4ff..0ee453fad3de 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -987,15 +987,12 @@ static int __init br_netfilter_init(void) if (brnf_sysctl_header == NULL) { printk(KERN_WARNING "br_netfilter: can't register to sysctl.\n"); - ret = -ENOMEM; - goto err1; + nf_unregister_hooks(br_nf_ops, ARRAY_SIZE(br_nf_ops)); + return -ENOMEM; } #endif printk(KERN_NOTICE "Bridge firewalling registered\n"); return 0; -err1: - nf_unregister_hooks(br_nf_ops, ARRAY_SIZE(br_nf_ops)); - return ret; } static void __exit br_netfilter_fini(void) diff --git a/net/core/dev.c b/net/core/dev.c index d030575532a2..8f9710c62e20 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4024,6 +4024,7 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff NAPI_GRO_CB(skb)->flush = 0; NAPI_GRO_CB(skb)->free = 0; NAPI_GRO_CB(skb)->udp_mark = 0; + NAPI_GRO_CB(skb)->gro_remcsum_start = 0; /* Setup for GRO checksum validation */ switch (skb->ip_summed) { @@ -5335,7 +5336,7 @@ EXPORT_SYMBOL(netdev_upper_dev_unlink); /** * netdev_bonding_info_change - Dispatch event about slave change * @dev: device - * @netdev_bonding_info: info to dispatch + * @bonding_info: info to dispatch * * Send NETDEV_BONDING_INFO to netdev notifiers with info. * The caller must hold the RTNL lock. diff --git a/net/core/filter.c b/net/core/filter.c index ec9baea10c16..f6bdc2b1ba01 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -531,7 +531,7 @@ do_pass: *insn = BPF_LDX_MEM(BPF_W, BPF_REG_A, BPF_REG_CTX, fp->k); break; - /* Unkown instruction. */ + /* Unknown instruction. */ default: goto err; } diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 9fa25b0ea145..b4899f5b7388 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -97,7 +97,7 @@ * New xmit() return, do_div and misc clean up by Stephen Hemminger * <shemminger@osdl.org> 040923 * - * Randy Dunlap fixed u64 printk compiler waring + * Randy Dunlap fixed u64 printk compiler warning * * Remove FCS from BW calculation. Lennert Buytenhek <buytenh@wantstofly.org> * New time handling. Lennert Buytenhek <buytenh@wantstofly.org> 041213 diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 5be499b6a2d2..ab293a3066b3 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2162,7 +2162,14 @@ replay: } err = rtnl_configure_link(dev, ifm); if (err < 0) { - unregister_netdevice(dev); + if (ops->newlink) { + LIST_HEAD(list_kill); + + ops->dellink(dev, &list_kill); + unregister_netdevice_many(&list_kill); + } else { + unregister_netdevice(dev); + } goto out; } diff --git a/net/dsa/slave.c b/net/dsa/slave.c index d104ae15836f..f23deadf42a0 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -521,10 +521,13 @@ static int dsa_slave_phy_setup(struct dsa_slave_priv *p, struct device_node *phy_dn, *port_dn; bool phy_is_fixed = false; u32 phy_flags = 0; - int ret; + int mode, ret; port_dn = cd->port_dn[p->port]; - p->phy_interface = of_get_phy_mode(port_dn); + mode = of_get_phy_mode(port_dn); + if (mode < 0) + mode = PHY_INTERFACE_MODE_NA; + p->phy_interface = mode; phy_dn = of_parse_phandle(port_dn, "phy-handle", 0); if (of_phy_is_fixed_link(port_dn)) { @@ -559,6 +562,8 @@ static int dsa_slave_phy_setup(struct dsa_slave_priv *p, if (!p->phy) return -ENODEV; + /* Use already configured phy mode */ + p->phy_interface = p->phy->interface; phy_connect_direct(slave_dev, p->phy, dsa_slave_adjust_link, p->phy_interface); } else { diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index f0b4a31d7bd6..3a8985c94581 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1186,7 +1186,7 @@ __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope) no_in_dev: /* Not loopback addresses on loopback should be preferred - in this case. It is importnat that lo is the first interface + in this case. It is important that lo is the first interface in dev_base list. */ for_each_netdev_rcu(net, dev) { diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c index 92ddea1e6457..ff069f6597ac 100644 --- a/net/ipv4/fou.c +++ b/net/ipv4/fou.c @@ -22,14 +22,18 @@ static LIST_HEAD(fou_list); struct fou { struct socket *sock; u8 protocol; + u8 flags; u16 port; struct udp_offload udp_offloads; struct list_head list; }; +#define FOU_F_REMCSUM_NOPARTIAL BIT(0) + struct fou_cfg { u16 type; u8 protocol; + u8 flags; struct udp_port_cfg udp_config; }; @@ -64,24 +68,20 @@ static int fou_udp_recv(struct sock *sk, struct sk_buff *skb) } static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr, - void *data, size_t hdrlen, u8 ipproto) + void *data, size_t hdrlen, u8 ipproto, + bool nopartial) { __be16 *pd = data; size_t start = ntohs(pd[0]); size_t offset = ntohs(pd[1]); size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start); - if (skb->remcsum_offload) { - /* Already processed in GRO path */ - skb->remcsum_offload = 0; - return guehdr; - } - if (!pskb_may_pull(skb, plen)) return NULL; guehdr = (struct guehdr *)&udp_hdr(skb)[1]; - skb_remcsum_process(skb, (void *)guehdr + hdrlen, start, offset); + skb_remcsum_process(skb, (void *)guehdr + hdrlen, + start, offset, nopartial); return guehdr; } @@ -142,7 +142,9 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb) if (flags & GUE_PFLAG_REMCSUM) { guehdr = gue_remcsum(skb, guehdr, data + doffset, - hdrlen, guehdr->proto_ctype); + hdrlen, guehdr->proto_ctype, + !!(fou->flags & + FOU_F_REMCSUM_NOPARTIAL)); if (!guehdr) goto drop; @@ -214,7 +216,8 @@ out_unlock: static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off, struct guehdr *guehdr, void *data, - size_t hdrlen, u8 ipproto) + size_t hdrlen, u8 ipproto, + struct gro_remcsum *grc, bool nopartial) { __be16 *pd = data; size_t start = ntohs(pd[0]); @@ -222,7 +225,7 @@ static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off, size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start); if (skb->remcsum_offload) - return guehdr; + return NULL; if (!NAPI_GRO_CB(skb)->csum_valid) return NULL; @@ -234,7 +237,8 @@ static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off, return NULL; } - skb_gro_remcsum_process(skb, (void *)guehdr + hdrlen, start, offset); + skb_gro_remcsum_process(skb, (void *)guehdr + hdrlen, + start, offset, grc, nopartial); skb->remcsum_offload = 1; @@ -254,6 +258,10 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head, void *data; u16 doffset = 0; int flush = 1; + struct fou *fou = container_of(uoff, struct fou, udp_offloads); + struct gro_remcsum grc; + + skb_gro_remcsum_init(&grc); off = skb_gro_offset(skb); len = off + sizeof(*guehdr); @@ -295,7 +303,9 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head, if (flags & GUE_PFLAG_REMCSUM) { guehdr = gue_gro_remcsum(skb, off, guehdr, data + doffset, hdrlen, - guehdr->proto_ctype); + guehdr->proto_ctype, &grc, + !!(fou->flags & + FOU_F_REMCSUM_NOPARTIAL)); if (!guehdr) goto out; @@ -345,6 +355,7 @@ out_unlock: rcu_read_unlock(); out: NAPI_GRO_CB(skb)->flush |= flush; + skb_gro_remcsum_cleanup(skb, &grc); return pp; } @@ -455,6 +466,7 @@ static int fou_create(struct net *net, struct fou_cfg *cfg, sk = sock->sk; + fou->flags = cfg->flags; fou->port = cfg->udp_config.local_udp_port; /* Initial for fou type */ @@ -541,6 +553,7 @@ static struct nla_policy fou_nl_policy[FOU_ATTR_MAX + 1] = { [FOU_ATTR_AF] = { .type = NLA_U8, }, [FOU_ATTR_IPPROTO] = { .type = NLA_U8, }, [FOU_ATTR_TYPE] = { .type = NLA_U8, }, + [FOU_ATTR_REMCSUM_NOPARTIAL] = { .type = NLA_FLAG, }, }; static int parse_nl_config(struct genl_info *info, @@ -571,6 +584,9 @@ static int parse_nl_config(struct genl_info *info, if (info->attrs[FOU_ATTR_TYPE]) cfg->type = nla_get_u8(info->attrs[FOU_ATTR_TYPE]); + if (info->attrs[FOU_ATTR_REMCSUM_NOPARTIAL]) + cfg->flags |= FOU_F_REMCSUM_NOPARTIAL; + return 0; } diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index 53db2c309572..ea82fd492c1b 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -134,6 +134,7 @@ static bool tcp_fastopen_create_child(struct sock *sk, struct tcp_sock *tp; struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue; struct sock *child; + u32 end_seq; req->num_retrans = 0; req->num_timeout = 0; @@ -185,20 +186,35 @@ static bool tcp_fastopen_create_child(struct sock *sk, /* Queue the data carried in the SYN packet. We need to first * bump skb's refcnt because the caller will attempt to free it. + * Note that IPv6 might also have used skb_get() trick + * in tcp_v6_conn_request() to keep this SYN around (treq->pktopts) + * So we need to eventually get a clone of the packet, + * before inserting it in sk_receive_queue. * * XXX (TFO) - we honor a zero-payload TFO request for now, * (any reason not to?) but no need to queue the skb since * there is no data. How about SYN+FIN? */ - if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1) { - skb = skb_get(skb); - skb_dst_drop(skb); - __skb_pull(skb, tcp_hdr(skb)->doff * 4); - skb_set_owner_r(skb, child); - __skb_queue_tail(&child->sk_receive_queue, skb); - tp->syn_data_acked = 1; + end_seq = TCP_SKB_CB(skb)->end_seq; + if (end_seq != TCP_SKB_CB(skb)->seq + 1) { + struct sk_buff *skb2; + + if (unlikely(skb_shared(skb))) + skb2 = skb_clone(skb, GFP_ATOMIC); + else + skb2 = skb_get(skb); + + if (likely(skb2)) { + skb_dst_drop(skb2); + __skb_pull(skb2, tcp_hdrlen(skb)); + skb_set_owner_r(skb2, child); + __skb_queue_tail(&child->sk_receive_queue, skb2); + tp->syn_data_acked = 1; + } else { + end_seq = TCP_SKB_CB(skb)->seq + 1; + } } - tcp_rsk(req)->rcv_nxt = tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; + tcp_rsk(req)->rcv_nxt = tp->rcv_nxt = end_seq; sk->sk_data_ready(sk); bh_unlock_sock(child); sock_put(child); diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index d10f6f4ead27..4915d8284a86 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c @@ -402,6 +402,13 @@ int udp_gro_complete(struct sk_buff *skb, int nhoff) } rcu_read_unlock(); + + if (skb->remcsum_offload) + skb_shinfo(skb)->gso_type |= SKB_GSO_TUNNEL_REMCSUM; + + skb->encapsulation = 1; + skb_set_inner_mac_header(skb, nhoff + sizeof(struct udphdr)); + return err; } @@ -410,9 +417,13 @@ static int udp4_gro_complete(struct sk_buff *skb, int nhoff) const struct iphdr *iph = ip_hdr(skb); struct udphdr *uh = (struct udphdr *)(skb->data + nhoff); - if (uh->check) + if (uh->check) { + skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL_CSUM; uh->check = ~udp_v4_check(skb->len - nhoff, iph->saddr, iph->daddr, 0); + } else { + skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL; + } return udp_gro_complete(skb, nhoff); } diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index 2f780cba6e12..f45d6db50a45 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -172,7 +172,7 @@ static void __net_exit ip6_fl_purge(struct net *net) { int i; - spin_lock(&ip6_fl_lock); + spin_lock_bh(&ip6_fl_lock); for (i = 0; i <= FL_HASH_MASK; i++) { struct ip6_flowlabel *fl; struct ip6_flowlabel __rcu **flp; @@ -190,7 +190,7 @@ static void __net_exit ip6_fl_purge(struct net *net) flp = &fl->next; } } - spin_unlock(&ip6_fl_lock); + spin_unlock_bh(&ip6_fl_lock); } static struct ip6_flowlabel *fl_intern(struct net *net, diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index d33df4cbd872..7deebf102cba 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1273,7 +1273,7 @@ emsgsize: /* If this is the first and only packet and device * supports checksum offloading, let's use it. */ - if (!skb && + if (!skb && sk->sk_protocol == IPPROTO_UDP && length + fragheaderlen < mtu && rt->dst.dev->features & NETIF_F_V6_CSUM && !exthdrlen) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 98565ce0ebcd..4688bd4d7f59 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -141,7 +141,7 @@ static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) u32 *p = NULL; if (!(rt->dst.flags & DST_HOST)) - return NULL; + return dst_cow_metrics_generic(dst, old); peer = rt6_get_peer_create(rt); if (peer) { diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c index a56276996b72..ab889bb16b3c 100644 --- a/net/ipv6/udp_offload.c +++ b/net/ipv6/udp_offload.c @@ -161,9 +161,13 @@ static int udp6_gro_complete(struct sk_buff *skb, int nhoff) const struct ipv6hdr *ipv6h = ipv6_hdr(skb); struct udphdr *uh = (struct udphdr *)(skb->data + nhoff); - if (uh->check) + if (uh->check) { + skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL_CSUM; uh->check = ~udp_v6_check(skb->len - nhoff, &ipv6h->saddr, &ipv6h->daddr, 0); + } else { + skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL; + } return udp_gro_complete(skb, nhoff); } diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c index 265e190f2218..c598f74063a1 100644 --- a/net/netfilter/nft_compat.c +++ b/net/netfilter/nft_compat.c @@ -19,6 +19,7 @@ #include <linux/netfilter/x_tables.h> #include <linux/netfilter_ipv4/ip_tables.h> #include <linux/netfilter_ipv6/ip6_tables.h> +#include <linux/netfilter_bridge/ebtables.h> #include <net/netfilter/nf_tables.h> static int nft_compat_chain_validate_dependency(const char *tablename, @@ -40,6 +41,7 @@ static int nft_compat_chain_validate_dependency(const char *tablename, union nft_entry { struct ipt_entry e4; struct ip6t_entry e6; + struct ebt_entry ebt; }; static inline void @@ -50,9 +52,9 @@ nft_compat_set_par(struct xt_action_param *par, void *xt, const void *xt_info) par->hotdrop = false; } -static void nft_target_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) +static void nft_target_eval_xt(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) { void *info = nft_expr_priv(expr); struct xt_target *target = expr->ops->data; @@ -66,7 +68,7 @@ static void nft_target_eval(const struct nft_expr *expr, if (pkt->xt.hotdrop) ret = NF_DROP; - switch(ret) { + switch (ret) { case XT_CONTINUE: data[NFT_REG_VERDICT].verdict = NFT_CONTINUE; break; @@ -74,7 +76,41 @@ static void nft_target_eval(const struct nft_expr *expr, data[NFT_REG_VERDICT].verdict = ret; break; } - return; +} + +static void nft_target_eval_bridge(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) +{ + void *info = nft_expr_priv(expr); + struct xt_target *target = expr->ops->data; + struct sk_buff *skb = pkt->skb; + int ret; + + nft_compat_set_par((struct xt_action_param *)&pkt->xt, target, info); + + ret = target->target(skb, &pkt->xt); + + if (pkt->xt.hotdrop) + ret = NF_DROP; + + switch (ret) { + case EBT_ACCEPT: + data[NFT_REG_VERDICT].verdict = NF_ACCEPT; + break; + case EBT_DROP: + data[NFT_REG_VERDICT].verdict = NF_DROP; + break; + case EBT_CONTINUE: + data[NFT_REG_VERDICT].verdict = NFT_CONTINUE; + break; + case EBT_RETURN: + data[NFT_REG_VERDICT].verdict = NFT_RETURN; + break; + default: + data[NFT_REG_VERDICT].verdict = ret; + break; + } } static const struct nla_policy nft_target_policy[NFTA_TARGET_MAX + 1] = { @@ -100,6 +136,10 @@ nft_target_set_tgchk_param(struct xt_tgchk_param *par, entry->e6.ipv6.proto = proto; entry->e6.ipv6.invflags = inv ? IP6T_INV_PROTO : 0; break; + case NFPROTO_BRIDGE: + entry->ebt.ethproto = proto; + entry->ebt.invflags = inv ? EBT_IPROTO : 0; + break; } par->entryinfo = entry; par->target = target; @@ -307,6 +347,10 @@ nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx, entry->e6.ipv6.proto = proto; entry->e6.ipv6.invflags = inv ? IP6T_INV_PROTO : 0; break; + case NFPROTO_BRIDGE: + entry->ebt.ethproto = proto; + entry->ebt.invflags = inv ? EBT_IPROTO : 0; + break; } par->entryinfo = entry; par->match = match; @@ -490,6 +534,9 @@ nfnl_compat_get(struct sock *nfnl, struct sk_buff *skb, case AF_INET6: fmt = "ip6t_%s"; break; + case NFPROTO_BRIDGE: + fmt = "ebt_%s"; + break; default: pr_err("nft_compat: unsupported protocol %d\n", nfmsg->nfgen_family); @@ -663,13 +710,17 @@ nft_target_select_ops(const struct nft_ctx *ctx, nft_target->ops.type = &nft_target_type; nft_target->ops.size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize)); - nft_target->ops.eval = nft_target_eval; nft_target->ops.init = nft_target_init; nft_target->ops.destroy = nft_target_destroy; nft_target->ops.dump = nft_target_dump; nft_target->ops.validate = nft_target_validate; nft_target->ops.data = target; + if (family == NFPROTO_BRIDGE) + nft_target->ops.eval = nft_target_eval_bridge; + else + nft_target->ops.eval = nft_target_eval_xt; + list_add(&nft_target->head, &nft_target_list); return &nft_target->ops; diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c index 6404a726d17b..9615b8b9fb37 100644 --- a/net/netfilter/nft_lookup.c +++ b/net/netfilter/nft_lookup.c @@ -39,6 +39,7 @@ static void nft_lookup_eval(const struct nft_expr *expr, static const struct nla_policy nft_lookup_policy[NFTA_LOOKUP_MAX + 1] = { [NFTA_LOOKUP_SET] = { .type = NLA_STRING }, + [NFTA_LOOKUP_SET_ID] = { .type = NLA_U32 }, [NFTA_LOOKUP_SREG] = { .type = NLA_U32 }, [NFTA_LOOKUP_DREG] = { .type = NLA_U32 }, }; diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index e2c348b8baca..50ec42f170a0 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -717,6 +717,8 @@ int ovs_flow_key_extract_userspace(const struct nlattr *attr, { int err; + memset(key, 0, OVS_SW_FLOW_KEY_METADATA_SIZE); + /* Extract metadata from netlink attributes. */ err = ovs_nla_get_flow_metadata(attr, key, log); if (err) diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index 993281e6278d..216f20b90aa5 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -1516,7 +1516,7 @@ int ovs_nla_put_identifier(const struct sw_flow *flow, struct sk_buff *skb) /* Called with ovs_mutex or RCU read lock. */ int ovs_nla_put_masked_key(const struct sw_flow *flow, struct sk_buff *skb) { - return ovs_nla_put_key(&flow->mask->key, &flow->key, + return ovs_nla_put_key(&flow->key, &flow->key, OVS_FLOW_ATTR_KEY, false, skb); } @@ -1746,7 +1746,7 @@ static int validate_and_copy_set_tun(const struct nlattr *attr, struct sw_flow_key key; struct ovs_tunnel_info *tun_info; struct nlattr *a; - int err, start, opts_type; + int err = 0, start, opts_type; ovs_match_init(&match, &key, NULL); opts_type = ipv4_tun_from_nlattr(nla_data(attr), &match, false, log); diff --git a/net/rds/cong.c b/net/rds/cong.c index e5b65acd650b..e6144b8246fd 100644 --- a/net/rds/cong.c +++ b/net/rds/cong.c @@ -221,7 +221,21 @@ void rds_cong_queue_updates(struct rds_cong_map *map) list_for_each_entry(conn, &map->m_conn_list, c_map_item) { if (!test_and_set_bit(0, &conn->c_map_queued)) { rds_stats_inc(s_cong_update_queued); - rds_send_xmit(conn); + /* We cannot inline the call to rds_send_xmit() here + * for two reasons (both pertaining to a TCP transport): + * 1. When we get here from the receive path, we + * are already holding the sock_lock (held by + * tcp_v4_rcv()). So inlining calls to + * tcp_setsockopt and/or tcp_sendmsg will deadlock + * when it tries to get the sock_lock()) + * 2. Interrupts are masked so that we can mark the + * the port congested from both send and recv paths. + * (See comment around declaration of rdc_cong_lock). + * An attempt to get the sock_lock() here will + * therefore trigger warnings. + * Defer the xmit to rds_send_worker() instead. + */ + queue_delayed_work(rds_wq, &conn->c_send_w, 0); } } diff --git a/samples/seccomp/bpf-fancy.c b/samples/seccomp/bpf-fancy.c index 8eb483aaec46..e8b24f443709 100644 --- a/samples/seccomp/bpf-fancy.c +++ b/samples/seccomp/bpf-fancy.c @@ -25,7 +25,9 @@ int main(int argc, char **argv) { - struct bpf_labels l; + struct bpf_labels l = { + .count = 0, + }; static const char msg1[] = "Please type something: "; static const char msg2[] = "You typed: "; char buf[256]; diff --git a/samples/seccomp/bpf-helper.c b/samples/seccomp/bpf-helper.c index 579cfe331886..05cb4d5ff9f5 100644 --- a/samples/seccomp/bpf-helper.c +++ b/samples/seccomp/bpf-helper.c @@ -10,6 +10,7 @@ */ #include <stdio.h> +#include <stdlib.h> #include <string.h> #include "bpf-helper.h" @@ -63,6 +64,11 @@ __u32 seccomp_bpf_label(struct bpf_labels *labels, const char *label) { struct __bpf_label *begin = labels->labels, *end; int id; + + if (labels->count == BPF_LABELS_MAX) { + fprintf(stderr, "Too many labels\n"); + exit(1); + } if (labels->count == 0) { begin->label = label; begin->location = 0xffffffff; diff --git a/scripts/Makefile b/scripts/Makefile index 72902b5f2721..2016a64497ab 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -36,6 +36,7 @@ subdir-$(CONFIG_MODVERSIONS) += genksyms subdir-y += mod subdir-$(CONFIG_SECURITY_SELINUX) += selinux subdir-$(CONFIG_DTC) += dtc +subdir-$(CONFIG_GDB_SCRIPTS) += gdb # Let clean descend into subdirs subdir- += basic kconfig package diff --git a/scripts/gdb/Makefile b/scripts/gdb/Makefile new file mode 100644 index 000000000000..62f5f65becfd --- /dev/null +++ b/scripts/gdb/Makefile @@ -0,0 +1 @@ +subdir-y := linux diff --git a/scripts/gdb/linux/.gitignore b/scripts/gdb/linux/.gitignore new file mode 100644 index 000000000000..52e4e61140d1 --- /dev/null +++ b/scripts/gdb/linux/.gitignore @@ -0,0 +1,2 @@ +*.pyc +*.pyo diff --git a/scripts/gdb/linux/Makefile b/scripts/gdb/linux/Makefile new file mode 100644 index 000000000000..6cf1ecf61057 --- /dev/null +++ b/scripts/gdb/linux/Makefile @@ -0,0 +1,11 @@ +always := gdb-scripts + +SRCTREE := $(shell cd $(srctree) && /bin/pwd) + +$(obj)/gdb-scripts: +ifneq ($(KBUILD_SRC),) + $(Q)ln -fsn $(SRCTREE)/$(obj)/*.py $(objtree)/$(obj) +endif + @: + +clean-files := *.pyc *.pyo $(if $(KBUILD_SRC),*.py) diff --git a/scripts/gdb/linux/cpus.py b/scripts/gdb/linux/cpus.py new file mode 100644 index 000000000000..4297b83fedef --- /dev/null +++ b/scripts/gdb/linux/cpus.py @@ -0,0 +1,135 @@ +# +# gdb helper commands and functions for Linux kernel debugging +# +# per-cpu tools +# +# Copyright (c) Siemens AG, 2011-2013 +# +# Authors: +# Jan Kiszka <jan.kiszka@siemens.com> +# +# This work is licensed under the terms of the GNU GPL version 2. +# + +import gdb + +from linux import tasks, utils + + +MAX_CPUS = 4096 + + +def get_current_cpu(): + if utils.get_gdbserver_type() == utils.GDBSERVER_QEMU: + return gdb.selected_thread().num - 1 + elif utils.get_gdbserver_type() == utils.GDBSERVER_KGDB: + tid = gdb.selected_thread().ptid[2] + if tid > (0x100000000 - MAX_CPUS - 2): + return 0x100000000 - tid - 2 + else: + return tasks.get_thread_info(tasks.get_task_by_pid(tid))['cpu'] + else: + raise gdb.GdbError("Sorry, obtaining the current CPU is not yet " + "supported with this gdb server.") + + +def per_cpu(var_ptr, cpu): + if cpu == -1: + cpu = get_current_cpu() + if utils.is_target_arch("sparc:v9"): + offset = gdb.parse_and_eval( + "trap_block[{0}].__per_cpu_base".format(str(cpu))) + else: + try: + offset = gdb.parse_and_eval( + "__per_cpu_offset[{0}]".format(str(cpu))) + except gdb.error: + # !CONFIG_SMP case + offset = 0 + pointer = var_ptr.cast(utils.get_long_type()) + offset + return pointer.cast(var_ptr.type).dereference() + + +cpu_mask = {} + + +def cpu_mask_invalidate(event): + global cpu_mask + cpu_mask = {} + gdb.events.stop.disconnect(cpu_mask_invalidate) + if hasattr(gdb.events, 'new_objfile'): + gdb.events.new_objfile.disconnect(cpu_mask_invalidate) + + +def cpu_list(mask_name): + global cpu_mask + mask = None + if mask_name in cpu_mask: + mask = cpu_mask[mask_name] + if mask is None: + mask = gdb.parse_and_eval(mask_name + ".bits") + if hasattr(gdb, 'events'): + cpu_mask[mask_name] = mask + gdb.events.stop.connect(cpu_mask_invalidate) + if hasattr(gdb.events, 'new_objfile'): + gdb.events.new_objfile.connect(cpu_mask_invalidate) + bits_per_entry = mask[0].type.sizeof * 8 + num_entries = mask.type.sizeof * 8 / bits_per_entry + entry = -1 + bits = 0 + + while True: + while bits == 0: + entry += 1 + if entry == num_entries: + return + bits = mask[entry] + if bits != 0: + bit = 0 + break + + while bits & 1 == 0: + bits >>= 1 + bit += 1 + + cpu = entry * bits_per_entry + bit + + bits >>= 1 + bit += 1 + + yield cpu + + +class PerCpu(gdb.Function): + """Return per-cpu variable. + +$lx_per_cpu("VAR"[, CPU]): Return the per-cpu variable called VAR for the +given CPU number. If CPU is omitted, the CPU of the current context is used. +Note that VAR has to be quoted as string.""" + + def __init__(self): + super(PerCpu, self).__init__("lx_per_cpu") + + def invoke(self, var_name, cpu=-1): + var_ptr = gdb.parse_and_eval("&" + var_name.string()) + return per_cpu(var_ptr, cpu) + + +PerCpu() + + +class LxCurrentFunc(gdb.Function): + """Return current task. + +$lx_current([CPU]): Return the per-cpu task variable for the given CPU +number. If CPU is omitted, the CPU of the current context is used.""" + + def __init__(self): + super(LxCurrentFunc, self).__init__("lx_current") + + def invoke(self, cpu=-1): + var_ptr = gdb.parse_and_eval("¤t_task") + return per_cpu(var_ptr, cpu).dereference() + + +LxCurrentFunc() diff --git a/scripts/gdb/linux/dmesg.py b/scripts/gdb/linux/dmesg.py new file mode 100644 index 000000000000..3c947f0c5dad --- /dev/null +++ b/scripts/gdb/linux/dmesg.py @@ -0,0 +1,65 @@ +# +# gdb helper commands and functions for Linux kernel debugging +# +# kernel log buffer dump +# +# Copyright (c) Siemens AG, 2011, 2012 +# +# Authors: +# Jan Kiszka <jan.kiszka@siemens.com> +# +# This work is licensed under the terms of the GNU GPL version 2. +# + +import gdb +import string + +from linux import utils + + +class LxDmesg(gdb.Command): + """Print Linux kernel log buffer.""" + + def __init__(self): + super(LxDmesg, self).__init__("lx-dmesg", gdb.COMMAND_DATA) + + def invoke(self, arg, from_tty): + log_buf_addr = int(str(gdb.parse_and_eval("log_buf")).split()[0], 16) + log_first_idx = int(gdb.parse_and_eval("log_first_idx")) + log_next_idx = int(gdb.parse_and_eval("log_next_idx")) + log_buf_len = int(gdb.parse_and_eval("log_buf_len")) + + inf = gdb.inferiors()[0] + start = log_buf_addr + log_first_idx + if log_first_idx < log_next_idx: + log_buf_2nd_half = -1 + length = log_next_idx - log_first_idx + log_buf = inf.read_memory(start, length) + else: + log_buf_2nd_half = log_buf_len - log_first_idx + log_buf = inf.read_memory(start, log_buf_2nd_half) + \ + inf.read_memory(log_buf_addr, log_next_idx) + + pos = 0 + while pos < log_buf.__len__(): + length = utils.read_u16(log_buf[pos + 8:pos + 10]) + if length == 0: + if log_buf_2nd_half == -1: + gdb.write("Corrupted log buffer!\n") + break + pos = log_buf_2nd_half + continue + + text_len = utils.read_u16(log_buf[pos + 10:pos + 12]) + text = log_buf[pos + 16:pos + 16 + text_len] + time_stamp = utils.read_u64(log_buf[pos:pos + 8]) + + for line in memoryview(text).tobytes().splitlines(): + gdb.write("[{time:12.6f}] {line}\n".format( + time=time_stamp / 1000000000.0, + line=line)) + + pos += length + + +LxDmesg() diff --git a/scripts/gdb/linux/modules.py b/scripts/gdb/linux/modules.py new file mode 100644 index 000000000000..a1504c4f1900 --- /dev/null +++ b/scripts/gdb/linux/modules.py @@ -0,0 +1,103 @@ +# +# gdb helper commands and functions for Linux kernel debugging +# +# module tools +# +# Copyright (c) Siemens AG, 2013 +# +# Authors: +# Jan Kiszka <jan.kiszka@siemens.com> +# +# This work is licensed under the terms of the GNU GPL version 2. +# + +import gdb + +from linux import cpus, utils + + +module_type = utils.CachedType("struct module") + + +def module_list(): + global module_type + module_ptr_type = module_type.get_type().pointer() + modules = gdb.parse_and_eval("modules") + entry = modules['next'] + end_of_list = modules.address + + while entry != end_of_list: + yield utils.container_of(entry, module_ptr_type, "list") + entry = entry['next'] + + +def find_module_by_name(name): + for module in module_list(): + if module['name'].string() == name: + return module + return None + + +class LxModule(gdb.Function): + """Find module by name and return the module variable. + +$lx_module("MODULE"): Given the name MODULE, iterate over all loaded modules +of the target and return that module variable which MODULE matches.""" + + def __init__(self): + super(LxModule, self).__init__("lx_module") + + def invoke(self, mod_name): + mod_name = mod_name.string() + module = find_module_by_name(mod_name) + if module: + return module.dereference() + else: + raise gdb.GdbError("Unable to find MODULE " + mod_name) + + +LxModule() + + +class LxLsmod(gdb.Command): + """List currently loaded modules.""" + + _module_use_type = utils.CachedType("struct module_use") + + def __init__(self): + super(LxLsmod, self).__init__("lx-lsmod", gdb.COMMAND_DATA) + + def invoke(self, arg, from_tty): + gdb.write( + "Address{0} Module Size Used by\n".format( + " " if utils.get_long_type().sizeof == 8 else "")) + + for module in module_list(): + ref = 0 + module_refptr = module['refptr'] + for cpu in cpus.cpu_list("cpu_possible_mask"): + refptr = cpus.per_cpu(module_refptr, cpu) + ref += refptr['incs'] + ref -= refptr['decs'] + + gdb.write("{address} {name:<19} {size:>8} {ref}".format( + address=str(module['module_core']).split()[0], + name=module['name'].string(), + size=str(module['core_size']), + ref=str(ref))) + + source_list = module['source_list'] + t = self._module_use_type.get_type().pointer() + entry = source_list['next'] + first = True + while entry != source_list.address: + use = utils.container_of(entry, t, "source_list") + gdb.write("{separator}{name}".format( + separator=" " if first else ",", + name=use['source']['name'].string())) + first = False + entry = entry['next'] + gdb.write("\n") + + +LxLsmod() diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py new file mode 100644 index 000000000000..cd5bea965d4e --- /dev/null +++ b/scripts/gdb/linux/symbols.py @@ -0,0 +1,177 @@ +# +# gdb helper commands and functions for Linux kernel debugging +# +# load kernel and module symbols +# +# Copyright (c) Siemens AG, 2011-2013 +# +# Authors: +# Jan Kiszka <jan.kiszka@siemens.com> +# +# This work is licensed under the terms of the GNU GPL version 2. +# + +import gdb +import os +import re +import string + +from linux import modules, utils + + +if hasattr(gdb, 'Breakpoint'): + class LoadModuleBreakpoint(gdb.Breakpoint): + def __init__(self, spec, gdb_command): + super(LoadModuleBreakpoint, self).__init__(spec, internal=True) + self.silent = True + self.gdb_command = gdb_command + + def stop(self): + module = gdb.parse_and_eval("mod") + module_name = module['name'].string() + cmd = self.gdb_command + + # enforce update if object file is not found + cmd.module_files_updated = False + + # Disable pagination while reporting symbol (re-)loading. + # The console input is blocked in this context so that we would + # get stuck waiting for the user to acknowledge paged output. + show_pagination = gdb.execute("show pagination", to_string=True) + pagination = show_pagination.endswith("on.\n") + gdb.execute("set pagination off") + + if module_name in cmd.loaded_modules: + gdb.write("refreshing all symbols to reload module " + "'{0}'\n".format(module_name)) + cmd.load_all_symbols() + else: + cmd.load_module_symbols(module) + + # restore pagination state + gdb.execute("set pagination %s" % ("on" if pagination else "off")) + + return False + + +class LxSymbols(gdb.Command): + """(Re-)load symbols of Linux kernel and currently loaded modules. + +The kernel (vmlinux) is taken from the current working directly. Modules (.ko) +are scanned recursively, starting in the same directory. Optionally, the module +search path can be extended by a space separated list of paths passed to the +lx-symbols command.""" + + module_paths = [] + module_files = [] + module_files_updated = False + loaded_modules = [] + breakpoint = None + + def __init__(self): + super(LxSymbols, self).__init__("lx-symbols", gdb.COMMAND_FILES, + gdb.COMPLETE_FILENAME) + + def _update_module_files(self): + self.module_files = [] + for path in self.module_paths: + gdb.write("scanning for modules in {0}\n".format(path)) + for root, dirs, files in os.walk(path): + for name in files: + if name.endswith(".ko"): + self.module_files.append(root + "/" + name) + self.module_files_updated = True + + def _get_module_file(self, module_name): + module_pattern = ".*/{0}\.ko$".format( + module_name.replace("_", r"[_\-]")) + for name in self.module_files: + if re.match(module_pattern, name) and os.path.exists(name): + return name + return None + + def _section_arguments(self, module): + try: + sect_attrs = module['sect_attrs'].dereference() + except gdb.error: + return "" + attrs = sect_attrs['attrs'] + section_name_to_address = { + attrs[n]['name'].string() : attrs[n]['address'] + for n in range(int(sect_attrs['nsections']))} + args = [] + for section_name in [".data", ".data..read_mostly", ".rodata", ".bss"]: + address = section_name_to_address.get(section_name) + if address: + args.append(" -s {name} {addr}".format( + name=section_name, addr=str(address))) + return "".join(args) + + def load_module_symbols(self, module): + module_name = module['name'].string() + module_addr = str(module['module_core']).split()[0] + + module_file = self._get_module_file(module_name) + if not module_file and not self.module_files_updated: + self._update_module_files() + module_file = self._get_module_file(module_name) + + if module_file: + gdb.write("loading @{addr}: {filename}\n".format( + addr=module_addr, filename=module_file)) + cmdline = "add-symbol-file {filename} {addr}{sections}".format( + filename=module_file, + addr=module_addr, + sections=self._section_arguments(module)) + gdb.execute(cmdline, to_string=True) + if not module_name in self.loaded_modules: + self.loaded_modules.append(module_name) + else: + gdb.write("no module object found for '{0}'\n".format(module_name)) + + def load_all_symbols(self): + gdb.write("loading vmlinux\n") + + # Dropping symbols will disable all breakpoints. So save their states + # and restore them afterward. + saved_states = [] + if hasattr(gdb, 'breakpoints') and not gdb.breakpoints() is None: + for bp in gdb.breakpoints(): + saved_states.append({'breakpoint': bp, 'enabled': bp.enabled}) + + # drop all current symbols and reload vmlinux + gdb.execute("symbol-file", to_string=True) + gdb.execute("symbol-file vmlinux") + + self.loaded_modules = [] + module_list = modules.module_list() + if not module_list: + gdb.write("no modules found\n") + else: + [self.load_module_symbols(module) for module in module_list] + + for saved_state in saved_states: + saved_state['breakpoint'].enabled = saved_state['enabled'] + + def invoke(self, arg, from_tty): + self.module_paths = arg.split() + self.module_paths.append(os.getcwd()) + + # enforce update + self.module_files = [] + self.module_files_updated = False + + self.load_all_symbols() + + if hasattr(gdb, 'Breakpoint'): + if not self.breakpoint is None: + self.breakpoint.delete() + self.breakpoint = None + self.breakpoint = LoadModuleBreakpoint( + "kernel/module.c:do_init_module", self) + else: + gdb.write("Note: symbol update on module loading not supported " + "with this gdb version\n") + + +LxSymbols() diff --git a/scripts/gdb/linux/tasks.py b/scripts/gdb/linux/tasks.py new file mode 100644 index 000000000000..e2037d9bb7eb --- /dev/null +++ b/scripts/gdb/linux/tasks.py @@ -0,0 +1,100 @@ +# +# gdb helper commands and functions for Linux kernel debugging +# +# task & thread tools +# +# Copyright (c) Siemens AG, 2011-2013 +# +# Authors: +# Jan Kiszka <jan.kiszka@siemens.com> +# +# This work is licensed under the terms of the GNU GPL version 2. +# + +import gdb + +from linux import utils + + +task_type = utils.CachedType("struct task_struct") + +def task_lists(): + global task_type + task_ptr_type = task_type.get_type().pointer() + init_task = gdb.parse_and_eval("init_task").address + t = g = init_task + + while True: + while True: + yield t + + t = utils.container_of(t['thread_group']['next'], + task_ptr_type, "thread_group") + if t == g: + break + + t = g = utils.container_of(g['tasks']['next'], + task_ptr_type, "tasks") + if t == init_task: + return + +def get_task_by_pid(pid): + for task in task_lists(): + if int(task['pid']) == pid: + return task + return None + + +class LxTaskByPidFunc(gdb.Function): + """Find Linux task by PID and return the task_struct variable. + +$lx_task_by_pid(PID): Given PID, iterate over all tasks of the target and +return that task_struct variable which PID matches.""" + + def __init__(self): + super(LxTaskByPidFunc, self).__init__("lx_task_by_pid") + + def invoke(self, pid): + task = get_task_by_pid(pid) + if task: + return task.dereference() + else: + raise gdb.GdbError("No task of PID " + str(pid)) + + +LxTaskByPidFunc() + + +thread_info_type = utils.CachedType("struct thread_info") + +ia64_task_size = None + + +def get_thread_info(task): + global thread_info_type + thread_info_ptr_type = thread_info_type.get_type().pointer() + if utils.is_target_arch("ia64"): + global ia64_task_size + if ia64_task_size is None: + ia64_task_size = gdb.parse_and_eval("sizeof(struct task_struct)") + thread_info_addr = task.address + ia64_task_size + thread_info = thread_info_addr.cast(thread_info_ptr_type) + else: + thread_info = task['stack'].cast(thread_info_ptr_type) + return thread_info.dereference() + + +class LxThreadInfoFunc (gdb.Function): + """Calculate Linux thread_info from task variable. + +$lx_thread_info(TASK): Given TASK, return the corresponding thread_info +variable.""" + + def __init__(self): + super(LxThreadInfoFunc, self).__init__("lx_thread_info") + + def invoke(self, task): + return get_thread_info(task) + + +LxThreadInfoFunc() diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py new file mode 100644 index 000000000000..128c306db3ee --- /dev/null +++ b/scripts/gdb/linux/utils.py @@ -0,0 +1,156 @@ +# +# gdb helper commands and functions for Linux kernel debugging +# +# common utilities +# +# Copyright (c) Siemens AG, 2011-2013 +# +# Authors: +# Jan Kiszka <jan.kiszka@siemens.com> +# +# This work is licensed under the terms of the GNU GPL version 2. +# + +import gdb + + +class CachedType: + def __init__(self, name): + self._type = None + self._name = name + + def _new_objfile_handler(self, event): + self._type = None + gdb.events.new_objfile.disconnect(self._new_objfile_handler) + + def get_type(self): + if self._type is None: + self._type = gdb.lookup_type(self._name) + if self._type is None: + raise gdb.GdbError( + "cannot resolve type '{0}'".format(self._name)) + if hasattr(gdb, 'events') and hasattr(gdb.events, 'new_objfile'): + gdb.events.new_objfile.connect(self._new_objfile_handler) + return self._type + + +long_type = CachedType("long") + + +def get_long_type(): + global long_type + return long_type.get_type() + + +def offset_of(typeobj, field): + element = gdb.Value(0).cast(typeobj) + return int(str(element[field].address).split()[0], 16) + + +def container_of(ptr, typeobj, member): + return (ptr.cast(get_long_type()) - + offset_of(typeobj, member)).cast(typeobj) + + +class ContainerOf(gdb.Function): + """Return pointer to containing data structure. + +$container_of(PTR, "TYPE", "ELEMENT"): Given PTR, return a pointer to the +data structure of the type TYPE in which PTR is the address of ELEMENT. +Note that TYPE and ELEMENT have to be quoted as strings.""" + + def __init__(self): + super(ContainerOf, self).__init__("container_of") + + def invoke(self, ptr, typename, elementname): + return container_of(ptr, gdb.lookup_type(typename.string()).pointer(), + elementname.string()) + +ContainerOf() + + +BIG_ENDIAN = 0 +LITTLE_ENDIAN = 1 +target_endianness = None + + +def get_target_endianness(): + global target_endianness + if target_endianness is None: + endian = gdb.execute("show endian", to_string=True) + if "little endian" in endian: + target_endianness = LITTLE_ENDIAN + elif "big endian" in endian: + target_endianness = BIG_ENDIAN + else: + raise gdb.GdgError("unknown endianness '{0}'".format(str(endian))) + return target_endianness + + +def read_u16(buffer): + if get_target_endianness() == LITTLE_ENDIAN: + return ord(buffer[0]) + (ord(buffer[1]) << 8) + else: + return ord(buffer[1]) + (ord(buffer[0]) << 8) + + +def read_u32(buffer): + if get_target_endianness() == LITTLE_ENDIAN: + return read_u16(buffer[0:2]) + (read_u16(buffer[2:4]) << 16) + else: + return read_u16(buffer[2:4]) + (read_u16(buffer[0:2]) << 16) + + +def read_u64(buffer): + if get_target_endianness() == LITTLE_ENDIAN: + return read_u32(buffer[0:4]) + (read_u32(buffer[4:8]) << 32) + else: + return read_u32(buffer[4:8]) + (read_u32(buffer[0:4]) << 32) + + +target_arch = None + + +def is_target_arch(arch): + if hasattr(gdb.Frame, 'architecture'): + return arch in gdb.newest_frame().architecture().name() + else: + global target_arch + if target_arch is None: + target_arch = gdb.execute("show architecture", to_string=True) + return arch in target_arch + + +GDBSERVER_QEMU = 0 +GDBSERVER_KGDB = 1 +gdbserver_type = None + + +def get_gdbserver_type(): + def exit_handler(event): + global gdbserver_type + gdbserver_type = None + gdb.events.exited.disconnect(exit_handler) + + def probe_qemu(): + try: + return gdb.execute("monitor info version", to_string=True) != "" + except: + return False + + def probe_kgdb(): + try: + thread_info = gdb.execute("info thread 2", to_string=True) + return "shadowCPU0" in thread_info + except: + return False + + global gdbserver_type + if gdbserver_type is None: + if probe_qemu(): + gdbserver_type = GDBSERVER_QEMU + elif probe_kgdb(): + gdbserver_type = GDBSERVER_KGDB + if not gdbserver_type is None and hasattr(gdb, 'events'): + gdb.events.exited.connect(exit_handler) + return gdbserver_type diff --git a/scripts/gdb/vmlinux-gdb.py b/scripts/gdb/vmlinux-gdb.py new file mode 100644 index 000000000000..48489285f119 --- /dev/null +++ b/scripts/gdb/vmlinux-gdb.py @@ -0,0 +1,30 @@ +# +# gdb helper commands and functions for Linux kernel debugging +# +# loader module +# +# Copyright (c) Siemens AG, 2012, 2013 +# +# Authors: +# Jan Kiszka <jan.kiszka@siemens.com> +# +# This work is licensed under the terms of the GNU GPL version 2. +# + +import os + +sys.path.insert(0, os.path.dirname(__file__) + "/scripts/gdb") + +try: + gdb.parse_and_eval("0") + gdb.execute("", to_string=True) +except: + gdb.write("NOTE: gdb 7.2 or later required for Linux helper scripts to " + "work.\n") +else: + import linux.utils + import linux.symbols + import linux.modules + import linux.dmesg + import linux.tasks + import linux.cpus diff --git a/security/commoncap.c b/security/commoncap.c index 2915d8503054..f66713bd7450 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -434,7 +434,6 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data */ static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_cap) { - struct dentry *dentry; int rc = 0; struct cpu_vfs_cap_data vcaps; @@ -446,9 +445,7 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_c if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) return 0; - dentry = dget(bprm->file->f_path.dentry); - - rc = get_vfs_caps_from_disk(dentry, &vcaps); + rc = get_vfs_caps_from_disk(bprm->file->f_path.dentry, &vcaps); if (rc < 0) { if (rc == -EINVAL) printk(KERN_NOTICE "%s: get_vfs_caps_from_disk returned %d for %s\n", @@ -464,7 +461,6 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_c __func__, rc, bprm->filename); out: - dput(dentry); if (rc) bprm_clear_caps(bprm); diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 0c7aea4dea54..486ef6fa393b 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -414,6 +414,7 @@ link_check_failed: link_prealloc_failed: mutex_unlock(&user->cons_lock); + key_put(key); kleave(" = %d [prelink]", ret); return ret; diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 33db1ad4fd10..1684bcc78b34 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -1195,30 +1195,8 @@ static const struct file_operations sel_commit_bools_ops = { static void sel_remove_entries(struct dentry *de) { - struct list_head *node; - - spin_lock(&de->d_lock); - node = de->d_subdirs.next; - while (node != &de->d_subdirs) { - struct dentry *d = list_entry(node, struct dentry, d_child); - - spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED); - list_del_init(node); - - if (d->d_inode) { - dget_dlock(d); - spin_unlock(&de->d_lock); - spin_unlock(&d->d_lock); - d_delete(d); - simple_unlink(de->d_inode, d); - dput(d); - spin_lock(&de->d_lock); - } else - spin_unlock(&d->d_lock); - node = de->d_subdirs.next; - } - - spin_unlock(&de->d_lock); + d_genocide(de); + shrink_dcache_parent(de); } #define BOOL_DIR_NAME "booleans" @@ -1668,37 +1646,13 @@ static int sel_make_class_dir_entries(char *classname, int index, return rc; } -static void sel_remove_classes(void) -{ - struct list_head *class_node; - - list_for_each(class_node, &class_dir->d_subdirs) { - struct dentry *class_subdir = list_entry(class_node, - struct dentry, d_child); - struct list_head *class_subdir_node; - - list_for_each(class_subdir_node, &class_subdir->d_subdirs) { - struct dentry *d = list_entry(class_subdir_node, - struct dentry, d_child); - - if (d->d_inode) - if (d->d_inode->i_mode & S_IFDIR) - sel_remove_entries(d); - } - - sel_remove_entries(class_subdir); - } - - sel_remove_entries(class_dir); -} - static int sel_make_classes(void) { int rc, nclasses, i; char **classes; /* delete any existing entries */ - sel_remove_classes(); + sel_remove_entries(class_dir); rc = security_get_classes(&classes, &nclasses); if (rc) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index a0ccce4e46f8..ed94f6f836e7 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -3818,6 +3818,18 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, } #endif /* CONFIG_IPV6 */ +#ifdef CONFIG_SECURITY_SMACK_NETFILTER + /* + * If there is a secmark use it rather than the CIPSO label. + * If there is no secmark fall back to CIPSO. + * The secmark is assumed to reflect policy better. + */ + if (skb && skb->secmark != 0) { + skp = smack_from_secid(skb->secmark); + goto access_check; + } +#endif /* CONFIG_SECURITY_SMACK_NETFILTER */ + netlbl_secattr_init(&secattr); rc = netlbl_skbuff_getattr(skb, family, &secattr); if (rc == 0) @@ -3826,6 +3838,10 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, skp = &smack_known_huh; netlbl_secattr_destroy(&secattr); +#ifdef CONFIG_SECURITY_SMACK_NETFILTER +access_check: +#endif + #ifdef CONFIG_AUDIT smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net); ad.a.u.net->family = family; |