diff options
125 files changed, 2781 insertions, 847 deletions
diff --git a/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt b/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt index 1486497a24c1..ce6a1a072028 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt @@ -4,11 +4,13 @@ Specifying interrupt information for devices 1) Interrupt client nodes ------------------------- -Nodes that describe devices which generate interrupts must contain an either an -"interrupts" property or an "interrupts-extended" property. These properties -contain a list of interrupt specifiers, one per output interrupt. The format of -the interrupt specifier is determined by the interrupt controller to which the -interrupts are routed; see section 2 below for details. +Nodes that describe devices which generate interrupts must contain an +"interrupts" property, an "interrupts-extended" property, or both. If both are +present, the latter should take precedence; the former may be provided simply +for compatibility with software that does not recognize the latter. These +properties contain a list of interrupt specifiers, one per output interrupt. The +format of the interrupt specifier is determined by the interrupt controller to +which the interrupts are routed; see section 2 below for details. Example: interrupt-parent = <&intc1>; diff --git a/Documentation/devicetree/bindings/pci/designware-pcie.txt b/Documentation/devicetree/bindings/pci/designware-pcie.txt index d0d15ee42834..ed0d9b9fff2b 100644 --- a/Documentation/devicetree/bindings/pci/designware-pcie.txt +++ b/Documentation/devicetree/bindings/pci/designware-pcie.txt @@ -2,6 +2,10 @@ Required properties: - compatible: should contain "snps,dw-pcie" to identify the core. +- reg: Should contain the configuration address space. +- reg-names: Must be "config" for the PCIe configuration space. + (The old way of getting the configuration address space from "ranges" + is deprecated and should be avoided.) - #address-cells: set to <3> - #size-cells: set to <2> - device_type: set to "pci" diff --git a/Documentation/devicetree/bindings/pci/ti-pci.txt b/Documentation/devicetree/bindings/pci/ti-pci.txt new file mode 100644 index 000000000000..3d217911b313 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/ti-pci.txt @@ -0,0 +1,59 @@ +TI PCI Controllers + +PCIe Designware Controller + - compatible: Should be "ti,dra7-pcie"" + - reg : Two register ranges as listed in the reg-names property + - reg-names : The first entry must be "ti-conf" for the TI specific registers + The second entry must be "rc-dbics" for the designware pcie + registers + The third entry must be "config" for the PCIe configuration space + - phys : list of PHY specifiers (used by generic PHY framework) + - phy-names : must be "pcie-phy0", "pcie-phy1", "pcie-phyN".. based on the + number of PHYs as specified in *phys* property. + - ti,hwmods : Name of the hwmod associated to the pcie, "pcie<X>", + where <X> is the instance number of the pcie from the HW spec. + - interrupts : Two interrupt entries must be specified. The first one is for + main interrupt line and the second for MSI interrupt line. + - #address-cells, + #size-cells, + #interrupt-cells, + device_type, + ranges, + num-lanes, + interrupt-map-mask, + interrupt-map : as specified in ../designware-pcie.txt + +Example: +axi { + compatible = "simple-bus"; + #size-cells = <1>; + #address-cells = <1>; + ranges = <0x51000000 0x51000000 0x3000 + 0x0 0x20000000 0x10000000>; + pcie@51000000 { + compatible = "ti,dra7-pcie"; + reg = <0x51000000 0x2000>, <0x51002000 0x14c>, <0x1000 0x2000>; + reg-names = "rc_dbics", "ti_conf", "config"; + interrupts = <0 232 0x4>, <0 233 0x4>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges = <0x81000000 0 0 0x03000 0 0x00010000 + 0x82000000 0 0x20013000 0x13000 0 0xffed000>; + #interrupt-cells = <1>; + num-lanes = <1>; + ti,hwmods = "pcie1"; + phys = <&pcie1_phy>; + phy-names = "pcie-phy0"; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc 1>, + <0 0 0 2 &pcie_intc 2>, + <0 0 0 3 &pcie_intc 3>, + <0 0 0 4 &pcie_intc 4>; + pcie_intc: interrupt-controller { + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + }; + }; +}; diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index b18dd1779029..f1997e9da61f 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -349,7 +349,11 @@ prototypes: locking rules: inode->i_lock may block fl_copy_lock: yes no -fl_release_private: maybe no +fl_release_private: maybe maybe[1] + +[1]: ->fl_release_private for flock or POSIX locks is currently allowed +to block. Leases however can still be freed while the i_lock is held and +so fl_release_private called on a lease should not block. ----------------------- lock_manager_operations --------------------------- prototypes: diff --git a/Documentation/laptops/00-INDEX b/Documentation/laptops/00-INDEX index d399ae1fc724..a3b4f209e562 100644 --- a/Documentation/laptops/00-INDEX +++ b/Documentation/laptops/00-INDEX @@ -18,3 +18,5 @@ sonypi.txt - info on Linux Sony Programmable I/O Device support. thinkpad-acpi.txt - information on the (IBM and Lenovo) ThinkPad ACPI Extras driver. +toshiba_haps.txt + - information on the Toshiba HDD Active Protection Sensor driver. diff --git a/Documentation/laptops/toshiba_haps.txt b/Documentation/laptops/toshiba_haps.txt new file mode 100644 index 000000000000..11dbcfdc9e7a --- /dev/null +++ b/Documentation/laptops/toshiba_haps.txt @@ -0,0 +1,76 @@ +Kernel driver toshiba_haps +Toshiba HDD Active Protection Sensor +==================================== + +Author: Azael Avalos <coproscefalo@gmail.com> + + +0. Contents +----------- + +1. Description +2. Interface +3. Accelerometer axes +4. Supported devices +5. Usage + + +1. Description +-------------- + +This driver provides support for the accelerometer found in various Toshiba +laptops, being called "Toshiba HDD Protection - Shock Sensor" officialy, +and detects laptops automatically with this device. +On Windows, Toshiba provided software monitors this device and provides +automatic HDD protection (head unload) on sudden moves or harsh vibrations, +however, this driver only provides a notification via a sysfs file to let +userspace tools or daemons act accordingly, as well as providing a sysfs +file to set the desired protection level or sensor sensibility. + + +2. Interface +------------ + +This device comes with 3 methods: +_STA - Checks existence of the device, returning Zero if the device does not + exists or is not supported. +PTLV - Sets the desired protection level. +RSSS - Shuts down the HDD protection interface for a few seconds, + then restores normal operation. + +Note: +The presence of Solid State Drives (SSD) can make this driver to fail loading, +given the fact that such drives have no movable parts, and thus, not requiring +any "protection" as well as failing during the evaluation of the _STA method +found under this device. + + +3. Accelerometer axes +--------------------- + +This device does not report any axes, however, to query the sensor position +a couple HCI (Hardware Configuration Interface) calls (0x6D and 0xA6) are +provided to query such information, handled by the kernel module toshiba_acpi +since kernel version 3.15. + + +4. Supported devices +-------------------- + +This driver binds itself to the ACPI device TOS620A, and any Toshiba laptop +with this device is supported, given the fact that they have the presence of +conventional HDD and not only SSD, or a combination of both HDD and SSD. + + +5. Usage +-------- + +The sysfs files under /sys/devices/LNXSYSTM:00/LNXSYBUS:00/TOS620A:00/ are: +protection_level - The protection_level is readable and writeable, and + provides a way to let userspace query the current protection + level, as well as set the desired protection level, the + available protection levels are: + 0 - Disabled | 1 - Low | 2 - Medium | 3 - High +reset_protection - The reset_protection entry is writeable only, being "1" + the only parameter it accepts, it is used to trigger + a reset of the protection interface. diff --git a/MAINTAINERS b/MAINTAINERS index 3bf3fc26c891..a3e0d10e386e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3843,10 +3843,13 @@ F: drivers/tty/serial/ucc_uart.c FREESCALE SOC SOUND DRIVERS M: Timur Tabi <timur@tabi.org> +M: Nicolin Chen <nicoleotsuka@gmail.com> +M: Xiubo Li <Li.Xiubo@freescale.com> L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: linuxppc-dev@lists.ozlabs.org S: Maintained F: sound/soc/fsl/fsl* +F: sound/soc/fsl/imx* F: sound/soc/fsl/mpc8610_hpcd.c FREEVXFS FILESYSTEM @@ -4446,6 +4449,13 @@ F: include/linux/i2c-*.h F: include/uapi/linux/i2c.h F: include/uapi/linux/i2c-*.h +I2C ACPI SUPPORT +M: Mika Westerberg <mika.westerberg@linux.intel.com> +L: linux-i2c@vger.kernel.org +L: linux-acpi@vger.kernel.org +S: Maintained +F: drivers/i2c/i2c-acpi.c + I2C-TAOS-EVM DRIVER M: Jean Delvare <jdelvare@suse.de> L: linux-i2c@vger.kernel.org @@ -6858,6 +6868,14 @@ S: Supported F: Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt F: drivers/pci/host/pci-tegra.c +PCI DRIVER FOR TI DRA7XX +M: Kishon Vijay Abraham I <kishon@ti.com> +L: linux-omap@vger.kernel.org +L: linux-pci@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/pci/ti-pci.txt +F: drivers/pci/host/pci-dra7xx.c + PCI DRIVER FOR RENESAS R-CAR M: Simon Horman <horms@verge.net.au> L: linux-pci@vger.kernel.org @@ -10017,7 +10035,7 @@ F: arch/x86/ X86 PLATFORM DRIVERS M: Matthew Garrett <matthew.garrett@nebula.com> L: platform-driver-x86@vger.kernel.org -T: git git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86.git +T: git git://cavan.codon.org.uk/platform-drivers-x86.git S: Maintained F: drivers/platform/x86/ @@ -1,7 +1,7 @@ VERSION = 3 -PATCHLEVEL = 16 +PATCHLEVEL = 17 SUBLEVEL = 0 -EXTRAVERSION = +EXTRAVERSION = -rc1 NAME = Shuffling Zombie Juror # *DOCUMENTATION* diff --git a/arch/frv/include/asm/processor.h b/arch/frv/include/asm/processor.h index a34f309e5801..6554e78893f2 100644 --- a/arch/frv/include/asm/processor.h +++ b/arch/frv/include/asm/processor.h @@ -129,7 +129,8 @@ unsigned long get_wchan(struct task_struct *p); #define KSTK_EIP(tsk) ((tsk)->thread.frame0->pc) #define KSTK_ESP(tsk) ((tsk)->thread.frame0->sp) -#define cpu_relax() barrier() +#define cpu_relax() barrier() +#define cpu_relax_lowlatency() cpu_relax() /* data cache prefetch */ #define ARCH_HAS_PREFETCH diff --git a/arch/ia64/include/asm/unistd.h b/arch/ia64/include/asm/unistd.h index 4254f5d3218c..10a14ead70b9 100644 --- a/arch/ia64/include/asm/unistd.h +++ b/arch/ia64/include/asm/unistd.h @@ -11,7 +11,7 @@ -#define NR_syscalls 316 /* length of syscall table */ +#define NR_syscalls 317 /* length of syscall table */ /* * The following defines stop scripts/checksyscalls.sh from complaining about diff --git a/arch/ia64/include/uapi/asm/unistd.h b/arch/ia64/include/uapi/asm/unistd.h index 99801c3be914..6a65bb7d0657 100644 --- a/arch/ia64/include/uapi/asm/unistd.h +++ b/arch/ia64/include/uapi/asm/unistd.h @@ -329,5 +329,6 @@ #define __NR_sched_getattr 1337 #define __NR_renameat2 1338 #define __NR_getrandom 1339 +#define __NR_memfd_create 1339 #endif /* _UAPI_ASM_IA64_UNISTD_H */ diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S index 4c13837a9269..01edf242eb29 100644 --- a/arch/ia64/kernel/entry.S +++ b/arch/ia64/kernel/entry.S @@ -1777,6 +1777,7 @@ sys_call_table: data8 sys_sched_getattr data8 sys_renameat2 data8 sys_getrandom + data8 sys_memfd_create // 1340 .org sys_call_table + 8*NR_syscalls // guard against failures to increase NR_syscalls #endif /* __IA64_ASM_PARAVIRTUALIZED_NATIVE */ diff --git a/arch/microblaze/include/uapi/asm/unistd.h b/arch/microblaze/include/uapi/asm/unistd.h index 4e1ddc930a68..1c2380bf8fe6 100644 --- a/arch/microblaze/include/uapi/asm/unistd.h +++ b/arch/microblaze/include/uapi/asm/unistd.h @@ -399,5 +399,8 @@ #define __NR_sched_setattr 381 #define __NR_sched_getattr 382 #define __NR_renameat2 383 +#define __NR_seccomp 384 +#define __NR_getrandom 385 +#define __NR_memfd_create 386 #endif /* _UAPI_ASM_MICROBLAZE_UNISTD_H */ diff --git a/arch/microblaze/kernel/syscall_table.S b/arch/microblaze/kernel/syscall_table.S index 1a23d5d5480c..de59ee1d7010 100644 --- a/arch/microblaze/kernel/syscall_table.S +++ b/arch/microblaze/kernel/syscall_table.S @@ -384,3 +384,6 @@ ENTRY(sys_call_table) .long sys_sched_setattr .long sys_sched_getattr .long sys_renameat2 + .long sys_seccomp + .long sys_getrandom /* 385 */ + .long sys_memfd_create diff --git a/arch/powerpc/kvm/book3s_hv_builtin.c b/arch/powerpc/kvm/book3s_hv_builtin.c index 329d7fdd0a6a..b9615ba5b083 100644 --- a/arch/powerpc/kvm/book3s_hv_builtin.c +++ b/arch/powerpc/kvm/book3s_hv_builtin.c @@ -101,7 +101,7 @@ struct kvm_rma_info *kvm_alloc_rma() ri = kmalloc(sizeof(struct kvm_rma_info), GFP_KERNEL); if (!ri) return NULL; - page = cma_alloc(kvm_cma, kvm_rma_pages, get_order(kvm_rma_pages)); + page = cma_alloc(kvm_cma, kvm_rma_pages, order_base_2(kvm_rma_pages)); if (!page) goto err_out; atomic_set(&ri->use_count, 1); @@ -135,12 +135,12 @@ struct page *kvm_alloc_hpt(unsigned long nr_pages) { unsigned long align_pages = HPT_ALIGN_PAGES; - VM_BUG_ON(get_order(nr_pages) < KVM_CMA_CHUNK_ORDER - PAGE_SHIFT); + VM_BUG_ON(order_base_2(nr_pages) < KVM_CMA_CHUNK_ORDER - PAGE_SHIFT); /* Old CPUs require HPT aligned on a multiple of its size */ if (!cpu_has_feature(CPU_FTR_ARCH_206)) align_pages = nr_pages; - return cma_alloc(kvm_cma, nr_pages, get_order(align_pages)); + return cma_alloc(kvm_cma, nr_pages, order_base_2(align_pages)); } EXPORT_SYMBOL_GPL(kvm_alloc_hpt); diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 572460175ba5..7c492ed9087b 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -95,7 +95,7 @@ static inline gfn_t gfn_to_index(gfn_t gfn, gfn_t base_gfn, int level) #define KVM_REFILL_PAGES 25 #define KVM_MAX_CPUID_ENTRIES 80 #define KVM_NR_FIXED_MTRR_REGION 88 -#define KVM_NR_VAR_MTRR 10 +#define KVM_NR_VAR_MTRR 8 #define ASYNC_PF_PER_VCPU 64 diff --git a/arch/x86/include/uapi/asm/msr-index.h b/arch/x86/include/uapi/asm/msr-index.h index eac9e92fe181..e21331ce368f 100644 --- a/arch/x86/include/uapi/asm/msr-index.h +++ b/arch/x86/include/uapi/asm/msr-index.h @@ -149,6 +149,9 @@ #define MSR_CORE_C1_RES 0x00000660 +#define MSR_CC6_DEMOTION_POLICY_CONFIG 0x00000668 +#define MSR_MC6_DEMOTION_POLICY_CONFIG 0x00000669 + #define MSR_AMD64_MC0_MASK 0xc0010044 #define MSR_IA32_MCx_CTL(x) (MSR_IA32_MC0_CTL + 4*(x)) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 56657b0bb3bb..03954f7900f5 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -1491,9 +1491,6 @@ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt, goto exception; break; case VCPU_SREG_CS: - if (in_task_switch && rpl != dpl) - goto exception; - if (!(seg_desc.type & 8)) goto exception; @@ -4394,8 +4391,11 @@ done_prefixes: ctxt->execute = opcode.u.execute; + if (unlikely(ctxt->ud) && likely(!(ctxt->d & EmulateOnUD))) + return EMULATION_FAILED; + if (unlikely(ctxt->d & - (NotImpl|EmulateOnUD|Stack|Op3264|Sse|Mmx|Intercept|CheckPerm))) { + (NotImpl|Stack|Op3264|Sse|Mmx|Intercept|CheckPerm))) { /* * These are copied unconditionally here, and checked unconditionally * in x86_emulate_insn. @@ -4406,9 +4406,6 @@ done_prefixes: if (ctxt->d & NotImpl) return EMULATION_FAILED; - if (!(ctxt->d & EmulateOnUD) && ctxt->ud) - return EMULATION_FAILED; - if (mode == X86EMUL_MODE_PROT64 && (ctxt->d & Stack)) ctxt->op_bytes = 8; diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index 0027137daa56..2e3139eda93b 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -116,6 +116,7 @@ static int probe_common(struct virtio_device *vdev) .cleanup = virtio_cleanup, .priv = (unsigned long)vi, .name = vi->name, + .quality = 1000, }; vdev->priv = vi; diff --git a/drivers/hid/hid-cherry.c b/drivers/hid/hid-cherry.c index 1bdcccc54a1d..f745d2c1325e 100644 --- a/drivers/hid/hid-cherry.c +++ b/drivers/hid/hid-cherry.c @@ -28,7 +28,7 @@ static __u8 *ch_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { - if (*rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) { + if (*rsize >= 18 && rdesc[11] == 0x3c && rdesc[12] == 0x02) { hid_info(hdev, "fixing up Cherry Cymotion report descriptor\n"); rdesc[11] = rdesc[16] = 0xff; rdesc[12] = rdesc[17] = 0x03; diff --git a/drivers/hid/hid-huion.c b/drivers/hid/hid-huion.c index 60f44cd1b0ed..61b68ca27790 100644 --- a/drivers/hid/hid-huion.c +++ b/drivers/hid/hid-huion.c @@ -84,6 +84,15 @@ static const __u8 huion_tablet_rdesc_template[] = { 0xC0 /* End Collection */ }; +/* Parameter indices */ +enum huion_prm { + HUION_PRM_X_LM = 1, + HUION_PRM_Y_LM = 2, + HUION_PRM_PRESSURE_LM = 4, + HUION_PRM_RESOLUTION = 5, + HUION_PRM_NUM +}; + /* Driver data */ struct huion_drvdata { __u8 *rdesc; @@ -115,7 +124,12 @@ static int huion_tablet_enable(struct hid_device *hdev) int rc; struct usb_device *usb_dev = hid_to_usb_dev(hdev); struct huion_drvdata *drvdata = hid_get_drvdata(hdev); - __le16 buf[6]; + __le16 *buf = NULL; + size_t len; + s32 params[HUION_PH_ID_NUM]; + s32 resolution; + __u8 *p; + s32 v; /* * Read string descriptor containing tablet parameters. The specific @@ -123,65 +137,79 @@ static int huion_tablet_enable(struct hid_device *hdev) * driver traffic. * NOTE: This enables fully-functional tablet mode. */ + len = HUION_PRM_NUM * sizeof(*buf); + buf = kmalloc(len, GFP_KERNEL); + if (buf == NULL) { + hid_err(hdev, "failed to allocate parameter buffer\n"); + rc = -ENOMEM; + goto cleanup; + } rc = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, (USB_DT_STRING << 8) + 0x64, - 0x0409, buf, sizeof(buf), + 0x0409, buf, len, USB_CTRL_GET_TIMEOUT); - if (rc == -EPIPE) - hid_warn(hdev, "device parameters not found\n"); - else if (rc < 0) - hid_warn(hdev, "failed to get device parameters: %d\n", rc); - else if (rc != sizeof(buf)) - hid_warn(hdev, "invalid device parameters\n"); - else { - s32 params[HUION_PH_ID_NUM]; - s32 resolution; - __u8 *p; - s32 v; + if (rc == -EPIPE) { + hid_err(hdev, "device parameters not found\n"); + rc = -ENODEV; + goto cleanup; + } else if (rc < 0) { + hid_err(hdev, "failed to get device parameters: %d\n", rc); + rc = -ENODEV; + goto cleanup; + } else if (rc != len) { + hid_err(hdev, "invalid device parameters\n"); + rc = -ENODEV; + goto cleanup; + } - /* Extract device parameters */ - params[HUION_PH_ID_X_LM] = le16_to_cpu(buf[1]); - params[HUION_PH_ID_Y_LM] = le16_to_cpu(buf[2]); - params[HUION_PH_ID_PRESSURE_LM] = le16_to_cpu(buf[4]); - resolution = le16_to_cpu(buf[5]); - if (resolution == 0) { - params[HUION_PH_ID_X_PM] = 0; - params[HUION_PH_ID_Y_PM] = 0; - } else { - params[HUION_PH_ID_X_PM] = params[HUION_PH_ID_X_LM] * - 1000 / resolution; - params[HUION_PH_ID_Y_PM] = params[HUION_PH_ID_Y_LM] * - 1000 / resolution; - } + /* Extract device parameters */ + params[HUION_PH_ID_X_LM] = le16_to_cpu(buf[HUION_PRM_X_LM]); + params[HUION_PH_ID_Y_LM] = le16_to_cpu(buf[HUION_PRM_Y_LM]); + params[HUION_PH_ID_PRESSURE_LM] = + le16_to_cpu(buf[HUION_PRM_PRESSURE_LM]); + resolution = le16_to_cpu(buf[HUION_PRM_RESOLUTION]); + if (resolution == 0) { + params[HUION_PH_ID_X_PM] = 0; + params[HUION_PH_ID_Y_PM] = 0; + } else { + params[HUION_PH_ID_X_PM] = params[HUION_PH_ID_X_LM] * + 1000 / resolution; + params[HUION_PH_ID_Y_PM] = params[HUION_PH_ID_Y_LM] * + 1000 / resolution; + } - /* Allocate fixed report descriptor */ - drvdata->rdesc = devm_kmalloc(&hdev->dev, - sizeof(huion_tablet_rdesc_template), - GFP_KERNEL); - if (drvdata->rdesc == NULL) { - hid_err(hdev, "failed to allocate fixed rdesc\n"); - return -ENOMEM; - } - drvdata->rsize = sizeof(huion_tablet_rdesc_template); + /* Allocate fixed report descriptor */ + drvdata->rdesc = devm_kmalloc(&hdev->dev, + sizeof(huion_tablet_rdesc_template), + GFP_KERNEL); + if (drvdata->rdesc == NULL) { + hid_err(hdev, "failed to allocate fixed rdesc\n"); + rc = -ENOMEM; + goto cleanup; + } + drvdata->rsize = sizeof(huion_tablet_rdesc_template); - /* Format fixed report descriptor */ - memcpy(drvdata->rdesc, huion_tablet_rdesc_template, - drvdata->rsize); - for (p = drvdata->rdesc; - p <= drvdata->rdesc + drvdata->rsize - 4;) { - if (p[0] == 0xFE && p[1] == 0xED && p[2] == 0x1D && - p[3] < sizeof(params)) { - v = params[p[3]]; - put_unaligned(cpu_to_le32(v), (s32 *)p); - p += 4; - } else { - p++; - } + /* Format fixed report descriptor */ + memcpy(drvdata->rdesc, huion_tablet_rdesc_template, + drvdata->rsize); + for (p = drvdata->rdesc; + p <= drvdata->rdesc + drvdata->rsize - 4;) { + if (p[0] == 0xFE && p[1] == 0xED && p[2] == 0x1D && + p[3] < sizeof(params)) { + v = params[p[3]]; + put_unaligned(cpu_to_le32(v), (s32 *)p); + p += 4; + } else { + p++; } } - return 0; + rc = 0; + +cleanup: + kfree(buf); + return rc; } static int huion_probe(struct hid_device *hdev, const struct hid_device_id *id) diff --git a/drivers/hid/hid-kye.c b/drivers/hid/hid-kye.c index e77696367591..b92bf01a1ae8 100644 --- a/drivers/hid/hid-kye.c +++ b/drivers/hid/hid-kye.c @@ -300,7 +300,7 @@ static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc, * - change the button usage range to 4-7 for the extra * buttons */ - if (*rsize >= 74 && + if (*rsize >= 75 && rdesc[61] == 0x05 && rdesc[62] == 0x08 && rdesc[63] == 0x19 && rdesc[64] == 0x08 && rdesc[65] == 0x29 && rdesc[66] == 0x0f && diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index a976f48263f6..f91ff145db9a 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -345,14 +345,14 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, struct usb_device_descriptor *udesc; __u16 bcdDevice, rev_maj, rev_min; - if ((drv_data->quirks & LG_RDESC) && *rsize >= 90 && rdesc[83] == 0x26 && + if ((drv_data->quirks & LG_RDESC) && *rsize >= 91 && rdesc[83] == 0x26 && rdesc[84] == 0x8c && rdesc[85] == 0x02) { hid_info(hdev, "fixing up Logitech keyboard report descriptor\n"); rdesc[84] = rdesc[89] = 0x4d; rdesc[85] = rdesc[90] = 0x10; } - if ((drv_data->quirks & LG_RDESC_REL_ABS) && *rsize >= 50 && + if ((drv_data->quirks & LG_RDESC_REL_ABS) && *rsize >= 51 && rdesc[32] == 0x81 && rdesc[33] == 0x06 && rdesc[49] == 0x81 && rdesc[50] == 0x06) { hid_info(hdev, diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index cc2bd2022198..7835717bc020 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -451,13 +451,13 @@ static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *at drv_data = hid_get_drvdata(hid); if (!drv_data) { hid_err(hid, "Private driver data not found!\n"); - return 0; + return -EINVAL; } entry = drv_data->device_props; if (!entry) { hid_err(hid, "Device properties not found!\n"); - return 0; + return -EINVAL; } if (range == 0) diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 486dbde2ba2d..b7ba82960c79 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -238,13 +238,6 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev, return; } - if ((dj_report->device_index < DJ_DEVICE_INDEX_MIN) || - (dj_report->device_index > DJ_DEVICE_INDEX_MAX)) { - dev_err(&djrcv_hdev->dev, "%s: invalid device index:%d\n", - __func__, dj_report->device_index); - return; - } - if (djrcv_dev->paired_dj_devices[dj_report->device_index]) { /* The device is already known. No need to reallocate it. */ dbg_hid("%s: device is already known\n", __func__); @@ -557,7 +550,7 @@ static int logi_dj_ll_raw_request(struct hid_device *hid, if (!out_buf) return -ENOMEM; - if (count < DJREPORT_SHORT_LENGTH - 2) + if (count > DJREPORT_SHORT_LENGTH - 2) count = DJREPORT_SHORT_LENGTH - 2; out_buf[0] = REPORT_ID_DJ_SHORT; @@ -690,6 +683,12 @@ static int logi_dj_raw_event(struct hid_device *hdev, * device (via hid_input_report() ) and return 1 so hid-core does not do * anything else with it. */ + if ((dj_report->device_index < DJ_DEVICE_INDEX_MIN) || + (dj_report->device_index > DJ_DEVICE_INDEX_MAX)) { + dev_err(&hdev->dev, "%s: invalid device index:%d\n", + __func__, dj_report->device_index); + return false; + } spin_lock_irqsave(&djrcv_dev->lock, flags); if (dj_report->report_id == REPORT_ID_DJ_SHORT) { diff --git a/drivers/hid/hid-monterey.c b/drivers/hid/hid-monterey.c index 9e14c00eb1b6..25daf28b26bd 100644 --- a/drivers/hid/hid-monterey.c +++ b/drivers/hid/hid-monterey.c @@ -24,7 +24,7 @@ static __u8 *mr_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { - if (*rsize >= 30 && rdesc[29] == 0x05 && rdesc[30] == 0x09) { + if (*rsize >= 31 && rdesc[29] == 0x05 && rdesc[30] == 0x09) { hid_info(hdev, "fixing up button/consumer in HID report descriptor\n"); rdesc[30] = 0x0c; } diff --git a/drivers/hid/hid-petalynx.c b/drivers/hid/hid-petalynx.c index 736b2502df4f..6aca4f2554bf 100644 --- a/drivers/hid/hid-petalynx.c +++ b/drivers/hid/hid-petalynx.c @@ -25,7 +25,7 @@ static __u8 *pl_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { - if (*rsize >= 60 && rdesc[39] == 0x2a && rdesc[40] == 0xf5 && + if (*rsize >= 62 && rdesc[39] == 0x2a && rdesc[40] == 0xf5 && rdesc[41] == 0x00 && rdesc[59] == 0x26 && rdesc[60] == 0xf9 && rdesc[61] == 0x00) { hid_info(hdev, "fixing up Petalynx Maxter Remote report descriptor\n"); diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index 0dc25142f451..8389e8109218 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -909,10 +909,15 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) return ret; } - if (!test_bit(RMI_STARTED, &data->flags)) { - hid_hw_stop(hdev); - return -EIO; - } + if (!test_bit(RMI_STARTED, &data->flags)) + /* + * The device maybe in the bootloader if rmi_input_configured + * failed to find F11 in the PDT. Print an error, but don't + * return an error from rmi_probe so that hidraw will be + * accessible from userspace. That way a userspace tool + * can be used to reload working firmware on the touchpad. + */ + hid_err(hdev, "Device failed to be properly configured\n"); return 0; } diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index e244e449cbba..2ac25760a9a9 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -604,9 +604,9 @@ static int sensor_hub_probe(struct hid_device *hdev, ret = -EINVAL; goto err_stop_hw; } - sd->hid_sensor_hub_client_devs = kzalloc(dev_cnt * - sizeof(struct mfd_cell), - GFP_KERNEL); + sd->hid_sensor_hub_client_devs = devm_kzalloc(&hdev->dev, dev_cnt * + sizeof(struct mfd_cell), + GFP_KERNEL); if (sd->hid_sensor_hub_client_devs == NULL) { hid_err(hdev, "Failed to allocate memory for mfd cells\n"); ret = -ENOMEM; @@ -618,11 +618,12 @@ static int sensor_hub_probe(struct hid_device *hdev, if (collection->type == HID_COLLECTION_PHYSICAL) { - hsdev = kzalloc(sizeof(*hsdev), GFP_KERNEL); + hsdev = devm_kzalloc(&hdev->dev, sizeof(*hsdev), + GFP_KERNEL); if (!hsdev) { hid_err(hdev, "cannot allocate hid_sensor_hub_device\n"); ret = -ENOMEM; - goto err_no_mem; + goto err_stop_hw; } hsdev->hdev = hdev; hsdev->vendor_id = hdev->vendor; @@ -631,13 +632,13 @@ static int sensor_hub_probe(struct hid_device *hdev, if (last_hsdev) last_hsdev->end_collection_index = i; last_hsdev = hsdev; - name = kasprintf(GFP_KERNEL, "HID-SENSOR-%x", - collection->usage); + name = devm_kasprintf(&hdev->dev, GFP_KERNEL, + "HID-SENSOR-%x", + collection->usage); if (name == NULL) { hid_err(hdev, "Failed MFD device name\n"); ret = -ENOMEM; - kfree(hsdev); - goto err_no_mem; + goto err_stop_hw; } sd->hid_sensor_hub_client_devs[ sd->hid_sensor_client_cnt].id = @@ -661,16 +662,10 @@ static int sensor_hub_probe(struct hid_device *hdev, ret = mfd_add_devices(&hdev->dev, 0, sd->hid_sensor_hub_client_devs, sd->hid_sensor_client_cnt, NULL, 0, NULL); if (ret < 0) - goto err_no_mem; + goto err_stop_hw; return ret; -err_no_mem: - for (i = 0; i < sd->hid_sensor_client_cnt; ++i) { - kfree(sd->hid_sensor_hub_client_devs[i].name); - kfree(sd->hid_sensor_hub_client_devs[i].platform_data); - } - kfree(sd->hid_sensor_hub_client_devs); err_stop_hw: hid_hw_stop(hdev); @@ -681,7 +676,6 @@ static void sensor_hub_remove(struct hid_device *hdev) { struct sensor_hub_data *data = hid_get_drvdata(hdev); unsigned long flags; - int i; hid_dbg(hdev, " hardware removed\n"); hid_hw_close(hdev); @@ -691,11 +685,6 @@ static void sensor_hub_remove(struct hid_device *hdev) complete(&data->pending.ready); spin_unlock_irqrestore(&data->lock, flags); mfd_remove_devices(&hdev->dev); - for (i = 0; i < data->hid_sensor_client_cnt; ++i) { - kfree(data->hid_sensor_hub_client_devs[i].name); - kfree(data->hid_sensor_hub_client_devs[i].platform_data); - } - kfree(data->hid_sensor_hub_client_devs); hid_set_drvdata(hdev, NULL); mutex_destroy(&data->mutex); } diff --git a/drivers/hid/hid-sunplus.c b/drivers/hid/hid-sunplus.c index 87fc91e1c8de..91072fa54663 100644 --- a/drivers/hid/hid-sunplus.c +++ b/drivers/hid/hid-sunplus.c @@ -24,7 +24,7 @@ static __u8 *sp_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { - if (*rsize >= 107 && rdesc[104] == 0x26 && rdesc[105] == 0x80 && + if (*rsize >= 112 && rdesc[104] == 0x26 && rdesc[105] == 0x80 && rdesc[106] == 0x03) { hid_info(hdev, "fixing up Sunplus Wireless Desktop report descriptor\n"); rdesc[105] = rdesc[110] = 0x03; diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 3e3b680dc007..b51a402752c4 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -23,17 +23,14 @@ config I2C This I2C support can also be built as a module. If so, the module will be called i2c-core. -config I2C_ACPI - bool "I2C ACPI support" - select I2C - depends on ACPI +config ACPI_I2C_OPREGION + bool "ACPI I2C Operation region support" + depends on I2C=y && ACPI default y help - Say Y here if you want to enable ACPI I2C support. This includes support - for automatic enumeration of I2C slave devices and support for ACPI I2C - Operation Regions. Operation Regions allow firmware (BIOS) code to - access I2C slave devices, such as smart batteries through an I2C host - controller driver. + Say Y here if you want to enable ACPI I2C operation region support. + Operation Regions allow firmware (BIOS) code to access I2C slave devices, + such as smart batteries through an I2C host controller driver. if I2C diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index a1f590cbb435..e0228b228256 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -3,7 +3,7 @@ # i2ccore-y := i2c-core.o -i2ccore-$(CONFIG_I2C_ACPI) += i2c-acpi.o +i2ccore-$(CONFIG_ACPI) += i2c-acpi.o obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o obj-$(CONFIG_I2C) += i2ccore.o diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 2994690b26e9..10467a327749 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -164,6 +164,7 @@ /* Older devices have their ID defined in <linux/pci_ids.h> */ #define PCI_DEVICE_ID_INTEL_BAYTRAIL_SMBUS 0x0f12 +#define PCI_DEVICE_ID_INTEL_BRASWELL_SMBUS 0x2292 #define PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS 0x1c22 #define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS 0x1d22 /* Patsburg also has three 'Integrated Device Function' SMBus controllers */ @@ -828,6 +829,7 @@ static const struct pci_device_id i801_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WILDCATPOINT_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BAYTRAIL_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BRASWELL_SMBUS) }, { 0, } }; diff --git a/drivers/i2c/i2c-acpi.c b/drivers/i2c/i2c-acpi.c index e8b61967334b..0dbc18c15c43 100644 --- a/drivers/i2c/i2c-acpi.c +++ b/drivers/i2c/i2c-acpi.c @@ -126,6 +126,7 @@ void acpi_i2c_register_devices(struct i2c_adapter *adap) dev_warn(&adap->dev, "failed to enumerate I2C slaves\n"); } +#ifdef CONFIG_ACPI_I2C_OPREGION static int acpi_gsb_i2c_read_bytes(struct i2c_client *client, u8 cmd, u8 *data, u8 data_len) { @@ -360,3 +361,4 @@ void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter) acpi_bus_detach_private_data(handle); } +#endif diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 4d140bbbe100..9b7ee7e427df 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -89,6 +89,7 @@ struct idle_cpu { * Indicate which enable bits to clear here. */ unsigned long auto_demotion_disable_flags; + bool byt_auto_demotion_disable_flag; bool disable_promotion_to_c1e; }; @@ -442,6 +443,66 @@ static struct cpuidle_state hsw_cstates[] = { { .enter = NULL } }; +static struct cpuidle_state bdw_cstates[] = { + { + .name = "C1-BDW", + .desc = "MWAIT 0x00", + .flags = MWAIT2flg(0x00) | CPUIDLE_FLAG_TIME_VALID, + .exit_latency = 2, + .target_residency = 2, + .enter = &intel_idle }, + { + .name = "C1E-BDW", + .desc = "MWAIT 0x01", + .flags = MWAIT2flg(0x01) | CPUIDLE_FLAG_TIME_VALID, + .exit_latency = 10, + .target_residency = 20, + .enter = &intel_idle }, + { + .name = "C3-BDW", + .desc = "MWAIT 0x10", + .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 40, + .target_residency = 100, + .enter = &intel_idle }, + { + .name = "C6-BDW", + .desc = "MWAIT 0x20", + .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 133, + .target_residency = 400, + .enter = &intel_idle }, + { + .name = "C7s-BDW", + .desc = "MWAIT 0x32", + .flags = MWAIT2flg(0x32) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 166, + .target_residency = 500, + .enter = &intel_idle }, + { + .name = "C8-BDW", + .desc = "MWAIT 0x40", + .flags = MWAIT2flg(0x40) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 300, + .target_residency = 900, + .enter = &intel_idle }, + { + .name = "C9-BDW", + .desc = "MWAIT 0x50", + .flags = MWAIT2flg(0x50) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 600, + .target_residency = 1800, + .enter = &intel_idle }, + { + .name = "C10-BDW", + .desc = "MWAIT 0x60", + .flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 2600, + .target_residency = 7700, + .enter = &intel_idle }, + { + .enter = NULL } +}; static struct cpuidle_state atom_cstates[] = { { @@ -613,6 +674,7 @@ static const struct idle_cpu idle_cpu_snb = { static const struct idle_cpu idle_cpu_byt = { .state_table = byt_cstates, .disable_promotion_to_c1e = true, + .byt_auto_demotion_disable_flag = true, }; static const struct idle_cpu idle_cpu_ivb = { @@ -630,6 +692,11 @@ static const struct idle_cpu idle_cpu_hsw = { .disable_promotion_to_c1e = true, }; +static const struct idle_cpu idle_cpu_bdw = { + .state_table = bdw_cstates, + .disable_promotion_to_c1e = true, +}; + static const struct idle_cpu idle_cpu_avn = { .state_table = avn_cstates, .disable_promotion_to_c1e = true, @@ -658,7 +725,10 @@ static const struct x86_cpu_id intel_idle_ids[] = { ICPU(0x3f, idle_cpu_hsw), ICPU(0x45, idle_cpu_hsw), ICPU(0x46, idle_cpu_hsw), - ICPU(0x4D, idle_cpu_avn), + ICPU(0x4d, idle_cpu_avn), + ICPU(0x3d, idle_cpu_bdw), + ICPU(0x4f, idle_cpu_bdw), + ICPU(0x56, idle_cpu_bdw), {} }; MODULE_DEVICE_TABLE(x86cpu, intel_idle_ids); @@ -814,6 +884,11 @@ static int __init intel_idle_cpuidle_driver_init(void) if (icpu->auto_demotion_disable_flags) on_each_cpu(auto_demotion_disable, NULL, 1); + if (icpu->byt_auto_demotion_disable_flag) { + wrmsrl(MSR_CC6_DEMOTION_POLICY_CONFIG, 0); + wrmsrl(MSR_MC6_DEMOTION_POLICY_CONFIG, 0); + } + if (icpu->disable_promotion_to_c1e) /* each-cpu is redundant */ on_each_cpu(c1e_promotion_disable, NULL, 1); diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index b08c18871323..6703751d87d7 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -2953,6 +2953,7 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr, */ if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)) { end_reshape(conf); + close_sync(conf); return 0; } @@ -3081,6 +3082,7 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr, } r10_bio = mempool_alloc(conf->r10buf_pool, GFP_NOIO); + r10_bio->state = 0; raise_barrier(conf, rb2 != NULL); atomic_set(&r10_bio->remaining, 0); @@ -3269,6 +3271,7 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr, if (sync_blocks < max_sync) max_sync = sync_blocks; r10_bio = mempool_alloc(conf->r10buf_pool, GFP_NOIO); + r10_bio->state = 0; r10_bio->mddev = mddev; atomic_set(&r10_bio->remaining, 0); @@ -4384,6 +4387,7 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr, read_more: /* Now schedule reads for blocks from sector_nr to last */ r10_bio = mempool_alloc(conf->r10buf_pool, GFP_NOIO); + r10_bio->state = 0; raise_barrier(conf, sectors_done != 0); atomic_set(&r10_bio->remaining, 0); r10_bio->mddev = mddev; @@ -4398,6 +4402,7 @@ read_more: * on all the target devices. */ // FIXME + mempool_free(r10_bio, conf->r10buf_pool); set_bit(MD_RECOVERY_INTR, &mddev->recovery); return sectors_done; } @@ -4410,7 +4415,7 @@ read_more: read_bio->bi_private = r10_bio; read_bio->bi_end_io = end_sync_read; read_bio->bi_rw = READ; - read_bio->bi_flags &= ~(BIO_POOL_MASK - 1); + read_bio->bi_flags &= (~0UL << BIO_RESET_BITS); read_bio->bi_flags |= 1 << BIO_UPTODATE; read_bio->bi_vcnt = 0; read_bio->bi_iter.bi_size = 0; diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 6234b2e84587..183588b11fc1 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -2922,7 +2922,7 @@ static int fetch_block(struct stripe_head *sh, struct stripe_head_state *s, (!test_bit(R5_Insync, &dev->flags) || test_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) && !test_bit(R5_OVERWRITE, &fdev[0]->flags)) || (sh->raid_conf->level == 6 && s->failed && s->to_write && - s->to_write < sh->raid_conf->raid_disks - 2 && + s->to_write - s->non_overwrite < sh->raid_conf->raid_disks - 2 && (!test_bit(R5_Insync, &dev->flags) || test_bit(STRIPE_PREREAD_ACTIVE, &sh->state))))) { /* we would like to get this block, possibly by computing it, * otherwise read it if the backing disk is insync @@ -3817,6 +3817,8 @@ static void handle_stripe(struct stripe_head *sh) set_bit(R5_Wantwrite, &dev->flags); if (prexor) continue; + if (s.failed > 1) + continue; if (!test_bit(R5_Insync, &dev->flags) || ((i == sh->pd_idx || i == sh->qd_idx) && s.failed == 0)) diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index f46a24ffa3fe..79cb8313c7d8 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -453,7 +453,7 @@ static int __init __reserved_mem_reserve_reg(unsigned long node, base = dt_mem_next_cell(dt_root_addr_cells, &prop); size = dt_mem_next_cell(dt_root_size_cells, &prop); - if (base && size && + if (size && early_init_dt_reserve_memory_arch(base, size, nomap) == 0) pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %ld MiB\n", uname, &base, (unsigned long)size / SZ_1M); diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 3e06a699352d..1471e0a223a5 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -301,16 +301,17 @@ int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_ar /* Get the reg property (if any) */ addr = of_get_property(device, "reg", NULL); + /* Try the new-style interrupts-extended first */ + res = of_parse_phandle_with_args(device, "interrupts-extended", + "#interrupt-cells", index, out_irq); + if (!res) + return of_irq_parse_raw(addr, out_irq); + /* Get the interrupts property */ intspec = of_get_property(device, "interrupts", &intlen); - if (intspec == NULL) { - /* Try the new-style interrupts-extended */ - res = of_parse_phandle_with_args(device, "interrupts-extended", - "#interrupt-cells", index, out_irq); - if (res) - return -EINVAL; - return of_irq_parse_raw(addr, out_irq); - } + if (intspec == NULL) + return -EINVAL; + intlen /= sizeof(*intspec); pr_debug(" intspec=%d intlen=%d\n", be32_to_cpup(intspec), intlen); diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c index d41002667833..a737cb5974de 100644 --- a/drivers/of/selftest.c +++ b/drivers/of/selftest.c @@ -27,6 +27,7 @@ static struct selftest_results { #define NO_OF_NODES 2 static struct device_node *nodes[NO_OF_NODES]; static int last_node_index; +static bool selftest_live_tree; #define selftest(result, fmt, ...) { \ if (!(result)) { \ @@ -630,13 +631,6 @@ static int attach_node_and_children(struct device_node *np) { struct device_node *next, *root = np, *dup; - if (!np) { - pr_warn("%s: No tree to attach; not running tests\n", - __func__); - return -ENODATA; - } - - /* skip root node */ np = np->child; /* storing a copy in temporary node */ @@ -672,12 +666,12 @@ static int attach_node_and_children(struct device_node *np) static int __init selftest_data_add(void) { void *selftest_data; - struct device_node *selftest_data_node; + struct device_node *selftest_data_node, *np; extern uint8_t __dtb_testcases_begin[]; extern uint8_t __dtb_testcases_end[]; const int size = __dtb_testcases_end - __dtb_testcases_begin; - if (!size || !of_allnodes) { + if (!size) { pr_warn("%s: No testcase data to attach; not running tests\n", __func__); return -ENODATA; @@ -692,6 +686,22 @@ static int __init selftest_data_add(void) return -ENOMEM; } of_fdt_unflatten_tree(selftest_data, &selftest_data_node); + if (!selftest_data_node) { + pr_warn("%s: No tree to attach; not running tests\n", __func__); + return -ENODATA; + } + + if (!of_allnodes) { + /* enabling flag for removing nodes */ + selftest_live_tree = true; + of_allnodes = selftest_data_node; + + for_each_of_allnodes(np) + __of_attach_node_sysfs(np); + of_aliases = of_find_node_by_path("/aliases"); + of_chosen = of_find_node_by_path("/chosen"); + return 0; + } /* attach the sub-tree to live tree */ return attach_node_and_children(selftest_data_node); @@ -723,6 +733,18 @@ static void selftest_data_remove(void) struct device_node *np; struct property *prop; + if (selftest_live_tree) { + of_node_put(of_aliases); + of_node_put(of_chosen); + of_aliases = NULL; + of_chosen = NULL; + for_each_child_of_node(of_allnodes, np) + detach_node_and_children(np); + __of_detach_node_sysfs(of_allnodes); + of_allnodes = NULL; + return; + } + while (last_node_index >= 0) { if (nodes[last_node_index]) { np = of_find_node_by_path(nodes[last_node_index]->full_name); diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 2d8a4d05d78f..8922c376456a 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -1,9 +1,18 @@ menu "PCI host controller drivers" depends on PCI +config PCI_DRA7XX + bool "TI DRA7xx PCIe controller" + select PCIE_DW + depends on OF && HAS_IOMEM && TI_PIPE3 + help + Enables support for the PCIe controller in the DRA7xx SoC. There + are two instances of PCIe controller in DRA7xx. This controller can + act both as EP and RC. This reuses the Designware core. + config PCI_MVEBU bool "Marvell EBU PCIe controller" - depends on ARCH_MVEBU || ARCH_DOVE || ARCH_KIRKWOOD + depends on ARCH_MVEBU || ARCH_DOVE depends on OF config PCIE_DW diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index 0daec7941aba..d0e88f114ff9 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_PCIE_DW) += pcie-designware.o +obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o obj-$(CONFIG_PCI_IMX6) += pci-imx6.o obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o diff --git a/drivers/pci/host/pci-dra7xx.c b/drivers/pci/host/pci-dra7xx.c new file mode 100644 index 000000000000..52b34fee07fd --- /dev/null +++ b/drivers/pci/host/pci-dra7xx.c @@ -0,0 +1,458 @@ +/* + * pcie-dra7xx - PCIe controller driver for TI DRA7xx SoCs + * + * Copyright (C) 2013-2014 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Kishon Vijay Abraham I <kishon@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/resource.h> +#include <linux/types.h> + +#include "pcie-designware.h" + +/* PCIe controller wrapper DRA7XX configuration registers */ + +#define PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN 0x0024 +#define PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN 0x0028 +#define ERR_SYS BIT(0) +#define ERR_FATAL BIT(1) +#define ERR_NONFATAL BIT(2) +#define ERR_COR BIT(3) +#define ERR_AXI BIT(4) +#define ERR_ECRC BIT(5) +#define PME_TURN_OFF BIT(8) +#define PME_TO_ACK BIT(9) +#define PM_PME BIT(10) +#define LINK_REQ_RST BIT(11) +#define LINK_UP_EVT BIT(12) +#define CFG_BME_EVT BIT(13) +#define CFG_MSE_EVT BIT(14) +#define INTERRUPTS (ERR_SYS | ERR_FATAL | ERR_NONFATAL | ERR_COR | ERR_AXI | \ + ERR_ECRC | PME_TURN_OFF | PME_TO_ACK | PM_PME | \ + LINK_REQ_RST | LINK_UP_EVT | CFG_BME_EVT | CFG_MSE_EVT) + +#define PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI 0x0034 +#define PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI 0x0038 +#define INTA BIT(0) +#define INTB BIT(1) +#define INTC BIT(2) +#define INTD BIT(3) +#define MSI BIT(4) +#define LEG_EP_INTERRUPTS (INTA | INTB | INTC | INTD) + +#define PCIECTRL_DRA7XX_CONF_DEVICE_CMD 0x0104 +#define LTSSM_EN 0x1 + +#define PCIECTRL_DRA7XX_CONF_PHY_CS 0x010C +#define LINK_UP BIT(16) + +struct dra7xx_pcie { + void __iomem *base; + struct phy **phy; + int phy_count; + struct device *dev; + struct pcie_port pp; +}; + +#define to_dra7xx_pcie(x) container_of((x), struct dra7xx_pcie, pp) + +static inline u32 dra7xx_pcie_readl(struct dra7xx_pcie *pcie, u32 offset) +{ + return readl(pcie->base + offset); +} + +static inline void dra7xx_pcie_writel(struct dra7xx_pcie *pcie, u32 offset, + u32 value) +{ + writel(value, pcie->base + offset); +} + +static int dra7xx_pcie_link_up(struct pcie_port *pp) +{ + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp); + u32 reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_PHY_CS); + + return !!(reg & LINK_UP); +} + +static int dra7xx_pcie_establish_link(struct pcie_port *pp) +{ + u32 reg; + unsigned int retries = 1000; + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp); + + if (dw_pcie_link_up(pp)) { + dev_err(pp->dev, "link is already up\n"); + return 0; + } + + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD); + reg |= LTSSM_EN; + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg); + + while (retries--) { + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_PHY_CS); + if (reg & LINK_UP) + break; + usleep_range(10, 20); + } + + if (retries == 0) { + dev_err(pp->dev, "link is not up\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static void dra7xx_pcie_enable_interrupts(struct pcie_port *pp) +{ + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp); + + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN, + ~INTERRUPTS); + dra7xx_pcie_writel(dra7xx, + PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN, INTERRUPTS); + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI, + ~LEG_EP_INTERRUPTS & ~MSI); + + if (IS_ENABLED(CONFIG_PCI_MSI)) + dra7xx_pcie_writel(dra7xx, + PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI, MSI); + else + dra7xx_pcie_writel(dra7xx, + PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI, + LEG_EP_INTERRUPTS); +} + +static void dra7xx_pcie_host_init(struct pcie_port *pp) +{ + dw_pcie_setup_rc(pp); + dra7xx_pcie_establish_link(pp); + if (IS_ENABLED(CONFIG_PCI_MSI)) + dw_pcie_msi_init(pp); + dra7xx_pcie_enable_interrupts(pp); +} + +static struct pcie_host_ops dra7xx_pcie_host_ops = { + .link_up = dra7xx_pcie_link_up, + .host_init = dra7xx_pcie_host_init, +}; + +static int dra7xx_pcie_intx_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); + irq_set_chip_data(irq, domain->host_data); + set_irq_flags(irq, IRQF_VALID); + + return 0; +} + +static const struct irq_domain_ops intx_domain_ops = { + .map = dra7xx_pcie_intx_map, +}; + +static int dra7xx_pcie_init_irq_domain(struct pcie_port *pp) +{ + struct device *dev = pp->dev; + struct device_node *node = dev->of_node; + struct device_node *pcie_intc_node = of_get_next_child(node, NULL); + + if (!pcie_intc_node) { + dev_err(dev, "No PCIe Intc node found\n"); + return PTR_ERR(pcie_intc_node); + } + + pp->irq_domain = irq_domain_add_linear(pcie_intc_node, 4, + &intx_domain_ops, pp); + if (!pp->irq_domain) { + dev_err(dev, "Failed to get a INTx IRQ domain\n"); + return PTR_ERR(pp->irq_domain); + } + + return 0; +} + +static irqreturn_t dra7xx_pcie_msi_irq_handler(int irq, void *arg) +{ + struct pcie_port *pp = arg; + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp); + u32 reg; + + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI); + + switch (reg) { + case MSI: + dw_handle_msi_irq(pp); + break; + case INTA: + case INTB: + case INTC: + case INTD: + generic_handle_irq(irq_find_mapping(pp->irq_domain, ffs(reg))); + break; + } + + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI, reg); + + return IRQ_HANDLED; +} + + +static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg) +{ + struct dra7xx_pcie *dra7xx = arg; + u32 reg; + + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN); + + if (reg & ERR_SYS) + dev_dbg(dra7xx->dev, "System Error\n"); + + if (reg & ERR_FATAL) + dev_dbg(dra7xx->dev, "Fatal Error\n"); + + if (reg & ERR_NONFATAL) + dev_dbg(dra7xx->dev, "Non Fatal Error\n"); + + if (reg & ERR_COR) + dev_dbg(dra7xx->dev, "Correctable Error\n"); + + if (reg & ERR_AXI) + dev_dbg(dra7xx->dev, "AXI tag lookup fatal Error\n"); + + if (reg & ERR_ECRC) + dev_dbg(dra7xx->dev, "ECRC Error\n"); + + if (reg & PME_TURN_OFF) + dev_dbg(dra7xx->dev, + "Power Management Event Turn-Off message received\n"); + + if (reg & PME_TO_ACK) + dev_dbg(dra7xx->dev, + "Power Management Turn-Off Ack message received\n"); + + if (reg & PM_PME) + dev_dbg(dra7xx->dev, + "PM Power Management Event message received\n"); + + if (reg & LINK_REQ_RST) + dev_dbg(dra7xx->dev, "Link Request Reset\n"); + + if (reg & LINK_UP_EVT) + dev_dbg(dra7xx->dev, "Link-up state change\n"); + + if (reg & CFG_BME_EVT) + dev_dbg(dra7xx->dev, "CFG 'Bus Master Enable' change\n"); + + if (reg & CFG_MSE_EVT) + dev_dbg(dra7xx->dev, "CFG 'Memory Space Enable' change\n"); + + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN, reg); + + return IRQ_HANDLED; +} + +static int add_pcie_port(struct dra7xx_pcie *dra7xx, + struct platform_device *pdev) +{ + int ret; + struct pcie_port *pp; + struct resource *res; + struct device *dev = &pdev->dev; + + pp = &dra7xx->pp; + pp->dev = dev; + pp->ops = &dra7xx_pcie_host_ops; + + pp->irq = platform_get_irq(pdev, 1); + if (pp->irq < 0) { + dev_err(dev, "missing IRQ resource\n"); + return -EINVAL; + } + + ret = devm_request_irq(&pdev->dev, pp->irq, + dra7xx_pcie_msi_irq_handler, IRQF_SHARED, + "dra7-pcie-msi", pp); + if (ret) { + dev_err(&pdev->dev, "failed to request irq\n"); + return ret; + } + + if (!IS_ENABLED(CONFIG_PCI_MSI)) { + ret = dra7xx_pcie_init_irq_domain(pp); + if (ret < 0) + return ret; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbics"); + pp->dbi_base = devm_ioremap(dev, res->start, resource_size(res)); + if (!pp->dbi_base) + return -ENOMEM; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dra7xx->dev, "failed to initialize host\n"); + return ret; + } + + return 0; +} + +static int __init dra7xx_pcie_probe(struct platform_device *pdev) +{ + u32 reg; + int ret; + int irq; + int i; + int phy_count; + struct phy **phy; + void __iomem *base; + struct resource *res; + struct dra7xx_pcie *dra7xx; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + char name[10]; + + dra7xx = devm_kzalloc(dev, sizeof(*dra7xx), GFP_KERNEL); + if (!dra7xx) + return -ENOMEM; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "missing IRQ resource\n"); + return -EINVAL; + } + + ret = devm_request_irq(dev, irq, dra7xx_pcie_irq_handler, + IRQF_SHARED, "dra7xx-pcie-main", dra7xx); + if (ret) { + dev_err(dev, "failed to request irq\n"); + return ret; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ti_conf"); + base = devm_ioremap_nocache(dev, res->start, resource_size(res)); + if (!base) + return -ENOMEM; + + phy_count = of_property_count_strings(np, "phy-names"); + if (phy_count < 0) { + dev_err(dev, "unable to find the strings\n"); + return phy_count; + } + + phy = devm_kzalloc(dev, sizeof(*phy) * phy_count, GFP_KERNEL); + if (!phy) + return -ENOMEM; + + for (i = 0; i < phy_count; i++) { + snprintf(name, sizeof(name), "pcie-phy%d", i); + phy[i] = devm_phy_get(dev, name); + if (IS_ERR(phy[i])) + return PTR_ERR(phy[i]); + + ret = phy_init(phy[i]); + if (ret < 0) + goto err_phy; + + ret = phy_power_on(phy[i]); + if (ret < 0) { + phy_exit(phy[i]); + goto err_phy; + } + } + + dra7xx->base = base; + dra7xx->phy = phy; + dra7xx->dev = dev; + dra7xx->phy_count = phy_count; + + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (IS_ERR_VALUE(ret)) { + dev_err(dev, "pm_runtime_get_sync failed\n"); + goto err_phy; + } + + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD); + reg &= ~LTSSM_EN; + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg); + + platform_set_drvdata(pdev, dra7xx); + + ret = add_pcie_port(dra7xx, pdev); + if (ret < 0) + goto err_add_port; + + return 0; + +err_add_port: + pm_runtime_put(dev); + pm_runtime_disable(dev); + +err_phy: + while (--i >= 0) { + phy_power_off(phy[i]); + phy_exit(phy[i]); + } + + return ret; +} + +static int __exit dra7xx_pcie_remove(struct platform_device *pdev) +{ + struct dra7xx_pcie *dra7xx = platform_get_drvdata(pdev); + struct pcie_port *pp = &dra7xx->pp; + struct device *dev = &pdev->dev; + int count = dra7xx->phy_count; + + if (pp->irq_domain) + irq_domain_remove(pp->irq_domain); + pm_runtime_put(dev); + pm_runtime_disable(dev); + while (count--) { + phy_power_off(dra7xx->phy[count]); + phy_exit(dra7xx->phy[count]); + } + + return 0; +} + +static const struct of_device_id of_dra7xx_pcie_match[] = { + { .compatible = "ti,dra7-pcie", }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_dra7xx_pcie_match); + +static struct platform_driver dra7xx_pcie_driver = { + .remove = __exit_p(dra7xx_pcie_remove), + .driver = { + .name = "dra7-pcie", + .owner = THIS_MODULE, + .of_match_table = of_dra7xx_pcie_match, + }, +}; + +module_platform_driver_probe(dra7xx_pcie_driver, dra7xx_pcie_probe); + +MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>"); +MODULE_DESCRIPTION("TI PCIe controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index abd65784618d..0fb0fdb223d5 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -25,6 +25,7 @@ */ #include <linux/clk.h> +#include <linux/debugfs.h> #include <linux/delay.h> #include <linux/export.h> #include <linux/interrupt.h> @@ -276,6 +277,7 @@ struct tegra_pcie { unsigned int num_supplies; const struct tegra_pcie_soc_data *soc_data; + struct dentry *debugfs; }; struct tegra_pcie_port { @@ -1739,6 +1741,115 @@ static const struct of_device_id tegra_pcie_of_match[] = { }; MODULE_DEVICE_TABLE(of, tegra_pcie_of_match); +static void *tegra_pcie_ports_seq_start(struct seq_file *s, loff_t *pos) +{ + struct tegra_pcie *pcie = s->private; + + if (list_empty(&pcie->ports)) + return NULL; + + seq_printf(s, "Index Status\n"); + + return seq_list_start(&pcie->ports, *pos); +} + +static void *tegra_pcie_ports_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + struct tegra_pcie *pcie = s->private; + + return seq_list_next(v, &pcie->ports, pos); +} + +static void tegra_pcie_ports_seq_stop(struct seq_file *s, void *v) +{ +} + +static int tegra_pcie_ports_seq_show(struct seq_file *s, void *v) +{ + bool up = false, active = false; + struct tegra_pcie_port *port; + unsigned int value; + + port = list_entry(v, struct tegra_pcie_port, list); + + value = readl(port->base + RP_VEND_XP); + + if (value & RP_VEND_XP_DL_UP) + up = true; + + value = readl(port->base + RP_LINK_CONTROL_STATUS); + + if (value & RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE) + active = true; + + seq_printf(s, "%2u ", port->index); + + if (up) + seq_printf(s, "up"); + + if (active) { + if (up) + seq_printf(s, ", "); + + seq_printf(s, "active"); + } + + seq_printf(s, "\n"); + return 0; +} + +static const struct seq_operations tegra_pcie_ports_seq_ops = { + .start = tegra_pcie_ports_seq_start, + .next = tegra_pcie_ports_seq_next, + .stop = tegra_pcie_ports_seq_stop, + .show = tegra_pcie_ports_seq_show, +}; + +static int tegra_pcie_ports_open(struct inode *inode, struct file *file) +{ + struct tegra_pcie *pcie = inode->i_private; + struct seq_file *s; + int err; + + err = seq_open(file, &tegra_pcie_ports_seq_ops); + if (err) + return err; + + s = file->private_data; + s->private = pcie; + + return 0; +} + +static const struct file_operations tegra_pcie_ports_ops = { + .owner = THIS_MODULE, + .open = tegra_pcie_ports_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int tegra_pcie_debugfs_init(struct tegra_pcie *pcie) +{ + struct dentry *file; + + pcie->debugfs = debugfs_create_dir("pcie", NULL); + if (!pcie->debugfs) + return -ENOMEM; + + file = debugfs_create_file("ports", S_IFREG | S_IRUGO, pcie->debugfs, + pcie, &tegra_pcie_ports_ops); + if (!file) + goto remove; + + return 0; + +remove: + debugfs_remove_recursive(pcie->debugfs); + pcie->debugfs = NULL; + return -ENOMEM; +} + static int tegra_pcie_probe(struct platform_device *pdev) { const struct of_device_id *match; @@ -1793,6 +1904,13 @@ static int tegra_pcie_probe(struct platform_device *pdev) goto disable_msi; } + if (IS_ENABLED(CONFIG_DEBUG_FS)) { + err = tegra_pcie_debugfs_init(pcie); + if (err < 0) + dev_err(&pdev->dev, "failed to setup debugfs: %d\n", + err); + } + platform_set_drvdata(pdev, pcie); return 0; diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 1eaf4df3618a..52bd3a143563 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -20,6 +20,7 @@ #include <linux/of_pci.h> #include <linux/pci.h> #include <linux/pci_regs.h> +#include <linux/platform_device.h> #include <linux/types.h> #include "pcie-designware.h" @@ -217,27 +218,47 @@ static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0) return 0; } +static void dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq) +{ + unsigned int res, bit, val; + + res = (irq / 32) * 12; + bit = irq % 32; + dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); + val &= ~(1 << bit); + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); +} + static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base, unsigned int nvec, unsigned int pos) { - unsigned int i, res, bit, val; + unsigned int i; for (i = 0; i < nvec; i++) { irq_set_msi_desc_off(irq_base, i, NULL); clear_bit(pos + i, pp->msi_irq_in_use); /* Disable corresponding interrupt on MSI controller */ - res = ((pos + i) / 32) * 12; - bit = (pos + i) % 32; - dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); - val &= ~(1 << bit); - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); + if (pp->ops->msi_clear_irq) + pp->ops->msi_clear_irq(pp, pos + i); + else + dw_pcie_msi_clear_irq(pp, pos + i); } } +static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq) +{ + unsigned int res, bit, val; + + res = (irq / 32) * 12; + bit = irq % 32; + dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); + val |= 1 << bit; + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); +} + static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) { - int res, bit, irq, pos0, pos1, i; - u32 val; + int irq, pos0, pos1, i; struct pcie_port *pp = sys_to_pcie(desc->dev->bus->sysdata); if (!pp) { @@ -281,11 +302,10 @@ static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) } set_bit(pos0 + i, pp->msi_irq_in_use); /*Enable corresponding interrupt in MSI interrupt controller */ - res = ((pos0 + i) / 32) * 12; - bit = (pos0 + i) % 32; - dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); - val |= 1 << bit; - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); + if (pp->ops->msi_set_irq) + pp->ops->msi_set_irq(pp, pos0 + i); + else + dw_pcie_msi_set_irq(pp, pos0 + i); } *pos = pos0; @@ -353,7 +373,10 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, */ desc->msi_attrib.multiple = msgvec; - msg.address_lo = virt_to_phys((void *)pp->msi_data); + if (pp->ops->get_msi_data) + msg.address_lo = pp->ops->get_msi_data(pp); + else + msg.address_lo = virt_to_phys((void *)pp->msi_data); msg.address_hi = 0x0; msg.data = pos; write_msi_msg(irq, &msg); @@ -396,10 +419,35 @@ static const struct irq_domain_ops msi_domain_ops = { int __init dw_pcie_host_init(struct pcie_port *pp) { struct device_node *np = pp->dev->of_node; + struct platform_device *pdev = to_platform_device(pp->dev); struct of_pci_range range; struct of_pci_range_parser parser; - u32 val; - int i; + struct resource *cfg_res; + u32 val, na, ns; + const __be32 *addrp; + int i, index; + + /* Find the address cell size and the number of cells in order to get + * the untranslated address. + */ + of_property_read_u32(np, "#address-cells", &na); + ns = of_n_size_cells(np); + + cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); + if (cfg_res) { + pp->config.cfg0_size = resource_size(cfg_res)/2; + pp->config.cfg1_size = resource_size(cfg_res)/2; + pp->cfg0_base = cfg_res->start; + pp->cfg1_base = cfg_res->start + pp->config.cfg0_size; + + /* Find the untranslated configuration space address */ + index = of_property_match_string(np, "reg-names", "config"); + addrp = of_get_address(np, index, false, false); + pp->cfg0_mod_base = of_read_number(addrp, ns); + pp->cfg1_mod_base = pp->cfg0_mod_base + pp->config.cfg0_size; + } else { + dev_err(pp->dev, "missing *config* reg space\n"); + } if (of_pci_range_parser_init(&parser, np)) { dev_err(pp->dev, "missing ranges property\n"); @@ -422,17 +470,33 @@ int __init dw_pcie_host_init(struct pcie_port *pp) pp->config.io_size = resource_size(&pp->io); pp->config.io_bus_addr = range.pci_addr; pp->io_base = range.cpu_addr; + + /* Find the untranslated IO space address */ + pp->io_mod_base = of_read_number(parser.range - + parser.np + na, ns); } if (restype == IORESOURCE_MEM) { of_pci_range_to_resource(&range, np, &pp->mem); pp->mem.name = "MEM"; pp->config.mem_size = resource_size(&pp->mem); pp->config.mem_bus_addr = range.pci_addr; + + /* Find the untranslated MEM space address */ + pp->mem_mod_base = of_read_number(parser.range - + parser.np + na, ns); } if (restype == 0) { of_pci_range_to_resource(&range, np, &pp->cfg); pp->config.cfg0_size = resource_size(&pp->cfg)/2; pp->config.cfg1_size = resource_size(&pp->cfg)/2; + pp->cfg0_base = pp->cfg.start; + pp->cfg1_base = pp->cfg.start + pp->config.cfg0_size; + + /* Find the untranslated configuration space address */ + pp->cfg0_mod_base = of_read_number(parser.range - + parser.np + na, ns); + pp->cfg1_mod_base = pp->cfg0_mod_base + + pp->config.cfg0_size; } } @@ -445,8 +509,6 @@ int __init dw_pcie_host_init(struct pcie_port *pp) } } - pp->cfg0_base = pp->cfg.start; - pp->cfg1_base = pp->cfg.start + pp->config.cfg0_size; pp->mem_base = pp->mem.start; pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base, @@ -509,9 +571,9 @@ static void dw_pcie_prog_viewport_cfg0(struct pcie_port *pp, u32 busdev) /* Program viewport 0 : OUTBOUND : CFG0 */ dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0, PCIE_ATU_VIEWPORT); - dw_pcie_writel_rc(pp, pp->cfg0_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->cfg0_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->cfg0_base + pp->config.cfg0_size - 1, + dw_pcie_writel_rc(pp, pp->cfg0_mod_base, PCIE_ATU_LOWER_BASE); + dw_pcie_writel_rc(pp, (pp->cfg0_mod_base >> 32), PCIE_ATU_UPPER_BASE); + dw_pcie_writel_rc(pp, pp->cfg0_mod_base + pp->config.cfg0_size - 1, PCIE_ATU_LIMIT); dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET); dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET); @@ -525,9 +587,9 @@ static void dw_pcie_prog_viewport_cfg1(struct pcie_port *pp, u32 busdev) dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1, PCIE_ATU_VIEWPORT); dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG1, PCIE_ATU_CR1); - dw_pcie_writel_rc(pp, pp->cfg1_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->cfg1_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->cfg1_base + pp->config.cfg1_size - 1, + dw_pcie_writel_rc(pp, pp->cfg1_mod_base, PCIE_ATU_LOWER_BASE); + dw_pcie_writel_rc(pp, (pp->cfg1_mod_base >> 32), PCIE_ATU_UPPER_BASE); + dw_pcie_writel_rc(pp, pp->cfg1_mod_base + pp->config.cfg1_size - 1, PCIE_ATU_LIMIT); dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET); dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET); @@ -540,9 +602,9 @@ static void dw_pcie_prog_viewport_mem_outbound(struct pcie_port *pp) dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0, PCIE_ATU_VIEWPORT); dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_MEM, PCIE_ATU_CR1); - dw_pcie_writel_rc(pp, pp->mem_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->mem_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->mem_base + pp->config.mem_size - 1, + dw_pcie_writel_rc(pp, pp->mem_mod_base, PCIE_ATU_LOWER_BASE); + dw_pcie_writel_rc(pp, (pp->mem_mod_base >> 32), PCIE_ATU_UPPER_BASE); + dw_pcie_writel_rc(pp, pp->mem_mod_base + pp->config.mem_size - 1, PCIE_ATU_LIMIT); dw_pcie_writel_rc(pp, pp->config.mem_bus_addr, PCIE_ATU_LOWER_TARGET); dw_pcie_writel_rc(pp, upper_32_bits(pp->config.mem_bus_addr), @@ -556,9 +618,9 @@ static void dw_pcie_prog_viewport_io_outbound(struct pcie_port *pp) dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1, PCIE_ATU_VIEWPORT); dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_IO, PCIE_ATU_CR1); - dw_pcie_writel_rc(pp, pp->io_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->io_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->io_base + pp->config.io_size - 1, + dw_pcie_writel_rc(pp, pp->io_mod_base, PCIE_ATU_LOWER_BASE); + dw_pcie_writel_rc(pp, (pp->io_mod_base >> 32), PCIE_ATU_UPPER_BASE); + dw_pcie_writel_rc(pp, pp->io_mod_base + pp->config.io_size - 1, PCIE_ATU_LIMIT); dw_pcie_writel_rc(pp, pp->config.io_bus_addr, PCIE_ATU_LOWER_TARGET); dw_pcie_writel_rc(pp, upper_32_bits(pp->config.io_bus_addr), @@ -656,7 +718,11 @@ static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, } if (bus->number != pp->root_bus_nr) - ret = dw_pcie_rd_other_conf(pp, bus, devfn, + if (pp->ops->rd_other_conf) + ret = pp->ops->rd_other_conf(pp, bus, devfn, + where, size, val); + else + ret = dw_pcie_rd_other_conf(pp, bus, devfn, where, size, val); else ret = dw_pcie_rd_own_conf(pp, where, size, val); @@ -679,7 +745,11 @@ static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn, return PCIBIOS_DEVICE_NOT_FOUND; if (bus->number != pp->root_bus_nr) - ret = dw_pcie_wr_other_conf(pp, bus, devfn, + if (pp->ops->wr_other_conf) + ret = pp->ops->wr_other_conf(pp, bus, devfn, + where, size, val); + else + ret = dw_pcie_wr_other_conf(pp, bus, devfn, where, size, val); else ret = dw_pcie_wr_own_conf(pp, where, size, val); diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index 77f592faa7bf..daf81f922cda 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -36,11 +36,15 @@ struct pcie_port { u8 root_bus_nr; void __iomem *dbi_base; u64 cfg0_base; + u64 cfg0_mod_base; void __iomem *va_cfg0_base; u64 cfg1_base; + u64 cfg1_mod_base; void __iomem *va_cfg1_base; u64 io_base; + u64 io_mod_base; u64 mem_base; + u64 mem_mod_base; struct resource cfg; struct resource io; struct resource mem; @@ -61,8 +65,15 @@ struct pcie_host_ops { u32 val, void __iomem *dbi_base); int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val); int (*wr_own_conf)(struct pcie_port *pp, int where, int size, u32 val); + int (*rd_other_conf)(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, u32 *val); + int (*wr_other_conf)(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, u32 val); int (*link_up)(struct pcie_port *pp); void (*host_init)(struct pcie_port *pp); + void (*msi_set_irq)(struct pcie_port *pp, int irq); + void (*msi_clear_irq)(struct pcie_port *pp, int irq); + u32 (*get_msi_data)(struct pcie_port *pp); }; int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val); diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 172f26ce59ac..3bbcbf12c1fb 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -652,6 +652,25 @@ config TOSHIBA_BT_RFKILL If you have a modern Toshiba laptop with a Bluetooth and an RFKill switch (such as the Portege R500), say Y. +config TOSHIBA_HAPS + tristate "Toshiba HDD Active Protection Sensor" + depends on ACPI + ---help--- + This driver adds support for the built-in accelerometer + found on recent Toshiba laptops equiped with HID TOS620A + device. + + This driver receives ACPI notify events 0x80 when the sensor + detects a sudden move or a harsh vibration, as well as an + ACPI notify event 0x81 whenever the movement or vibration has + been stabilized. + + Also provides sysfs entries to get/set the desired protection + level and reseting the HDD protection interface. + + If you have a recent Toshiba laptop with a built-in accelerometer + device, say Y. + config ACPI_CMPC tristate "CMPC Laptop Extras" depends on X86 && ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index c4ca428fd3db..f82232b1fc4d 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o +obj-$(CONFIG_TOSHIBA_HAPS) += toshiba_haps.o obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index bbf78b2d6d93..96a0b75c52c9 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -96,7 +96,7 @@ enum acer_wmi_event_ids { WMID_ACCEL_EVENT = 0x5, }; -static const struct key_entry acer_wmi_keymap[] = { +static const struct key_entry acer_wmi_keymap[] __initconst = { {KE_KEY, 0x01, {KEY_WLAN} }, /* WiFi */ {KE_KEY, 0x03, {KEY_WLAN} }, /* WiFi */ {KE_KEY, 0x04, {KEY_WLAN} }, /* WiFi */ @@ -294,7 +294,7 @@ struct quirk_entry { static struct quirk_entry *quirks; -static void set_quirks(void) +static void __init set_quirks(void) { if (!interface) return; @@ -306,7 +306,7 @@ static void set_quirks(void) interface->capability |= ACER_CAP_BRIGHTNESS; } -static int dmi_matched(const struct dmi_system_id *dmi) +static int __init dmi_matched(const struct dmi_system_id *dmi) { quirks = dmi->driver_data; return 1; @@ -337,7 +337,7 @@ static struct quirk_entry quirk_lenovo_ideapad_s205 = { }; /* The Aspire One has a dummy ACPI-WMI interface - disable it */ -static struct dmi_system_id acer_blacklist[] = { +static const struct dmi_system_id acer_blacklist[] __initconst = { { .ident = "Acer Aspire One (SSD)", .matches = { @@ -355,7 +355,7 @@ static struct dmi_system_id acer_blacklist[] = { {} }; -static struct dmi_system_id acer_quirks[] = { +static const struct dmi_system_id acer_quirks[] __initconst = { { .callback = dmi_matched, .ident = "Acer Aspire 1360", @@ -530,14 +530,15 @@ static struct dmi_system_id acer_quirks[] = { {} }; -static int video_set_backlight_video_vendor(const struct dmi_system_id *d) +static int __init +video_set_backlight_video_vendor(const struct dmi_system_id *d) { interface->capability &= ~ACER_CAP_BRIGHTNESS; pr_info("Brightness must be controlled by generic video driver\n"); return 0; } -static const struct dmi_system_id video_vendor_dmi_table[] = { +static const struct dmi_system_id video_vendor_dmi_table[] __initconst = { { .callback = video_set_backlight_video_vendor, .ident = "Acer TravelMate 4750", @@ -582,7 +583,7 @@ static const struct dmi_system_id video_vendor_dmi_table[] = { }; /* Find which quirks are needed for a particular vendor/ model pair */ -static void find_quirks(void) +static void __init find_quirks(void) { if (!force_series) { dmi_check_system(acer_quirks); @@ -749,7 +750,7 @@ static acpi_status AMW0_set_u32(u32 value, u32 cap) return wmab_execute(&args, NULL); } -static acpi_status AMW0_find_mailled(void) +static acpi_status __init AMW0_find_mailled(void) { struct wmab_args args; struct wmab_ret ret; @@ -781,16 +782,16 @@ static acpi_status AMW0_find_mailled(void) return AE_OK; } -static int AMW0_set_cap_acpi_check_device_found; +static int AMW0_set_cap_acpi_check_device_found __initdata; -static acpi_status AMW0_set_cap_acpi_check_device_cb(acpi_handle handle, +static acpi_status __init AMW0_set_cap_acpi_check_device_cb(acpi_handle handle, u32 level, void *context, void **retval) { AMW0_set_cap_acpi_check_device_found = 1; return AE_OK; } -static const struct acpi_device_id norfkill_ids[] = { +static const struct acpi_device_id norfkill_ids[] __initconst = { { "VPC2004", 0}, { "IBM0068", 0}, { "LEN0068", 0}, @@ -798,7 +799,7 @@ static const struct acpi_device_id norfkill_ids[] = { { "", 0}, }; -static int AMW0_set_cap_acpi_check_device(void) +static int __init AMW0_set_cap_acpi_check_device(void) { const struct acpi_device_id *id; @@ -808,7 +809,7 @@ static int AMW0_set_cap_acpi_check_device(void) return AMW0_set_cap_acpi_check_device_found; } -static acpi_status AMW0_set_capabilities(void) +static acpi_status __init AMW0_set_capabilities(void) { struct wmab_args args; struct wmab_ret ret; @@ -1184,7 +1185,7 @@ static acpi_status wmid_v2_set_u32(u32 value, u32 cap) return wmid3_set_device_status(value, device); } -static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy) +static void __init type_aa_dmi_decode(const struct dmi_header *header, void *d) { struct hotkey_function_type_aa *type_aa; @@ -1209,7 +1210,7 @@ static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy) commun_fn_key_number = type_aa->commun_fn_key_number; } -static acpi_status WMID_set_capabilities(void) +static acpi_status __init WMID_set_capabilities(void) { struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; union acpi_object *obj; @@ -1658,7 +1659,7 @@ static ssize_t show_bool_threeg(struct device *dev, u32 result; \ acpi_status status; - pr_info("This threeg sysfs will be removed in 2012 - used by: %s\n", + pr_info("This threeg sysfs will be removed in 2014 - used by: %s\n", current->comm); status = get_u32(&result, ACER_CAP_THREEG); if (ACPI_SUCCESS(status)) @@ -1671,7 +1672,7 @@ static ssize_t set_bool_threeg(struct device *dev, { u32 tmp = simple_strtoul(buf, NULL, 10); acpi_status status = set_u32(tmp, ACER_CAP_THREEG); - pr_info("This threeg sysfs will be removed in 2012 - used by: %s\n", + pr_info("This threeg sysfs will be removed in 2014 - used by: %s\n", current->comm); if (ACPI_FAILURE(status)) return -EINVAL; @@ -1683,7 +1684,7 @@ static DEVICE_ATTR(threeg, S_IRUGO | S_IWUSR, show_bool_threeg, static ssize_t show_interface(struct device *dev, struct device_attribute *attr, char *buf) { - pr_info("This interface sysfs will be removed in 2012 - used by: %s\n", + pr_info("This interface sysfs will be removed in 2014 - used by: %s\n", current->comm); switch (interface->type) { case ACER_AMW0: @@ -1777,7 +1778,7 @@ static void acer_wmi_notify(u32 value, void *context) } } -static acpi_status +static acpi_status __init wmid3_set_lm_mode(struct lm_input_params *params, struct lm_return_value *return_value) { @@ -1811,7 +1812,7 @@ wmid3_set_lm_mode(struct lm_input_params *params, return status; } -static int acer_wmi_enable_ec_raw(void) +static int __init acer_wmi_enable_ec_raw(void) { struct lm_return_value return_value; acpi_status status; @@ -1834,7 +1835,7 @@ static int acer_wmi_enable_ec_raw(void) return status; } -static int acer_wmi_enable_lm(void) +static int __init acer_wmi_enable_lm(void) { struct lm_return_value return_value; acpi_status status; @@ -2043,6 +2044,7 @@ static int acer_platform_remove(struct platform_device *device) return 0; } +#ifdef CONFIG_PM_SLEEP static int acer_suspend(struct device *dev) { u32 value; @@ -2083,6 +2085,10 @@ static int acer_resume(struct device *dev) return 0; } +#else +#define acer_suspend NULL +#define acer_resume NULL +#endif static SIMPLE_DEV_PM_OPS(acer_pm, acer_suspend, acer_resume); @@ -2120,7 +2126,7 @@ static int remove_sysfs(struct platform_device *device) return 0; } -static int create_sysfs(void) +static int __init create_sysfs(void) { int retval = -ENOMEM; @@ -2149,7 +2155,7 @@ static void remove_debugfs(void) debugfs_remove(interface->debug.root); } -static int create_debugfs(void) +static int __init create_debugfs(void) { interface->debug.root = debugfs_create_dir("acer-wmi", NULL); if (!interface->debug.root) { diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c index 297b6640213f..c5af23b64438 100644 --- a/drivers/platform/x86/alienware-wmi.c +++ b/drivers/platform/x86/alienware-wmi.c @@ -59,25 +59,33 @@ enum WMAX_CONTROL_STATES { struct quirk_entry { u8 num_zones; + u8 hdmi_mux; }; static struct quirk_entry *quirks; static struct quirk_entry quirk_unknown = { .num_zones = 2, + .hdmi_mux = 0, }; static struct quirk_entry quirk_x51_family = { .num_zones = 3, + .hdmi_mux = 0. }; -static int dmi_matched(const struct dmi_system_id *dmi) +static struct quirk_entry quirk_asm100 = { + .num_zones = 2, + .hdmi_mux = 1, +}; + +static int __init dmi_matched(const struct dmi_system_id *dmi) { quirks = dmi->driver_data; return 1; } -static struct dmi_system_id alienware_quirks[] = { +static const struct dmi_system_id alienware_quirks[] __initconst = { { .callback = dmi_matched, .ident = "Alienware X51 R1", @@ -96,6 +104,15 @@ static struct dmi_system_id alienware_quirks[] = { }, .driver_data = &quirk_x51_family, }, + { + .callback = dmi_matched, + .ident = "Alienware ASM100", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"), + }, + .driver_data = &quirk_asm100, + }, {} }; @@ -537,7 +554,8 @@ static struct attribute_group hdmi_attribute_group = { static void remove_hdmi(struct platform_device *dev) { - sysfs_remove_group(&dev->dev.kobj, &hdmi_attribute_group); + if (quirks->hdmi_mux > 0) + sysfs_remove_group(&dev->dev.kobj, &hdmi_attribute_group); } static int create_hdmi(struct platform_device *dev) @@ -583,7 +601,7 @@ static int __init alienware_wmi_init(void) if (ret) goto fail_platform_device2; - if (interface == WMAX) { + if (quirks->hdmi_mux > 0) { ret = create_hdmi(platform_device); if (ret) goto fail_prep_hdmi; diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index ddf0eefd862c..3a4951f46065 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -70,17 +70,35 @@ static struct quirk_entry quirk_asus_x55u = { .no_display_toggle = true, }; -static struct quirk_entry quirk_asus_x401u = { +static struct quirk_entry quirk_asus_wapf4 = { .wapf = 4, }; +static struct quirk_entry quirk_asus_x200ca = { + .wapf = 2, +}; + static int dmi_matched(const struct dmi_system_id *dmi) { quirks = dmi->driver_data; return 1; } -static struct dmi_system_id asus_quirks[] = { +static const struct dmi_system_id asus_quirks[] = { + { + .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. U32U", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "U32U"), + }, + /* + * Note this machine has a Brazos APU, and most Brazos Asus + * machines need quirk_asus_x55u / wmi_backlight_power but + * here acpi-video seems to work fine for backlight control. + */ + .driver_data = &quirk_asus_wapf4, + }, { .callback = dmi_matched, .ident = "ASUSTeK COMPUTER INC. X401U", @@ -97,7 +115,7 @@ static struct dmi_system_id asus_quirks[] = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_PRODUCT_NAME, "X401A"), }, - .driver_data = &quirk_asus_x401u, + .driver_data = &quirk_asus_wapf4, }, { .callback = dmi_matched, @@ -106,7 +124,7 @@ static struct dmi_system_id asus_quirks[] = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_PRODUCT_NAME, "X401A1"), }, - .driver_data = &quirk_asus_x401u, + .driver_data = &quirk_asus_wapf4, }, { .callback = dmi_matched, @@ -124,7 +142,7 @@ static struct dmi_system_id asus_quirks[] = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_PRODUCT_NAME, "X501A"), }, - .driver_data = &quirk_asus_x401u, + .driver_data = &quirk_asus_wapf4, }, { .callback = dmi_matched, @@ -133,7 +151,7 @@ static struct dmi_system_id asus_quirks[] = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_PRODUCT_NAME, "X501A1"), }, - .driver_data = &quirk_asus_x401u, + .driver_data = &quirk_asus_wapf4, }, { .callback = dmi_matched, @@ -142,7 +160,25 @@ static struct dmi_system_id asus_quirks[] = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_PRODUCT_NAME, "X550CA"), }, - .driver_data = &quirk_asus_x401u, + .driver_data = &quirk_asus_wapf4, + }, + { + .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. X550CC", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X550CC"), + }, + .driver_data = &quirk_asus_wapf4, + }, + { + .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. X550CL", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X550CL"), + }, + .driver_data = &quirk_asus_wapf4, }, { .callback = dmi_matched, @@ -151,7 +187,7 @@ static struct dmi_system_id asus_quirks[] = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_PRODUCT_NAME, "X55A"), }, - .driver_data = &quirk_asus_x401u, + .driver_data = &quirk_asus_wapf4, }, { .callback = dmi_matched, @@ -160,7 +196,7 @@ static struct dmi_system_id asus_quirks[] = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_PRODUCT_NAME, "X55C"), }, - .driver_data = &quirk_asus_x401u, + .driver_data = &quirk_asus_wapf4, }, { .callback = dmi_matched, @@ -178,7 +214,7 @@ static struct dmi_system_id asus_quirks[] = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_PRODUCT_NAME, "X55VD"), }, - .driver_data = &quirk_asus_x401u, + .driver_data = &quirk_asus_wapf4, }, { .callback = dmi_matched, @@ -187,7 +223,16 @@ static struct dmi_system_id asus_quirks[] = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_PRODUCT_NAME, "X75A"), }, - .driver_data = &quirk_asus_x401u, + .driver_data = &quirk_asus_wapf4, + }, + { + .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. X75VBP", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X75VBP"), + }, + .driver_data = &quirk_asus_wapf4, }, { .callback = dmi_matched, @@ -196,7 +241,7 @@ static struct dmi_system_id asus_quirks[] = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_PRODUCT_NAME, "1015E"), }, - .driver_data = &quirk_asus_x401u, + .driver_data = &quirk_asus_wapf4, }, { .callback = dmi_matched, @@ -205,7 +250,16 @@ static struct dmi_system_id asus_quirks[] = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_PRODUCT_NAME, "1015U"), }, - .driver_data = &quirk_asus_x401u, + .driver_data = &quirk_asus_wapf4, + }, + { + .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. X200CA", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X200CA"), + }, + .driver_data = &quirk_asus_x200ca, }, {}, }; diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 3c6ccedc82b6..21fc932da3a1 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -46,6 +46,7 @@ #include <linux/platform_device.h> #include <linux/thermal.h> #include <linux/acpi.h> +#include <linux/dmi.h> #include <acpi/video.h> #include "asus-wmi.h" @@ -554,7 +555,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus) goto error; } - if (wlan_led_presence(asus) && (asus->driver->quirks->wapf == 4)) { + if (wlan_led_presence(asus) && (asus->driver->quirks->wapf > 0)) { INIT_WORK(&asus->wlan_led_work, wlan_led_update); asus->wlan_led.name = "asus::wlan"; @@ -884,7 +885,7 @@ static int asus_new_rfkill(struct asus_wmi *asus, return -EINVAL; if ((dev_id == ASUS_WMI_DEVID_WLAN) && - (asus->driver->quirks->wapf == 4)) + (asus->driver->quirks->wapf > 0)) rfkill_set_led_trigger_name(*rfkill, "asus-wlan"); rfkill_init_sw_state(*rfkill, !result); @@ -1270,10 +1271,7 @@ static int asus_wmi_backlight_init(struct asus_wmi *asus) int power; max = read_brightness_max(asus); - - if (max == -ENODEV) - max = 0; - else if (max < 0) + if (max < 0) return max; power = read_backlight_power(asus); @@ -1734,6 +1732,7 @@ static int asus_wmi_add(struct platform_device *pdev) struct platform_driver *pdrv = to_platform_driver(pdev->dev.driver); struct asus_wmi_driver *wdrv = to_asus_wmi_driver(pdrv); struct asus_wmi *asus; + const char *chassis_type; acpi_status status; int err; u32 result; @@ -1770,6 +1769,11 @@ static int asus_wmi_add(struct platform_device *pdev) if (err) goto fail_rfkill; + /* Some Asus desktop boards export an acpi-video backlight interface, + stop this from showing up */ + chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE); + if (chassis_type && !strcmp(chassis_type, "3")) + acpi_video_dmi_promote_vendor(); if (asus->driver->quirks->wmi_backlight_power) acpi_video_dmi_promote_vendor(); if (!acpi_video_backlight_support()) { diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index 7297df2ebf50..26bfd7bb5c13 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c @@ -1028,7 +1028,7 @@ static int compal_probe(struct platform_device *pdev) return err; hwmon_dev = hwmon_device_register_with_groups(&pdev->dev, - DRIVER_NAME, data, + "compal", data, compal_hwmon_groups); if (IS_ERR(hwmon_dev)) { err = PTR_ERR(hwmon_dev); diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index fed4111ac31a..233d2ee598a6 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -70,7 +70,7 @@ static struct quirk_entry quirk_dell_vostro_v130 = { .touchpad_led = 1, }; -static int dmi_matched(const struct dmi_system_id *dmi) +static int __init dmi_matched(const struct dmi_system_id *dmi) { quirks = dmi->driver_data; return 1; @@ -123,7 +123,7 @@ static const struct dmi_system_id dell_device_table[] __initconst = { }; MODULE_DEVICE_TABLE(dmi, dell_device_table); -static struct dmi_system_id dell_quirks[] = { +static const struct dmi_system_id dell_quirks[] __initconst = { { .callback = dmi_matched, .ident = "Dell Vostro V130", @@ -780,7 +780,7 @@ static struct led_classdev touchpad_led = { .flags = LED_CORE_SUSPENDRESUME, }; -static int touchpad_led_init(struct device *dev) +static int __init touchpad_led_init(struct device *dev) { return led_classdev_register(dev, &touchpad_led); } diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 9b0c57cd1d4a..bd533c22be57 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -1053,20 +1053,20 @@ static ssize_t show_sys_hwmon(int (*get)(void), char *buf) return sprintf(buf, "%d\n", get()); } -#define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \ +#define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _get, _set) \ static ssize_t show_##_name(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - return show_sys_hwmon(_set, buf); \ + return show_sys_hwmon(_get, buf); \ } \ static ssize_t store_##_name(struct device *dev, \ struct device_attribute *attr, \ const char *buf, size_t count) \ { \ - return store_sys_hwmon(_get, buf, count); \ + return store_sys_hwmon(_set, buf, count); \ } \ - static DEVICE_ATTR(_name, _mode, show_##_name, store_##_name); + static DEVICE_ATTR(_name, _mode, show_##_name, store_##_name) EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL); EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR, diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 6112933f6278..14fd2ecb06a1 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -145,7 +145,7 @@ static int dmi_matched(const struct dmi_system_id *dmi) return 1; } -static struct dmi_system_id asus_quirks[] = { +static const struct dmi_system_id asus_quirks[] = { { .callback = dmi_matched, .ident = "ASUSTeK Computer INC. 1000H", diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index e6f336270c21..87aa28c4280f 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -129,15 +129,14 @@ #define FUJLAPTOP_DBG_INFO 0x0004 #define FUJLAPTOP_DBG_TRACE 0x0008 -#define dbg_printk(a_dbg_level, format, arg...) \ +#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG +#define vdbg_printk(a_dbg_level, format, arg...) \ do { if (dbg_level & a_dbg_level) \ printk(FUJLAPTOP_DEBUG "%s: " format, __func__ , ## arg); \ } while (0) -#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG -#define vdbg_printk(a_dbg_level, format, arg...) \ - dbg_printk(a_dbg_level, format, ## arg) #else -#define vdbg_printk(a_dbg_level, format, arg...) +#define vdbg_printk(a_dbg_level, format, arg...) \ + do { } while (0) #endif /* Device controlling the backlight and associated keys */ @@ -564,7 +563,7 @@ static struct platform_driver fujitsupf_driver = { } }; -static void dmi_check_cb_common(const struct dmi_system_id *id) +static void __init dmi_check_cb_common(const struct dmi_system_id *id) { pr_info("Identified laptop model '%s'\n", id->ident); if (use_alt_lcd_levels == -1) { @@ -578,7 +577,7 @@ static void dmi_check_cb_common(const struct dmi_system_id *id) } } -static int dmi_check_cb_s6410(const struct dmi_system_id *id) +static int __init dmi_check_cb_s6410(const struct dmi_system_id *id) { dmi_check_cb_common(id); fujitsu->keycode1 = KEY_SCREENLOCK; /* "Lock" */ @@ -586,7 +585,7 @@ static int dmi_check_cb_s6410(const struct dmi_system_id *id) return 1; } -static int dmi_check_cb_s6420(const struct dmi_system_id *id) +static int __init dmi_check_cb_s6420(const struct dmi_system_id *id) { dmi_check_cb_common(id); fujitsu->keycode1 = KEY_SCREENLOCK; /* "Lock" */ @@ -594,7 +593,7 @@ static int dmi_check_cb_s6420(const struct dmi_system_id *id) return 1; } -static int dmi_check_cb_p8010(const struct dmi_system_id *id) +static int __init dmi_check_cb_p8010(const struct dmi_system_id *id) { dmi_check_cb_common(id); fujitsu->keycode1 = KEY_HELP; /* "Support" */ @@ -603,7 +602,7 @@ static int dmi_check_cb_p8010(const struct dmi_system_id *id) return 1; } -static struct dmi_system_id fujitsu_dmi_table[] = { +static const struct dmi_system_id fujitsu_dmi_table[] __initconst = { { .ident = "Fujitsu Siemens S6410", .matches = { diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c index c3784baceae3..53bdbb01bd3f 100644 --- a/drivers/platform/x86/fujitsu-tablet.c +++ b/drivers/platform/x86/fujitsu-tablet.c @@ -315,21 +315,21 @@ static irqreturn_t fujitsu_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static void fujitsu_dmi_common(const struct dmi_system_id *dmi) +static void __init fujitsu_dmi_common(const struct dmi_system_id *dmi) { pr_info("%s\n", dmi->ident); memcpy(fujitsu.config.keymap, dmi->driver_data, sizeof(fujitsu.config.keymap)); } -static int fujitsu_dmi_lifebook(const struct dmi_system_id *dmi) +static int __init fujitsu_dmi_lifebook(const struct dmi_system_id *dmi) { fujitsu_dmi_common(dmi); fujitsu.config.quirks |= INVERT_TABLET_MODE_BIT; return 1; } -static int fujitsu_dmi_stylistic(const struct dmi_system_id *dmi) +static int __init fujitsu_dmi_stylistic(const struct dmi_system_id *dmi) { fujitsu_dmi_common(dmi); fujitsu.config.quirks |= FORCE_TABLET_MODE_IF_UNDOCK; diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 484a8673b835..4c559640dcba 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -295,7 +295,7 @@ static int hp_wmi_tablet_state(void) return (state & 0x4) ? 1 : 0; } -static int hp_wmi_bios_2009_later(void) +static int __init hp_wmi_bios_2009_later(void) { int state = 0; int ret = hp_wmi_perform_query(HPWMI_FEATURE_QUERY, 0, &state, @@ -704,7 +704,7 @@ static void cleanup_sysfs(struct platform_device *device) device_remove_file(&device->dev, &dev_attr_postcode); } -static int hp_wmi_rfkill_setup(struct platform_device *device) +static int __init hp_wmi_rfkill_setup(struct platform_device *device) { int err; int wireless = 0; @@ -806,7 +806,7 @@ register_wifi_error: return err; } -static int hp_wmi_rfkill2_setup(struct platform_device *device) +static int __init hp_wmi_rfkill2_setup(struct platform_device *device) { int err, i; struct bios_rfkill2_state state; diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c index 3dc934438c28..13e14ec1d3d7 100644 --- a/drivers/platform/x86/hp_accel.c +++ b/drivers/platform/x86/hp_accel.c @@ -74,7 +74,7 @@ static inline void delayed_sysfs_set(struct led_classdev *led_cdev, /* HP-specific accelerometer driver ------------------------------------ */ /* For automatic insertion of the module */ -static struct acpi_device_id lis3lv02d_device_ids[] = { +static const struct acpi_device_id lis3lv02d_device_ids[] = { {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ {"HPQ6000", 0}, /* HP Mobile Data Protection System PNP */ {"HPQ6007", 0}, /* HP Mobile Data Protection System PNP */ @@ -192,7 +192,7 @@ DEFINE_CONV(xy_swap_yz_inverted, 2, -1, -3); }, \ .driver_data = &lis3lv02d_axis_##_axis \ } -static struct dmi_system_id lis3lv02d_dmi_ids[] = { +static const struct dmi_system_id lis3lv02d_dmi_ids[] = { /* product names are truncated to match all kinds of a same model */ AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted), AXIS_DMI_MATCH("NC84x0", "HP Compaq nc84", z_inverted), diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index b4c495a62eec..fc468a3d95ce 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -87,6 +87,8 @@ struct ideapad_private { struct backlight_device *blightdev; struct dentry *debug; unsigned long cfg; + bool has_hw_rfkill_switch; + bool has_touchpad_control; }; static bool no_bt_rfkill; @@ -439,7 +441,7 @@ static umode_t ideapad_is_visible(struct kobject *kobj, return supported ? attr->mode : 0; } -static struct attribute_group ideapad_attribute_group = { +static const struct attribute_group ideapad_attribute_group = { .is_visible = ideapad_is_visible, .attrs = ideapad_attributes }; @@ -454,7 +456,7 @@ struct ideapad_rfk_data { int type; }; -const struct ideapad_rfk_data ideapad_rfk_data[] = { +const const struct ideapad_rfk_data ideapad_rfk_data[] = { { "ideapad_wlan", CFG_WIFI_BIT, VPCCMD_W_WIFI, RFKILL_TYPE_WLAN }, { "ideapad_bluetooth", CFG_BT_BIT, VPCCMD_W_BT, RFKILL_TYPE_BLUETOOTH }, { "ideapad_3g", CFG_3G_BIT, VPCCMD_W_3G, RFKILL_TYPE_WWAN }, @@ -473,12 +475,14 @@ static struct rfkill_ops ideapad_rfk_ops = { static void ideapad_sync_rfk_state(struct ideapad_private *priv) { - unsigned long hw_blocked; + unsigned long hw_blocked = 0; int i; - if (read_ec_data(priv->adev->handle, VPCCMD_R_RF, &hw_blocked)) - return; - hw_blocked = !hw_blocked; + if (priv->has_hw_rfkill_switch) { + if (read_ec_data(priv->adev->handle, VPCCMD_R_RF, &hw_blocked)) + return; + hw_blocked = !hw_blocked; + } for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) if (priv->rfk[i]) @@ -763,6 +767,9 @@ static void ideapad_sync_touchpad_state(struct ideapad_private *priv) { unsigned long value; + if (!priv->has_touchpad_control) + return; + /* Without reading from EC touchpad LED doesn't switch state */ if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value)) { /* Some IdeaPads don't really turn off touchpad - they only @@ -821,14 +828,39 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data) } } -/* Blacklist for devices where the ideapad rfkill interface does not work */ -static struct dmi_system_id rfkill_blacklist[] = { - /* The Lenovo Yoga 2 11 always reports everything as blocked */ +/* + * Some ideapads don't have a hardware rfkill switch, reading VPCCMD_R_RF + * always results in 0 on these models, causing ideapad_laptop to wrongly + * report all radios as hardware-blocked. + */ +static struct dmi_system_id no_hw_rfkill_list[] = { { - .ident = "Lenovo Yoga 2 11", + .ident = "Lenovo Yoga 2 11 / 13 / Pro", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 2 11"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 2"), + }, + }, + {} +}; + +/* + * Some models don't offer touchpad ctrl through the ideapad interface, causing + * ideapad_sync_touchpad_state to send wrong touchpad enable/disable events. + */ +static struct dmi_system_id no_touchpad_ctrl_list[] = { + { + .ident = "Lenovo Yoga 1 series", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo IdeaPad Yoga"), + }, + }, + { + .ident = "Lenovo Yoga 2 11 / 13 / Pro", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 2"), }, }, {} @@ -856,6 +888,8 @@ static int ideapad_acpi_add(struct platform_device *pdev) priv->cfg = cfg; priv->adev = adev; priv->platform_device = pdev; + priv->has_hw_rfkill_switch = !dmi_check_system(no_hw_rfkill_list); + priv->has_touchpad_control = !dmi_check_system(no_touchpad_ctrl_list); ret = ideapad_sysfs_init(priv); if (ret) @@ -869,11 +903,17 @@ static int ideapad_acpi_add(struct platform_device *pdev) if (ret) goto input_failed; - if (!dmi_check_system(rfkill_blacklist)) { - for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) - if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg)) - ideapad_register_rfkill(priv, i); - } + /* + * On some models without a hw-switch (the yoga 2 13 at least) + * VPCCMD_W_RF must be explicitly set to 1 for the wifi to work. + */ + if (!priv->has_hw_rfkill_switch) + write_ec_cmd(priv->adev->handle, VPCCMD_W_RF, 1); + + for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) + if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg)) + ideapad_register_rfkill(priv, i); + ideapad_sync_rfk_state(priv); ideapad_sync_touchpad_state(priv); diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c index a0d1f576cf40..c0242ed13d9e 100644 --- a/drivers/platform/x86/intel_ips.c +++ b/drivers/platform/x86/intel_ips.c @@ -269,7 +269,7 @@ struct ips_mcp_limits { /* Max temps are -10 degrees C to avoid PROCHOT# */ -struct ips_mcp_limits ips_sv_limits = { +static struct ips_mcp_limits ips_sv_limits = { .mcp_power_limit = 35000, .core_power_limit = 29000, .mch_power_limit = 20000, @@ -277,7 +277,7 @@ struct ips_mcp_limits ips_sv_limits = { .mch_temp_limit = 90 }; -struct ips_mcp_limits ips_lv_limits = { +static struct ips_mcp_limits ips_lv_limits = { .mcp_power_limit = 25000, .core_power_limit = 21000, .mch_power_limit = 13000, @@ -285,7 +285,7 @@ struct ips_mcp_limits ips_lv_limits = { .mch_temp_limit = 90 }; -struct ips_mcp_limits ips_ulv_limits = { +static struct ips_mcp_limits ips_ulv_limits = { .mcp_power_limit = 18000, .core_power_limit = 14000, .mch_power_limit = 11000, diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 9c5a07417b2b..26ad9ff12ac5 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -2389,7 +2389,7 @@ static int sony_nc_lid_resume_setup(struct platform_device *pd, lid_ctl->attrs[LID_RESUME_S3].store = sony_nc_lid_resume_store; } for (i = 0; i < LID_RESUME_MAX && - lid_ctl->attrs[LID_RESUME_S3].attr.name; i++) { + lid_ctl->attrs[i].attr.name; i++) { result = device_create_file(&pd->dev, &lid_ctl->attrs[i]); if (result) goto liderror; diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index d82f196e3cfe..3bbc6eb60de5 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -3174,7 +3174,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) KEY_UNKNOWN, /* Extra keys in use since the X240 / T440 / T540 */ - KEY_CONFIG, KEY_SEARCH, KEY_SCALE, KEY_COMPUTER, + KEY_CONFIG, KEY_SEARCH, KEY_SCALE, KEY_FILE, }, }; @@ -6144,7 +6144,7 @@ static int brightness_set(unsigned int value) { int res; - if (value > bright_maxlvl || value < 0) + if (value > bright_maxlvl) return -EINVAL; vdbg_printk(TPACPI_DBG_BRGHT, @@ -6860,7 +6860,7 @@ static int volume_alsa_mute_put(struct snd_kcontrol *kcontrol, return volume_alsa_set_mute(!ucontrol->value.integer.value[0]); } -static struct snd_kcontrol_new volume_alsa_control_vol = { +static struct snd_kcontrol_new volume_alsa_control_vol __initdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Console Playback Volume", .index = 0, @@ -6869,7 +6869,7 @@ static struct snd_kcontrol_new volume_alsa_control_vol = { .get = volume_alsa_vol_get, }; -static struct snd_kcontrol_new volume_alsa_control_mute = { +static struct snd_kcontrol_new volume_alsa_control_mute __initdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Console Playback Switch", .index = 0, diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 76441dcbe5ff..b062d3d7b373 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -222,6 +222,12 @@ static const struct dmi_system_id toshiba_alt_keymap_dmi[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Satellite M840"), }, }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "Qosmio X75-A"), + }, + }, {} }; @@ -229,6 +235,7 @@ static const struct key_entry toshiba_acpi_alt_keymap[] = { { KE_KEY, 0x157, { KEY_MUTE } }, { KE_KEY, 0x102, { KEY_ZOOMOUT } }, { KE_KEY, 0x103, { KEY_ZOOMIN } }, + { KE_KEY, 0x12c, { KEY_KBDILLUMTOGGLE } }, { KE_KEY, 0x139, { KEY_ZOOMRESET } }, { KE_KEY, 0x13e, { KEY_SWITCHVIDEOMODE } }, { KE_KEY, 0x13c, { KEY_BRIGHTNESSDOWN } }, @@ -872,7 +879,9 @@ static int lcd_proc_open(struct inode *inode, struct file *file) static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value) { - u32 hci_result; + u32 in[HCI_WORDS] = { HCI_SET, HCI_LCD_BRIGHTNESS, 0, 0, 0, 0 }; + u32 out[HCI_WORDS]; + acpi_status status; if (dev->tr_backlight_supported) { bool enable = !value; @@ -883,9 +892,20 @@ static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value) value--; } - value = value << HCI_LCD_BRIGHTNESS_SHIFT; - hci_write1(dev, HCI_LCD_BRIGHTNESS, value, &hci_result); - return hci_result == HCI_SUCCESS ? 0 : -EIO; + in[2] = value << HCI_LCD_BRIGHTNESS_SHIFT; + status = hci_raw(dev, in, out); + if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) { + pr_err("ACPI call to set brightness failed"); + return -EIO; + } + /* Extra check for "incomplete" backlight method, where the AML code + * doesn't check for HCI_SET or HCI_GET and returns HCI_SUCCESS, + * the actual brightness, and in some cases the max brightness. + */ + if (out[2] > 0 || out[3] == 0xE000) + return -ENODEV; + + return out[0] == HCI_SUCCESS ? 0 : -EIO; } static int set_lcd_status(struct backlight_device *bd) diff --git a/drivers/platform/x86/toshiba_haps.c b/drivers/platform/x86/toshiba_haps.c new file mode 100644 index 000000000000..65300b6a84b9 --- /dev/null +++ b/drivers/platform/x86/toshiba_haps.c @@ -0,0 +1,265 @@ +/* + * Toshiba HDD Active Protection Sensor (HAPS) driver + * + * Copyright (C) 2014 Azael Avalos <coproscefalo@gmail.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. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/acpi.h> + +MODULE_AUTHOR("Azael Avalos <coproscefalo@gmail.com>"); +MODULE_DESCRIPTION("Toshiba HDD Active Protection Sensor"); +MODULE_LICENSE("GPL"); + +struct toshiba_haps_dev { + struct acpi_device *acpi_dev; + + int protection_level; +}; + +static struct toshiba_haps_dev *toshiba_haps; + +/* HAPS functions */ +static int toshiba_haps_reset_protection(acpi_handle handle) +{ + acpi_status status; + + status = acpi_evaluate_object(handle, "RSSS", NULL, NULL); + if (ACPI_FAILURE(status)) { + pr_err("Unable to reset the HDD protection\n"); + return -EIO; + } + + return 0; +} + +static int toshiba_haps_protection_level(acpi_handle handle, int level) +{ + acpi_status status; + + status = acpi_execute_simple_method(handle, "PTLV", level); + if (ACPI_FAILURE(status)) { + pr_err("Error while setting the protection level\n"); + return -EIO; + } + + pr_info("HDD protection level set to: %d\n", level); + + return 0; +} + +/* sysfs files */ +static ssize_t protection_level_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct toshiba_haps_dev *haps = dev_get_drvdata(dev); + + return sprintf(buf, "%i\n", haps->protection_level); +} + +static ssize_t protection_level_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct toshiba_haps_dev *haps = dev_get_drvdata(dev); + int level, ret; + + if (sscanf(buf, "%d", &level) != 1 || level < 0 || level > 3) + return -EINVAL; + + /* Set the sensor level. + * Acceptable levels are: + * 0 - Disabled | 1 - Low | 2 - Medium | 3 - High + */ + ret = toshiba_haps_protection_level(haps->acpi_dev->handle, level); + if (ret != 0) + return ret; + + haps->protection_level = level; + + return count; +} + +static ssize_t reset_protection_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct toshiba_haps_dev *haps = dev_get_drvdata(dev); + int reset, ret; + + if (sscanf(buf, "%d", &reset) != 1 || reset != 1) + return -EINVAL; + + /* Reset the protection interface */ + ret = toshiba_haps_reset_protection(haps->acpi_dev->handle); + if (ret != 0) + return ret; + + return count; +} + +static DEVICE_ATTR(protection_level, S_IRUGO | S_IWUSR, + protection_level_show, protection_level_store); +static DEVICE_ATTR(reset_protection, S_IWUSR, NULL, reset_protection_store); + +static struct attribute *haps_attributes[] = { + &dev_attr_protection_level.attr, + &dev_attr_reset_protection.attr, + NULL, +}; + +static struct attribute_group haps_attr_group = { + .attrs = haps_attributes, +}; + +/* + * ACPI stuff + */ +static void toshiba_haps_notify(struct acpi_device *device, u32 event) +{ + pr_info("Received event: 0x%x", event); + + acpi_bus_generate_netlink_event(device->pnp.device_class, + dev_name(&device->dev), + event, 0); +} + +static int toshiba_haps_remove(struct acpi_device *device) +{ + sysfs_remove_group(&device->dev.kobj, &haps_attr_group); + + if (toshiba_haps) + toshiba_haps = NULL; + + return 0; +} + +/* Helper function */ +static int toshiba_haps_available(acpi_handle handle) +{ + acpi_status status; + u64 hdd_present; + + /* + * A non existent device as well as having (only) + * Solid State Drives can cause the call to fail. + */ + status = acpi_evaluate_integer(handle, "_STA", NULL, + &hdd_present); + if (ACPI_FAILURE(status) || !hdd_present) { + pr_info("HDD protection not available or using SSD\n"); + return 0; + } + + return 1; +} + +static int toshiba_haps_add(struct acpi_device *acpi_dev) +{ + struct toshiba_haps_dev *haps; + int ret; + + if (toshiba_haps) + return -EBUSY; + + if (!toshiba_haps_available(acpi_dev->handle)) + return -ENODEV; + + pr_info("Toshiba HDD Active Protection Sensor device\n"); + + haps = kzalloc(sizeof(struct toshiba_haps_dev), GFP_KERNEL); + if (!haps) + return -ENOMEM; + + haps->acpi_dev = acpi_dev; + haps->protection_level = 2; + acpi_dev->driver_data = haps; + dev_set_drvdata(&acpi_dev->dev, haps); + + /* Set the protection level, currently at level 2 (Medium) */ + ret = toshiba_haps_protection_level(acpi_dev->handle, 2); + if (ret != 0) + return ret; + + ret = sysfs_create_group(&acpi_dev->dev.kobj, &haps_attr_group); + if (ret) + return ret; + + toshiba_haps = haps; + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int toshiba_haps_suspend(struct device *device) +{ + struct toshiba_haps_dev *haps; + int ret; + + haps = acpi_driver_data(to_acpi_device(device)); + + /* Deactivate the protection on suspend */ + ret = toshiba_haps_protection_level(haps->acpi_dev->handle, 0); + + return ret; +} + +static int toshiba_haps_resume(struct device *device) +{ + struct toshiba_haps_dev *haps; + int ret; + + haps = acpi_driver_data(to_acpi_device(device)); + + /* Set the stored protection level */ + ret = toshiba_haps_protection_level(haps->acpi_dev->handle, + haps->protection_level); + + /* Reset the protection on resume */ + ret = toshiba_haps_reset_protection(haps->acpi_dev->handle); + if (ret != 0) + return ret; + + return ret; +} +#endif + +static SIMPLE_DEV_PM_OPS(toshiba_haps_pm, + toshiba_haps_suspend, toshiba_haps_resume); + +static const struct acpi_device_id haps_device_ids[] = { + {"TOS620A", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, haps_device_ids); + +static struct acpi_driver toshiba_haps_driver = { + .name = "Toshiba HAPS", + .owner = THIS_MODULE, + .ids = haps_device_ids, + .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, + .ops = { + .add = toshiba_haps_add, + .remove = toshiba_haps_remove, + .notify = toshiba_haps_notify, + }, + .drv.pm = &toshiba_haps_pm, +}; + +module_acpi_driver(toshiba_haps_driver); diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 43d13295e63d..737e56d46f61 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -256,10 +256,6 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable) block = &wblock->gblock; handle = wblock->handle; - if (!block) - return AE_NOT_EXIST; - - snprintf(method, 5, "WE%02X", block->notify_id); status = acpi_execute_simple_method(handle, method, enable); diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index df3306019a7e..d81f3cc43ff1 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -377,6 +377,10 @@ scsi_alloc_host_cmd_pool(struct Scsi_Host *shost) pool->slab_flags |= SLAB_CACHE_DMA; pool->gfp_mask = __GFP_DMA; } + + if (hostt->cmd_size) + hostt->cmd_pool = pool; + return pool; } @@ -421,8 +425,10 @@ out: out_free_slab: kmem_cache_destroy(pool->cmd_slab); out_free_pool: - if (hostt->cmd_size) + if (hostt->cmd_size) { scsi_free_host_cmd_pool(pool); + hostt->cmd_pool = NULL; + } goto out; } @@ -444,8 +450,10 @@ static void scsi_put_host_cmd_pool(struct Scsi_Host *shost) if (!--pool->users) { kmem_cache_destroy(pool->cmd_slab); kmem_cache_destroy(pool->sense_slab); - if (hostt->cmd_size) + if (hostt->cmd_size) { scsi_free_host_cmd_pool(pool); + hostt->cmd_pool = NULL; + } } mutex_unlock(&host_cmd_pool_mutex); } diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 9c44392b748f..ce62e8798cc8 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1774,7 +1774,7 @@ static void scsi_request_fn(struct request_queue *q) blk_requeue_request(q, req); atomic_dec(&sdev->device_busy); out_delay: - if (atomic_read(&sdev->device_busy) && !scsi_device_blocked(sdev)) + if (!atomic_read(&sdev->device_busy) && !scsi_device_blocked(sdev)) blk_delay_queue(q, SCSI_QUEUE_DELAY); } @@ -192,7 +192,6 @@ static struct file *aio_private_file(struct kioctx *ctx, loff_t nr_pages) } file->f_flags = O_RDWR; - file->private_data = ctx; return file; } @@ -202,7 +201,7 @@ static struct dentry *aio_mount(struct file_system_type *fs_type, static const struct dentry_operations ops = { .d_dname = simple_dname, }; - return mount_pseudo(fs_type, "aio:", NULL, &ops, 0xa10a10a1); + return mount_pseudo(fs_type, "aio:", NULL, &ops, AIO_RING_MAGIC); } /* aio_setup @@ -556,8 +555,7 @@ static int ioctx_add_table(struct kioctx *ctx, struct mm_struct *mm) struct aio_ring *ring; spin_lock(&mm->ioctx_lock); - rcu_read_lock(); - table = rcu_dereference(mm->ioctx_table); + table = rcu_dereference_raw(mm->ioctx_table); while (1) { if (table) @@ -565,7 +563,6 @@ static int ioctx_add_table(struct kioctx *ctx, struct mm_struct *mm) if (!table->table[i]) { ctx->id = i; table->table[i] = ctx; - rcu_read_unlock(); spin_unlock(&mm->ioctx_lock); /* While kioctx setup is in progress, @@ -579,8 +576,6 @@ static int ioctx_add_table(struct kioctx *ctx, struct mm_struct *mm) } new_nr = (table ? table->nr : 1) * 4; - - rcu_read_unlock(); spin_unlock(&mm->ioctx_lock); table = kzalloc(sizeof(*table) + sizeof(struct kioctx *) * @@ -591,8 +586,7 @@ static int ioctx_add_table(struct kioctx *ctx, struct mm_struct *mm) table->nr = new_nr; spin_lock(&mm->ioctx_lock); - rcu_read_lock(); - old = rcu_dereference(mm->ioctx_table); + old = rcu_dereference_raw(mm->ioctx_table); if (!old) { rcu_assign_pointer(mm->ioctx_table, table); @@ -739,12 +733,9 @@ static int kill_ioctx(struct mm_struct *mm, struct kioctx *ctx, spin_lock(&mm->ioctx_lock); - rcu_read_lock(); - table = rcu_dereference(mm->ioctx_table); - + table = rcu_dereference_raw(mm->ioctx_table); WARN_ON(ctx != table->table[ctx->id]); table->table[ctx->id] = NULL; - rcu_read_unlock(); spin_unlock(&mm->ioctx_lock); /* percpu_ref_kill() will do the necessary call_rcu() */ @@ -793,40 +784,30 @@ EXPORT_SYMBOL(wait_on_sync_kiocb); */ void exit_aio(struct mm_struct *mm) { - struct kioctx_table *table; - struct kioctx *ctx; - unsigned i = 0; - - while (1) { - rcu_read_lock(); - table = rcu_dereference(mm->ioctx_table); - - do { - if (!table || i >= table->nr) { - rcu_read_unlock(); - rcu_assign_pointer(mm->ioctx_table, NULL); - if (table) - kfree(table); - return; - } + struct kioctx_table *table = rcu_dereference_raw(mm->ioctx_table); + int i; - ctx = table->table[i++]; - } while (!ctx); + if (!table) + return; - rcu_read_unlock(); + for (i = 0; i < table->nr; ++i) { + struct kioctx *ctx = table->table[i]; + if (!ctx) + continue; /* - * We don't need to bother with munmap() here - - * exit_mmap(mm) is coming and it'll unmap everything. - * Since aio_free_ring() uses non-zero ->mmap_size - * as indicator that it needs to unmap the area, - * just set it to 0; aio_free_ring() is the only - * place that uses ->mmap_size, so it's safe. + * We don't need to bother with munmap() here - exit_mmap(mm) + * is coming and it'll unmap everything. And we simply can't, + * this is not necessarily our ->mm. + * Since kill_ioctx() uses non-zero ->mmap_size as indicator + * that it needs to unmap the area, just set it to 0. */ ctx->mmap_size = 0; - kill_ioctx(mm, ctx, NULL); } + + RCU_INIT_POINTER(mm->ioctx_table, NULL); + kfree(table); } static void put_reqs_available(struct kioctx *ctx, unsigned nr) @@ -834,10 +815,8 @@ static void put_reqs_available(struct kioctx *ctx, unsigned nr) struct kioctx_cpu *kcpu; unsigned long flags; - preempt_disable(); - kcpu = this_cpu_ptr(ctx->cpu); - local_irq_save(flags); + kcpu = this_cpu_ptr(ctx->cpu); kcpu->reqs_available += nr; while (kcpu->reqs_available >= ctx->req_batch * 2) { @@ -846,7 +825,6 @@ static void put_reqs_available(struct kioctx *ctx, unsigned nr) } local_irq_restore(flags); - preempt_enable(); } static bool get_reqs_available(struct kioctx *ctx) @@ -855,10 +833,8 @@ static bool get_reqs_available(struct kioctx *ctx) bool ret = false; unsigned long flags; - preempt_disable(); - kcpu = this_cpu_ptr(ctx->cpu); - local_irq_save(flags); + kcpu = this_cpu_ptr(ctx->cpu); if (!kcpu->reqs_available) { int old, avail = atomic_read(&ctx->reqs_available); @@ -878,7 +854,6 @@ static bool get_reqs_available(struct kioctx *ctx) kcpu->reqs_available--; out: local_irq_restore(flags); - preempt_enable(); return ret; } @@ -1047,7 +1022,7 @@ void aio_complete(struct kiocb *iocb, long res, long res2) } EXPORT_SYMBOL(aio_complete); -/* aio_read_events +/* aio_read_events_ring * Pull an event off of the ioctx's event ring. Returns the number of * events fetched */ @@ -1270,12 +1245,12 @@ static ssize_t aio_setup_vectored_rw(struct kiocb *kiocb, if (compat) ret = compat_rw_copy_check_uvector(rw, (struct compat_iovec __user *)buf, - *nr_segs, 1, *iovec, iovec); + *nr_segs, UIO_FASTIOV, *iovec, iovec); else #endif ret = rw_copy_check_uvector(rw, (struct iovec __user *)buf, - *nr_segs, 1, *iovec, iovec); + *nr_segs, UIO_FASTIOV, *iovec, iovec); if (ret < 0) return ret; @@ -1299,9 +1274,8 @@ static ssize_t aio_setup_single_vector(struct kiocb *kiocb, } /* - * aio_setup_iocb: - * Performs the initial checks and aio retry method - * setup for the kiocb at the time of io submission. + * aio_run_iocb: + * Performs the initial checks and io submission. */ static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode, char __user *buf, bool compat) @@ -1313,7 +1287,7 @@ static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode, fmode_t mode; aio_rw_op *rw_op; rw_iter_op *iter_op; - struct iovec inline_vec, *iovec = &inline_vec; + struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs; struct iov_iter iter; switch (opcode) { @@ -1348,7 +1322,7 @@ rw_common: if (!ret) ret = rw_verify_area(rw, file, &req->ki_pos, req->ki_nbytes); if (ret < 0) { - if (iovec != &inline_vec) + if (iovec != inline_vecs) kfree(iovec); return ret; } @@ -1395,7 +1369,7 @@ rw_common: return -EINVAL; } - if (iovec != &inline_vec) + if (iovec != inline_vecs) kfree(iovec); if (ret != -EIOCBQUEUED) { diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index e25564bfcb46..54a201dac7f9 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -276,9 +276,8 @@ static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path, } if (ret > 0) goto next; - ret = ulist_add_merge(parents, eb->start, - (uintptr_t)eie, - (u64 *)&old, GFP_NOFS); + ret = ulist_add_merge_ptr(parents, eb->start, + eie, (void **)&old, GFP_NOFS); if (ret < 0) break; if (!ret && extent_item_pos) { @@ -1001,16 +1000,19 @@ again: ret = -EIO; goto out; } + btrfs_tree_read_lock(eb); + btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK); ret = find_extent_in_eb(eb, bytenr, *extent_item_pos, &eie); + btrfs_tree_read_unlock_blocking(eb); free_extent_buffer(eb); if (ret < 0) goto out; ref->inode_list = eie; } - ret = ulist_add_merge(refs, ref->parent, - (uintptr_t)ref->inode_list, - (u64 *)&eie, GFP_NOFS); + ret = ulist_add_merge_ptr(refs, ref->parent, + ref->inode_list, + (void **)&eie, GFP_NOFS); if (ret < 0) goto out; if (!ret && extent_item_pos) { diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index 4794923c410c..43527fd78825 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -84,12 +84,6 @@ struct btrfs_inode { */ struct list_head delalloc_inodes; - /* - * list for tracking inodes that must be sent to disk before a - * rename or truncate commit - */ - struct list_head ordered_operations; - /* node for the red-black tree that links inodes in subvolume root */ struct rb_node rb_node; diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index aeab453b8e24..44ee5d2e52a4 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -280,9 +280,9 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans, WARN_ON(btrfs_header_generation(buf) > trans->transid); if (new_root_objectid == BTRFS_TREE_RELOC_OBJECTID) - ret = btrfs_inc_ref(trans, root, cow, 1, 1); + ret = btrfs_inc_ref(trans, root, cow, 1); else - ret = btrfs_inc_ref(trans, root, cow, 0, 1); + ret = btrfs_inc_ref(trans, root, cow, 0); if (ret) return ret; @@ -1035,14 +1035,14 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans, if ((owner == root->root_key.objectid || root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) && !(flags & BTRFS_BLOCK_FLAG_FULL_BACKREF)) { - ret = btrfs_inc_ref(trans, root, buf, 1, 1); + ret = btrfs_inc_ref(trans, root, buf, 1); BUG_ON(ret); /* -ENOMEM */ if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) { - ret = btrfs_dec_ref(trans, root, buf, 0, 1); + ret = btrfs_dec_ref(trans, root, buf, 0); BUG_ON(ret); /* -ENOMEM */ - ret = btrfs_inc_ref(trans, root, cow, 1, 1); + ret = btrfs_inc_ref(trans, root, cow, 1); BUG_ON(ret); /* -ENOMEM */ } new_flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF; @@ -1050,9 +1050,9 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans, if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) - ret = btrfs_inc_ref(trans, root, cow, 1, 1); + ret = btrfs_inc_ref(trans, root, cow, 1); else - ret = btrfs_inc_ref(trans, root, cow, 0, 1); + ret = btrfs_inc_ref(trans, root, cow, 0); BUG_ON(ret); /* -ENOMEM */ } if (new_flags != 0) { @@ -1069,11 +1069,11 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans, if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF) { if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) - ret = btrfs_inc_ref(trans, root, cow, 1, 1); + ret = btrfs_inc_ref(trans, root, cow, 1); else - ret = btrfs_inc_ref(trans, root, cow, 0, 1); + ret = btrfs_inc_ref(trans, root, cow, 0); BUG_ON(ret); /* -ENOMEM */ - ret = btrfs_dec_ref(trans, root, buf, 1, 1); + ret = btrfs_dec_ref(trans, root, buf, 1); BUG_ON(ret); /* -ENOMEM */ } clean_tree_block(trans, root, buf); diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index be91397f4e92..8e29b614fe93 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3326,9 +3326,9 @@ int btrfs_reserve_extent(struct btrfs_root *root, u64 num_bytes, u64 min_alloc_size, u64 empty_size, u64 hint_byte, struct btrfs_key *ins, int is_data, int delalloc); int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct extent_buffer *buf, int full_backref, int no_quota); + struct extent_buffer *buf, int full_backref); int btrfs_dec_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct extent_buffer *buf, int full_backref, int no_quota); + struct extent_buffer *buf, int full_backref); int btrfs_set_disk_extent_flags(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 bytenr, u64 num_bytes, u64 flags, diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 08e65e9cf2aa..d0ed9e664f7d 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -60,8 +60,6 @@ static void end_workqueue_fn(struct btrfs_work *work); static void free_fs_root(struct btrfs_root *root); static int btrfs_check_super_valid(struct btrfs_fs_info *fs_info, int read_only); -static void btrfs_destroy_ordered_operations(struct btrfs_transaction *t, - struct btrfs_root *root); static void btrfs_destroy_ordered_extents(struct btrfs_root *root); static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans, struct btrfs_root *root); @@ -3829,34 +3827,6 @@ static void btrfs_error_commit_super(struct btrfs_root *root) btrfs_cleanup_transaction(root); } -static void btrfs_destroy_ordered_operations(struct btrfs_transaction *t, - struct btrfs_root *root) -{ - struct btrfs_inode *btrfs_inode; - struct list_head splice; - - INIT_LIST_HEAD(&splice); - - mutex_lock(&root->fs_info->ordered_operations_mutex); - spin_lock(&root->fs_info->ordered_root_lock); - - list_splice_init(&t->ordered_operations, &splice); - while (!list_empty(&splice)) { - btrfs_inode = list_entry(splice.next, struct btrfs_inode, - ordered_operations); - - list_del_init(&btrfs_inode->ordered_operations); - spin_unlock(&root->fs_info->ordered_root_lock); - - btrfs_invalidate_inodes(btrfs_inode->root); - - spin_lock(&root->fs_info->ordered_root_lock); - } - - spin_unlock(&root->fs_info->ordered_root_lock); - mutex_unlock(&root->fs_info->ordered_operations_mutex); -} - static void btrfs_destroy_ordered_extents(struct btrfs_root *root) { struct btrfs_ordered_extent *ordered; @@ -4093,8 +4063,6 @@ again: void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans, struct btrfs_root *root) { - btrfs_destroy_ordered_operations(cur_trans, root); - btrfs_destroy_delayed_refs(cur_trans, root); cur_trans->state = TRANS_STATE_COMMIT_START; diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 813537f362f9..102ed3143976 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -3057,7 +3057,7 @@ out: static int __btrfs_mod_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct extent_buffer *buf, - int full_backref, int inc, int no_quota) + int full_backref, int inc) { u64 bytenr; u64 num_bytes; @@ -3111,7 +3111,7 @@ static int __btrfs_mod_ref(struct btrfs_trans_handle *trans, key.offset -= btrfs_file_extent_offset(buf, fi); ret = process_func(trans, root, bytenr, num_bytes, parent, ref_root, key.objectid, - key.offset, no_quota); + key.offset, 1); if (ret) goto fail; } else { @@ -3119,7 +3119,7 @@ static int __btrfs_mod_ref(struct btrfs_trans_handle *trans, num_bytes = btrfs_level_size(root, level - 1); ret = process_func(trans, root, bytenr, num_bytes, parent, ref_root, level - 1, 0, - no_quota); + 1); if (ret) goto fail; } @@ -3130,15 +3130,15 @@ fail: } int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct extent_buffer *buf, int full_backref, int no_quota) + struct extent_buffer *buf, int full_backref) { - return __btrfs_mod_ref(trans, root, buf, full_backref, 1, no_quota); + return __btrfs_mod_ref(trans, root, buf, full_backref, 1); } int btrfs_dec_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct extent_buffer *buf, int full_backref, int no_quota) + struct extent_buffer *buf, int full_backref) { - return __btrfs_mod_ref(trans, root, buf, full_backref, 0, no_quota); + return __btrfs_mod_ref(trans, root, buf, full_backref, 0); } static int write_one_cache_group(struct btrfs_trans_handle *trans, @@ -7478,6 +7478,220 @@ reada: wc->reada_slot = slot; } +static int account_leaf_items(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct extent_buffer *eb) +{ + int nr = btrfs_header_nritems(eb); + int i, extent_type, ret; + struct btrfs_key key; + struct btrfs_file_extent_item *fi; + u64 bytenr, num_bytes; + + for (i = 0; i < nr; i++) { + btrfs_item_key_to_cpu(eb, &key, i); + + if (key.type != BTRFS_EXTENT_DATA_KEY) + continue; + + fi = btrfs_item_ptr(eb, i, struct btrfs_file_extent_item); + /* filter out non qgroup-accountable extents */ + extent_type = btrfs_file_extent_type(eb, fi); + + if (extent_type == BTRFS_FILE_EXTENT_INLINE) + continue; + + bytenr = btrfs_file_extent_disk_bytenr(eb, fi); + if (!bytenr) + continue; + + num_bytes = btrfs_file_extent_disk_num_bytes(eb, fi); + + ret = btrfs_qgroup_record_ref(trans, root->fs_info, + root->objectid, + bytenr, num_bytes, + BTRFS_QGROUP_OPER_SUB_SUBTREE, 0); + if (ret) + return ret; + } + return 0; +} + +/* + * Walk up the tree from the bottom, freeing leaves and any interior + * nodes which have had all slots visited. If a node (leaf or + * interior) is freed, the node above it will have it's slot + * incremented. The root node will never be freed. + * + * At the end of this function, we should have a path which has all + * slots incremented to the next position for a search. If we need to + * read a new node it will be NULL and the node above it will have the + * correct slot selected for a later read. + * + * If we increment the root nodes slot counter past the number of + * elements, 1 is returned to signal completion of the search. + */ +static int adjust_slots_upwards(struct btrfs_root *root, + struct btrfs_path *path, int root_level) +{ + int level = 0; + int nr, slot; + struct extent_buffer *eb; + + if (root_level == 0) + return 1; + + while (level <= root_level) { + eb = path->nodes[level]; + nr = btrfs_header_nritems(eb); + path->slots[level]++; + slot = path->slots[level]; + if (slot >= nr || level == 0) { + /* + * Don't free the root - we will detect this + * condition after our loop and return a + * positive value for caller to stop walking the tree. + */ + if (level != root_level) { + btrfs_tree_unlock_rw(eb, path->locks[level]); + path->locks[level] = 0; + + free_extent_buffer(eb); + path->nodes[level] = NULL; + path->slots[level] = 0; + } + } else { + /* + * We have a valid slot to walk back down + * from. Stop here so caller can process these + * new nodes. + */ + break; + } + + level++; + } + + eb = path->nodes[root_level]; + if (path->slots[root_level] >= btrfs_header_nritems(eb)) + return 1; + + return 0; +} + +/* + * root_eb is the subtree root and is locked before this function is called. + */ +static int account_shared_subtree(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct extent_buffer *root_eb, + u64 root_gen, + int root_level) +{ + int ret = 0; + int level; + struct extent_buffer *eb = root_eb; + struct btrfs_path *path = NULL; + + BUG_ON(root_level < 0 || root_level > BTRFS_MAX_LEVEL); + BUG_ON(root_eb == NULL); + + if (!root->fs_info->quota_enabled) + return 0; + + if (!extent_buffer_uptodate(root_eb)) { + ret = btrfs_read_buffer(root_eb, root_gen); + if (ret) + goto out; + } + + if (root_level == 0) { + ret = account_leaf_items(trans, root, root_eb); + goto out; + } + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + /* + * Walk down the tree. Missing extent blocks are filled in as + * we go. Metadata is accounted every time we read a new + * extent block. + * + * When we reach a leaf, we account for file extent items in it, + * walk back up the tree (adjusting slot pointers as we go) + * and restart the search process. + */ + extent_buffer_get(root_eb); /* For path */ + path->nodes[root_level] = root_eb; + path->slots[root_level] = 0; + path->locks[root_level] = 0; /* so release_path doesn't try to unlock */ +walk_down: + level = root_level; + while (level >= 0) { + if (path->nodes[level] == NULL) { + int child_bsize = root->nodesize; + int parent_slot; + u64 child_gen; + u64 child_bytenr; + + /* We need to get child blockptr/gen from + * parent before we can read it. */ + eb = path->nodes[level + 1]; + parent_slot = path->slots[level + 1]; + child_bytenr = btrfs_node_blockptr(eb, parent_slot); + child_gen = btrfs_node_ptr_generation(eb, parent_slot); + + eb = read_tree_block(root, child_bytenr, child_bsize, + child_gen); + if (!eb || !extent_buffer_uptodate(eb)) { + ret = -EIO; + goto out; + } + + path->nodes[level] = eb; + path->slots[level] = 0; + + btrfs_tree_read_lock(eb); + btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK); + path->locks[level] = BTRFS_READ_LOCK_BLOCKING; + + ret = btrfs_qgroup_record_ref(trans, root->fs_info, + root->objectid, + child_bytenr, + child_bsize, + BTRFS_QGROUP_OPER_SUB_SUBTREE, + 0); + if (ret) + goto out; + + } + + if (level == 0) { + ret = account_leaf_items(trans, root, path->nodes[level]); + if (ret) + goto out; + + /* Nonzero return here means we completed our search */ + ret = adjust_slots_upwards(root, path, root_level); + if (ret) + break; + + /* Restart search with new slots */ + goto walk_down; + } + + level--; + } + + ret = 0; +out: + btrfs_free_path(path); + + return ret; +} + /* * helper to process tree block while walking down the tree. * @@ -7532,9 +7746,9 @@ static noinline int walk_down_proc(struct btrfs_trans_handle *trans, /* wc->stage == UPDATE_BACKREF */ if (!(wc->flags[level] & flag)) { BUG_ON(!path->locks[level]); - ret = btrfs_inc_ref(trans, root, eb, 1, wc->for_reloc); + ret = btrfs_inc_ref(trans, root, eb, 1); BUG_ON(ret); /* -ENOMEM */ - ret = btrfs_dec_ref(trans, root, eb, 0, wc->for_reloc); + ret = btrfs_dec_ref(trans, root, eb, 0); BUG_ON(ret); /* -ENOMEM */ ret = btrfs_set_disk_extent_flags(trans, root, eb->start, eb->len, flag, @@ -7581,6 +7795,7 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans, int level = wc->level; int reada = 0; int ret = 0; + bool need_account = false; generation = btrfs_node_ptr_generation(path->nodes[level], path->slots[level]); @@ -7626,6 +7841,7 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans, if (wc->stage == DROP_REFERENCE) { if (wc->refs[level - 1] > 1) { + need_account = true; if (level == 1 && (wc->flags[0] & BTRFS_BLOCK_FLAG_FULL_BACKREF)) goto skip; @@ -7689,6 +7905,16 @@ skip: parent = 0; } + if (need_account) { + ret = account_shared_subtree(trans, root, next, + generation, level - 1); + if (ret) { + printk_ratelimited(KERN_ERR "BTRFS: %s Error " + "%d accounting shared subtree. Quota " + "is out of sync, rescan required.\n", + root->fs_info->sb->s_id, ret); + } + } ret = btrfs_free_extent(trans, root, bytenr, blocksize, parent, root->root_key.objectid, level - 1, 0, 0); BUG_ON(ret); /* -ENOMEM */ @@ -7769,12 +7995,17 @@ static noinline int walk_up_proc(struct btrfs_trans_handle *trans, if (wc->refs[level] == 1) { if (level == 0) { if (wc->flags[level] & BTRFS_BLOCK_FLAG_FULL_BACKREF) - ret = btrfs_dec_ref(trans, root, eb, 1, - wc->for_reloc); + ret = btrfs_dec_ref(trans, root, eb, 1); else - ret = btrfs_dec_ref(trans, root, eb, 0, - wc->for_reloc); + ret = btrfs_dec_ref(trans, root, eb, 0); BUG_ON(ret); /* -ENOMEM */ + ret = account_leaf_items(trans, root, eb); + if (ret) { + printk_ratelimited(KERN_ERR "BTRFS: %s Error " + "%d accounting leaf items. Quota " + "is out of sync, rescan required.\n", + root->fs_info->sb->s_id, ret); + } } /* make block locked assertion in clean_tree_block happy */ if (!path->locks[level] && @@ -7900,6 +8131,8 @@ int btrfs_drop_snapshot(struct btrfs_root *root, int level; bool root_dropped = false; + btrfs_debug(root->fs_info, "Drop subvolume %llu", root->objectid); + path = btrfs_alloc_path(); if (!path) { err = -ENOMEM; @@ -8025,6 +8258,24 @@ int btrfs_drop_snapshot(struct btrfs_root *root, goto out_end_trans; } + /* + * Qgroup update accounting is run from + * delayed ref handling. This usually works + * out because delayed refs are normally the + * only way qgroup updates are added. However, + * we may have added updates during our tree + * walk so run qgroups here to make sure we + * don't lose any updates. + */ + ret = btrfs_delayed_qgroup_accounting(trans, + root->fs_info); + if (ret) + printk_ratelimited(KERN_ERR "BTRFS: Failure %d " + "running qgroup updates " + "during snapshot delete. " + "Quota is out of sync, " + "rescan required.\n", ret); + btrfs_end_transaction_throttle(trans, tree_root); if (!for_reloc && btrfs_need_cleaner_sleep(root)) { pr_debug("BTRFS: drop snapshot early exit\n"); @@ -8078,6 +8329,14 @@ int btrfs_drop_snapshot(struct btrfs_root *root, } root_dropped = true; out_end_trans: + ret = btrfs_delayed_qgroup_accounting(trans, tree_root->fs_info); + if (ret) + printk_ratelimited(KERN_ERR "BTRFS: Failure %d " + "running qgroup updates " + "during snapshot delete. " + "Quota is out of sync, " + "rescan required.\n", ret); + btrfs_end_transaction_throttle(trans, tree_root); out_free: kfree(wc); diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index f46cfe45d686..54c84daec9b5 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -756,7 +756,7 @@ again: found_next = 1; if (ret != 0) goto insert; - slot = 0; + slot = path->slots[0]; } btrfs_item_key_to_cpu(path->nodes[0], &found_key, slot); if (found_key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 1f2b99cb55ea..d3afac292d67 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1838,33 +1838,9 @@ out: int btrfs_release_file(struct inode *inode, struct file *filp) { - /* - * ordered_data_close is set by settattr when we are about to truncate - * a file from a non-zero size to a zero size. This tries to - * flush down new bytes that may have been written if the - * application were using truncate to replace a file in place. - */ - if (test_and_clear_bit(BTRFS_INODE_ORDERED_DATA_CLOSE, - &BTRFS_I(inode)->runtime_flags)) { - struct btrfs_trans_handle *trans; - struct btrfs_root *root = BTRFS_I(inode)->root; - - /* - * We need to block on a committing transaction to keep us from - * throwing a ordered operation on to the list and causing - * something like sync to deadlock trying to flush out this - * inode. - */ - trans = btrfs_start_transaction(root, 0); - if (IS_ERR(trans)) - return PTR_ERR(trans); - btrfs_add_ordered_operation(trans, BTRFS_I(inode)->root, inode); - btrfs_end_transaction(trans, root); - if (inode->i_size > BTRFS_ORDERED_OPERATIONS_FLUSH_LIMIT) - filemap_flush(inode->i_mapping); - } if (filp->private_data) btrfs_ioctl_trans_end(filp); + filemap_flush(inode->i_mapping); return 0; } diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 3183742d6f0d..03708ef3deef 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -709,6 +709,18 @@ retry: unlock_extent(io_tree, async_extent->start, async_extent->start + async_extent->ram_size - 1); + + /* + * we need to redirty the pages if we decide to + * fallback to uncompressed IO, otherwise we + * will not submit these pages down to lower + * layers. + */ + extent_range_redirty_for_io(inode, + async_extent->start, + async_extent->start + + async_extent->ram_size - 1); + goto retry; } goto out_free; @@ -7939,27 +7951,6 @@ static int btrfs_truncate(struct inode *inode) BUG_ON(ret); /* - * setattr is responsible for setting the ordered_data_close flag, - * but that is only tested during the last file release. That - * could happen well after the next commit, leaving a great big - * window where new writes may get lost if someone chooses to write - * to this file after truncating to zero - * - * The inode doesn't have any dirty data here, and so if we commit - * this is a noop. If someone immediately starts writing to the inode - * it is very likely we'll catch some of their writes in this - * transaction, and the commit will find this file on the ordered - * data list with good things to send down. - * - * This is a best effort solution, there is still a window where - * using truncate to replace the contents of the file will - * end up with a zero length file after a crash. - */ - if (inode->i_size == 0 && test_bit(BTRFS_INODE_ORDERED_DATA_CLOSE, - &BTRFS_I(inode)->runtime_flags)) - btrfs_add_ordered_operation(trans, root, inode); - - /* * So if we truncate and then write and fsync we normally would just * write the extents that changed, which is a problem if we need to * first truncate that entire inode. So set this flag so we write out @@ -8106,7 +8097,6 @@ struct inode *btrfs_alloc_inode(struct super_block *sb) mutex_init(&ei->delalloc_mutex); btrfs_ordered_inode_tree_init(&ei->ordered_tree); INIT_LIST_HEAD(&ei->delalloc_inodes); - INIT_LIST_HEAD(&ei->ordered_operations); RB_CLEAR_NODE(&ei->rb_node); return inode; @@ -8146,17 +8136,6 @@ void btrfs_destroy_inode(struct inode *inode) if (!root) goto free; - /* - * Make sure we're properly removed from the ordered operation - * lists. - */ - smp_mb(); - if (!list_empty(&BTRFS_I(inode)->ordered_operations)) { - spin_lock(&root->fs_info->ordered_root_lock); - list_del_init(&BTRFS_I(inode)->ordered_operations); - spin_unlock(&root->fs_info->ordered_root_lock); - } - if (test_bit(BTRFS_INODE_HAS_ORPHAN_ITEM, &BTRFS_I(inode)->runtime_flags)) { btrfs_info(root->fs_info, "inode %llu still on the orphan list", @@ -8338,12 +8317,10 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, ret = 0; /* - * we're using rename to replace one file with another. - * and the replacement file is large. Start IO on it now so - * we don't add too much work to the end of the transaction + * we're using rename to replace one file with another. Start IO on it + * now so we don't add too much work to the end of the transaction */ - if (new_inode && S_ISREG(old_inode->i_mode) && new_inode->i_size && - old_inode->i_size > BTRFS_ORDERED_OPERATIONS_FLUSH_LIMIT) + if (new_inode && S_ISREG(old_inode->i_mode) && new_inode->i_size) filemap_flush(old_inode->i_mapping); /* close the racy window with snapshot create/destroy ioctl */ @@ -8391,12 +8368,6 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, */ btrfs_pin_log_trans(root); } - /* - * make sure the inode gets flushed if it is replacing - * something. - */ - if (new_inode && new_inode->i_size && S_ISREG(old_inode->i_mode)) - btrfs_add_ordered_operation(trans, root, old_inode); inode_inc_iversion(old_dir); inode_inc_iversion(new_dir); diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index 7187b14faa6c..963895c1f801 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -571,18 +571,6 @@ void btrfs_remove_ordered_extent(struct inode *inode, trace_btrfs_ordered_extent_remove(inode, entry); - /* - * we have no more ordered extents for this inode and - * no dirty pages. We can safely remove it from the - * list of ordered extents - */ - if (RB_EMPTY_ROOT(&tree->tree) && - !mapping_tagged(inode->i_mapping, PAGECACHE_TAG_DIRTY)) { - spin_lock(&root->fs_info->ordered_root_lock); - list_del_init(&BTRFS_I(inode)->ordered_operations); - spin_unlock(&root->fs_info->ordered_root_lock); - } - if (!root->nr_ordered_extents) { spin_lock(&root->fs_info->ordered_root_lock); BUG_ON(list_empty(&root->ordered_root)); @@ -687,81 +675,6 @@ void btrfs_wait_ordered_roots(struct btrfs_fs_info *fs_info, int nr) } /* - * this is used during transaction commit to write all the inodes - * added to the ordered operation list. These files must be fully on - * disk before the transaction commits. - * - * we have two modes here, one is to just start the IO via filemap_flush - * and the other is to wait for all the io. When we wait, we have an - * extra check to make sure the ordered operation list really is empty - * before we return - */ -int btrfs_run_ordered_operations(struct btrfs_trans_handle *trans, - struct btrfs_root *root, int wait) -{ - struct btrfs_inode *btrfs_inode; - struct inode *inode; - struct btrfs_transaction *cur_trans = trans->transaction; - struct list_head splice; - struct list_head works; - struct btrfs_delalloc_work *work, *next; - int ret = 0; - - INIT_LIST_HEAD(&splice); - INIT_LIST_HEAD(&works); - - mutex_lock(&root->fs_info->ordered_extent_flush_mutex); - spin_lock(&root->fs_info->ordered_root_lock); - list_splice_init(&cur_trans->ordered_operations, &splice); - while (!list_empty(&splice)) { - btrfs_inode = list_entry(splice.next, struct btrfs_inode, - ordered_operations); - inode = &btrfs_inode->vfs_inode; - - list_del_init(&btrfs_inode->ordered_operations); - - /* - * the inode may be getting freed (in sys_unlink path). - */ - inode = igrab(inode); - if (!inode) - continue; - - if (!wait) - list_add_tail(&BTRFS_I(inode)->ordered_operations, - &cur_trans->ordered_operations); - spin_unlock(&root->fs_info->ordered_root_lock); - - work = btrfs_alloc_delalloc_work(inode, wait, 1); - if (!work) { - spin_lock(&root->fs_info->ordered_root_lock); - if (list_empty(&BTRFS_I(inode)->ordered_operations)) - list_add_tail(&btrfs_inode->ordered_operations, - &splice); - list_splice_tail(&splice, - &cur_trans->ordered_operations); - spin_unlock(&root->fs_info->ordered_root_lock); - ret = -ENOMEM; - goto out; - } - list_add_tail(&work->list, &works); - btrfs_queue_work(root->fs_info->flush_workers, - &work->work); - - cond_resched(); - spin_lock(&root->fs_info->ordered_root_lock); - } - spin_unlock(&root->fs_info->ordered_root_lock); -out: - list_for_each_entry_safe(work, next, &works, list) { - list_del_init(&work->list); - btrfs_wait_and_free_delalloc_work(work); - } - mutex_unlock(&root->fs_info->ordered_extent_flush_mutex); - return ret; -} - -/* * Used to start IO or wait for a given ordered extent to finish. * * If wait is one, this effectively waits on page writeback for all the pages @@ -1120,42 +1033,6 @@ out: return index; } - -/* - * add a given inode to the list of inodes that must be fully on - * disk before a transaction commit finishes. - * - * This basically gives us the ext3 style data=ordered mode, and it is mostly - * used to make sure renamed files are fully on disk. - * - * It is a noop if the inode is already fully on disk. - * - * If trans is not null, we'll do a friendly check for a transaction that - * is already flushing things and force the IO down ourselves. - */ -void btrfs_add_ordered_operation(struct btrfs_trans_handle *trans, - struct btrfs_root *root, struct inode *inode) -{ - struct btrfs_transaction *cur_trans = trans->transaction; - u64 last_mod; - - last_mod = max(BTRFS_I(inode)->generation, BTRFS_I(inode)->last_trans); - - /* - * if this file hasn't been changed since the last transaction - * commit, we can safely return without doing anything - */ - if (last_mod <= root->fs_info->last_trans_committed) - return; - - spin_lock(&root->fs_info->ordered_root_lock); - if (list_empty(&BTRFS_I(inode)->ordered_operations)) { - list_add_tail(&BTRFS_I(inode)->ordered_operations, - &cur_trans->ordered_operations); - } - spin_unlock(&root->fs_info->ordered_root_lock); -} - int __init ordered_data_init(void) { btrfs_ordered_extent_cache = kmem_cache_create("btrfs_ordered_extent", diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h index 246897058efb..d81a274d621e 100644 --- a/fs/btrfs/ordered-data.h +++ b/fs/btrfs/ordered-data.h @@ -190,11 +190,6 @@ int btrfs_ordered_update_i_size(struct inode *inode, u64 offset, struct btrfs_ordered_extent *ordered); int btrfs_find_ordered_sum(struct inode *inode, u64 offset, u64 disk_bytenr, u32 *sum, int len); -int btrfs_run_ordered_operations(struct btrfs_trans_handle *trans, - struct btrfs_root *root, int wait); -void btrfs_add_ordered_operation(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct inode *inode); int btrfs_wait_ordered_extents(struct btrfs_root *root, int nr); void btrfs_wait_ordered_roots(struct btrfs_fs_info *fs_info, int nr); void btrfs_get_logged_extents(struct inode *inode, diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index 98cb6b2630f9..b497498484be 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -1201,6 +1201,50 @@ out: mutex_unlock(&fs_info->qgroup_ioctl_lock); return ret; } + +static int comp_oper_exist(struct btrfs_qgroup_operation *oper1, + struct btrfs_qgroup_operation *oper2) +{ + /* + * Ignore seq and type here, we're looking for any operation + * at all related to this extent on that root. + */ + if (oper1->bytenr < oper2->bytenr) + return -1; + if (oper1->bytenr > oper2->bytenr) + return 1; + if (oper1->ref_root < oper2->ref_root) + return -1; + if (oper1->ref_root > oper2->ref_root) + return 1; + return 0; +} + +static int qgroup_oper_exists(struct btrfs_fs_info *fs_info, + struct btrfs_qgroup_operation *oper) +{ + struct rb_node *n; + struct btrfs_qgroup_operation *cur; + int cmp; + + spin_lock(&fs_info->qgroup_op_lock); + n = fs_info->qgroup_op_tree.rb_node; + while (n) { + cur = rb_entry(n, struct btrfs_qgroup_operation, n); + cmp = comp_oper_exist(cur, oper); + if (cmp < 0) { + n = n->rb_right; + } else if (cmp) { + n = n->rb_left; + } else { + spin_unlock(&fs_info->qgroup_op_lock); + return -EEXIST; + } + } + spin_unlock(&fs_info->qgroup_op_lock); + return 0; +} + static int comp_oper(struct btrfs_qgroup_operation *oper1, struct btrfs_qgroup_operation *oper2) { @@ -1290,6 +1334,23 @@ int btrfs_qgroup_record_ref(struct btrfs_trans_handle *trans, oper->seq = atomic_inc_return(&fs_info->qgroup_op_seq); INIT_LIST_HEAD(&oper->elem.list); oper->elem.seq = 0; + + if (type == BTRFS_QGROUP_OPER_SUB_SUBTREE) { + /* + * If any operation for this bytenr/ref_root combo + * exists, then we know it's not exclusively owned and + * shouldn't be queued up. + * + * This also catches the case where we have a cloned + * extent that gets queued up multiple times during + * drop snapshot. + */ + if (qgroup_oper_exists(fs_info, oper)) { + kfree(oper); + return 0; + } + } + ret = insert_qgroup_oper(fs_info, oper); if (ret) { /* Shouldn't happen so have an assert for developers */ @@ -1884,6 +1945,111 @@ out: } /* + * Process a reference to a shared subtree. This type of operation is + * queued during snapshot removal when we encounter extents which are + * shared between more than one root. + */ +static int qgroup_subtree_accounting(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *fs_info, + struct btrfs_qgroup_operation *oper) +{ + struct ulist *roots = NULL; + struct ulist_node *unode; + struct ulist_iterator uiter; + struct btrfs_qgroup_list *glist; + struct ulist *parents; + int ret = 0; + int err; + struct btrfs_qgroup *qg; + u64 root_obj = 0; + struct seq_list elem = {}; + + parents = ulist_alloc(GFP_NOFS); + if (!parents) + return -ENOMEM; + + btrfs_get_tree_mod_seq(fs_info, &elem); + ret = btrfs_find_all_roots(trans, fs_info, oper->bytenr, + elem.seq, &roots); + btrfs_put_tree_mod_seq(fs_info, &elem); + if (ret < 0) + return ret; + + if (roots->nnodes != 1) + goto out; + + ULIST_ITER_INIT(&uiter); + unode = ulist_next(roots, &uiter); /* Only want 1 so no need to loop */ + /* + * If we find our ref root then that means all refs + * this extent has to the root have not yet been + * deleted. In that case, we do nothing and let the + * last ref for this bytenr drive our update. + * + * This can happen for example if an extent is + * referenced multiple times in a snapshot (clone, + * etc). If we are in the middle of snapshot removal, + * queued updates for such an extent will find the + * root if we have not yet finished removing the + * snapshot. + */ + if (unode->val == oper->ref_root) + goto out; + + root_obj = unode->val; + BUG_ON(!root_obj); + + spin_lock(&fs_info->qgroup_lock); + qg = find_qgroup_rb(fs_info, root_obj); + if (!qg) + goto out_unlock; + + qg->excl += oper->num_bytes; + qg->excl_cmpr += oper->num_bytes; + qgroup_dirty(fs_info, qg); + + /* + * Adjust counts for parent groups. First we find all + * parents, then in the 2nd loop we do the adjustment + * while adding parents of the parents to our ulist. + */ + list_for_each_entry(glist, &qg->groups, next_group) { + err = ulist_add(parents, glist->group->qgroupid, + ptr_to_u64(glist->group), GFP_ATOMIC); + if (err < 0) { + ret = err; + goto out_unlock; + } + } + + ULIST_ITER_INIT(&uiter); + while ((unode = ulist_next(parents, &uiter))) { + qg = u64_to_ptr(unode->aux); + qg->excl += oper->num_bytes; + qg->excl_cmpr += oper->num_bytes; + qgroup_dirty(fs_info, qg); + + /* Add any parents of the parents */ + list_for_each_entry(glist, &qg->groups, next_group) { + err = ulist_add(parents, glist->group->qgroupid, + ptr_to_u64(glist->group), GFP_ATOMIC); + if (err < 0) { + ret = err; + goto out_unlock; + } + } + } + +out_unlock: + spin_unlock(&fs_info->qgroup_lock); + +out: + ulist_free(roots); + ulist_free(parents); + return ret; +} + +/* * btrfs_qgroup_account_ref is called for every ref that is added to or deleted * from the fs. First, all roots referencing the extent are searched, and * then the space is accounted accordingly to the different roots. The @@ -1920,6 +2086,9 @@ static int btrfs_qgroup_account(struct btrfs_trans_handle *trans, case BTRFS_QGROUP_OPER_SUB_SHARED: ret = qgroup_shared_accounting(trans, fs_info, oper); break; + case BTRFS_QGROUP_OPER_SUB_SUBTREE: + ret = qgroup_subtree_accounting(trans, fs_info, oper); + break; default: ASSERT(0); } diff --git a/fs/btrfs/qgroup.h b/fs/btrfs/qgroup.h index 5952ff1fbd7a..18cc68ca3090 100644 --- a/fs/btrfs/qgroup.h +++ b/fs/btrfs/qgroup.h @@ -44,6 +44,7 @@ enum btrfs_qgroup_operation_type { BTRFS_QGROUP_OPER_ADD_SHARED, BTRFS_QGROUP_OPER_SUB_EXCL, BTRFS_QGROUP_OPER_SUB_SHARED, + BTRFS_QGROUP_OPER_SUB_SUBTREE, }; struct btrfs_qgroup_operation { diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 67b48b9a03e0..c4124de4435b 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -1665,6 +1665,21 @@ static int btrfs_calc_avail_data_space(struct btrfs_root *root, u64 *free_bytes) return 0; } +/* + * Calculate numbers for 'df', pessimistic in case of mixed raid profiles. + * + * If there's a redundant raid level at DATA block groups, use the respective + * multiplier to scale the sizes. + * + * Unused device space usage is based on simulating the chunk allocator + * algorithm that respects the device sizes, order of allocations and the + * 'alloc_start' value, this is a close approximation of the actual use but + * there are other factors that may change the result (like a new metadata + * chunk). + * + * FIXME: not accurate for mixed block groups, total and free/used are ok, + * available appears slightly larger. + */ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct btrfs_fs_info *fs_info = btrfs_sb(dentry->d_sb); @@ -1675,6 +1690,8 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf) u64 total_free_data = 0; int bits = dentry->d_sb->s_blocksize_bits; __be32 *fsid = (__be32 *)fs_info->fsid; + unsigned factor = 1; + struct btrfs_block_rsv *block_rsv = &fs_info->global_block_rsv; int ret; /* holding chunk_muext to avoid allocating new chunks */ @@ -1682,30 +1699,52 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf) rcu_read_lock(); list_for_each_entry_rcu(found, head, list) { if (found->flags & BTRFS_BLOCK_GROUP_DATA) { + int i; + total_free_data += found->disk_total - found->disk_used; total_free_data -= btrfs_account_ro_block_groups_free_space(found); + + for (i = 0; i < BTRFS_NR_RAID_TYPES; i++) { + if (!list_empty(&found->block_groups[i])) { + switch (i) { + case BTRFS_RAID_DUP: + case BTRFS_RAID_RAID1: + case BTRFS_RAID_RAID10: + factor = 2; + } + } + } } total_used += found->disk_used; } + rcu_read_unlock(); - buf->f_namelen = BTRFS_NAME_LEN; - buf->f_blocks = btrfs_super_total_bytes(disk_super) >> bits; - buf->f_bfree = buf->f_blocks - (total_used >> bits); - buf->f_bsize = dentry->d_sb->s_blocksize; - buf->f_type = BTRFS_SUPER_MAGIC; + buf->f_blocks = div_u64(btrfs_super_total_bytes(disk_super), factor); + buf->f_blocks >>= bits; + buf->f_bfree = buf->f_blocks - (div_u64(total_used, factor) >> bits); + + /* Account global block reserve as used, it's in logical size already */ + spin_lock(&block_rsv->lock); + buf->f_bfree -= block_rsv->size >> bits; + spin_unlock(&block_rsv->lock); + buf->f_bavail = total_free_data; ret = btrfs_calc_avail_data_space(fs_info->tree_root, &total_free_data); if (ret) { mutex_unlock(&fs_info->chunk_mutex); return ret; } - buf->f_bavail += total_free_data; + buf->f_bavail += div_u64(total_free_data, factor); buf->f_bavail = buf->f_bavail >> bits; mutex_unlock(&fs_info->chunk_mutex); + buf->f_type = BTRFS_SUPER_MAGIC; + buf->f_bsize = dentry->d_sb->s_blocksize; + buf->f_namelen = BTRFS_NAME_LEN; + /* We treat it as constant endianness (it doesn't matter _which_) because we want the fsid to come out the same whether mounted on a big-endian or little-endian host */ diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 5f379affdf23..d89c6d3542ca 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -218,7 +218,6 @@ loop: spin_lock_init(&cur_trans->delayed_refs.lock); INIT_LIST_HEAD(&cur_trans->pending_snapshots); - INIT_LIST_HEAD(&cur_trans->ordered_operations); INIT_LIST_HEAD(&cur_trans->pending_chunks); INIT_LIST_HEAD(&cur_trans->switch_commits); list_add_tail(&cur_trans->list, &fs_info->trans_list); @@ -1612,27 +1611,6 @@ static void cleanup_transaction(struct btrfs_trans_handle *trans, kmem_cache_free(btrfs_trans_handle_cachep, trans); } -static int btrfs_flush_all_pending_stuffs(struct btrfs_trans_handle *trans, - struct btrfs_root *root) -{ - int ret; - - ret = btrfs_run_delayed_items(trans, root); - if (ret) - return ret; - - /* - * rename don't use btrfs_join_transaction, so, once we - * set the transaction to blocked above, we aren't going - * to get any new ordered operations. We can safely run - * it here and no for sure that nothing new will be added - * to the list - */ - ret = btrfs_run_ordered_operations(trans, root, 1); - - return ret; -} - static inline int btrfs_start_delalloc_flush(struct btrfs_fs_info *fs_info) { if (btrfs_test_opt(fs_info->tree_root, FLUSHONCOMMIT)) @@ -1653,13 +1631,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, struct btrfs_transaction *prev_trans = NULL; int ret; - ret = btrfs_run_ordered_operations(trans, root, 0); - if (ret) { - btrfs_abort_transaction(trans, root, ret); - btrfs_end_transaction(trans, root); - return ret; - } - /* Stop the commit early if ->aborted is set */ if (unlikely(ACCESS_ONCE(cur_trans->aborted))) { ret = cur_trans->aborted; @@ -1740,7 +1711,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, if (ret) goto cleanup_transaction; - ret = btrfs_flush_all_pending_stuffs(trans, root); + ret = btrfs_run_delayed_items(trans, root); if (ret) goto cleanup_transaction; @@ -1748,7 +1719,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, extwriter_counter_read(cur_trans) == 0); /* some pending stuffs might be added after the previous flush. */ - ret = btrfs_flush_all_pending_stuffs(trans, root); + ret = btrfs_run_delayed_items(trans, root); if (ret) goto cleanup_transaction; diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h index 7dd558ed0716..579be51b27e5 100644 --- a/fs/btrfs/transaction.h +++ b/fs/btrfs/transaction.h @@ -55,7 +55,6 @@ struct btrfs_transaction { wait_queue_head_t writer_wait; wait_queue_head_t commit_wait; struct list_head pending_snapshots; - struct list_head ordered_operations; struct list_head pending_chunks; struct list_head switch_commits; struct btrfs_delayed_ref_root delayed_refs; diff --git a/fs/btrfs/ulist.h b/fs/btrfs/ulist.h index 7f78cbf5cf41..4c29db604bbe 100644 --- a/fs/btrfs/ulist.h +++ b/fs/btrfs/ulist.h @@ -57,6 +57,21 @@ void ulist_free(struct ulist *ulist); int ulist_add(struct ulist *ulist, u64 val, u64 aux, gfp_t gfp_mask); int ulist_add_merge(struct ulist *ulist, u64 val, u64 aux, u64 *old_aux, gfp_t gfp_mask); + +/* just like ulist_add_merge() but take a pointer for the aux data */ +static inline int ulist_add_merge_ptr(struct ulist *ulist, u64 val, void *aux, + void **old_aux, gfp_t gfp_mask) +{ +#if BITS_PER_LONG == 32 + u64 old64 = (uintptr_t)*old_aux; + int ret = ulist_add_merge(ulist, val, (uintptr_t)aux, &old64, gfp_mask); + *old_aux = (void *)((uintptr_t)old64); + return ret; +#else + return ulist_add_merge(ulist, val, (u64)aux, (u64 *)old_aux, gfp_mask); +#endif +} + struct ulist_node *ulist_next(struct ulist *ulist, struct ulist_iterator *uiter); diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index ac4f260155c8..889b98455750 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -207,6 +207,19 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf) return 0; } +static long cifs_fallocate(struct file *file, int mode, loff_t off, loff_t len) +{ + struct super_block *sb = file->f_path.dentry->d_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + struct TCP_Server_Info *server = tcon->ses->server; + + if (server->ops->fallocate) + return server->ops->fallocate(file, tcon, mode, off, len); + + return -EOPNOTSUPP; +} + static int cifs_permission(struct inode *inode, int mask) { struct cifs_sb_info *cifs_sb; @@ -812,8 +825,9 @@ static int cifs_setlease(struct file *file, long arg, struct file_lock **lease) if (!(S_ISREG(inode->i_mode))) return -EINVAL; - /* check if file is oplocked */ - if (((arg == F_RDLCK) && CIFS_CACHE_READ(CIFS_I(inode))) || + /* Check if file is oplocked if this is request for new lease */ + if (arg == F_UNLCK || + ((arg == F_RDLCK) && CIFS_CACHE_READ(CIFS_I(inode))) || ((arg == F_WRLCK) && CIFS_CACHE_WRITE(CIFS_I(inode)))) return generic_setlease(file, arg, lease); else if (tlink_tcon(cfile->tlink)->local_lease && @@ -908,6 +922,7 @@ const struct file_operations cifs_file_ops = { .unlocked_ioctl = cifs_ioctl, #endif /* CONFIG_CIFS_POSIX */ .setlease = cifs_setlease, + .fallocate = cifs_fallocate, }; const struct file_operations cifs_file_strict_ops = { @@ -927,6 +942,7 @@ const struct file_operations cifs_file_strict_ops = { .unlocked_ioctl = cifs_ioctl, #endif /* CONFIG_CIFS_POSIX */ .setlease = cifs_setlease, + .fallocate = cifs_fallocate, }; const struct file_operations cifs_file_direct_ops = { @@ -947,6 +963,7 @@ const struct file_operations cifs_file_direct_ops = { #endif /* CONFIG_CIFS_POSIX */ .llseek = cifs_llseek, .setlease = cifs_setlease, + .fallocate = cifs_fallocate, }; const struct file_operations cifs_file_nobrl_ops = { @@ -965,6 +982,7 @@ const struct file_operations cifs_file_nobrl_ops = { .unlocked_ioctl = cifs_ioctl, #endif /* CONFIG_CIFS_POSIX */ .setlease = cifs_setlease, + .fallocate = cifs_fallocate, }; const struct file_operations cifs_file_strict_nobrl_ops = { @@ -983,6 +1001,7 @@ const struct file_operations cifs_file_strict_nobrl_ops = { .unlocked_ioctl = cifs_ioctl, #endif /* CONFIG_CIFS_POSIX */ .setlease = cifs_setlease, + .fallocate = cifs_fallocate, }; const struct file_operations cifs_file_direct_nobrl_ops = { @@ -1002,6 +1021,7 @@ const struct file_operations cifs_file_direct_nobrl_ops = { #endif /* CONFIG_CIFS_POSIX */ .llseek = cifs_llseek, .setlease = cifs_setlease, + .fallocate = cifs_fallocate, }; const struct file_operations cifs_dir_ops = { diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 0012e1e291d4..dfc731b02aa9 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -409,6 +409,10 @@ struct smb_version_operations { /* get mtu credits */ int (*wait_mtu_credits)(struct TCP_Server_Info *, unsigned int, unsigned int *, unsigned int *); + /* check if we need to issue closedir */ + bool (*dir_needs_close)(struct cifsFileInfo *); + long (*fallocate)(struct file *, struct cifs_tcon *, int, loff_t, + loff_t); }; struct smb_version_values { @@ -883,6 +887,7 @@ struct cifs_tcon { for this mount even if server would support */ bool local_lease:1; /* check leases (only) on local system not remote */ bool broken_posix_open; /* e.g. Samba server versions < 3.3.2, 3.2.9 */ + bool broken_sparse_sup; /* if server or share does not support sparse */ bool need_reconnect:1; /* connection reset, tid now invalid */ #ifdef CONFIG_CIFS_SMB2 bool print:1; /* set if connection to printer share */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 33df36ef9d52..5f9822ac0245 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -2253,6 +2253,29 @@ typedef struct { /* minimum includes first three fields, and empty FS Name */ #define MIN_FS_ATTR_INFO_SIZE 12 + +/* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */ +#define FILE_SUPPORT_INTEGRITY_STREAMS 0x04000000 +#define FILE_SUPPORTS_USN_JOURNAL 0x02000000 +#define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000 +#define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000 +#define FILE_SUPPORTS_HARD_LINKS 0x00400000 +#define FILE_SUPPORTS_TRANSACTIONS 0x00200000 +#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000 +#define FILE_READ_ONLY_VOLUME 0x00080000 +#define FILE_NAMED_STREAMS 0x00040000 +#define FILE_SUPPORTS_ENCRYPTION 0x00020000 +#define FILE_SUPPORTS_OBJECT_IDS 0x00010000 +#define FILE_VOLUME_IS_COMPRESSED 0x00008000 +#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100 +#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080 +#define FILE_SUPPORTS_SPARSE_FILES 0x00000040 +#define FILE_VOLUME_QUOTAS 0x00000020 +#define FILE_FILE_COMPRESSION 0x00000010 +#define FILE_PERSISTENT_ACLS 0x00000008 +#define FILE_UNICODE_ON_DISK 0x00000004 +#define FILE_CASE_PRESERVED_NAMES 0x00000002 +#define FILE_CASE_SENSITIVE_SEARCH 0x00000001 typedef struct { __le32 Attributes; __le32 MaxPathNameComponentLength; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 4ab2f79ffa7a..d5fec92e0360 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -762,7 +762,7 @@ int cifs_closedir(struct inode *inode, struct file *file) cifs_dbg(FYI, "Freeing private data in close dir\n"); spin_lock(&cifs_file_list_lock); - if (!cfile->srch_inf.endOfSearch && !cfile->invalidHandle) { + if (server->ops->dir_needs_close(cfile)) { cfile->invalidHandle = true; spin_unlock(&cifs_file_list_lock); if (server->ops->close_dir) diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 426d6c6ad8bf..949ec909ec9a 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1727,6 +1727,12 @@ unlink_target: target_dentry, to_name); } + /* force revalidate to go get info when needed */ + CIFS_I(source_dir)->time = CIFS_I(target_dir)->time = 0; + + source_dir->i_ctime = source_dir->i_mtime = target_dir->i_ctime = + target_dir->i_mtime = current_fs_time(source_dir->i_sb); + cifs_rename_exit: kfree(info_buf_source); kfree(from_name); diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 81340c6253eb..b7415d596dbd 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -574,13 +574,6 @@ void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock) cinode->oplock = 0; } -static int -cifs_oplock_break_wait(void *unused) -{ - schedule(); - return signal_pending(current) ? -ERESTARTSYS : 0; -} - /* * We wait for oplock breaks to be processed before we attempt to perform * writes. diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index b15862e0f68c..798c80a41c88 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -593,7 +593,7 @@ find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos, /* close and restart search */ cifs_dbg(FYI, "search backing up - close and restart search\n"); spin_lock(&cifs_file_list_lock); - if (!cfile->srch_inf.endOfSearch && !cfile->invalidHandle) { + if (server->ops->dir_needs_close(cfile)) { cfile->invalidHandle = true; spin_unlock(&cifs_file_list_lock); if (server->ops->close) diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index 5e8c22d6c7b9..1a6df4b03f67 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -1015,6 +1015,12 @@ cifs_wp_retry_size(struct inode *inode) return CIFS_SB(inode->i_sb)->wsize; } +static bool +cifs_dir_needs_close(struct cifsFileInfo *cfile) +{ + return !cfile->srch_inf.endOfSearch && !cfile->invalidHandle; +} + struct smb_version_operations smb1_operations = { .send_cancel = send_nt_cancel, .compare_fids = cifs_compare_fids, @@ -1086,6 +1092,7 @@ struct smb_version_operations smb1_operations = { .create_mf_symlink = cifs_create_mf_symlink, .is_read_op = cifs_is_read_op, .wp_retry_size = cifs_wp_retry_size, + .dir_needs_close = cifs_dir_needs_close, #ifdef CONFIG_CIFS_XATTR .query_all_EAs = CIFSSMBQAllEAs, .set_EA = CIFSSMBSetEA, diff --git a/fs/cifs/smb2maperror.c b/fs/cifs/smb2maperror.c index e31a9dfdcd39..af59d03db492 100644 --- a/fs/cifs/smb2maperror.c +++ b/fs/cifs/smb2maperror.c @@ -214,7 +214,7 @@ static const struct status_to_posix_error smb2_error_map_table[] = { {STATUS_BREAKPOINT, -EIO, "STATUS_BREAKPOINT"}, {STATUS_SINGLE_STEP, -EIO, "STATUS_SINGLE_STEP"}, {STATUS_BUFFER_OVERFLOW, -EIO, "STATUS_BUFFER_OVERFLOW"}, - {STATUS_NO_MORE_FILES, -EIO, "STATUS_NO_MORE_FILES"}, + {STATUS_NO_MORE_FILES, -ENODATA, "STATUS_NO_MORE_FILES"}, {STATUS_WAKE_SYSTEM_DEBUGGER, -EIO, "STATUS_WAKE_SYSTEM_DEBUGGER"}, {STATUS_HANDLES_CLOSED, -EIO, "STATUS_HANDLES_CLOSED"}, {STATUS_NO_INHERITANCE, -EIO, "STATUS_NO_INHERITANCE"}, @@ -298,7 +298,7 @@ static const struct status_to_posix_error smb2_error_map_table[] = { {STATUS_INVALID_PARAMETER, -EINVAL, "STATUS_INVALID_PARAMETER"}, {STATUS_NO_SUCH_DEVICE, -ENODEV, "STATUS_NO_SUCH_DEVICE"}, {STATUS_NO_SUCH_FILE, -ENOENT, "STATUS_NO_SUCH_FILE"}, - {STATUS_INVALID_DEVICE_REQUEST, -EIO, "STATUS_INVALID_DEVICE_REQUEST"}, + {STATUS_INVALID_DEVICE_REQUEST, -EOPNOTSUPP, "STATUS_INVALID_DEVICE_REQUEST"}, {STATUS_END_OF_FILE, -ENODATA, "STATUS_END_OF_FILE"}, {STATUS_WRONG_VOLUME, -EIO, "STATUS_WRONG_VOLUME"}, {STATUS_NO_MEDIA_IN_DEVICE, -EIO, "STATUS_NO_MEDIA_IN_DEVICE"}, diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index f2e6ac29a8d6..4aa7a0f07d6e 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c @@ -178,9 +178,24 @@ smb2_check_message(char *buf, unsigned int length) /* Windows 7 server returns 24 bytes more */ if (clc_len + 20 == len && command == SMB2_OPLOCK_BREAK_HE) return 0; - /* server can return one byte more */ + /* server can return one byte more due to implied bcc[0] */ if (clc_len == 4 + len + 1) return 0; + + /* + * MacOS server pads after SMB2.1 write response with 3 bytes + * of junk. Other servers match RFC1001 len to actual + * SMB2/SMB3 frame length (header + smb2 response specific data) + * Log the server error (once), but allow it and continue + * since the frame is parseable. + */ + if (clc_len < 4 /* RFC1001 header size */ + len) { + printk_once(KERN_WARNING + "SMB2 server sent bad RFC1001 len %d not %d\n", + len, clc_len - 4); + return 0; + } + return 1; } return 0; diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 77f8aeb9c2fc..5a48aa290dfe 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -731,11 +731,72 @@ smb2_sync_write(const unsigned int xid, struct cifsFileInfo *cfile, return SMB2_write(xid, parms, written, iov, nr_segs); } +/* Set or clear the SPARSE_FILE attribute based on value passed in setsparse */ +static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, struct inode *inode, __u8 setsparse) +{ + struct cifsInodeInfo *cifsi; + int rc; + + cifsi = CIFS_I(inode); + + /* if file already sparse don't bother setting sparse again */ + if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && setsparse) + return true; /* already sparse */ + + if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && !setsparse) + return true; /* already not sparse */ + + /* + * Can't check for sparse support on share the usual way via the + * FS attribute info (FILE_SUPPORTS_SPARSE_FILES) on the share + * since Samba server doesn't set the flag on the share, yet + * supports the set sparse FSCTL and returns sparse correctly + * in the file attributes. If we fail setting sparse though we + * mark that server does not support sparse files for this share + * to avoid repeatedly sending the unsupported fsctl to server + * if the file is repeatedly extended. + */ + if (tcon->broken_sparse_sup) + return false; + + rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, FSCTL_SET_SPARSE, + true /* is_fctl */, &setsparse, 1, NULL, NULL); + if (rc) { + tcon->broken_sparse_sup = true; + cifs_dbg(FYI, "set sparse rc = %d\n", rc); + return false; + } + + if (setsparse) + cifsi->cifsAttrs |= FILE_ATTRIBUTE_SPARSE_FILE; + else + cifsi->cifsAttrs &= (~FILE_ATTRIBUTE_SPARSE_FILE); + + return true; +} + static int smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon, struct cifsFileInfo *cfile, __u64 size, bool set_alloc) { __le64 eof = cpu_to_le64(size); + struct inode *inode; + + /* + * If extending file more than one page make sparse. Many Linux fs + * make files sparse by default when extending via ftruncate + */ + inode = cfile->dentry->d_inode; + + if (!set_alloc && (size > inode->i_size + 8192)) { + __u8 set_sparse = 1; + + /* whether set sparse succeeds or not, extend the file */ + smb2_set_sparse(xid, tcon, cfile, inode, set_sparse); + } + return SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, cfile->fid.volatile_fid, cfile->pid, &eof, false); } @@ -954,6 +1015,105 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, return rc; } +static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, + loff_t offset, loff_t len, bool keep_size) +{ + struct inode *inode; + struct cifsInodeInfo *cifsi; + struct cifsFileInfo *cfile = file->private_data; + struct file_zero_data_information fsctl_buf; + long rc; + unsigned int xid; + + xid = get_xid(); + + inode = cfile->dentry->d_inode; + cifsi = CIFS_I(inode); + + /* if file not oplocked can't be sure whether asking to extend size */ + if (!CIFS_CACHE_READ(cifsi)) + if (keep_size == false) + return -EOPNOTSUPP; + + /* + * Must check if file sparse since fallocate -z (zero range) assumes + * non-sparse allocation + */ + if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)) + return -EOPNOTSUPP; + + /* + * need to make sure we are not asked to extend the file since the SMB3 + * fsctl does not change the file size. In the future we could change + * this to zero the first part of the range then set the file size + * which for a non sparse file would zero the newly extended range + */ + if (keep_size == false) + if (i_size_read(inode) < offset + len) + return -EOPNOTSUPP; + + cifs_dbg(FYI, "offset %lld len %lld", offset, len); + + fsctl_buf.FileOffset = cpu_to_le64(offset); + fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len); + + rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, + true /* is_fctl */, (char *)&fsctl_buf, + sizeof(struct file_zero_data_information), NULL, NULL); + free_xid(xid); + return rc; +} + +static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, + loff_t offset, loff_t len) +{ + struct inode *inode; + struct cifsInodeInfo *cifsi; + struct cifsFileInfo *cfile = file->private_data; + struct file_zero_data_information fsctl_buf; + long rc; + unsigned int xid; + __u8 set_sparse = 1; + + xid = get_xid(); + + inode = cfile->dentry->d_inode; + cifsi = CIFS_I(inode); + + /* Need to make file sparse, if not already, before freeing range. */ + /* Consider adding equivalent for compressed since it could also work */ + if (!smb2_set_sparse(xid, tcon, cfile, inode, set_sparse)) + return -EOPNOTSUPP; + + cifs_dbg(FYI, "offset %lld len %lld", offset, len); + + fsctl_buf.FileOffset = cpu_to_le64(offset); + fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len); + + rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, + true /* is_fctl */, (char *)&fsctl_buf, + sizeof(struct file_zero_data_information), NULL, NULL); + free_xid(xid); + return rc; +} + +static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode, + loff_t off, loff_t len) +{ + /* KEEP_SIZE already checked for by do_fallocate */ + if (mode & FALLOC_FL_PUNCH_HOLE) + return smb3_punch_hole(file, tcon, off, len); + else if (mode & FALLOC_FL_ZERO_RANGE) { + if (mode & FALLOC_FL_KEEP_SIZE) + return smb3_zero_range(file, tcon, off, len, true); + return smb3_zero_range(file, tcon, off, len, false); + } + + return -EOPNOTSUPP; +} + static void smb2_downgrade_oplock(struct TCP_Server_Info *server, struct cifsInodeInfo *cinode, bool set_level2) @@ -1161,6 +1321,12 @@ smb2_wp_retry_size(struct inode *inode) SMB2_MAX_BUFFER_SIZE); } +static bool +smb2_dir_needs_close(struct cifsFileInfo *cfile) +{ + return !cfile->invalidHandle; +} + struct smb_version_operations smb20_operations = { .compare_fids = smb2_compare_fids, .setup_request = smb2_setup_request, @@ -1236,6 +1402,7 @@ struct smb_version_operations smb20_operations = { .parse_lease_buf = smb2_parse_lease_buf, .clone_range = smb2_clone_range, .wp_retry_size = smb2_wp_retry_size, + .dir_needs_close = smb2_dir_needs_close, }; struct smb_version_operations smb21_operations = { @@ -1313,6 +1480,7 @@ struct smb_version_operations smb21_operations = { .parse_lease_buf = smb2_parse_lease_buf, .clone_range = smb2_clone_range, .wp_retry_size = smb2_wp_retry_size, + .dir_needs_close = smb2_dir_needs_close, }; struct smb_version_operations smb30_operations = { @@ -1393,6 +1561,8 @@ struct smb_version_operations smb30_operations = { .clone_range = smb2_clone_range, .validate_negotiate = smb3_validate_negotiate, .wp_retry_size = smb2_wp_retry_size, + .dir_needs_close = smb2_dir_needs_close, + .fallocate = smb3_fallocate, }; struct smb_version_values smb20_values = { diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 42ebc1a8be6c..fa0dd044213b 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -907,7 +907,8 @@ tcon_exit: tcon_error_exit: if (rsp->hdr.Status == STATUS_BAD_NETWORK_NAME) { cifs_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree); - tcon->bad_network_name = true; + if (tcon) + tcon->bad_network_name = true; } goto tcon_exit; } @@ -1224,7 +1225,9 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, cifs_dbg(FYI, "SMB2 IOCTL\n"); - *out_data = NULL; + if (out_data != NULL) + *out_data = NULL; + /* zero out returned data len, in case of error */ if (plen) *plen = 0; @@ -2177,6 +2180,10 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, rsp = (struct smb2_query_directory_rsp *)iov[0].iov_base; if (rc) { + if (rc == -ENODATA && rsp->hdr.Status == STATUS_NO_MORE_FILES) { + srch_inf->endOfSearch = true; + rc = 0; + } cifs_stats_fail_inc(tcon, SMB2_QUERY_DIRECTORY_HE); goto qdir_exit; } @@ -2214,11 +2221,6 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, else cifs_dbg(VFS, "illegal search buffer type\n"); - if (rsp->hdr.Status == STATUS_NO_MORE_FILES) - srch_inf->endOfSearch = 1; - else - srch_inf->endOfSearch = 0; - return rc; qdir_exit: diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index 69f3595d3952..fbe486c285a9 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -573,6 +573,12 @@ struct copychunk_ioctl { __u32 Reserved2; } __packed; +/* this goes in the ioctl buffer when doing FSCTL_SET_ZERO_DATA */ +struct file_zero_data_information { + __le64 FileOffset; + __le64 BeyondFinalZero; +} __packed; + struct copychunk_ioctl_rsp { __le32 ChunksWritten; __le32 ChunkBytesWritten; diff --git a/fs/cifs/smbfsctl.h b/fs/cifs/smbfsctl.h index 0e538b5c9622..83efa59535be 100644 --- a/fs/cifs/smbfsctl.h +++ b/fs/cifs/smbfsctl.h @@ -63,7 +63,7 @@ #define FSCTL_SET_OBJECT_ID_EXTENDED 0x000900BC /* BB add struct */ #define FSCTL_CREATE_OR_GET_OBJECT_ID 0x000900C0 /* BB add struct */ #define FSCTL_SET_SPARSE 0x000900C4 /* BB add struct */ -#define FSCTL_SET_ZERO_DATA 0x000900C8 /* BB add struct */ +#define FSCTL_SET_ZERO_DATA 0x000980C8 #define FSCTL_SET_ENCRYPTION 0x000900D7 /* BB add struct */ #define FSCTL_ENCRYPTION_FSCTL_IO 0x000900DB /* BB add struct */ #define FSCTL_WRITE_RAW_ENCRYPTED 0x000900DF /* BB add struct */ diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 08cdfe5461e3..622e88249024 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -2828,8 +2828,9 @@ static int ext3_statfs (struct dentry * dentry, struct kstatfs * buf) */ overhead += ngroups * (2 + sbi->s_itb_per_group); - /* Add the journal blocks as well */ - overhead += sbi->s_journal->j_maxlen; + /* Add the internal journal blocks as well */ + if (sbi->s_journal && !sbi->journal_bdev) + overhead += sbi->s_journal->j_maxlen; sbi->s_overhead_last = overhead; smp_wmb(); diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 4556ce1af5b0..5ddaf8625d3b 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -61,7 +61,7 @@ static void isofs_put_super(struct super_block *sb) return; } -static int isofs_read_inode(struct inode *); +static int isofs_read_inode(struct inode *, int relocated); static int isofs_statfs (struct dentry *, struct kstatfs *); static struct kmem_cache *isofs_inode_cachep; @@ -1259,7 +1259,7 @@ out_toomany: goto out; } -static int isofs_read_inode(struct inode *inode) +static int isofs_read_inode(struct inode *inode, int relocated) { struct super_block *sb = inode->i_sb; struct isofs_sb_info *sbi = ISOFS_SB(sb); @@ -1404,7 +1404,7 @@ static int isofs_read_inode(struct inode *inode) */ if (!high_sierra) { - parse_rock_ridge_inode(de, inode); + parse_rock_ridge_inode(de, inode, relocated); /* if we want uid/gid set, override the rock ridge setting */ if (sbi->s_uid_set) inode->i_uid = sbi->s_uid; @@ -1483,9 +1483,10 @@ static int isofs_iget5_set(struct inode *ino, void *data) * offset that point to the underlying meta-data for the inode. The * code below is otherwise similar to the iget() code in * include/linux/fs.h */ -struct inode *isofs_iget(struct super_block *sb, - unsigned long block, - unsigned long offset) +struct inode *__isofs_iget(struct super_block *sb, + unsigned long block, + unsigned long offset, + int relocated) { unsigned long hashval; struct inode *inode; @@ -1507,7 +1508,7 @@ struct inode *isofs_iget(struct super_block *sb, return ERR_PTR(-ENOMEM); if (inode->i_state & I_NEW) { - ret = isofs_read_inode(inode); + ret = isofs_read_inode(inode, relocated); if (ret < 0) { iget_failed(inode); inode = ERR_PTR(ret); diff --git a/fs/isofs/isofs.h b/fs/isofs/isofs.h index 99167238518d..0ac4c1f73fbd 100644 --- a/fs/isofs/isofs.h +++ b/fs/isofs/isofs.h @@ -107,7 +107,7 @@ extern int iso_date(char *, int); struct inode; /* To make gcc happy */ -extern int parse_rock_ridge_inode(struct iso_directory_record *, struct inode *); +extern int parse_rock_ridge_inode(struct iso_directory_record *, struct inode *, int relocated); extern int get_rock_ridge_filename(struct iso_directory_record *, char *, struct inode *); extern int isofs_name_translate(struct iso_directory_record *, char *, struct inode *); @@ -118,9 +118,24 @@ extern struct dentry *isofs_lookup(struct inode *, struct dentry *, unsigned int extern struct buffer_head *isofs_bread(struct inode *, sector_t); extern int isofs_get_blocks(struct inode *, sector_t, struct buffer_head **, unsigned long); -extern struct inode *isofs_iget(struct super_block *sb, - unsigned long block, - unsigned long offset); +struct inode *__isofs_iget(struct super_block *sb, + unsigned long block, + unsigned long offset, + int relocated); + +static inline struct inode *isofs_iget(struct super_block *sb, + unsigned long block, + unsigned long offset) +{ + return __isofs_iget(sb, block, offset, 0); +} + +static inline struct inode *isofs_iget_reloc(struct super_block *sb, + unsigned long block, + unsigned long offset) +{ + return __isofs_iget(sb, block, offset, 1); +} /* Because the inode number is no longer relevant to finding the * underlying meta-data for an inode, we are free to choose a more diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c index c0bf42472e40..f488bbae541a 100644 --- a/fs/isofs/rock.c +++ b/fs/isofs/rock.c @@ -288,12 +288,16 @@ eio: goto out; } +#define RR_REGARD_XA 1 +#define RR_RELOC_DE 2 + static int parse_rock_ridge_inode_internal(struct iso_directory_record *de, - struct inode *inode, int regard_xa) + struct inode *inode, int flags) { int symlink_len = 0; int cnt, sig; + unsigned int reloc_block; struct inode *reloc; struct rock_ridge *rr; int rootflag; @@ -305,7 +309,7 @@ parse_rock_ridge_inode_internal(struct iso_directory_record *de, init_rock_state(&rs, inode); setup_rock_ridge(de, inode, &rs); - if (regard_xa) { + if (flags & RR_REGARD_XA) { rs.chr += 14; rs.len -= 14; if (rs.len < 0) @@ -485,12 +489,22 @@ repeat: "relocated directory\n"); goto out; case SIG('C', 'L'): - ISOFS_I(inode)->i_first_extent = - isonum_733(rr->u.CL.location); - reloc = - isofs_iget(inode->i_sb, - ISOFS_I(inode)->i_first_extent, - 0); + if (flags & RR_RELOC_DE) { + printk(KERN_ERR + "ISOFS: Recursive directory relocation " + "is not supported\n"); + goto eio; + } + reloc_block = isonum_733(rr->u.CL.location); + if (reloc_block == ISOFS_I(inode)->i_iget5_block && + ISOFS_I(inode)->i_iget5_offset == 0) { + printk(KERN_ERR + "ISOFS: Directory relocation points to " + "itself\n"); + goto eio; + } + ISOFS_I(inode)->i_first_extent = reloc_block; + reloc = isofs_iget_reloc(inode->i_sb, reloc_block, 0); if (IS_ERR(reloc)) { ret = PTR_ERR(reloc); goto out; @@ -637,9 +651,11 @@ static char *get_symlink_chunk(char *rpnt, struct rock_ridge *rr, char *plimit) return rpnt; } -int parse_rock_ridge_inode(struct iso_directory_record *de, struct inode *inode) +int parse_rock_ridge_inode(struct iso_directory_record *de, struct inode *inode, + int relocated) { - int result = parse_rock_ridge_inode_internal(de, inode, 0); + int flags = relocated ? RR_RELOC_DE : 0; + int result = parse_rock_ridge_inode_internal(de, inode, flags); /* * if rockridge flag was reset and we didn't look for attributes @@ -647,7 +663,8 @@ int parse_rock_ridge_inode(struct iso_directory_record *de, struct inode *inode) */ if ((ISOFS_SB(inode->i_sb)->s_rock_offset == -1) && (ISOFS_SB(inode->i_sb)->s_rock == 2)) { - result = parse_rock_ridge_inode_internal(de, inode, 14); + result = parse_rock_ridge_inode_internal(de, inode, + flags | RR_REGARD_XA); } return result; } diff --git a/fs/locks.c b/fs/locks.c index a6f54802d277..cb66fb05ad4a 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -247,6 +247,18 @@ void locks_free_lock(struct file_lock *fl) } EXPORT_SYMBOL(locks_free_lock); +static void +locks_dispose_list(struct list_head *dispose) +{ + struct file_lock *fl; + + while (!list_empty(dispose)) { + fl = list_first_entry(dispose, struct file_lock, fl_block); + list_del_init(&fl->fl_block); + locks_free_lock(fl); + } +} + void locks_init_lock(struct file_lock *fl) { memset(fl, 0, sizeof(struct file_lock)); @@ -285,7 +297,8 @@ EXPORT_SYMBOL(__locks_copy_lock); void locks_copy_lock(struct file_lock *new, struct file_lock *fl) { - locks_release_private(new); + /* "new" must be a freshly-initialized lock */ + WARN_ON_ONCE(new->fl_ops); __locks_copy_lock(new, fl); new->fl_file = fl->fl_file; @@ -650,12 +663,16 @@ static void locks_unlink_lock(struct file_lock **thisfl_p) * * Must be called with i_lock held! */ -static void locks_delete_lock(struct file_lock **thisfl_p) +static void locks_delete_lock(struct file_lock **thisfl_p, + struct list_head *dispose) { struct file_lock *fl = *thisfl_p; locks_unlink_lock(thisfl_p); - locks_free_lock(fl); + if (dispose) + list_add(&fl->fl_block, dispose); + else + locks_free_lock(fl); } /* Determine if lock sys_fl blocks lock caller_fl. Common functionality @@ -811,6 +828,7 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) struct inode * inode = file_inode(filp); int error = 0; int found = 0; + LIST_HEAD(dispose); if (!(request->fl_flags & FL_ACCESS) && (request->fl_type != F_UNLCK)) { new_fl = locks_alloc_lock(); @@ -833,7 +851,7 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) if (request->fl_type == fl->fl_type) goto out; found = 1; - locks_delete_lock(before); + locks_delete_lock(before, &dispose); break; } @@ -880,6 +898,7 @@ out: spin_unlock(&inode->i_lock); if (new_fl) locks_free_lock(new_fl); + locks_dispose_list(&dispose); return error; } @@ -893,6 +912,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str struct file_lock **before; int error; bool added = false; + LIST_HEAD(dispose); /* * We may need two file_lock structures for this operation, @@ -988,7 +1008,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str else request->fl_end = fl->fl_end; if (added) { - locks_delete_lock(before); + locks_delete_lock(before, &dispose); continue; } request = fl; @@ -1018,21 +1038,24 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str * one (This may happen several times). */ if (added) { - locks_delete_lock(before); + locks_delete_lock(before, &dispose); continue; } - /* Replace the old lock with the new one. - * Wake up anybody waiting for the old one, - * as the change in lock type might satisfy - * their needs. + /* + * Replace the old lock with new_fl, and + * remove the old one. It's safe to do the + * insert here since we know that we won't be + * using new_fl later, and that the lock is + * just replacing an existing lock. */ - locks_wake_up_blocks(fl); - fl->fl_start = request->fl_start; - fl->fl_end = request->fl_end; - fl->fl_type = request->fl_type; - locks_release_private(fl); - locks_copy_private(fl, request); - request = fl; + error = -ENOLCK; + if (!new_fl) + goto out; + locks_copy_lock(new_fl, request); + request = new_fl; + new_fl = NULL; + locks_delete_lock(before, &dispose); + locks_insert_lock(before, request); added = true; } } @@ -1093,6 +1116,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str locks_free_lock(new_fl); if (new_fl2) locks_free_lock(new_fl2); + locks_dispose_list(&dispose); return error; } @@ -1268,7 +1292,7 @@ int lease_modify(struct file_lock **before, int arg) printk(KERN_ERR "locks_delete_lock: fasync == %p\n", fl->fl_fasync); fl->fl_fasync = NULL; } - locks_delete_lock(before); + locks_delete_lock(before, NULL); } return 0; } @@ -1737,13 +1761,10 @@ static int do_fcntl_add_lease(unsigned int fd, struct file *filp, long arg) ret = fl; spin_lock(&inode->i_lock); error = __vfs_setlease(filp, arg, &ret); - if (error) { - spin_unlock(&inode->i_lock); - locks_free_lock(fl); - goto out_free_fasync; - } - if (ret != fl) - locks_free_lock(fl); + if (error) + goto out_unlock; + if (ret == fl) + fl = NULL; /* * fasync_insert_entry() returns the old entry if any. @@ -1755,9 +1776,10 @@ static int do_fcntl_add_lease(unsigned int fd, struct file *filp, long arg) new = NULL; error = __f_setown(filp, task_pid(current), PIDTYPE_PID, 0); +out_unlock: spin_unlock(&inode->i_lock); - -out_free_fasync: + if (fl) + locks_free_lock(fl); if (new) fasync_free(new); return error; @@ -2320,6 +2342,7 @@ void locks_remove_file(struct file *filp) struct inode * inode = file_inode(filp); struct file_lock *fl; struct file_lock **before; + LIST_HEAD(dispose); if (!inode->i_flock) return; @@ -2365,12 +2388,13 @@ void locks_remove_file(struct file *filp) fl->fl_type, fl->fl_flags, fl->fl_start, fl->fl_end); - locks_delete_lock(before); + locks_delete_lock(before, &dispose); continue; } before = &fl->fl_next; } spin_unlock(&inode->i_lock); + locks_dispose_list(&dispose); } /** @@ -2452,7 +2476,11 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl, seq_puts(f, "FLOCK ADVISORY "); } } else if (IS_LEASE(fl)) { - seq_puts(f, "LEASE "); + if (fl->fl_flags & FL_DELEG) + seq_puts(f, "DELEG "); + else + seq_puts(f, "LEASE "); + if (lease_breaking(fl)) seq_puts(f, "BREAKING "); else if (fl->fl_file) diff --git a/fs/udf/namei.c b/fs/udf/namei.c index 9737cba1357d..83a06001742b 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -1014,7 +1014,7 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry, fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err); if (!fi) - goto out_no_entry; + goto out_fail; cfi.icb.extLength = cpu_to_le32(sb->s_blocksize); cfi.icb.extLocation = cpu_to_lelb(iinfo->i_location); if (UDF_SB(inode->i_sb)->s_lvid_bh) { @@ -1036,6 +1036,7 @@ out: out_no_entry: up_write(&iinfo->i_data_sem); +out_fail: inode_dec_link_count(inode); iput(inode); goto out; diff --git a/include/linux/i2c.h b/include/linux/i2c.h index ea507665896c..a95efeb53a8b 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -577,16 +577,20 @@ static inline struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node } #endif /* CONFIG_OF */ -#ifdef CONFIG_I2C_ACPI -int acpi_i2c_install_space_handler(struct i2c_adapter *adapter); -void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter); +#ifdef CONFIG_ACPI void acpi_i2c_register_devices(struct i2c_adapter *adap); #else static inline void acpi_i2c_register_devices(struct i2c_adapter *adap) { } +#endif /* CONFIG_ACPI */ + +#ifdef CONFIG_ACPI_I2C_OPREGION +int acpi_i2c_install_space_handler(struct i2c_adapter *adapter); +void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter); +#else static inline void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter) { } static inline int acpi_i2c_install_space_handler(struct i2c_adapter *adapter) { return 0; } -#endif +#endif /* CONFIG_ACPI_I2C_OPREGION */ #endif /* _LINUX_I2C_H */ diff --git a/kernel/module.c b/kernel/module.c index 6f69463f0066..03214bd288e9 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -3304,6 +3304,11 @@ static int load_module(struct load_info *info, const char __user *uargs, mutex_lock(&module_mutex); module_bug_cleanup(mod); mutex_unlock(&module_mutex); + + /* we can't deallocate the module until we clear memory protection */ + unset_module_init_ro_nx(mod); + unset_module_core_ro_nx(mod); + ddebug_cleanup: dynamic_debug_remove(info->debug); synchronize_sched(); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 36badba2dcec..99d7d7fecaad 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -50,6 +50,8 @@ MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info"); #define is_haswell_plus(codec) (is_haswell(codec) || is_broadwell(codec)) #define is_valleyview(codec) ((codec)->vendor_id == 0x80862882) +#define is_cherryview(codec) ((codec)->vendor_id == 0x80862883) +#define is_valleyview_plus(codec) (is_valleyview(codec) || is_cherryview(codec)) struct hdmi_spec_per_cvt { hda_nid_t cvt_nid; @@ -1459,7 +1461,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, mux_idx); /* configure unused pins to choose other converters */ - if (is_haswell_plus(codec) || is_valleyview(codec)) + if (is_haswell_plus(codec) || is_valleyview_plus(codec)) intel_not_share_assigned_cvt(codec, per_pin->pin_nid, mux_idx); snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid); @@ -1598,7 +1600,8 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) * and this can make HW reset converter selection on a pin. */ if (eld->eld_valid && !old_eld_valid && per_pin->setup) { - if (is_haswell_plus(codec) || is_valleyview(codec)) { + if (is_haswell_plus(codec) || + is_valleyview_plus(codec)) { intel_verify_pin_cvt_connect(codec, per_pin); intel_not_share_assigned_cvt(codec, pin_nid, per_pin->mux_idx); @@ -1779,7 +1782,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, bool non_pcm; int pinctl; - if (is_haswell_plus(codec) || is_valleyview(codec)) { + if (is_haswell_plus(codec) || is_valleyview_plus(codec)) { /* Verify pin:cvt selections to avoid silent audio after S3. * After S3, the audio driver restores pin:cvt selections * but this can happen before gfx is ready and such selection @@ -2330,9 +2333,8 @@ static int patch_generic_hdmi(struct hda_codec *codec) intel_haswell_fixup_enable_dp12(codec); } - if (is_haswell(codec) || is_valleyview(codec)) { + if (is_haswell_plus(codec) || is_valleyview_plus(codec)) codec->depop_delay = 0; - } if (hdmi_parse_codec(codec) < 0) { codec->spec = NULL; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 6b38ec3c6e57..d71270a3f73f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -181,6 +181,8 @@ static void alc_fix_pll(struct hda_codec *codec) spec->pll_coef_idx); val = snd_hda_codec_read(codec, spec->pll_nid, 0, AC_VERB_GET_PROC_COEF, 0); + if (val == -1) + return; snd_hda_codec_write(codec, spec->pll_nid, 0, AC_VERB_SET_COEF_INDEX, spec->pll_coef_idx); snd_hda_codec_write(codec, spec->pll_nid, 0, AC_VERB_SET_PROC_COEF, @@ -2806,6 +2808,8 @@ static void alc286_shutup(struct hda_codec *codec) static void alc269vb_toggle_power_output(struct hda_codec *codec, int power_up) { int val = alc_read_coef_idx(codec, 0x04); + if (val == -1) + return; if (power_up) val |= 1 << 11; else @@ -3264,6 +3268,15 @@ static int alc269_resume(struct hda_codec *codec) snd_hda_codec_resume_cache(codec); alc_inv_dmic_sync(codec, true); hda_call_check_power_status(codec, 0x01); + + /* on some machine, the BIOS will clear the codec gpio data when enter + * suspend, and won't restore the data after resume, so we restore it + * in the driver. + */ + if (spec->gpio_led) + snd_hda_codec_write(codec, codec->afg, 0, AC_VERB_SET_GPIO_DATA, + spec->gpio_led); + if (spec->has_alc5505_dsp) alc5505_dsp_resume(codec); @@ -5311,27 +5324,30 @@ static void alc269_fill_coef(struct hda_codec *codec) if ((alc_get_coef0(codec) & 0x00ff) == 0x017) { val = alc_read_coef_idx(codec, 0x04); /* Power up output pin */ - alc_write_coef_idx(codec, 0x04, val | (1<<11)); + if (val != -1) + alc_write_coef_idx(codec, 0x04, val | (1<<11)); } if ((alc_get_coef0(codec) & 0x00ff) == 0x018) { val = alc_read_coef_idx(codec, 0xd); - if ((val & 0x0c00) >> 10 != 0x1) { + if (val != -1 && (val & 0x0c00) >> 10 != 0x1) { /* Capless ramp up clock control */ alc_write_coef_idx(codec, 0xd, val | (1<<10)); } val = alc_read_coef_idx(codec, 0x17); - if ((val & 0x01c0) >> 6 != 0x4) { + if (val != -1 && (val & 0x01c0) >> 6 != 0x4) { /* Class D power on reset */ alc_write_coef_idx(codec, 0x17, val | (1<<7)); } } val = alc_read_coef_idx(codec, 0xd); /* Class D */ - alc_write_coef_idx(codec, 0xd, val | (1<<14)); + if (val != -1) + alc_write_coef_idx(codec, 0xd, val | (1<<14)); val = alc_read_coef_idx(codec, 0x4); /* HP */ - alc_write_coef_idx(codec, 0x4, val | (1<<11)); + if (val != -1) + alc_write_coef_idx(codec, 0x4, val | (1<<11)); } /* diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index bd41ee4da078..2c71f16bd661 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1278,6 +1278,8 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, else rates = &arizona_48k_bclk_rates[0]; + wl = snd_pcm_format_width(params_format(params)); + if (tdm_slots) { arizona_aif_dbg(dai, "Configuring for %d %d bit TDM slots\n", tdm_slots, tdm_width); @@ -1285,6 +1287,7 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, channels = tdm_slots; } else { bclk_target = snd_soc_params_to_bclk(params); + tdm_width = wl; } if (chan_limit && chan_limit < channels) { @@ -1319,8 +1322,7 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, arizona_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n", rates[bclk], rates[bclk] / lrclk); - wl = snd_pcm_format_width(params_format(params)); - frame = wl << ARIZONA_AIF1TX_WL_SHIFT | wl; + frame = wl << ARIZONA_AIF1TX_WL_SHIFT | tdm_width; reconfig = arizona_aif_cfg_changed(codec, base, bclk, lrclk, frame); diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 163ec3855fd4..0c8aefab404c 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -259,13 +259,13 @@ static const struct soc_enum pcm512x_veds = pcm512x_ramp_step_text); static const struct snd_kcontrol_new pcm512x_controls[] = { -SOC_DOUBLE_R_TLV("Playback Digital Volume", PCM512x_DIGITAL_VOLUME_2, +SOC_DOUBLE_R_TLV("Digital Playback Volume", PCM512x_DIGITAL_VOLUME_2, PCM512x_DIGITAL_VOLUME_3, 0, 255, 1, digital_tlv), SOC_DOUBLE_TLV("Playback Volume", PCM512x_ANALOG_GAIN_CTRL, PCM512x_LAGN_SHIFT, PCM512x_RAGN_SHIFT, 1, 1, analog_tlv), SOC_DOUBLE_TLV("Playback Boost Volume", PCM512x_ANALOG_GAIN_BOOST, PCM512x_AGBL_SHIFT, PCM512x_AGBR_SHIFT, 1, 0, boost_tlv), -SOC_DOUBLE("Playback Digital Switch", PCM512x_MUTE, PCM512x_RQML_SHIFT, +SOC_DOUBLE("Digital Playback Switch", PCM512x_MUTE, PCM512x_RQML_SHIFT, PCM512x_RQMR_SHIFT, 1, 1), SOC_SINGLE("Deemphasis Switch", PCM512x_DSP, PCM512x_DEMP_SHIFT, 1, 1), diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index c28508da34cf..6a6b2ff7d7d7 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -403,7 +403,8 @@ out: return ret; } -static int davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div) +static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, + int div, bool explicit) { struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); @@ -420,7 +421,8 @@ static int davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div ACLKXDIV(div - 1), ACLKXDIV_MASK); mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRDIV(div - 1), ACLKRDIV_MASK); - mcasp->bclk_div = div; + if (explicit) + mcasp->bclk_div = div; break; case 2: /* BCLK/LRCLK ratio */ @@ -434,6 +436,12 @@ static int davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div return 0; } +static int davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, + int div) +{ + return __davinci_mcasp_set_clkdiv(dai, div_id, div, 1); +} + static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { @@ -738,7 +746,7 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, "Inaccurate BCLK: %u Hz / %u != %u Hz\n", mcasp->sysclk_freq, div, bclk_freq); } - davinci_mcasp_set_clkdiv(cpu_dai, 1, div); + __davinci_mcasp_set_clkdiv(cpu_dai, 1, div, 0); } ret = mcasp_common_hw_param(mcasp, substream->stream, diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index f54a8fc99291..f3012b645b51 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -49,7 +49,6 @@ config SND_SOC_FSL_ESAI tristate "Enhanced Serial Audio Interface (ESAI) module support" select REGMAP_MMIO select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n - select SND_SOC_FSL_UTILS help Say Y if you want to add Enhanced Synchronous Audio Interface (ESAI) support for the Freescale CPUs. diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 72d154e7dd03..a3b29ed84963 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -18,7 +18,6 @@ #include "fsl_esai.h" #include "imx-pcm.h" -#include "fsl_utils.h" #define FSL_ESAI_RATES SNDRV_PCM_RATE_8000_192000 #define FSL_ESAI_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ @@ -607,7 +606,6 @@ static struct snd_soc_dai_ops fsl_esai_dai_ops = { .hw_params = fsl_esai_hw_params, .set_sysclk = fsl_esai_set_dai_sysclk, .set_fmt = fsl_esai_set_dai_fmt, - .xlate_tdm_slot_mask = fsl_asoc_xlate_tdm_slot_mask, .set_tdm_slot = fsl_esai_set_dai_tdm_slot, }; diff --git a/sound/soc/intel/sst-acpi.c b/sound/soc/intel/sst-acpi.c index 42edc6f4fc4a..03d0a166b635 100644 --- a/sound/soc/intel/sst-acpi.c +++ b/sound/soc/intel/sst-acpi.c @@ -246,8 +246,8 @@ static struct sst_acpi_desc sst_acpi_broadwell_desc = { }; static struct sst_acpi_mach baytrail_machines[] = { - { "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-i2s_master" }, - { "193C9890", "byt-max98090", "intel/fw_sst_0f28.bin-i2s_master" }, + { "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-48kHz_i2s_master" }, + { "193C9890", "byt-max98090", "intel/fw_sst_0f28.bin-48kHz_i2s_master" }, {} }; diff --git a/sound/soc/intel/sst-baytrail-ipc.c b/sound/soc/intel/sst-baytrail-ipc.c index 67673a2c0f41..b4ad98c43e5c 100644 --- a/sound/soc/intel/sst-baytrail-ipc.c +++ b/sound/soc/intel/sst-baytrail-ipc.c @@ -817,7 +817,7 @@ static struct sst_dsp_device byt_dev = { .ops = &sst_byt_ops, }; -int sst_byt_dsp_suspend_noirq(struct device *dev, struct sst_pdata *pdata) +int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata) { struct sst_byt *byt = pdata->dsp; @@ -826,14 +826,6 @@ int sst_byt_dsp_suspend_noirq(struct device *dev, struct sst_pdata *pdata) sst_byt_drop_all(byt); dev_dbg(byt->dev, "dsp in reset\n"); - return 0; -} -EXPORT_SYMBOL_GPL(sst_byt_dsp_suspend_noirq); - -int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata) -{ - struct sst_byt *byt = pdata->dsp; - dev_dbg(byt->dev, "free all blocks and unload fw\n"); sst_fw_unload(byt->fw); diff --git a/sound/soc/intel/sst-baytrail-ipc.h b/sound/soc/intel/sst-baytrail-ipc.h index 06a4d202689b..8faff6dcf25d 100644 --- a/sound/soc/intel/sst-baytrail-ipc.h +++ b/sound/soc/intel/sst-baytrail-ipc.h @@ -66,7 +66,6 @@ int sst_byt_get_dsp_position(struct sst_byt *byt, int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata); void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata); struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt); -int sst_byt_dsp_suspend_noirq(struct device *dev, struct sst_pdata *pdata); int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata); int sst_byt_dsp_boot(struct device *dev, struct sst_pdata *pdata); int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata); diff --git a/sound/soc/intel/sst-baytrail-pcm.c b/sound/soc/intel/sst-baytrail-pcm.c index 599401c0c655..eab1c7d85187 100644 --- a/sound/soc/intel/sst-baytrail-pcm.c +++ b/sound/soc/intel/sst-baytrail-pcm.c @@ -59,6 +59,9 @@ struct sst_byt_priv_data { /* DAI data */ struct sst_byt_pcm_data pcm[BYT_PCM_COUNT]; + + /* flag indicating is stream context restore needed after suspend */ + bool restore_stream; }; /* this may get called several times by oss emulation */ @@ -184,7 +187,10 @@ static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd) sst_byt_stream_start(byt, pcm_data->stream, 0); break; case SNDRV_PCM_TRIGGER_RESUME: - schedule_work(&pcm_data->work); + if (pdata->restore_stream == true) + schedule_work(&pcm_data->work); + else + sst_byt_stream_resume(byt, pcm_data->stream); break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: sst_byt_stream_resume(byt, pcm_data->stream); @@ -193,6 +199,7 @@ static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd) sst_byt_stream_stop(byt, pcm_data->stream); break; case SNDRV_PCM_TRIGGER_SUSPEND: + pdata->restore_stream = false; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: sst_byt_stream_pause(byt, pcm_data->stream); break; @@ -404,26 +411,10 @@ static const struct snd_soc_component_driver byt_dai_component = { }; #ifdef CONFIG_PM -static int sst_byt_pcm_dev_suspend_noirq(struct device *dev) -{ - struct sst_pdata *sst_pdata = dev_get_platdata(dev); - int ret; - - dev_dbg(dev, "suspending noirq\n"); - - /* at this point all streams will be stopped and context saved */ - ret = sst_byt_dsp_suspend_noirq(dev, sst_pdata); - if (ret < 0) { - dev_err(dev, "failed to suspend %d\n", ret); - return ret; - } - - return ret; -} - static int sst_byt_pcm_dev_suspend_late(struct device *dev) { struct sst_pdata *sst_pdata = dev_get_platdata(dev); + struct sst_byt_priv_data *priv_data = dev_get_drvdata(dev); int ret; dev_dbg(dev, "suspending late\n"); @@ -434,34 +425,30 @@ static int sst_byt_pcm_dev_suspend_late(struct device *dev) return ret; } + priv_data->restore_stream = true; + return ret; } static int sst_byt_pcm_dev_resume_early(struct device *dev) { struct sst_pdata *sst_pdata = dev_get_platdata(dev); + int ret; dev_dbg(dev, "resume early\n"); /* load fw and boot DSP */ - return sst_byt_dsp_boot(dev, sst_pdata); -} - -static int sst_byt_pcm_dev_resume(struct device *dev) -{ - struct sst_pdata *sst_pdata = dev_get_platdata(dev); - - dev_dbg(dev, "resume\n"); + ret = sst_byt_dsp_boot(dev, sst_pdata); + if (ret) + return ret; /* wait for FW to finish booting */ return sst_byt_dsp_wait_for_ready(dev, sst_pdata); } static const struct dev_pm_ops sst_byt_pm_ops = { - .suspend_noirq = sst_byt_pcm_dev_suspend_noirq, .suspend_late = sst_byt_pcm_dev_suspend_late, .resume_early = sst_byt_pcm_dev_resume_early, - .resume = sst_byt_pcm_dev_resume, }; #define SST_BYT_PM_OPS (&sst_byt_pm_ops) diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 0109f6c2334e..a8e097433074 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -765,9 +765,7 @@ static int pxa_ssp_remove(struct snd_soc_dai *dai) SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) -#define PXA_SSP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ - SNDRV_PCM_FMTBIT_S24_LE | \ - SNDRV_PCM_FMTBIT_S32_LE) +#define PXA_SSP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) static const struct snd_soc_dai_ops pxa_ssp_dai_ops = { .startup = pxa_ssp_startup, diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 8348352dc2c6..177bd8639ef9 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2860,12 +2860,14 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int reg_val, val; - int ret = 0; - if (e->reg != SND_SOC_NOPM) - ret = soc_dapm_read(dapm, e->reg, ®_val); - else + if (e->reg != SND_SOC_NOPM) { + int ret = soc_dapm_read(dapm, e->reg, ®_val); + if (ret) + return ret; + } else { reg_val = dapm_kcontrol_get_value(kcontrol); + } val = (reg_val >> e->shift_l) & e->mask; ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(e, val); @@ -2875,7 +2877,7 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, ucontrol->value.enumerated.item[1] = val; } - return ret; + return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double); diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c index d0396af99fa0..5b1b807265a1 100644 --- a/tools/power/x86/turbostat/turbostat.c +++ b/tools/power/x86/turbostat/turbostat.c @@ -267,90 +267,90 @@ int get_msr(int cpu, off_t offset, unsigned long long *msr) /* * Example Format w/ field column widths: * - * Package Core CPU Avg_MHz Bzy_MHz TSC_MHz SMI %Busy CPU_%c1 CPU_%c3 CPU_%c6 CPU_%c7 CoreTmp PkgTmp Pkg%pc2 Pkg%pc3 Pkg%pc6 Pkg%pc7 PkgWatt CorWatt GFXWatt - * 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 + * Package Core CPU Avg_MHz Bzy_MHz TSC_MHz SMI %Busy CPU_%c1 CPU_%c3 CPU_%c6 CPU_%c7 CoreTmp PkgTmp Pkg%pc2 Pkg%pc3 Pkg%pc6 Pkg%pc7 PkgWatt CorWatt GFXWatt + * 123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678 */ void print_header(void) { if (show_pkg) - outp += sprintf(outp, "Package "); + outp += sprintf(outp, " Package"); if (show_core) - outp += sprintf(outp, " Core "); + outp += sprintf(outp, " Core"); if (show_cpu) - outp += sprintf(outp, " CPU "); + outp += sprintf(outp, " CPU"); if (has_aperf) - outp += sprintf(outp, "Avg_MHz "); + outp += sprintf(outp, " Avg_MHz"); if (do_nhm_cstates) - outp += sprintf(outp, " %%Busy "); + outp += sprintf(outp, " %%Busy"); if (has_aperf) - outp += sprintf(outp, "Bzy_MHz "); - outp += sprintf(outp, "TSC_MHz "); + outp += sprintf(outp, " Bzy_MHz"); + outp += sprintf(outp, " TSC_MHz"); if (do_smi) - outp += sprintf(outp, " SMI "); + outp += sprintf(outp, " SMI"); if (extra_delta_offset32) - outp += sprintf(outp, " count 0x%03X ", extra_delta_offset32); + outp += sprintf(outp, " count 0x%03X", extra_delta_offset32); if (extra_delta_offset64) - outp += sprintf(outp, " COUNT 0x%03X ", extra_delta_offset64); + outp += sprintf(outp, " COUNT 0x%03X", extra_delta_offset64); if (extra_msr_offset32) - outp += sprintf(outp, " MSR 0x%03X ", extra_msr_offset32); + outp += sprintf(outp, " MSR 0x%03X", extra_msr_offset32); if (extra_msr_offset64) - outp += sprintf(outp, " MSR 0x%03X ", extra_msr_offset64); + outp += sprintf(outp, " MSR 0x%03X", extra_msr_offset64); if (do_nhm_cstates) - outp += sprintf(outp, " CPU%%c1 "); + outp += sprintf(outp, " CPU%%c1"); if (do_nhm_cstates && !do_slm_cstates) - outp += sprintf(outp, " CPU%%c3 "); + outp += sprintf(outp, " CPU%%c3"); if (do_nhm_cstates) - outp += sprintf(outp, " CPU%%c6 "); + outp += sprintf(outp, " CPU%%c6"); if (do_snb_cstates) - outp += sprintf(outp, " CPU%%c7 "); + outp += sprintf(outp, " CPU%%c7"); if (do_dts) - outp += sprintf(outp, "CoreTmp "); + outp += sprintf(outp, " CoreTmp"); if (do_ptm) - outp += sprintf(outp, " PkgTmp "); + outp += sprintf(outp, " PkgTmp"); if (do_snb_cstates) - outp += sprintf(outp, "Pkg%%pc2 "); + outp += sprintf(outp, " Pkg%%pc2"); if (do_nhm_cstates && !do_slm_cstates) - outp += sprintf(outp, "Pkg%%pc3 "); + outp += sprintf(outp, " Pkg%%pc3"); if (do_nhm_cstates && !do_slm_cstates) - outp += sprintf(outp, "Pkg%%pc6 "); + outp += sprintf(outp, " Pkg%%pc6"); if (do_snb_cstates) - outp += sprintf(outp, "Pkg%%pc7 "); + outp += sprintf(outp, " Pkg%%pc7"); if (do_c8_c9_c10) { - outp += sprintf(outp, "Pkg%%pc8 "); - outp += sprintf(outp, "Pkg%%pc9 "); - outp += sprintf(outp, "Pk%%pc10 "); + outp += sprintf(outp, " Pkg%%pc8"); + outp += sprintf(outp, " Pkg%%pc9"); + outp += sprintf(outp, " Pk%%pc10"); } if (do_rapl && !rapl_joules) { if (do_rapl & RAPL_PKG) - outp += sprintf(outp, "PkgWatt "); + outp += sprintf(outp, " PkgWatt"); if (do_rapl & RAPL_CORES) - outp += sprintf(outp, "CorWatt "); + outp += sprintf(outp, " CorWatt"); if (do_rapl & RAPL_GFX) - outp += sprintf(outp, "GFXWatt "); + outp += sprintf(outp, " GFXWatt"); if (do_rapl & RAPL_DRAM) - outp += sprintf(outp, "RAMWatt "); + outp += sprintf(outp, " RAMWatt"); if (do_rapl & RAPL_PKG_PERF_STATUS) - outp += sprintf(outp, " PKG_%% "); + outp += sprintf(outp, " PKG_%%"); if (do_rapl & RAPL_DRAM_PERF_STATUS) - outp += sprintf(outp, " RAM_%% "); + outp += sprintf(outp, " RAM_%%"); } else { if (do_rapl & RAPL_PKG) - outp += sprintf(outp, " Pkg_J "); + outp += sprintf(outp, " Pkg_J"); if (do_rapl & RAPL_CORES) - outp += sprintf(outp, " Cor_J "); + outp += sprintf(outp, " Cor_J"); if (do_rapl & RAPL_GFX) - outp += sprintf(outp, " GFX_J "); + outp += sprintf(outp, " GFX_J"); if (do_rapl & RAPL_DRAM) - outp += sprintf(outp, " RAM_W "); + outp += sprintf(outp, " RAM_W"); if (do_rapl & RAPL_PKG_PERF_STATUS) - outp += sprintf(outp, " PKG_%% "); + outp += sprintf(outp, " PKG_%%"); if (do_rapl & RAPL_DRAM_PERF_STATUS) - outp += sprintf(outp, " RAM_%% "); - outp += sprintf(outp, " time "); + outp += sprintf(outp, " RAM_%%"); + outp += sprintf(outp, " time"); } outp += sprintf(outp, "\n"); diff --git a/virt/kvm/assigned-dev.c b/virt/kvm/assigned-dev.c index bf06577fea51..5819a2708d7e 100644 --- a/virt/kvm/assigned-dev.c +++ b/virt/kvm/assigned-dev.c @@ -526,8 +526,10 @@ static int assign_guest_irq(struct kvm *kvm, dev->irq_requested_type |= guest_irq_type; if (dev->ack_notifier.gsi != -1) kvm_register_irq_ack_notifier(kvm, &dev->ack_notifier); - } else + } else { kvm_free_irq_source_id(kvm, dev->irq_source_id); + dev->irq_source_id = -1; + } return r; } diff --git a/virt/kvm/iommu.c b/virt/kvm/iommu.c index 0df7d4b34dfe..714b94932312 100644 --- a/virt/kvm/iommu.c +++ b/virt/kvm/iommu.c @@ -61,6 +61,14 @@ static pfn_t kvm_pin_pages(struct kvm_memory_slot *slot, gfn_t gfn, return pfn; } +static void kvm_unpin_pages(struct kvm *kvm, pfn_t pfn, unsigned long npages) +{ + unsigned long i; + + for (i = 0; i < npages; ++i) + kvm_release_pfn_clean(pfn + i); +} + int kvm_iommu_map_pages(struct kvm *kvm, struct kvm_memory_slot *slot) { gfn_t gfn, end_gfn; @@ -123,6 +131,7 @@ int kvm_iommu_map_pages(struct kvm *kvm, struct kvm_memory_slot *slot) if (r) { printk(KERN_ERR "kvm_iommu_map_address:" "iommu failed to map pfn=%llx\n", pfn); + kvm_unpin_pages(kvm, pfn, page_size); goto unmap_pages; } @@ -134,7 +143,7 @@ int kvm_iommu_map_pages(struct kvm *kvm, struct kvm_memory_slot *slot) return 0; unmap_pages: - kvm_iommu_put_pages(kvm, slot->base_gfn, gfn); + kvm_iommu_put_pages(kvm, slot->base_gfn, gfn - slot->base_gfn); return r; } @@ -266,14 +275,6 @@ out_unlock: return r; } -static void kvm_unpin_pages(struct kvm *kvm, pfn_t pfn, unsigned long npages) -{ - unsigned long i; - - for (i = 0; i < npages; ++i) - kvm_release_pfn_clean(pfn + i); -} - static void kvm_iommu_put_pages(struct kvm *kvm, gfn_t base_gfn, unsigned long npages) { |