commit: 7b01f69e991da0456b5e4dbe270b81e7a9c413d0 Author: Mike Pagano <mpagano <AT> gentoo <DOT> org> AuthorDate: Tue Oct 22 16:59:03 2024 +0000 Commit: Mike Pagano <mpagano <AT> gentoo <DOT> org> CommitDate: Tue Oct 22 16:59:03 2024 +0000 URL: https://gitweb.gentoo.org/proj/linux-patches.git/commit/?id=7b01f69e
Linux patch 5.15.169 Signed-off-by: Mike Pagano <mpagano <AT> gentoo.org> 0000_README | 4 + 1168_linux-5.15.169.patch | 3973 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 3977 insertions(+) diff --git a/0000_README b/0000_README index a3fd22cc..93506049 100644 --- a/0000_README +++ b/0000_README @@ -715,6 +715,10 @@ Patch: 1167_linux-5.15.168.patch From: https://www.kernel.org Desc: Linux 5.15.168 +Patch: 1168_linux-5.15.169.patch +From: https://www.kernel.org +Desc: Linux 5.15.169 + Patch: 1500_XATTR_USER_PREFIX.patch From: https://bugs.gentoo.org/show_bug.cgi?id=470644 Desc: Support for namespace user.pax.* on tmpfs. diff --git a/1168_linux-5.15.169.patch b/1168_linux-5.15.169.patch new file mode 100644 index 00000000..5bf7d223 --- /dev/null +++ b/1168_linux-5.15.169.patch @@ -0,0 +1,3973 @@ +diff --git a/Makefile b/Makefile +index 38ffaecf1edf79..8c97377cd56eb1 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,7 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0 + VERSION = 5 + PATCHLEVEL = 15 +-SUBLEVEL = 168 ++SUBLEVEL = 169 + EXTRAVERSION = + NAME = Trick or Treat + +diff --git a/arch/arm64/kernel/probes/decode-insn.c b/arch/arm64/kernel/probes/decode-insn.c +index 104101f633b10e..492e50a6ddbfc7 100644 +--- a/arch/arm64/kernel/probes/decode-insn.c ++++ b/arch/arm64/kernel/probes/decode-insn.c +@@ -99,10 +99,6 @@ arm_probe_decode_insn(probe_opcode_t insn, struct arch_probe_insn *api) + aarch64_insn_is_blr(insn) || + aarch64_insn_is_ret(insn)) { + api->handler = simulate_br_blr_ret; +- } else if (aarch64_insn_is_ldr_lit(insn)) { +- api->handler = simulate_ldr_literal; +- } else if (aarch64_insn_is_ldrsw_lit(insn)) { +- api->handler = simulate_ldrsw_literal; + } else { + /* + * Instruction cannot be stepped out-of-line and we don't +@@ -140,6 +136,17 @@ arm_kprobe_decode_insn(kprobe_opcode_t *addr, struct arch_specific_insn *asi) + probe_opcode_t insn = le32_to_cpu(*addr); + probe_opcode_t *scan_end = NULL; + unsigned long size = 0, offset = 0; ++ struct arch_probe_insn *api = &asi->api; ++ ++ if (aarch64_insn_is_ldr_lit(insn)) { ++ api->handler = simulate_ldr_literal; ++ decoded = INSN_GOOD_NO_SLOT; ++ } else if (aarch64_insn_is_ldrsw_lit(insn)) { ++ api->handler = simulate_ldrsw_literal; ++ decoded = INSN_GOOD_NO_SLOT; ++ } else { ++ decoded = arm_probe_decode_insn(insn, &asi->api); ++ } + + /* + * If there's a symbol defined in front of and near enough to +@@ -157,7 +164,6 @@ arm_kprobe_decode_insn(kprobe_opcode_t *addr, struct arch_specific_insn *asi) + else + scan_end = addr - MAX_ATOMIC_CONTEXT_SIZE; + } +- decoded = arm_probe_decode_insn(insn, &asi->api); + + if (decoded != INSN_REJECTED && scan_end) + if (is_probed_address_atomic(addr - 1, scan_end)) +diff --git a/arch/arm64/kernel/probes/simulate-insn.c b/arch/arm64/kernel/probes/simulate-insn.c +index 22d0b32524763e..b65334ab79d2b0 100644 +--- a/arch/arm64/kernel/probes/simulate-insn.c ++++ b/arch/arm64/kernel/probes/simulate-insn.c +@@ -171,17 +171,15 @@ simulate_tbz_tbnz(u32 opcode, long addr, struct pt_regs *regs) + void __kprobes + simulate_ldr_literal(u32 opcode, long addr, struct pt_regs *regs) + { +- u64 *load_addr; ++ unsigned long load_addr; + int xn = opcode & 0x1f; +- int disp; + +- disp = ldr_displacement(opcode); +- load_addr = (u64 *) (addr + disp); ++ load_addr = addr + ldr_displacement(opcode); + + if (opcode & (1 << 30)) /* x0-x30 */ +- set_x_reg(regs, xn, *load_addr); ++ set_x_reg(regs, xn, READ_ONCE(*(u64 *)load_addr)); + else /* w0-w30 */ +- set_w_reg(regs, xn, *load_addr); ++ set_w_reg(regs, xn, READ_ONCE(*(u32 *)load_addr)); + + instruction_pointer_set(regs, instruction_pointer(regs) + 4); + } +@@ -189,14 +187,12 @@ simulate_ldr_literal(u32 opcode, long addr, struct pt_regs *regs) + void __kprobes + simulate_ldrsw_literal(u32 opcode, long addr, struct pt_regs *regs) + { +- s32 *load_addr; ++ unsigned long load_addr; + int xn = opcode & 0x1f; +- int disp; + +- disp = ldr_displacement(opcode); +- load_addr = (s32 *) (addr + disp); ++ load_addr = addr + ldr_displacement(opcode); + +- set_x_reg(regs, xn, *load_addr); ++ set_x_reg(regs, xn, READ_ONCE(*(s32 *)load_addr)); + + instruction_pointer_set(regs, instruction_pointer(regs) + 4); + } +diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c +index 9c038c8cebebcf..a756f3f5a230ae 100644 +--- a/arch/powerpc/mm/numa.c ++++ b/arch/powerpc/mm/numa.c +@@ -1162,6 +1162,9 @@ void __init mem_topology_setup(void) + { + int cpu; + ++ max_low_pfn = max_pfn = memblock_end_of_DRAM() >> PAGE_SHIFT; ++ min_low_pfn = MEMORY_START >> PAGE_SHIFT; ++ + /* + * Linux/mm assumes node 0 to be online at boot. However this is not + * true on PowerPC, where node 0 is similar to any other node, it +@@ -1206,9 +1209,6 @@ void __init initmem_init(void) + { + int nid; + +- max_low_pfn = memblock_end_of_DRAM() >> PAGE_SHIFT; +- max_pfn = max_low_pfn; +- + memblock_dump_all(); + + for_each_online_node(nid) { +diff --git a/arch/s390/kvm/diag.c b/arch/s390/kvm/diag.c +index 3c65b8258ae67a..2cc3ec034046c9 100644 +--- a/arch/s390/kvm/diag.c ++++ b/arch/s390/kvm/diag.c +@@ -77,7 +77,7 @@ static int __diag_page_ref_service(struct kvm_vcpu *vcpu) + vcpu->stat.instruction_diagnose_258++; + if (vcpu->run->s.regs.gprs[rx] & 7) + return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); +- rc = read_guest(vcpu, vcpu->run->s.regs.gprs[rx], rx, &parm, sizeof(parm)); ++ rc = read_guest_real(vcpu, vcpu->run->s.regs.gprs[rx], &parm, sizeof(parm)); + if (rc) + return kvm_s390_inject_prog_cond(vcpu, rc); + if (parm.parm_version != 2 || parm.parm_len < 5 || parm.code != 0x258) +diff --git a/arch/x86/entry/entry.S b/arch/x86/entry/entry.S +index 09e99d13fc0b30..f4419afc7147d8 100644 +--- a/arch/x86/entry/entry.S ++++ b/arch/x86/entry/entry.S +@@ -9,6 +9,8 @@ + #include <asm/unwind_hints.h> + #include <asm/segment.h> + #include <asm/cache.h> ++#include <asm/cpufeatures.h> ++#include <asm/nospec-branch.h> + + .pushsection .noinstr.text, "ax" + +@@ -17,6 +19,9 @@ SYM_FUNC_START(entry_ibpb) + movl $PRED_CMD_IBPB, %eax + xorl %edx, %edx + wrmsr ++ ++ /* Make sure IBPB clears return stack preductions too. */ ++ FILL_RETURN_BUFFER %rax, RSB_CLEAR_LOOPS, X86_BUG_IBPB_NO_RET + RET + SYM_FUNC_END(entry_ibpb) + /* For KVM */ +diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S +index ee5def1060c86c..475ce40401ec9b 100644 +--- a/arch/x86/entry/entry_32.S ++++ b/arch/x86/entry/entry_32.S +@@ -902,6 +902,8 @@ SYM_FUNC_START(entry_SYSENTER_32) + + /* Now ready to switch the cr3 */ + SWITCH_TO_USER_CR3 scratch_reg=%eax ++ /* Clobbers ZF */ ++ CLEAR_CPU_BUFFERS + + /* + * Restore all flags except IF. (We restore IF separately because +@@ -912,7 +914,6 @@ SYM_FUNC_START(entry_SYSENTER_32) + BUG_IF_WRONG_CR3 no_user_check=1 + popfl + popl %eax +- CLEAR_CPU_BUFFERS + + /* + * Return back to the vDSO, which will pop ecx and edx. +@@ -1175,7 +1176,6 @@ SYM_CODE_START(asm_exc_nmi) + + /* Not on SYSENTER stack. */ + call exc_nmi +- CLEAR_CPU_BUFFERS + jmp .Lnmi_return + + .Lnmi_from_sysenter_stack: +@@ -1196,6 +1196,7 @@ SYM_CODE_START(asm_exc_nmi) + + CHECK_AND_APPLY_ESPFIX + RESTORE_ALL_NMI cr3_reg=%edi pop=4 ++ CLEAR_CPU_BUFFERS + jmp .Lirq_return + + #ifdef CONFIG_X86_ESPFIX32 +@@ -1237,6 +1238,7 @@ SYM_CODE_START(asm_exc_nmi) + * 1 - orig_ax + */ + lss (1+5+6)*4(%esp), %esp # back to espfix stack ++ CLEAR_CPU_BUFFERS + jmp .Lirq_return + #endif + SYM_CODE_END(asm_exc_nmi) +diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h +index 18817817ea81bf..3c0f7e3324788d 100644 +--- a/arch/x86/include/asm/cpufeatures.h ++++ b/arch/x86/include/asm/cpufeatures.h +@@ -217,7 +217,7 @@ + #define X86_FEATURE_SPEC_STORE_BYPASS_DISABLE ( 7*32+23) /* "" Disable Speculative Store Bypass. */ + #define X86_FEATURE_LS_CFG_SSBD ( 7*32+24) /* "" AMD SSBD implementation via LS_CFG MSR */ + #define X86_FEATURE_IBRS ( 7*32+25) /* Indirect Branch Restricted Speculation */ +-#define X86_FEATURE_IBPB ( 7*32+26) /* Indirect Branch Prediction Barrier */ ++#define X86_FEATURE_IBPB ( 7*32+26) /* "ibpb" Indirect Branch Prediction Barrier without a guaranteed RSB flush */ + #define X86_FEATURE_STIBP ( 7*32+27) /* Single Thread Indirect Branch Predictors */ + #define X86_FEATURE_ZEN ( 7*32+28) /* "" CPU is AMD family 0x17 or above (Zen) */ + #define X86_FEATURE_L1TF_PTEINV ( 7*32+29) /* "" L1TF workaround PTE inversion */ +@@ -329,6 +329,7 @@ + #define X86_FEATURE_VIRT_SSBD (13*32+25) /* Virtualized Speculative Store Bypass Disable */ + #define X86_FEATURE_AMD_SSB_NO (13*32+26) /* "" Speculative Store Bypass is fixed in hardware. */ + #define X86_FEATURE_BTC_NO (13*32+29) /* "" Not vulnerable to Branch Type Confusion */ ++#define X86_FEATURE_AMD_IBPB_RET (13*32+30) /* "" IBPB clears return address predictor */ + + /* Thermal and Power Management Leaf, CPUID level 0x00000006 (EAX), word 14 */ + #define X86_FEATURE_DTHERM (14*32+ 0) /* Digital Thermal Sensor */ +@@ -480,4 +481,5 @@ + #define X86_BUG_DIV0 X86_BUG(1*32 + 1) /* AMD DIV0 speculation bug */ + #define X86_BUG_RFDS X86_BUG(1*32 + 2) /* CPU is vulnerable to Register File Data Sampling */ + #define X86_BUG_BHI X86_BUG(1*32 + 3) /* CPU is affected by Branch History Injection */ ++#define X86_BUG_IBPB_NO_RET X86_BUG(1*32 + 4) /* "ibpb_no_ret" IBPB omits return target predictions */ + #endif /* _ASM_X86_CPUFEATURES_H */ +diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c +index 4df7d694369a54..0baca232125ac3 100644 +--- a/arch/x86/kernel/apic/apic.c ++++ b/arch/x86/kernel/apic/apic.c +@@ -493,7 +493,19 @@ static int lapic_timer_shutdown(struct clock_event_device *evt) + v = apic_read(APIC_LVTT); + v |= (APIC_LVT_MASKED | LOCAL_TIMER_VECTOR); + apic_write(APIC_LVTT, v); +- apic_write(APIC_TMICT, 0); ++ ++ /* ++ * Setting APIC_LVT_MASKED (above) should be enough to tell ++ * the hardware that this timer will never fire. But AMD ++ * erratum 411 and some Intel CPU behavior circa 2024 say ++ * otherwise. Time for belt and suspenders programming: mask ++ * the timer _and_ zero the counter registers: ++ */ ++ if (v & APIC_LVT_TIMER_TSCDEADLINE) ++ wrmsrl(MSR_IA32_TSC_DEADLINE, 0); ++ else ++ apic_write(APIC_TMICT, 0); ++ + return 0; + } + +diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c +index 247545b57dff6b..f84d59cd180b36 100644 +--- a/arch/x86/kernel/cpu/bugs.c ++++ b/arch/x86/kernel/cpu/bugs.c +@@ -1092,7 +1092,24 @@ static void __init retbleed_select_mitigation(void) + + case RETBLEED_MITIGATION_IBPB: + setup_force_cpu_cap(X86_FEATURE_ENTRY_IBPB); ++ ++ /* ++ * IBPB on entry already obviates the need for ++ * software-based untraining so clear those in case some ++ * other mitigation like SRSO has selected them. ++ */ ++ setup_clear_cpu_cap(X86_FEATURE_UNRET); ++ setup_clear_cpu_cap(X86_FEATURE_RETHUNK); ++ + mitigate_smt = true; ++ ++ /* ++ * There is no need for RSB filling: entry_ibpb() ensures ++ * all predictions, including the RSB, are invalidated, ++ * regardless of IBPB implementation. ++ */ ++ setup_clear_cpu_cap(X86_FEATURE_RSB_VMEXIT); ++ + break; + + default: +@@ -2591,6 +2608,14 @@ static void __init srso_select_mitigation(void) + if (has_microcode) { + setup_force_cpu_cap(X86_FEATURE_ENTRY_IBPB); + srso_mitigation = SRSO_MITIGATION_IBPB; ++ ++ /* ++ * IBPB on entry already obviates the need for ++ * software-based untraining so clear those in case some ++ * other mitigation like Retbleed has selected them. ++ */ ++ setup_clear_cpu_cap(X86_FEATURE_UNRET); ++ setup_clear_cpu_cap(X86_FEATURE_RETHUNK); + } + } else { + pr_err("WARNING: kernel not compiled with CPU_IBPB_ENTRY.\n"); +@@ -2603,6 +2628,13 @@ static void __init srso_select_mitigation(void) + if (!boot_cpu_has(X86_FEATURE_ENTRY_IBPB) && has_microcode) { + setup_force_cpu_cap(X86_FEATURE_IBPB_ON_VMEXIT); + srso_mitigation = SRSO_MITIGATION_IBPB_ON_VMEXIT; ++ ++ /* ++ * There is no need for RSB filling: entry_ibpb() ensures ++ * all predictions, including the RSB, are invalidated, ++ * regardless of IBPB implementation. ++ */ ++ setup_clear_cpu_cap(X86_FEATURE_RSB_VMEXIT); + } + } else { + pr_err("WARNING: kernel not compiled with CPU_SRSO.\n"); +diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c +index 809e12f130d88f..f0cc4c616ceb34 100644 +--- a/arch/x86/kernel/cpu/common.c ++++ b/arch/x86/kernel/cpu/common.c +@@ -1352,6 +1352,9 @@ static void __init cpu_set_bug_bits(struct cpuinfo_x86 *c) + boot_cpu_has(X86_FEATURE_HYPERVISOR))) + setup_force_cpu_bug(X86_BUG_BHI); + ++ if (cpu_has(c, X86_FEATURE_AMD_IBPB) && !cpu_has(c, X86_FEATURE_AMD_IBPB_RET)) ++ setup_force_cpu_bug(X86_BUG_IBPB_NO_RET); ++ + if (cpu_matches(cpu_vuln_whitelist, NO_MELTDOWN)) + return; + +diff --git a/arch/x86/kernel/cpu/resctrl/core.c b/arch/x86/kernel/cpu/resctrl/core.c +index a5c51a14fbce8b..d852ee9ea221fb 100644 +--- a/arch/x86/kernel/cpu/resctrl/core.c ++++ b/arch/x86/kernel/cpu/resctrl/core.c +@@ -175,7 +175,7 @@ static inline bool rdt_get_mb_table(struct rdt_resource *r) + return false; + } + +-static bool __get_mem_config_intel(struct rdt_resource *r) ++static __init bool __get_mem_config_intel(struct rdt_resource *r) + { + struct rdt_hw_resource *hw_res = resctrl_to_arch_res(r); + union cpuid_0x10_3_eax eax; +@@ -210,7 +210,7 @@ static bool __get_mem_config_intel(struct rdt_resource *r) + return true; + } + +-static bool __rdt_get_mem_config_amd(struct rdt_resource *r) ++static __init bool __rdt_get_mem_config_amd(struct rdt_resource *r) + { + struct rdt_hw_resource *hw_res = resctrl_to_arch_res(r); + union cpuid_0x10_3_eax eax; +diff --git a/block/blk-rq-qos.c b/block/blk-rq-qos.c +index e83af7bc759194..26dd3e7bd00d3e 100644 +--- a/block/blk-rq-qos.c ++++ b/block/blk-rq-qos.c +@@ -225,8 +225,8 @@ static int rq_qos_wake_function(struct wait_queue_entry *curr, + + data->got_token = true; + smp_wmb(); +- list_del_init(&curr->entry); + wake_up_process(data->task); ++ list_del_init_careful(&curr->entry); + return 1; + } + +diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c +index fcd1910825b65d..4a5b937f78d918 100644 +--- a/drivers/bluetooth/btusb.c ++++ b/drivers/bluetooth/btusb.c +@@ -1002,10 +1002,15 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags) + if (!urb) + return -ENOMEM; + +- /* Use maximum HCI Event size so the USB stack handles +- * ZPL/short-transfer automatically. +- */ +- size = HCI_MAX_EVENT_SIZE; ++ if (le16_to_cpu(data->udev->descriptor.idVendor) == 0x0a12 && ++ le16_to_cpu(data->udev->descriptor.idProduct) == 0x0001) ++ /* Fake CSR devices don't seem to support sort-transter */ ++ size = le16_to_cpu(data->intr_ep->wMaxPacketSize); ++ else ++ /* Use maximum HCI Event size so the USB stack handles ++ * ZPL/short-transfer automatically. ++ */ ++ size = HCI_MAX_EVENT_SIZE; + + buf = kmalloc(size, mem_flags); + if (!buf) { +diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c +index 54f1ab3071f988..eb486c9c957570 100644 +--- a/drivers/gpu/drm/drm_gem_shmem_helper.c ++++ b/drivers/gpu/drm/drm_gem_shmem_helper.c +@@ -607,6 +607,9 @@ int drm_gem_shmem_mmap(struct drm_gem_shmem_object *shmem, struct vm_area_struct + return ret; + } + ++ if (is_cow_mapping(vma->vm_flags)) ++ return -EINVAL; ++ + ret = drm_gem_shmem_get_pages(shmem); + if (ret) + return ret; +diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c +index 46549d5179ee99..d49a6d4a430e2d 100644 +--- a/drivers/gpu/drm/radeon/radeon_encoders.c ++++ b/drivers/gpu/drm/radeon/radeon_encoders.c +@@ -41,7 +41,7 @@ static uint32_t radeon_encoder_clones(struct drm_encoder *encoder) + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_encoder *clone_encoder; +- uint32_t index_mask = 0; ++ uint32_t index_mask = drm_encoder_mask(encoder); + int count; + + /* DIG routing gets problematic */ +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +index e2a52b5de1733e..23010d60edfe4d 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +@@ -1417,6 +1417,7 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, + DRM_ERROR("Surface size cannot exceed %dx%d\n", + dev_priv->texture_max_width, + dev_priv->texture_max_height); ++ ret = -EINVAL; + goto err_out; + } + +diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig +index 86b83dc7b7d99b..1dca0e82b2e872 100644 +--- a/drivers/iio/adc/Kconfig ++++ b/drivers/iio/adc/Kconfig +@@ -1131,6 +1131,8 @@ config TI_ADS8344 + config TI_ADS8688 + tristate "Texas Instruments ADS8688" + depends on SPI && OF ++ select IIO_BUFFER ++ select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for Texas Instruments ADS8684 and + and ADS8688 ADC chips +@@ -1141,6 +1143,8 @@ config TI_ADS8688 + config TI_ADS124S08 + tristate "Texas Instruments ADS124S08" + depends on SPI && OF ++ select IIO_BUFFER ++ select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for Texas Instruments ADS124S08 + and ADS124S06 ADC chips +diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +index a4ec11a3b68a72..82ab168cc13eab 100644 +--- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c ++++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +@@ -32,7 +32,7 @@ static ssize_t _hid_sensor_set_report_latency(struct device *dev, + latency = integer * 1000 + fract / 1000; + ret = hid_sensor_set_report_latency(attrb, latency); + if (ret < 0) +- return len; ++ return ret; + + attrb->latency_ms = hid_sensor_get_report_latency(attrb); + +diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig +index 75e1f2b486388d..79a4fe015e98f9 100644 +--- a/drivers/iio/dac/Kconfig ++++ b/drivers/iio/dac/Kconfig +@@ -203,6 +203,7 @@ config AD5766 + config AD5770R + tristate "Analog Devices AD5770R IDAC driver" + depends on SPI_MASTER ++ select REGMAP_SPI + help + Say yes here to build support for Analog Devices AD5770R Digital to + Analog Converter. +@@ -283,6 +284,7 @@ config LPC18XX_DAC + config LTC1660 + tristate "Linear Technology LTC1660/LTC1665 DAC SPI driver" + depends on SPI ++ select REGMAP_SPI + help + Say yes here to build support for Linear Technology + LTC1660 and LTC1665 Digital to Analog Converters. +@@ -369,6 +371,7 @@ config STM32_DAC + + config STM32_DAC_CORE + tristate ++ select REGMAP_MMIO + + config TI_DAC082S085 + tristate "Texas Instruments 8/10/12-bit 2/4-channel DAC driver" +diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c +index 1880bd5bb2586c..f2462121aa0456 100644 +--- a/drivers/iio/light/opt3001.c ++++ b/drivers/iio/light/opt3001.c +@@ -138,6 +138,10 @@ static const struct opt3001_scale opt3001_scales[] = { + .val = 20966, + .val2 = 400000, + }, ++ { ++ .val = 41932, ++ .val2 = 800000, ++ }, + { + .val = 83865, + .val2 = 600000, +diff --git a/drivers/iio/light/veml6030.c b/drivers/iio/light/veml6030.c +index 3c937c55a10d89..9b9697d67850be 100644 +--- a/drivers/iio/light/veml6030.c ++++ b/drivers/iio/light/veml6030.c +@@ -99,9 +99,8 @@ static const char * const period_values[] = { + static ssize_t in_illuminance_period_available_show(struct device *dev, + struct device_attribute *attr, char *buf) + { ++ struct veml6030_data *data = iio_priv(dev_to_iio_dev(dev)); + int ret, reg, x; +- struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); +- struct veml6030_data *data = iio_priv(indio_dev); + + ret = regmap_read(data->regmap, VEML6030_REG_ALS_CONF, ®); + if (ret) { +@@ -780,7 +779,7 @@ static int veml6030_hw_init(struct iio_dev *indio_dev) + + /* Cache currently active measurement parameters */ + data->cur_gain = 3; +- data->cur_resolution = 4608; ++ data->cur_resolution = 5376; + data->cur_integration_time = 3; + + return ret; +diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig +index 7c7203ca3ac638..1449fd2016b4dd 100644 +--- a/drivers/iio/proximity/Kconfig ++++ b/drivers/iio/proximity/Kconfig +@@ -60,6 +60,8 @@ config LIDAR_LITE_V2 + config MB1232 + tristate "MaxSonar I2CXL family ultrasonic sensors" + depends on I2C ++ select IIO_BUFFER ++ select IIO_TRIGGERED_BUFFER + help + Say Y to build a driver for the ultrasonic sensors I2CXL of + MaxBotix which have an i2c interface. It can be used to measure +diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c +index acb870a877ec0d..1d2e3c12dc814e 100644 +--- a/drivers/iommu/intel/iommu.c ++++ b/drivers/iommu/intel/iommu.c +@@ -4465,8 +4465,10 @@ static int domain_context_clear_one_cb(struct pci_dev *pdev, u16 alias, void *op + */ + static void domain_context_clear(struct device_domain_info *info) + { +- if (!dev_is_pci(info->dev)) ++ if (!dev_is_pci(info->dev)) { + domain_context_clear_one(info, info->bus, info->devfn); ++ return; ++ } + + pci_for_each_dma_alias(to_pci_dev(info->dev), + &domain_context_clear_one_cb, info); +diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c +index 3fa6c718432612..567d758503fbc8 100644 +--- a/drivers/irqchip/irq-gic-v3-its.c ++++ b/drivers/irqchip/irq-gic-v3-its.c +@@ -779,6 +779,7 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its, + struct its_cmd_block *cmd, + struct its_cmd_desc *desc) + { ++ struct its_vpe *vpe = valid_vpe(its, desc->its_vmapp_cmd.vpe); + unsigned long vpt_addr, vconf_addr; + u64 target; + bool alloc; +@@ -788,9 +789,14 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its, + its_encode_valid(cmd, desc->its_vmapp_cmd.valid); + + if (!desc->its_vmapp_cmd.valid) { ++ alloc = !atomic_dec_return(&desc->its_vmapp_cmd.vpe->vmapp_count); + if (is_v4_1(its)) { +- alloc = !atomic_dec_return(&desc->its_vmapp_cmd.vpe->vmapp_count); + its_encode_alloc(cmd, alloc); ++ /* ++ * Unmapping a VPE is self-synchronizing on GICv4.1, ++ * no need to issue a VSYNC. ++ */ ++ vpe = NULL; + } + + goto out; +@@ -803,13 +809,13 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its, + its_encode_vpt_addr(cmd, vpt_addr); + its_encode_vpt_size(cmd, LPI_NRBITS - 1); + ++ alloc = !atomic_fetch_inc(&desc->its_vmapp_cmd.vpe->vmapp_count); ++ + if (!is_v4_1(its)) + goto out; + + vconf_addr = virt_to_phys(page_address(desc->its_vmapp_cmd.vpe->its_vm->vprop_page)); + +- alloc = !atomic_fetch_inc(&desc->its_vmapp_cmd.vpe->vmapp_count); +- + its_encode_alloc(cmd, alloc); + + /* +@@ -825,7 +831,7 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its, + out: + its_fixup_cmd(cmd); + +- return valid_vpe(its, desc->its_vmapp_cmd.vpe); ++ return vpe; + } + + static struct its_vpe *its_build_vmapti_cmd(struct its_node *its, +@@ -3804,6 +3810,13 @@ static int its_vpe_set_affinity(struct irq_data *d, + unsigned long flags; + int from, cpu; + ++ /* ++ * Check if we're racing against a VPE being destroyed, for ++ * which we don't want to allow a VMOVP. ++ */ ++ if (!atomic_read(&vpe->vmapp_count)) ++ return -EINVAL; ++ + /* + * Changing affinity is mega expensive, so let's be as lazy as + * we can and only do it if we really have to. Also, if mapped +@@ -4440,9 +4453,8 @@ static int its_vpe_init(struct its_vpe *vpe) + raw_spin_lock_init(&vpe->vpe_lock); + vpe->vpe_id = vpe_id; + vpe->vpt_page = vpt_page; +- if (gic_rdists->has_rvpeid) +- atomic_set(&vpe->vmapp_count, 0); +- else ++ atomic_set(&vpe->vmapp_count, 0); ++ if (!gic_rdists->has_rvpeid) + vpe->vpe_proxy_event = -1; + + return 0; +diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c +index a3db76dcdb3ec9..6e392f3cbd3731 100644 +--- a/drivers/md/dm-crypt.c ++++ b/drivers/md/dm-crypt.c +@@ -69,10 +69,8 @@ struct dm_crypt_io { + struct bio *base_bio; + u8 *integrity_metadata; + bool integrity_metadata_from_pool:1; +- bool in_tasklet:1; + + struct work_struct work; +- struct tasklet_struct tasklet; + + struct convert_context ctx; + +@@ -1725,7 +1723,6 @@ static void crypt_io_init(struct dm_crypt_io *io, struct crypt_config *cc, + io->ctx.r.req = NULL; + io->integrity_metadata = NULL; + io->integrity_metadata_from_pool = false; +- io->in_tasklet = false; + atomic_set(&io->io_pending, 0); + } + +@@ -1734,12 +1731,6 @@ static void crypt_inc_pending(struct dm_crypt_io *io) + atomic_inc(&io->io_pending); + } + +-static void kcryptd_io_bio_endio(struct work_struct *work) +-{ +- struct dm_crypt_io *io = container_of(work, struct dm_crypt_io, work); +- bio_endio(io->base_bio); +-} +- + /* + * One of the bios was finished. Check for completion of + * the whole request and correctly clean up the buffer. +@@ -1763,20 +1754,6 @@ static void crypt_dec_pending(struct dm_crypt_io *io) + + base_bio->bi_status = error; + +- /* +- * If we are running this function from our tasklet, +- * we can't call bio_endio() here, because it will call +- * clone_endio() from dm.c, which in turn will +- * free the current struct dm_crypt_io structure with +- * our tasklet. In this case we need to delay bio_endio() +- * execution to after the tasklet is done and dequeued. +- */ +- if (io->in_tasklet) { +- INIT_WORK(&io->work, kcryptd_io_bio_endio); +- queue_work(cc->io_queue, &io->work); +- return; +- } +- + bio_endio(base_bio); + } + +@@ -2220,11 +2197,6 @@ static void kcryptd_crypt(struct work_struct *work) + kcryptd_crypt_write_convert(io); + } + +-static void kcryptd_crypt_tasklet(unsigned long work) +-{ +- kcryptd_crypt((struct work_struct *)work); +-} +- + static void kcryptd_queue_crypt(struct dm_crypt_io *io) + { + struct crypt_config *cc = io->cc; +@@ -2236,15 +2208,10 @@ static void kcryptd_queue_crypt(struct dm_crypt_io *io) + * irqs_disabled(): the kernel may run some IO completion from the idle thread, but + * it is being executed with irqs disabled. + */ +- if (in_hardirq() || irqs_disabled()) { +- io->in_tasklet = true; +- tasklet_init(&io->tasklet, kcryptd_crypt_tasklet, (unsigned long)&io->work); +- tasklet_schedule(&io->tasklet); ++ if (!(in_hardirq() || irqs_disabled())) { ++ kcryptd_crypt(&io->work); + return; + } +- +- kcryptd_crypt(&io->work); +- return; + } + + INIT_WORK(&io->work, kcryptd_crypt); +diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c +index dac56169851a2a..4e7359657941d8 100644 +--- a/drivers/net/ethernet/cadence/macb_main.c ++++ b/drivers/net/ethernet/cadence/macb_main.c +@@ -902,9 +902,6 @@ static int macb_mdiobus_register(struct macb *bp) + { + struct device_node *child, *np = bp->pdev->dev.of_node; + +- if (of_phy_is_fixed_link(np)) +- return mdiobus_register(bp->mii_bus); +- + /* Only create the PHY from the device tree if at least one PHY is + * described. Otherwise scan the entire MDIO bus. We do this to support + * old device tree that did not follow the best practices and did not +@@ -925,8 +922,19 @@ static int macb_mdiobus_register(struct macb *bp) + + static int macb_mii_init(struct macb *bp) + { ++ struct device_node *child, *np = bp->pdev->dev.of_node; + int err = -ENXIO; + ++ /* With fixed-link, we don't need to register the MDIO bus, ++ * except if we have a child named "mdio" in the device tree. ++ * In that case, some devices may be attached to the MACB's MDIO bus. ++ */ ++ child = of_get_child_by_name(np, "mdio"); ++ if (child) ++ of_node_put(child); ++ else if (of_phy_is_fixed_link(np)) ++ return macb_mii_probe(bp->dev); ++ + /* Enable management port */ + macb_writel(bp, NCR, MACB_BIT(MPE)); + +diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c +index 9e063eb17f527d..b51d54799669b9 100644 +--- a/drivers/net/ethernet/freescale/enetc/enetc.c ++++ b/drivers/net/ethernet/freescale/enetc/enetc.c +@@ -1223,7 +1223,6 @@ static void enetc_xdp_drop(struct enetc_bdr *rx_ring, int rx_ring_first, + &rx_ring->rx_swbd[rx_ring_first]); + enetc_bdr_idx_inc(rx_ring, &rx_ring_first); + } +- rx_ring->stats.xdp_drops++; + } + + static int enetc_clean_rx_ring_xdp(struct enetc_bdr *rx_ring, +@@ -1280,6 +1279,7 @@ static int enetc_clean_rx_ring_xdp(struct enetc_bdr *rx_ring, + fallthrough; + case XDP_DROP: + enetc_xdp_drop(rx_ring, orig_i, i); ++ rx_ring->stats.xdp_drops++; + break; + case XDP_PASS: + rxbd = orig_rxbd; +diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c +index 8400a379186ea0..180376c09cb41b 100644 +--- a/drivers/parport/procfs.c ++++ b/drivers/parport/procfs.c +@@ -51,12 +51,12 @@ static int do_active_device(struct ctl_table *table, int write, + + for (dev = port->devices; dev ; dev = dev->next) { + if(dev == port->cad) { +- len += snprintf(buffer, sizeof(buffer), "%s\n", dev->name); ++ len += scnprintf(buffer, sizeof(buffer), "%s\n", dev->name); + } + } + + if(!len) { +- len += snprintf(buffer, sizeof(buffer), "%s\n", "none"); ++ len += scnprintf(buffer, sizeof(buffer), "%s\n", "none"); + } + + if (len > *lenp) +@@ -87,19 +87,19 @@ static int do_autoprobe(struct ctl_table *table, int write, + } + + if ((str = info->class_name) != NULL) +- len += snprintf (buffer + len, sizeof(buffer) - len, "CLASS:%s;\n", str); ++ len += scnprintf (buffer + len, sizeof(buffer) - len, "CLASS:%s;\n", str); + + if ((str = info->model) != NULL) +- len += snprintf (buffer + len, sizeof(buffer) - len, "MODEL:%s;\n", str); ++ len += scnprintf (buffer + len, sizeof(buffer) - len, "MODEL:%s;\n", str); + + if ((str = info->mfr) != NULL) +- len += snprintf (buffer + len, sizeof(buffer) - len, "MANUFACTURER:%s;\n", str); ++ len += scnprintf (buffer + len, sizeof(buffer) - len, "MANUFACTURER:%s;\n", str); + + if ((str = info->description) != NULL) +- len += snprintf (buffer + len, sizeof(buffer) - len, "DESCRIPTION:%s;\n", str); ++ len += scnprintf (buffer + len, sizeof(buffer) - len, "DESCRIPTION:%s;\n", str); + + if ((str = info->cmdset) != NULL) +- len += snprintf (buffer + len, sizeof(buffer) - len, "COMMAND SET:%s;\n", str); ++ len += scnprintf (buffer + len, sizeof(buffer) - len, "COMMAND SET:%s;\n", str); + + if (len > *lenp) + len = *lenp; +@@ -128,7 +128,7 @@ static int do_hardware_base_addr(struct ctl_table *table, int write, + if (write) /* permissions prevent this anyway */ + return -EACCES; + +- len += snprintf (buffer, sizeof(buffer), "%lu\t%lu\n", port->base, port->base_hi); ++ len += scnprintf (buffer, sizeof(buffer), "%lu\t%lu\n", port->base, port->base_hi); + + if (len > *lenp) + len = *lenp; +@@ -155,7 +155,7 @@ static int do_hardware_irq(struct ctl_table *table, int write, + if (write) /* permissions prevent this anyway */ + return -EACCES; + +- len += snprintf (buffer, sizeof(buffer), "%d\n", port->irq); ++ len += scnprintf (buffer, sizeof(buffer), "%d\n", port->irq); + + if (len > *lenp) + len = *lenp; +@@ -182,7 +182,7 @@ static int do_hardware_dma(struct ctl_table *table, int write, + if (write) /* permissions prevent this anyway */ + return -EACCES; + +- len += snprintf (buffer, sizeof(buffer), "%d\n", port->dma); ++ len += scnprintf (buffer, sizeof(buffer), "%d\n", port->dma); + + if (len > *lenp) + len = *lenp; +@@ -213,7 +213,7 @@ static int do_hardware_modes(struct ctl_table *table, int write, + #define printmode(x) \ + do { \ + if (port->modes & PARPORT_MODE_##x) \ +- len += snprintf(buffer + len, sizeof(buffer) - len, "%s%s", f++ ? "," : "", #x); \ ++ len += scnprintf(buffer + len, sizeof(buffer) - len, "%s%s", f++ ? "," : "", #x); \ + } while (0) + int f = 0; + printmode(PCSPP); +diff --git a/drivers/pinctrl/pinctrl-ocelot.c b/drivers/pinctrl/pinctrl-ocelot.c +index b14f1b7a625ec2..9f1b88fa33b47d 100644 +--- a/drivers/pinctrl/pinctrl-ocelot.c ++++ b/drivers/pinctrl/pinctrl-ocelot.c +@@ -1279,21 +1279,21 @@ static void ocelot_irq_handler(struct irq_desc *desc) + unsigned int reg = 0, irq, i; + unsigned long irqs; + ++ chained_irq_enter(parent_chip, desc); ++ + for (i = 0; i < info->stride; i++) { + regmap_read(info->map, id_reg + 4 * i, ®); + if (!reg) + continue; + +- chained_irq_enter(parent_chip, desc); +- + irqs = reg; + + for_each_set_bit(irq, &irqs, + min(32U, info->desc->npins - 32 * i)) + generic_handle_domain_irq(chip->irq.domain, irq + 32 * i); +- +- chained_irq_exit(parent_chip, desc); + } ++ ++ chained_irq_exit(parent_chip, desc); + } + + static int ocelot_gpiochip_register(struct platform_device *pdev, +diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c +index 29a6a0099f8317..7bf9a0a87a0093 100644 +--- a/drivers/s390/char/sclp_vt220.c ++++ b/drivers/s390/char/sclp_vt220.c +@@ -320,7 +320,7 @@ sclp_vt220_add_msg(struct sclp_vt220_request *request, + buffer = (void *) ((addr_t) sccb + sccb->header.length); + + if (convertlf) { +- /* Perform Linefeed conversion (0x0a -> 0x0a 0x0d)*/ ++ /* Perform Linefeed conversion (0x0a -> 0x0d 0x0a)*/ + for (from=0, to=0; + (from < count) && (to < sclp_vt220_space_left(request)); + from++) { +@@ -329,8 +329,8 @@ sclp_vt220_add_msg(struct sclp_vt220_request *request, + /* Perform conversion */ + if (c == 0x0a) { + if (to + 1 < sclp_vt220_space_left(request)) { +- ((unsigned char *) buffer)[to++] = c; + ((unsigned char *) buffer)[to++] = 0x0d; ++ ((unsigned char *) buffer)[to++] = c; + } else + break; + +diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c +index f12d30a3307ced..15ffa16ba44739 100644 +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -1013,7 +1013,7 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) + td_to_noop(xhci, ring, cached_td, false); + cached_td->cancel_status = TD_CLEARED; + } +- ++ td_to_noop(xhci, ring, td, false); + td->cancel_status = TD_CLEARING_CACHE; + cached_td = td; + break; +diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h +index 977b3fdf4fb592..f2133f2a1767af 100644 +--- a/drivers/usb/host/xhci.h ++++ b/drivers/usb/host/xhci.h +@@ -1286,7 +1286,7 @@ enum xhci_setup_dev { + /* Set TR Dequeue Pointer command TRB fields, 6.4.3.9 */ + #define TRB_TO_STREAM_ID(p) ((((p) & (0xffff << 16)) >> 16)) + #define STREAM_ID_FOR_TRB(p) ((((p)) & 0xffff) << 16) +-#define SCT_FOR_TRB(p) (((p) << 1) & 0x7) ++#define SCT_FOR_TRB(p) (((p) & 0x7) << 1) + + /* Link TRB specific fields */ + #define TRB_TC (1<<1) +diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c +index d34458f11d825c..9d2c8dc14945dc 100644 +--- a/drivers/usb/serial/option.c ++++ b/drivers/usb/serial/option.c +@@ -279,6 +279,7 @@ static void option_instat_callback(struct urb *urb); + #define QUECTEL_PRODUCT_EG912Y 0x6001 + #define QUECTEL_PRODUCT_EC200S_CN 0x6002 + #define QUECTEL_PRODUCT_EC200A 0x6005 ++#define QUECTEL_PRODUCT_EG916Q 0x6007 + #define QUECTEL_PRODUCT_EM061K_LWW 0x6008 + #define QUECTEL_PRODUCT_EM061K_LCN 0x6009 + #define QUECTEL_PRODUCT_EC200T 0x6026 +@@ -1270,6 +1271,7 @@ static const struct usb_device_id option_ids[] = { + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200S_CN, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200T, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG912Y, 0xff, 0, 0) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG916Q, 0xff, 0x00, 0x00) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500K, 0xff, 0x00, 0x00) }, + + { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6001) }, +@@ -1380,10 +1382,16 @@ static const struct usb_device_id option_ids[] = { + .driver_info = NCTRL(0) | RSVD(1) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a0, 0xff), /* Telit FN20C04 (rmnet) */ + .driver_info = RSVD(0) | NCTRL(3) }, ++ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a2, 0xff), /* Telit FN920C04 (MBIM) */ ++ .driver_info = NCTRL(4) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a4, 0xff), /* Telit FN20C04 (rmnet) */ + .driver_info = RSVD(0) | NCTRL(3) }, ++ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a7, 0xff), /* Telit FN920C04 (MBIM) */ ++ .driver_info = NCTRL(4) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a9, 0xff), /* Telit FN20C04 (rmnet) */ + .driver_info = RSVD(0) | NCTRL(2) | RSVD(3) | RSVD(4) }, ++ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10aa, 0xff), /* Telit FN920C04 (MBIM) */ ++ .driver_info = NCTRL(3) | RSVD(4) | RSVD(5) }, + { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910), + .driver_info = NCTRL(0) | RSVD(1) | RSVD(3) }, + { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910_DUAL_MODEM), +diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c +index 5369d82e0bfb56..e69e2a4f99b928 100644 +--- a/fs/fat/namei_vfat.c ++++ b/fs/fat/namei_vfat.c +@@ -1020,7 +1020,7 @@ static int vfat_rename(struct user_namespace *mnt_userns, struct inode *old_dir, + if (corrupt < 0) { + fat_fs_error(new_dir->i_sb, + "%s: Filesystem corrupted (i_pos %lld)", +- __func__, sinfo.i_pos); ++ __func__, new_i_pos); + } + goto out; + } +diff --git a/fs/nilfs2/dir.c b/fs/nilfs2/dir.c +index 5c0e280c83eeae..365cae5c3e3519 100644 +--- a/fs/nilfs2/dir.c ++++ b/fs/nilfs2/dir.c +@@ -331,6 +331,8 @@ static int nilfs_readdir(struct file *file, struct dir_context *ctx) + * returns the page in which the entry was found, and the entry itself + * (as a parameter - res_dir). Page is returned mapped and unlocked. + * Entry is guaranteed to be valid. ++ * ++ * On failure, returns an error pointer and the caller should ignore res_page. + */ + struct nilfs_dir_entry * + nilfs_find_entry(struct inode *dir, const struct qstr *qstr, +@@ -358,22 +360,24 @@ nilfs_find_entry(struct inode *dir, const struct qstr *qstr, + do { + char *kaddr = nilfs_get_page(dir, n, &page); + +- if (!IS_ERR(kaddr)) { +- de = (struct nilfs_dir_entry *)kaddr; +- kaddr += nilfs_last_byte(dir, n) - reclen; +- while ((char *) de <= kaddr) { +- if (de->rec_len == 0) { +- nilfs_error(dir->i_sb, +- "zero-length directory entry"); +- nilfs_put_page(page); +- goto out; +- } +- if (nilfs_match(namelen, name, de)) +- goto found; +- de = nilfs_next_entry(de); ++ if (IS_ERR(kaddr)) ++ return ERR_CAST(kaddr); ++ ++ de = (struct nilfs_dir_entry *)kaddr; ++ kaddr += nilfs_last_byte(dir, n) - reclen; ++ while ((char *)de <= kaddr) { ++ if (de->rec_len == 0) { ++ nilfs_error(dir->i_sb, ++ "zero-length directory entry"); ++ nilfs_put_page(page); ++ goto out; + } +- nilfs_put_page(page); ++ if (nilfs_match(namelen, name, de)) ++ goto found; ++ de = nilfs_next_entry(de); + } ++ nilfs_put_page(page); ++ + if (++n >= npages) + n = 0; + /* next page is past the blocks we've got */ +@@ -386,7 +390,7 @@ nilfs_find_entry(struct inode *dir, const struct qstr *qstr, + } + } while (n != start); + out: +- return NULL; ++ return ERR_PTR(-ENOENT); + + found: + *res_page = page; +@@ -431,19 +435,19 @@ struct nilfs_dir_entry *nilfs_dotdot(struct inode *dir, struct page **p) + return NULL; + } + +-ino_t nilfs_inode_by_name(struct inode *dir, const struct qstr *qstr) ++int nilfs_inode_by_name(struct inode *dir, const struct qstr *qstr, ino_t *ino) + { +- ino_t res = 0; + struct nilfs_dir_entry *de; + struct page *page; + + de = nilfs_find_entry(dir, qstr, &page); +- if (de) { +- res = le64_to_cpu(de->inode); +- kunmap(page); +- put_page(page); +- } +- return res; ++ if (IS_ERR(de)) ++ return PTR_ERR(de); ++ ++ *ino = le64_to_cpu(de->inode); ++ kunmap(page); ++ put_page(page); ++ return 0; + } + + /* Releases the page */ +diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c +index 91eebeb0c48b4c..af3a74bd0fbd48 100644 +--- a/fs/nilfs2/namei.c ++++ b/fs/nilfs2/namei.c +@@ -55,12 +55,20 @@ nilfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) + { + struct inode *inode; + ino_t ino; ++ int res; + + if (dentry->d_name.len > NILFS_NAME_LEN) + return ERR_PTR(-ENAMETOOLONG); + +- ino = nilfs_inode_by_name(dir, &dentry->d_name); +- inode = ino ? nilfs_iget(dir->i_sb, NILFS_I(dir)->i_root, ino) : NULL; ++ res = nilfs_inode_by_name(dir, &dentry->d_name, &ino); ++ if (res) { ++ if (res != -ENOENT) ++ return ERR_PTR(res); ++ inode = NULL; ++ } else { ++ inode = nilfs_iget(dir->i_sb, NILFS_I(dir)->i_root, ino); ++ } ++ + return d_splice_alias(inode, dentry); + } + +@@ -263,10 +271,11 @@ static int nilfs_do_unlink(struct inode *dir, struct dentry *dentry) + struct page *page; + int err; + +- err = -ENOENT; + de = nilfs_find_entry(dir, &dentry->d_name, &page); +- if (!de) ++ if (IS_ERR(de)) { ++ err = PTR_ERR(de); + goto out; ++ } + + inode = d_inode(dentry); + err = -EIO; +@@ -361,10 +370,11 @@ static int nilfs_rename(struct user_namespace *mnt_userns, + if (unlikely(err)) + return err; + +- err = -ENOENT; + old_de = nilfs_find_entry(old_dir, &old_dentry->d_name, &old_page); +- if (!old_de) ++ if (IS_ERR(old_de)) { ++ err = PTR_ERR(old_de); + goto out; ++ } + + if (S_ISDIR(old_inode->i_mode)) { + err = -EIO; +@@ -381,10 +391,12 @@ static int nilfs_rename(struct user_namespace *mnt_userns, + if (dir_de && !nilfs_empty_dir(new_inode)) + goto out_dir; + +- err = -ENOENT; +- new_de = nilfs_find_entry(new_dir, &new_dentry->d_name, &new_page); +- if (!new_de) ++ new_de = nilfs_find_entry(new_dir, &new_dentry->d_name, ++ &new_page); ++ if (IS_ERR(new_de)) { ++ err = PTR_ERR(new_de); + goto out_dir; ++ } + nilfs_set_link(new_dir, new_de, new_page, old_inode); + nilfs_mark_inode_dirty(new_dir); + new_inode->i_ctime = current_time(new_inode); +@@ -438,13 +450,14 @@ static int nilfs_rename(struct user_namespace *mnt_userns, + */ + static struct dentry *nilfs_get_parent(struct dentry *child) + { +- unsigned long ino; ++ ino_t ino; ++ int res; + struct inode *inode; + struct nilfs_root *root; + +- ino = nilfs_inode_by_name(d_inode(child), &dotdot_name); +- if (!ino) +- return ERR_PTR(-ENOENT); ++ res = nilfs_inode_by_name(d_inode(child), &dotdot_name, &ino); ++ if (res) ++ return ERR_PTR(res); + + root = NILFS_I(d_inode(child))->i_root; + +diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h +index a4491d29ad5705..935fb5234c24d5 100644 +--- a/fs/nilfs2/nilfs.h ++++ b/fs/nilfs2/nilfs.h +@@ -233,7 +233,7 @@ static inline __u32 nilfs_mask_flags(umode_t mode, __u32 flags) + + /* dir.c */ + extern int nilfs_add_link(struct dentry *, struct inode *); +-extern ino_t nilfs_inode_by_name(struct inode *, const struct qstr *); ++int nilfs_inode_by_name(struct inode *dir, const struct qstr *qstr, ino_t *ino); + extern int nilfs_make_empty(struct inode *, struct inode *); + extern struct nilfs_dir_entry * + nilfs_find_entry(struct inode *, const struct qstr *, struct page **); +diff --git a/fs/udf/dir.c b/fs/udf/dir.c +index 42e3e551fa4c34..212393b12c2266 100644 +--- a/fs/udf/dir.c ++++ b/fs/udf/dir.c +@@ -39,26 +39,13 @@ + static int udf_readdir(struct file *file, struct dir_context *ctx) + { + struct inode *dir = file_inode(file); +- struct udf_inode_info *iinfo = UDF_I(dir); +- struct udf_fileident_bh fibh = { .sbh = NULL, .ebh = NULL}; +- struct fileIdentDesc *fi = NULL; +- struct fileIdentDesc cfi; +- udf_pblk_t block, iblock; + loff_t nf_pos, emit_pos = 0; + int flen; +- unsigned char *fname = NULL, *copy_name = NULL; +- unsigned char *nameptr; +- uint16_t liu; +- uint8_t lfi; +- loff_t size = udf_ext0_offset(dir) + dir->i_size; +- struct buffer_head *tmp, *bha[16]; +- struct kernel_lb_addr eloc; +- uint32_t elen; +- sector_t offset; +- int i, num, ret = 0; +- struct extent_position epos = { NULL, 0, {0, 0} }; ++ unsigned char *fname = NULL; ++ int ret = 0; + struct super_block *sb = dir->i_sb; + bool pos_valid = false; ++ struct udf_fileident_iter iter; + + if (ctx->pos == 0) { + if (!dir_emit_dot(file, ctx)) +@@ -66,7 +53,7 @@ static int udf_readdir(struct file *file, struct dir_context *ctx) + ctx->pos = 1; + } + nf_pos = (ctx->pos - 1) << 2; +- if (nf_pos >= size) ++ if (nf_pos >= dir->i_size) + goto out; + + /* +@@ -90,138 +77,57 @@ static int udf_readdir(struct file *file, struct dir_context *ctx) + goto out; + } + +- if (nf_pos == 0) +- nf_pos = udf_ext0_offset(dir); +- +- fibh.soffset = fibh.eoffset = nf_pos & (sb->s_blocksize - 1); +- if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { +- if (inode_bmap(dir, nf_pos >> sb->s_blocksize_bits, +- &epos, &eloc, &elen, &offset) +- != (EXT_RECORDED_ALLOCATED >> 30)) { +- ret = -ENOENT; +- goto out; +- } +- block = udf_get_lb_pblock(sb, &eloc, offset); +- if ((++offset << sb->s_blocksize_bits) < elen) { +- if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) +- epos.offset -= sizeof(struct short_ad); +- else if (iinfo->i_alloc_type == +- ICBTAG_FLAG_AD_LONG) +- epos.offset -= sizeof(struct long_ad); +- } else { +- offset = 0; +- } +- +- if (!(fibh.sbh = fibh.ebh = udf_tread(sb, block))) { +- ret = -EIO; +- goto out; +- } +- +- if (!(offset & ((16 >> (sb->s_blocksize_bits - 9)) - 1))) { +- i = 16 >> (sb->s_blocksize_bits - 9); +- if (i + offset > (elen >> sb->s_blocksize_bits)) +- i = (elen >> sb->s_blocksize_bits) - offset; +- for (num = 0; i > 0; i--) { +- block = udf_get_lb_pblock(sb, &eloc, offset + i); +- tmp = udf_tgetblk(sb, block); +- if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp)) +- bha[num++] = tmp; +- else +- brelse(tmp); +- } +- if (num) { +- ll_rw_block(REQ_OP_READ, REQ_RAHEAD, num, bha); +- for (i = 0; i < num; i++) +- brelse(bha[i]); +- } +- } +- } +- +- while (nf_pos < size) { ++ for (ret = udf_fiiter_init(&iter, dir, nf_pos); ++ !ret && iter.pos < dir->i_size; ++ ret = udf_fiiter_advance(&iter)) { + struct kernel_lb_addr tloc; +- loff_t cur_pos = nf_pos; ++ udf_pblk_t iblock; + +- /* Update file position only if we got past the current one */ +- if (nf_pos >= emit_pos) { +- ctx->pos = (nf_pos >> 2) + 1; +- pos_valid = true; +- } +- +- fi = udf_fileident_read(dir, &nf_pos, &fibh, &cfi, &epos, &eloc, +- &elen, &offset); +- if (!fi) +- goto out; + /* Still not at offset where user asked us to read from? */ +- if (cur_pos < emit_pos) ++ if (iter.pos < emit_pos) + continue; + +- liu = le16_to_cpu(cfi.lengthOfImpUse); +- lfi = cfi.lengthFileIdent; +- +- if (fibh.sbh == fibh.ebh) { +- nameptr = udf_get_fi_ident(fi); +- } else { +- int poffset; /* Unpaded ending offset */ +- +- poffset = fibh.soffset + sizeof(struct fileIdentDesc) + liu + lfi; +- +- if (poffset >= lfi) { +- nameptr = (char *)(fibh.ebh->b_data + poffset - lfi); +- } else { +- if (!copy_name) { +- copy_name = kmalloc(UDF_NAME_LEN, +- GFP_NOFS); +- if (!copy_name) { +- ret = -ENOMEM; +- goto out; +- } +- } +- nameptr = copy_name; +- memcpy(nameptr, udf_get_fi_ident(fi), +- lfi - poffset); +- memcpy(nameptr + lfi - poffset, +- fibh.ebh->b_data, poffset); +- } +- } ++ /* Update file position only if we got past the current one */ ++ pos_valid = true; ++ ctx->pos = (iter.pos >> 2) + 1; + +- if ((cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) { ++ if (iter.fi.fileCharacteristics & FID_FILE_CHAR_DELETED) { + if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNDELETE)) + continue; + } + +- if ((cfi.fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0) { ++ if (iter.fi.fileCharacteristics & FID_FILE_CHAR_HIDDEN) { + if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNHIDE)) + continue; + } + +- if (cfi.fileCharacteristics & FID_FILE_CHAR_PARENT) { ++ if (iter.fi.fileCharacteristics & FID_FILE_CHAR_PARENT) { + if (!dir_emit_dotdot(file, ctx)) +- goto out; ++ goto out_iter; + continue; + } + +- flen = udf_get_filename(sb, nameptr, lfi, fname, UDF_NAME_LEN); ++ flen = udf_get_filename(sb, iter.name, ++ iter.fi.lengthFileIdent, fname, UDF_NAME_LEN); + if (flen < 0) + continue; + +- tloc = lelb_to_cpu(cfi.icb.extLocation); ++ tloc = lelb_to_cpu(iter.fi.icb.extLocation); + iblock = udf_get_lb_pblock(sb, &tloc, 0); + if (!dir_emit(ctx, fname, flen, iblock, DT_UNKNOWN)) +- goto out; +- } /* end while */ +- +- ctx->pos = (nf_pos >> 2) + 1; +- pos_valid = true; ++ goto out_iter; ++ } + ++ if (!ret) { ++ ctx->pos = (iter.pos >> 2) + 1; ++ pos_valid = true; ++ } ++out_iter: ++ udf_fiiter_release(&iter); + out: + if (pos_valid) + file->f_version = inode_query_iversion(dir); +- if (fibh.sbh != fibh.ebh) +- brelse(fibh.ebh); +- brelse(fibh.sbh); +- brelse(epos.bh); + kfree(fname); +- kfree(copy_name); + + return ret; + } +diff --git a/fs/udf/directory.c b/fs/udf/directory.c +index 73720320f0ab74..e97ffae0783301 100644 +--- a/fs/udf/directory.c ++++ b/fs/udf/directory.c +@@ -17,183 +17,457 @@ + #include <linux/fs.h> + #include <linux/string.h> + #include <linux/bio.h> ++#include <linux/crc-itu-t.h> ++#include <linux/iversion.h> + +-struct fileIdentDesc *udf_fileident_read(struct inode *dir, loff_t *nf_pos, +- struct udf_fileident_bh *fibh, +- struct fileIdentDesc *cfi, +- struct extent_position *epos, +- struct kernel_lb_addr *eloc, uint32_t *elen, +- sector_t *offset) ++static int udf_verify_fi(struct udf_fileident_iter *iter) + { +- struct fileIdentDesc *fi; +- int i, num; +- udf_pblk_t block; +- struct buffer_head *tmp, *bha[16]; +- struct udf_inode_info *iinfo = UDF_I(dir); +- +- fibh->soffset = fibh->eoffset; ++ unsigned int len; ++ ++ if (iter->fi.descTag.tagIdent != cpu_to_le16(TAG_IDENT_FID)) { ++ udf_err(iter->dir->i_sb, ++ "directory (ino %lu) has entry at pos %llu with incorrect tag %x\n", ++ iter->dir->i_ino, (unsigned long long)iter->pos, ++ le16_to_cpu(iter->fi.descTag.tagIdent)); ++ return -EFSCORRUPTED; ++ } ++ len = udf_dir_entry_len(&iter->fi); ++ if (le16_to_cpu(iter->fi.lengthOfImpUse) & 3) { ++ udf_err(iter->dir->i_sb, ++ "directory (ino %lu) has entry at pos %llu with unaligned lenght of impUse field\n", ++ iter->dir->i_ino, (unsigned long long)iter->pos); ++ return -EFSCORRUPTED; ++ } ++ /* ++ * This is in fact allowed by the spec due to long impUse field but ++ * we don't support it. If there is real media with this large impUse ++ * field, support can be added. ++ */ ++ if (len > 1 << iter->dir->i_blkbits) { ++ udf_err(iter->dir->i_sb, ++ "directory (ino %lu) has too big (%u) entry at pos %llu\n", ++ iter->dir->i_ino, len, (unsigned long long)iter->pos); ++ return -EFSCORRUPTED; ++ } ++ if (iter->pos + len > iter->dir->i_size) { ++ udf_err(iter->dir->i_sb, ++ "directory (ino %lu) has entry past directory size at pos %llu\n", ++ iter->dir->i_ino, (unsigned long long)iter->pos); ++ return -EFSCORRUPTED; ++ } ++ if (udf_dir_entry_len(&iter->fi) != ++ sizeof(struct tag) + le16_to_cpu(iter->fi.descTag.descCRCLength)) { ++ udf_err(iter->dir->i_sb, ++ "directory (ino %lu) has entry where CRC length (%u) does not match entry length (%u)\n", ++ iter->dir->i_ino, ++ (unsigned)le16_to_cpu(iter->fi.descTag.descCRCLength), ++ (unsigned)(udf_dir_entry_len(&iter->fi) - ++ sizeof(struct tag))); ++ return -EFSCORRUPTED; ++ } ++ return 0; ++} + ++static int udf_copy_fi(struct udf_fileident_iter *iter) ++{ ++ struct udf_inode_info *iinfo = UDF_I(iter->dir); ++ int blksize = 1 << iter->dir->i_blkbits; ++ int err, off, len, nameoff; ++ ++ /* Skip copying when we are at EOF */ ++ if (iter->pos >= iter->dir->i_size) { ++ iter->name = NULL; ++ return 0; ++ } ++ if (iter->dir->i_size < iter->pos + sizeof(struct fileIdentDesc)) { ++ udf_err(iter->dir->i_sb, ++ "directory (ino %lu) has entry straddling EOF\n", ++ iter->dir->i_ino); ++ return -EFSCORRUPTED; ++ } + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { +- fi = udf_get_fileident(iinfo->i_data - +- (iinfo->i_efe ? +- sizeof(struct extendedFileEntry) : +- sizeof(struct fileEntry)), +- dir->i_sb->s_blocksize, +- &(fibh->eoffset)); +- if (!fi) +- return NULL; +- +- *nf_pos += fibh->eoffset - fibh->soffset; +- +- memcpy((uint8_t *)cfi, (uint8_t *)fi, ++ memcpy(&iter->fi, iinfo->i_data + iinfo->i_lenEAttr + iter->pos, + sizeof(struct fileIdentDesc)); +- +- return fi; ++ err = udf_verify_fi(iter); ++ if (err < 0) ++ return err; ++ iter->name = iinfo->i_data + iinfo->i_lenEAttr + iter->pos + ++ sizeof(struct fileIdentDesc) + ++ le16_to_cpu(iter->fi.lengthOfImpUse); ++ return 0; + } + +- if (fibh->eoffset == dir->i_sb->s_blocksize) { +- uint32_t lextoffset = epos->offset; +- unsigned char blocksize_bits = dir->i_sb->s_blocksize_bits; +- +- if (udf_next_aext(dir, epos, eloc, elen, 1) != +- (EXT_RECORDED_ALLOCATED >> 30)) +- return NULL; ++ off = iter->pos & (blksize - 1); ++ len = min_t(int, sizeof(struct fileIdentDesc), blksize - off); ++ memcpy(&iter->fi, iter->bh[0]->b_data + off, len); ++ if (len < sizeof(struct fileIdentDesc)) ++ memcpy((char *)(&iter->fi) + len, iter->bh[1]->b_data, ++ sizeof(struct fileIdentDesc) - len); ++ err = udf_verify_fi(iter); ++ if (err < 0) ++ return err; ++ ++ /* Handle directory entry name */ ++ nameoff = off + sizeof(struct fileIdentDesc) + ++ le16_to_cpu(iter->fi.lengthOfImpUse); ++ if (off + udf_dir_entry_len(&iter->fi) <= blksize) { ++ iter->name = iter->bh[0]->b_data + nameoff; ++ } else if (nameoff >= blksize) { ++ iter->name = iter->bh[1]->b_data + (nameoff - blksize); ++ } else { ++ iter->name = iter->namebuf; ++ len = blksize - nameoff; ++ memcpy(iter->name, iter->bh[0]->b_data + nameoff, len); ++ memcpy(iter->name + len, iter->bh[1]->b_data, ++ iter->fi.lengthFileIdent - len); ++ } ++ return 0; ++} + +- block = udf_get_lb_pblock(dir->i_sb, eloc, *offset); ++/* Readahead 8k once we are at 8k boundary */ ++static void udf_readahead_dir(struct udf_fileident_iter *iter) ++{ ++ unsigned int ralen = 16 >> (iter->dir->i_blkbits - 9); ++ struct buffer_head *tmp, *bha[16]; ++ int i, num; ++ udf_pblk_t blk; ++ ++ if (iter->loffset & (ralen - 1)) ++ return; ++ ++ if (iter->loffset + ralen > (iter->elen >> iter->dir->i_blkbits)) ++ ralen = (iter->elen >> iter->dir->i_blkbits) - iter->loffset; ++ num = 0; ++ for (i = 0; i < ralen; i++) { ++ blk = udf_get_lb_pblock(iter->dir->i_sb, &iter->eloc, ++ iter->loffset + i); ++ tmp = udf_tgetblk(iter->dir->i_sb, blk); ++ if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp)) ++ bha[num++] = tmp; ++ else ++ brelse(tmp); ++ } ++ if (num) { ++ ll_rw_block(REQ_OP_READ, REQ_RAHEAD, num, bha); ++ for (i = 0; i < num; i++) ++ brelse(bha[i]); ++ } ++} + +- (*offset)++; ++static struct buffer_head *udf_fiiter_bread_blk(struct udf_fileident_iter *iter) ++{ ++ udf_pblk_t blk; + +- if ((*offset << blocksize_bits) >= *elen) +- *offset = 0; +- else +- epos->offset = lextoffset; ++ udf_readahead_dir(iter); ++ blk = udf_get_lb_pblock(iter->dir->i_sb, &iter->eloc, iter->loffset); ++ return udf_tread(iter->dir->i_sb, blk); ++} + +- brelse(fibh->sbh); +- fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block); +- if (!fibh->sbh) +- return NULL; +- fibh->soffset = fibh->eoffset = 0; +- +- if (!(*offset & ((16 >> (blocksize_bits - 9)) - 1))) { +- i = 16 >> (blocksize_bits - 9); +- if (i + *offset > (*elen >> blocksize_bits)) +- i = (*elen >> blocksize_bits)-*offset; +- for (num = 0; i > 0; i--) { +- block = udf_get_lb_pblock(dir->i_sb, eloc, +- *offset + i); +- tmp = udf_tgetblk(dir->i_sb, block); +- if (tmp && !buffer_uptodate(tmp) && +- !buffer_locked(tmp)) +- bha[num++] = tmp; +- else +- brelse(tmp); +- } +- if (num) { +- ll_rw_block(REQ_OP_READ, REQ_RAHEAD, num, bha); +- for (i = 0; i < num; i++) +- brelse(bha[i]); +- } ++/* ++ * Updates loffset to point to next directory block; eloc, elen & epos are ++ * updated if we need to traverse to the next extent as well. ++ */ ++static int udf_fiiter_advance_blk(struct udf_fileident_iter *iter) ++{ ++ iter->loffset++; ++ if (iter->loffset < iter->elen >> iter->dir->i_blkbits) ++ return 0; ++ ++ iter->loffset = 0; ++ if (udf_next_aext(iter->dir, &iter->epos, &iter->eloc, &iter->elen, 1) ++ != (EXT_RECORDED_ALLOCATED >> 30)) { ++ if (iter->pos == iter->dir->i_size) { ++ iter->elen = 0; ++ return 0; + } +- } else if (fibh->sbh != fibh->ebh) { +- brelse(fibh->sbh); +- fibh->sbh = fibh->ebh; ++ udf_err(iter->dir->i_sb, ++ "extent after position %llu not allocated in directory (ino %lu)\n", ++ (unsigned long long)iter->pos, iter->dir->i_ino); ++ return -EFSCORRUPTED; + } ++ return 0; ++} + +- fi = udf_get_fileident(fibh->sbh->b_data, dir->i_sb->s_blocksize, +- &(fibh->eoffset)); +- +- if (!fi) +- return NULL; ++static int udf_fiiter_load_bhs(struct udf_fileident_iter *iter) ++{ ++ int blksize = 1 << iter->dir->i_blkbits; ++ int off = iter->pos & (blksize - 1); ++ int err; ++ struct fileIdentDesc *fi; + +- *nf_pos += fibh->eoffset - fibh->soffset; ++ /* Is there any further extent we can map from? */ ++ if (!iter->bh[0] && iter->elen) { ++ iter->bh[0] = udf_fiiter_bread_blk(iter); ++ if (!iter->bh[0]) { ++ err = -ENOMEM; ++ goto out_brelse; ++ } ++ if (!buffer_uptodate(iter->bh[0])) { ++ err = -EIO; ++ goto out_brelse; ++ } ++ } ++ /* There's no next block so we are done */ ++ if (iter->pos >= iter->dir->i_size) ++ return 0; ++ /* Need to fetch next block as well? */ ++ if (off + sizeof(struct fileIdentDesc) > blksize) ++ goto fetch_next; ++ fi = (struct fileIdentDesc *)(iter->bh[0]->b_data + off); ++ /* Need to fetch next block to get name? */ ++ if (off + udf_dir_entry_len(fi) > blksize) { ++fetch_next: ++ udf_fiiter_advance_blk(iter); ++ iter->bh[1] = udf_fiiter_bread_blk(iter); ++ if (!iter->bh[1]) { ++ err = -ENOMEM; ++ goto out_brelse; ++ } ++ if (!buffer_uptodate(iter->bh[1])) { ++ err = -EIO; ++ goto out_brelse; ++ } ++ } ++ return 0; ++out_brelse: ++ brelse(iter->bh[0]); ++ brelse(iter->bh[1]); ++ iter->bh[0] = iter->bh[1] = NULL; ++ return err; ++} + +- if (fibh->eoffset <= dir->i_sb->s_blocksize) { +- memcpy((uint8_t *)cfi, (uint8_t *)fi, +- sizeof(struct fileIdentDesc)); +- } else if (fibh->eoffset > dir->i_sb->s_blocksize) { +- uint32_t lextoffset = epos->offset; ++int udf_fiiter_init(struct udf_fileident_iter *iter, struct inode *dir, ++ loff_t pos) ++{ ++ struct udf_inode_info *iinfo = UDF_I(dir); ++ int err = 0; ++ ++ iter->dir = dir; ++ iter->bh[0] = iter->bh[1] = NULL; ++ iter->pos = pos; ++ iter->elen = 0; ++ iter->epos.bh = NULL; ++ iter->name = NULL; ++ ++ if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) ++ return udf_copy_fi(iter); ++ ++ if (inode_bmap(dir, iter->pos >> dir->i_blkbits, &iter->epos, ++ &iter->eloc, &iter->elen, &iter->loffset) != ++ (EXT_RECORDED_ALLOCATED >> 30)) { ++ if (pos == dir->i_size) ++ return 0; ++ udf_err(dir->i_sb, ++ "position %llu not allocated in directory (ino %lu)\n", ++ (unsigned long long)pos, dir->i_ino); ++ return -EFSCORRUPTED; ++ } ++ err = udf_fiiter_load_bhs(iter); ++ if (err < 0) ++ return err; ++ err = udf_copy_fi(iter); ++ if (err < 0) { ++ udf_fiiter_release(iter); ++ return err; ++ } ++ return 0; ++} + +- if (udf_next_aext(dir, epos, eloc, elen, 1) != +- (EXT_RECORDED_ALLOCATED >> 30)) +- return NULL; ++int udf_fiiter_advance(struct udf_fileident_iter *iter) ++{ ++ unsigned int oldoff, len; ++ int blksize = 1 << iter->dir->i_blkbits; ++ int err; ++ ++ oldoff = iter->pos & (blksize - 1); ++ len = udf_dir_entry_len(&iter->fi); ++ iter->pos += len; ++ if (UDF_I(iter->dir)->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { ++ if (oldoff + len >= blksize) { ++ brelse(iter->bh[0]); ++ iter->bh[0] = NULL; ++ /* Next block already loaded? */ ++ if (iter->bh[1]) { ++ iter->bh[0] = iter->bh[1]; ++ iter->bh[1] = NULL; ++ } else { ++ udf_fiiter_advance_blk(iter); ++ } ++ } ++ err = udf_fiiter_load_bhs(iter); ++ if (err < 0) ++ return err; ++ } ++ return udf_copy_fi(iter); ++} + +- block = udf_get_lb_pblock(dir->i_sb, eloc, *offset); ++void udf_fiiter_release(struct udf_fileident_iter *iter) ++{ ++ iter->dir = NULL; ++ brelse(iter->bh[0]); ++ brelse(iter->bh[1]); ++ iter->bh[0] = iter->bh[1] = NULL; ++} + +- (*offset)++; ++static void udf_copy_to_bufs(void *buf1, int len1, void *buf2, int len2, ++ int off, void *src, int len) ++{ ++ int copy; ++ ++ if (off >= len1) { ++ off -= len1; ++ } else { ++ copy = min(off + len, len1) - off; ++ memcpy(buf1 + off, src, copy); ++ src += copy; ++ len -= copy; ++ off = 0; ++ } ++ if (len > 0) { ++ if (WARN_ON_ONCE(off + len > len2 || !buf2)) ++ return; ++ memcpy(buf2 + off, src, len); ++ } ++} + +- if ((*offset << dir->i_sb->s_blocksize_bits) >= *elen) +- *offset = 0; +- else +- epos->offset = lextoffset; ++static uint16_t udf_crc_fi_bufs(void *buf1, int len1, void *buf2, int len2, ++ int off, int len) ++{ ++ int copy; ++ uint16_t crc = 0; ++ ++ if (off >= len1) { ++ off -= len1; ++ } else { ++ copy = min(off + len, len1) - off; ++ crc = crc_itu_t(crc, buf1 + off, copy); ++ len -= copy; ++ off = 0; ++ } ++ if (len > 0) { ++ if (WARN_ON_ONCE(off + len > len2 || !buf2)) ++ return 0; ++ crc = crc_itu_t(crc, buf2 + off, len); ++ } ++ return crc; ++} + +- fibh->soffset -= dir->i_sb->s_blocksize; +- fibh->eoffset -= dir->i_sb->s_blocksize; ++static void udf_copy_fi_to_bufs(char *buf1, int len1, char *buf2, int len2, ++ int off, struct fileIdentDesc *fi, ++ uint8_t *impuse, uint8_t *name) ++{ ++ uint16_t crc; ++ int fioff = off; ++ int crcoff = off + sizeof(struct tag); ++ unsigned int crclen = udf_dir_entry_len(fi) - sizeof(struct tag); ++ ++ udf_copy_to_bufs(buf1, len1, buf2, len2, off, fi, ++ sizeof(struct fileIdentDesc)); ++ off += sizeof(struct fileIdentDesc); ++ if (impuse) ++ udf_copy_to_bufs(buf1, len1, buf2, len2, off, impuse, ++ le16_to_cpu(fi->lengthOfImpUse)); ++ off += le16_to_cpu(fi->lengthOfImpUse); ++ if (name) ++ udf_copy_to_bufs(buf1, len1, buf2, len2, off, name, ++ fi->lengthFileIdent); ++ ++ crc = udf_crc_fi_bufs(buf1, len1, buf2, len2, crcoff, crclen); ++ fi->descTag.descCRC = cpu_to_le16(crc); ++ fi->descTag.descCRCLength = cpu_to_le16(crclen); ++ fi->descTag.tagChecksum = udf_tag_checksum(&fi->descTag); ++ ++ udf_copy_to_bufs(buf1, len1, buf2, len2, fioff, fi, sizeof(struct tag)); ++} + +- fibh->ebh = udf_tread(dir->i_sb, block); +- if (!fibh->ebh) +- return NULL; ++void udf_fiiter_write_fi(struct udf_fileident_iter *iter, uint8_t *impuse) ++{ ++ struct udf_inode_info *iinfo = UDF_I(iter->dir); ++ void *buf1, *buf2 = NULL; ++ int len1, len2 = 0, off; ++ int blksize = 1 << iter->dir->i_blkbits; + +- if (sizeof(struct fileIdentDesc) > -fibh->soffset) { +- int fi_len; ++ off = iter->pos & (blksize - 1); ++ if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { ++ buf1 = iinfo->i_data + iinfo->i_lenEAttr; ++ len1 = iter->dir->i_size; ++ } else { ++ buf1 = iter->bh[0]->b_data; ++ len1 = blksize; ++ if (iter->bh[1]) { ++ buf2 = iter->bh[1]->b_data; ++ len2 = blksize; ++ } ++ } + +- memcpy((uint8_t *)cfi, (uint8_t *)fi, -fibh->soffset); +- memcpy((uint8_t *)cfi - fibh->soffset, +- fibh->ebh->b_data, +- sizeof(struct fileIdentDesc) + fibh->soffset); ++ udf_copy_fi_to_bufs(buf1, len1, buf2, len2, off, &iter->fi, impuse, ++ iter->name == iter->namebuf ? iter->name : NULL); + +- fi_len = udf_dir_entry_len(cfi); +- *nf_pos += fi_len - (fibh->eoffset - fibh->soffset); +- fibh->eoffset = fibh->soffset + fi_len; +- } else { +- memcpy((uint8_t *)cfi, (uint8_t *)fi, +- sizeof(struct fileIdentDesc)); +- } ++ if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { ++ mark_inode_dirty(iter->dir); ++ } else { ++ mark_buffer_dirty_inode(iter->bh[0], iter->dir); ++ if (iter->bh[1]) ++ mark_buffer_dirty_inode(iter->bh[1], iter->dir); + } +- /* Got last entry outside of dir size - fs is corrupted! */ +- if (*nf_pos > dir->i_size) +- return NULL; +- return fi; ++ inode_inc_iversion(iter->dir); + } + +-struct fileIdentDesc *udf_get_fileident(void *buffer, int bufsize, int *offset) ++void udf_fiiter_update_elen(struct udf_fileident_iter *iter, uint32_t new_elen) + { +- struct fileIdentDesc *fi; +- int lengthThisIdent; +- uint8_t *ptr; +- int padlen; ++ struct udf_inode_info *iinfo = UDF_I(iter->dir); ++ int diff = new_elen - iter->elen; ++ ++ /* Skip update when we already went past the last extent */ ++ if (!iter->elen) ++ return; ++ iter->elen = new_elen; ++ if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) ++ iter->epos.offset -= sizeof(struct short_ad); ++ else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) ++ iter->epos.offset -= sizeof(struct long_ad); ++ udf_write_aext(iter->dir, &iter->epos, &iter->eloc, iter->elen, 1); ++ iinfo->i_lenExtents += diff; ++ mark_inode_dirty(iter->dir); ++} + +- if ((!buffer) || (!offset)) { +- udf_debug("invalidparms, buffer=%p, offset=%p\n", +- buffer, offset); +- return NULL; ++/* Append new block to directory. @iter is expected to point at EOF */ ++int udf_fiiter_append_blk(struct udf_fileident_iter *iter) ++{ ++ struct udf_inode_info *iinfo = UDF_I(iter->dir); ++ int blksize = 1 << iter->dir->i_blkbits; ++ struct buffer_head *bh; ++ sector_t block; ++ uint32_t old_elen = iter->elen; ++ int err; ++ ++ if (WARN_ON_ONCE(iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)) ++ return -EINVAL; ++ ++ /* Round up last extent in the file */ ++ udf_fiiter_update_elen(iter, ALIGN(iter->elen, blksize)); ++ ++ /* Allocate new block and refresh mapping information */ ++ block = iinfo->i_lenExtents >> iter->dir->i_blkbits; ++ bh = udf_bread(iter->dir, block, 1, &err); ++ if (!bh) { ++ udf_fiiter_update_elen(iter, old_elen); ++ return err; + } +- +- ptr = buffer; +- +- if ((*offset > 0) && (*offset < bufsize)) +- ptr += *offset; +- fi = (struct fileIdentDesc *)ptr; +- if (fi->descTag.tagIdent != cpu_to_le16(TAG_IDENT_FID)) { +- udf_debug("0x%x != TAG_IDENT_FID\n", +- le16_to_cpu(fi->descTag.tagIdent)); +- udf_debug("offset: %d sizeof: %lu bufsize: %d\n", +- *offset, (unsigned long)sizeof(struct fileIdentDesc), +- bufsize); +- return NULL; ++ if (inode_bmap(iter->dir, block, &iter->epos, &iter->eloc, &iter->elen, ++ &iter->loffset) != (EXT_RECORDED_ALLOCATED >> 30)) { ++ udf_err(iter->dir->i_sb, ++ "block %llu not allocated in directory (ino %lu)\n", ++ (unsigned long long)block, iter->dir->i_ino); ++ return -EFSCORRUPTED; + } +- if ((*offset + sizeof(struct fileIdentDesc)) > bufsize) +- lengthThisIdent = sizeof(struct fileIdentDesc); +- else +- lengthThisIdent = sizeof(struct fileIdentDesc) + +- fi->lengthFileIdent + le16_to_cpu(fi->lengthOfImpUse); +- +- /* we need to figure padding, too! */ +- padlen = lengthThisIdent % UDF_NAME_PAD; +- if (padlen) +- lengthThisIdent += (UDF_NAME_PAD - padlen); +- *offset = *offset + lengthThisIdent; +- +- return fi; ++ if (!(iter->pos & (blksize - 1))) { ++ brelse(iter->bh[0]); ++ iter->bh[0] = bh; ++ } else { ++ iter->bh[1] = bh; ++ } ++ return 0; + } + + struct short_ad *udf_get_fileshortad(uint8_t *ptr, int maxoffset, uint32_t *offset, +diff --git a/fs/udf/inode.c b/fs/udf/inode.c +index da6fb28b4eeacb..e68490991f5c64 100644 +--- a/fs/udf/inode.c ++++ b/fs/udf/inode.c +@@ -324,96 +324,6 @@ int udf_expand_file_adinicb(struct inode *inode) + return err; + } + +-struct buffer_head *udf_expand_dir_adinicb(struct inode *inode, +- udf_pblk_t *block, int *err) +-{ +- udf_pblk_t newblock; +- struct buffer_head *dbh = NULL; +- struct kernel_lb_addr eloc; +- uint8_t alloctype; +- struct extent_position epos; +- +- struct udf_fileident_bh sfibh, dfibh; +- loff_t f_pos = udf_ext0_offset(inode); +- int size = udf_ext0_offset(inode) + inode->i_size; +- struct fileIdentDesc cfi, *sfi, *dfi; +- struct udf_inode_info *iinfo = UDF_I(inode); +- +- if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) +- alloctype = ICBTAG_FLAG_AD_SHORT; +- else +- alloctype = ICBTAG_FLAG_AD_LONG; +- +- if (!inode->i_size) { +- iinfo->i_alloc_type = alloctype; +- mark_inode_dirty(inode); +- return NULL; +- } +- +- /* alloc block, and copy data to it */ +- *block = udf_new_block(inode->i_sb, inode, +- iinfo->i_location.partitionReferenceNum, +- iinfo->i_location.logicalBlockNum, err); +- if (!(*block)) +- return NULL; +- newblock = udf_get_pblock(inode->i_sb, *block, +- iinfo->i_location.partitionReferenceNum, +- 0); +- if (!newblock) +- return NULL; +- dbh = udf_tgetblk(inode->i_sb, newblock); +- if (!dbh) +- return NULL; +- lock_buffer(dbh); +- memset(dbh->b_data, 0x00, inode->i_sb->s_blocksize); +- set_buffer_uptodate(dbh); +- unlock_buffer(dbh); +- mark_buffer_dirty_inode(dbh, inode); +- +- sfibh.soffset = sfibh.eoffset = +- f_pos & (inode->i_sb->s_blocksize - 1); +- sfibh.sbh = sfibh.ebh = NULL; +- dfibh.soffset = dfibh.eoffset = 0; +- dfibh.sbh = dfibh.ebh = dbh; +- while (f_pos < size) { +- iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB; +- sfi = udf_fileident_read(inode, &f_pos, &sfibh, &cfi, NULL, +- NULL, NULL, NULL); +- if (!sfi) { +- brelse(dbh); +- return NULL; +- } +- iinfo->i_alloc_type = alloctype; +- sfi->descTag.tagLocation = cpu_to_le32(*block); +- dfibh.soffset = dfibh.eoffset; +- dfibh.eoffset += (sfibh.eoffset - sfibh.soffset); +- dfi = (struct fileIdentDesc *)(dbh->b_data + dfibh.soffset); +- if (udf_write_fi(inode, sfi, dfi, &dfibh, sfi->impUse, +- udf_get_fi_ident(sfi))) { +- iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB; +- brelse(dbh); +- return NULL; +- } +- } +- mark_buffer_dirty_inode(dbh, inode); +- +- memset(iinfo->i_data + iinfo->i_lenEAttr, 0, iinfo->i_lenAlloc); +- iinfo->i_lenAlloc = 0; +- eloc.logicalBlockNum = *block; +- eloc.partitionReferenceNum = +- iinfo->i_location.partitionReferenceNum; +- iinfo->i_lenExtents = inode->i_size; +- epos.bh = NULL; +- epos.block = iinfo->i_location; +- epos.offset = udf_file_entry_alloc_offset(inode); +- udf_add_aext(inode, &epos, &eloc, inode->i_size, 0); +- /* UniqueID stuff */ +- +- brelse(epos.bh); +- mark_inode_dirty(inode); +- return dbh; +-} +- + static int udf_get_block(struct inode *inode, sector_t block, + struct buffer_head *bh_result, int create) + { +diff --git a/fs/udf/namei.c b/fs/udf/namei.c +index 0e30a50060d9dc..5e39cbc7452432 100644 +--- a/fs/udf/namei.c ++++ b/fs/udf/namei.c +@@ -41,283 +41,93 @@ static inline int udf_match(int len1, const unsigned char *name1, int len2, + return !memcmp(name1, name2, len1); + } + +-int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi, +- struct fileIdentDesc *sfi, struct udf_fileident_bh *fibh, +- uint8_t *impuse, uint8_t *fileident) +-{ +- uint16_t crclen = fibh->eoffset - fibh->soffset - sizeof(struct tag); +- uint16_t crc; +- int offset; +- uint16_t liu = le16_to_cpu(cfi->lengthOfImpUse); +- uint8_t lfi = cfi->lengthFileIdent; +- int padlen = fibh->eoffset - fibh->soffset - liu - lfi - +- sizeof(struct fileIdentDesc); +- int adinicb = 0; +- +- if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) +- adinicb = 1; +- +- offset = fibh->soffset + sizeof(struct fileIdentDesc); +- +- if (impuse) { +- if (adinicb || (offset + liu < 0)) { +- memcpy((uint8_t *)sfi->impUse, impuse, liu); +- } else if (offset >= 0) { +- memcpy(fibh->ebh->b_data + offset, impuse, liu); +- } else { +- memcpy((uint8_t *)sfi->impUse, impuse, -offset); +- memcpy(fibh->ebh->b_data, impuse - offset, +- liu + offset); +- } +- } +- +- offset += liu; +- +- if (fileident) { +- if (adinicb || (offset + lfi < 0)) { +- memcpy(sfi->impUse + liu, fileident, lfi); +- } else if (offset >= 0) { +- memcpy(fibh->ebh->b_data + offset, fileident, lfi); +- } else { +- memcpy(sfi->impUse + liu, fileident, -offset); +- memcpy(fibh->ebh->b_data, fileident - offset, +- lfi + offset); +- } +- } +- +- offset += lfi; +- +- if (adinicb || (offset + padlen < 0)) { +- memset(sfi->impUse + liu + lfi, 0x00, padlen); +- } else if (offset >= 0) { +- memset(fibh->ebh->b_data + offset, 0x00, padlen); +- } else { +- memset(sfi->impUse + liu + lfi, 0x00, -offset); +- memset(fibh->ebh->b_data, 0x00, padlen + offset); +- } +- +- crc = crc_itu_t(0, (uint8_t *)cfi + sizeof(struct tag), +- sizeof(struct fileIdentDesc) - sizeof(struct tag)); +- +- if (fibh->sbh == fibh->ebh) { +- crc = crc_itu_t(crc, (uint8_t *)sfi->impUse, +- crclen + sizeof(struct tag) - +- sizeof(struct fileIdentDesc)); +- } else if (sizeof(struct fileIdentDesc) >= -fibh->soffset) { +- crc = crc_itu_t(crc, fibh->ebh->b_data + +- sizeof(struct fileIdentDesc) + +- fibh->soffset, +- crclen + sizeof(struct tag) - +- sizeof(struct fileIdentDesc)); +- } else { +- crc = crc_itu_t(crc, (uint8_t *)sfi->impUse, +- -fibh->soffset - sizeof(struct fileIdentDesc)); +- crc = crc_itu_t(crc, fibh->ebh->b_data, fibh->eoffset); +- } +- +- cfi->descTag.descCRC = cpu_to_le16(crc); +- cfi->descTag.descCRCLength = cpu_to_le16(crclen); +- cfi->descTag.tagChecksum = udf_tag_checksum(&cfi->descTag); +- +- if (adinicb || (sizeof(struct fileIdentDesc) <= -fibh->soffset)) { +- memcpy((uint8_t *)sfi, (uint8_t *)cfi, +- sizeof(struct fileIdentDesc)); +- } else { +- memcpy((uint8_t *)sfi, (uint8_t *)cfi, -fibh->soffset); +- memcpy(fibh->ebh->b_data, (uint8_t *)cfi - fibh->soffset, +- sizeof(struct fileIdentDesc) + fibh->soffset); +- } +- +- if (adinicb) { +- mark_inode_dirty(inode); +- } else { +- if (fibh->sbh != fibh->ebh) +- mark_buffer_dirty_inode(fibh->ebh, inode); +- mark_buffer_dirty_inode(fibh->sbh, inode); +- } +- inode_inc_iversion(inode); +- +- return 0; +-} +- + /** +- * udf_find_entry - find entry in given directory. ++ * udf_fiiter_find_entry - find entry in given directory. + * + * @dir: directory inode to search in + * @child: qstr of the name +- * @fibh: buffer head / inode with file identifier descriptor we found +- * @cfi: found file identifier descriptor with given name ++ * @iter: iter to use for searching + * + * This function searches in the directory @dir for a file name @child. When +- * found, @fibh points to the buffer head(s) (bh is NULL for in ICB +- * directories) containing the file identifier descriptor (FID). In that case +- * the function returns pointer to the FID in the buffer or inode - but note +- * that FID may be split among two buffers (blocks) so accessing it via that +- * pointer isn't easily possible. This pointer can be used only as an iterator +- * for other directory manipulation functions. For inspection of the FID @cfi +- * can be used - the found FID is copied there. ++ * found, @iter points to the position in the directory with given entry. + * +- * Returns pointer to FID, NULL when nothing found, or error code. ++ * Returns 0 on success, < 0 on error (including -ENOENT). + */ +-static struct fileIdentDesc *udf_find_entry(struct inode *dir, +- const struct qstr *child, +- struct udf_fileident_bh *fibh, +- struct fileIdentDesc *cfi) ++static int udf_fiiter_find_entry(struct inode *dir, const struct qstr *child, ++ struct udf_fileident_iter *iter) + { +- struct fileIdentDesc *fi = NULL; +- loff_t f_pos; +- udf_pblk_t block; + int flen; +- unsigned char *fname = NULL, *copy_name = NULL; +- unsigned char *nameptr; +- uint8_t lfi; +- uint16_t liu; +- loff_t size; +- struct kernel_lb_addr eloc; +- uint32_t elen; +- sector_t offset; +- struct extent_position epos = {}; +- struct udf_inode_info *dinfo = UDF_I(dir); ++ unsigned char *fname = NULL; ++ struct super_block *sb = dir->i_sb; + int isdotdot = child->len == 2 && + child->name[0] == '.' && child->name[1] == '.'; +- struct super_block *sb = dir->i_sb; +- +- size = udf_ext0_offset(dir) + dir->i_size; +- f_pos = udf_ext0_offset(dir); +- +- fibh->sbh = fibh->ebh = NULL; +- fibh->soffset = fibh->eoffset = f_pos & (sb->s_blocksize - 1); +- if (dinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { +- if (inode_bmap(dir, f_pos >> sb->s_blocksize_bits, &epos, +- &eloc, &elen, &offset) != (EXT_RECORDED_ALLOCATED >> 30)) { +- fi = ERR_PTR(-EIO); +- goto out_err; +- } +- +- block = udf_get_lb_pblock(sb, &eloc, offset); +- if ((++offset << sb->s_blocksize_bits) < elen) { +- if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) +- epos.offset -= sizeof(struct short_ad); +- else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) +- epos.offset -= sizeof(struct long_ad); +- } else +- offset = 0; +- +- fibh->sbh = fibh->ebh = udf_tread(sb, block); +- if (!fibh->sbh) { +- fi = ERR_PTR(-EIO); +- goto out_err; +- } +- } ++ int ret; + + fname = kmalloc(UDF_NAME_LEN, GFP_NOFS); +- if (!fname) { +- fi = ERR_PTR(-ENOMEM); +- goto out_err; +- } +- +- while (f_pos < size) { +- fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &epos, &eloc, +- &elen, &offset); +- if (!fi) { +- fi = ERR_PTR(-EIO); +- goto out_err; +- } +- +- liu = le16_to_cpu(cfi->lengthOfImpUse); +- lfi = cfi->lengthFileIdent; +- +- if (fibh->sbh == fibh->ebh) { +- nameptr = udf_get_fi_ident(fi); +- } else { +- int poffset; /* Unpaded ending offset */ +- +- poffset = fibh->soffset + sizeof(struct fileIdentDesc) + +- liu + lfi; +- +- if (poffset >= lfi) +- nameptr = (uint8_t *)(fibh->ebh->b_data + +- poffset - lfi); +- else { +- if (!copy_name) { +- copy_name = kmalloc(UDF_NAME_LEN_CS0, +- GFP_NOFS); +- if (!copy_name) { +- fi = ERR_PTR(-ENOMEM); +- goto out_err; +- } +- } +- nameptr = copy_name; +- memcpy(nameptr, udf_get_fi_ident(fi), +- lfi - poffset); +- memcpy(nameptr + lfi - poffset, +- fibh->ebh->b_data, poffset); +- } +- } ++ if (!fname) ++ return -ENOMEM; + +- if ((cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) { ++ for (ret = udf_fiiter_init(iter, dir, 0); ++ !ret && iter->pos < dir->i_size; ++ ret = udf_fiiter_advance(iter)) { ++ if (iter->fi.fileCharacteristics & FID_FILE_CHAR_DELETED) { + if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNDELETE)) + continue; + } + +- if ((cfi->fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0) { ++ if (iter->fi.fileCharacteristics & FID_FILE_CHAR_HIDDEN) { + if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNHIDE)) + continue; + } + +- if ((cfi->fileCharacteristics & FID_FILE_CHAR_PARENT) && ++ if ((iter->fi.fileCharacteristics & FID_FILE_CHAR_PARENT) && + isdotdot) + goto out_ok; + +- if (!lfi) ++ if (!iter->fi.lengthFileIdent) + continue; + +- flen = udf_get_filename(sb, nameptr, lfi, fname, UDF_NAME_LEN); ++ flen = udf_get_filename(sb, iter->name, ++ iter->fi.lengthFileIdent, fname, UDF_NAME_LEN); + if (flen < 0) { +- fi = ERR_PTR(flen); ++ ret = flen; + goto out_err; + } + + if (udf_match(flen, fname, child->len, child->name)) + goto out_ok; + } ++ if (!ret) ++ ret = -ENOENT; + +- fi = NULL; + out_err: +- if (fibh->sbh != fibh->ebh) +- brelse(fibh->ebh); +- brelse(fibh->sbh); ++ udf_fiiter_release(iter); + out_ok: +- brelse(epos.bh); + kfree(fname); +- kfree(copy_name); + +- return fi; ++ return ret; + } + + static struct dentry *udf_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) + { + struct inode *inode = NULL; +- struct fileIdentDesc cfi; +- struct udf_fileident_bh fibh; +- struct fileIdentDesc *fi; ++ struct udf_fileident_iter iter; ++ int err; + + if (dentry->d_name.len > UDF_NAME_LEN) + return ERR_PTR(-ENAMETOOLONG); + +- fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi); +- if (IS_ERR(fi)) +- return ERR_CAST(fi); ++ err = udf_fiiter_find_entry(dir, &dentry->d_name, &iter); ++ if (err < 0 && err != -ENOENT) ++ return ERR_PTR(err); + +- if (fi) { ++ if (err == 0) { + struct kernel_lb_addr loc; + +- if (fibh.sbh != fibh.ebh) +- brelse(fibh.ebh); +- brelse(fibh.sbh); ++ loc = lelb_to_cpu(iter.fi.icb.extLocation); ++ udf_fiiter_release(&iter); + +- loc = lelb_to_cpu(cfi.icb.extLocation); + inode = udf_iget(dir->i_sb, &loc); + if (IS_ERR(inode)) + return ERR_CAST(inode); +@@ -326,281 +136,227 @@ static struct dentry *udf_lookup(struct inode *dir, struct dentry *dentry, + return d_splice_alias(inode, dentry); + } + +-static struct fileIdentDesc *udf_add_entry(struct inode *dir, +- struct dentry *dentry, +- struct udf_fileident_bh *fibh, +- struct fileIdentDesc *cfi, int *err) ++static int udf_expand_dir_adinicb(struct inode *inode, udf_pblk_t *block) + { +- struct super_block *sb = dir->i_sb; +- struct fileIdentDesc *fi = NULL; +- unsigned char *name = NULL; +- int namelen; +- loff_t f_pos; +- loff_t size = udf_ext0_offset(dir) + dir->i_size; +- int nfidlen; +- udf_pblk_t block; ++ udf_pblk_t newblock; ++ struct buffer_head *dbh = NULL; + struct kernel_lb_addr eloc; +- uint32_t elen = 0; +- sector_t offset; +- struct extent_position epos = {}; +- struct udf_inode_info *dinfo; ++ struct extent_position epos; ++ uint8_t alloctype; ++ struct udf_inode_info *iinfo = UDF_I(inode); ++ struct udf_fileident_iter iter; ++ uint8_t *impuse; ++ int ret; + +- fibh->sbh = fibh->ebh = NULL; +- name = kmalloc(UDF_NAME_LEN_CS0, GFP_NOFS); +- if (!name) { +- *err = -ENOMEM; +- goto out_err; +- } ++ if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) ++ alloctype = ICBTAG_FLAG_AD_SHORT; ++ else ++ alloctype = ICBTAG_FLAG_AD_LONG; + +- if (dentry) { +- if (!dentry->d_name.len) { +- *err = -EINVAL; +- goto out_err; +- } +- namelen = udf_put_filename(sb, dentry->d_name.name, +- dentry->d_name.len, +- name, UDF_NAME_LEN_CS0); +- if (!namelen) { +- *err = -ENAMETOOLONG; +- goto out_err; +- } +- } else { +- namelen = 0; ++ if (!inode->i_size) { ++ iinfo->i_alloc_type = alloctype; ++ mark_inode_dirty(inode); ++ return 0; + } + +- nfidlen = ALIGN(sizeof(struct fileIdentDesc) + namelen, UDF_NAME_PAD); +- +- f_pos = udf_ext0_offset(dir); +- +- fibh->soffset = fibh->eoffset = f_pos & (dir->i_sb->s_blocksize - 1); +- dinfo = UDF_I(dir); +- if (dinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { +- if (inode_bmap(dir, f_pos >> dir->i_sb->s_blocksize_bits, &epos, +- &eloc, &elen, &offset) != (EXT_RECORDED_ALLOCATED >> 30)) { +- block = udf_get_lb_pblock(dir->i_sb, +- &dinfo->i_location, 0); +- fibh->soffset = fibh->eoffset = sb->s_blocksize; +- goto add; +- } +- block = udf_get_lb_pblock(dir->i_sb, &eloc, offset); +- if ((++offset << dir->i_sb->s_blocksize_bits) < elen) { +- if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) +- epos.offset -= sizeof(struct short_ad); +- else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) +- epos.offset -= sizeof(struct long_ad); +- } else +- offset = 0; +- +- fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block); +- if (!fibh->sbh) { +- *err = -EIO; +- goto out_err; +- } ++ /* alloc block, and copy data to it */ ++ *block = udf_new_block(inode->i_sb, inode, ++ iinfo->i_location.partitionReferenceNum, ++ iinfo->i_location.logicalBlockNum, &ret); ++ if (!(*block)) ++ return ret; ++ newblock = udf_get_pblock(inode->i_sb, *block, ++ iinfo->i_location.partitionReferenceNum, ++ 0); ++ if (newblock == 0xffffffff) ++ return -EFSCORRUPTED; ++ dbh = udf_tgetblk(inode->i_sb, newblock); ++ if (!dbh) ++ return -ENOMEM; ++ lock_buffer(dbh); ++ memcpy(dbh->b_data, iinfo->i_data, inode->i_size); ++ memset(dbh->b_data + inode->i_size, 0, ++ inode->i_sb->s_blocksize - inode->i_size); ++ set_buffer_uptodate(dbh); ++ unlock_buffer(dbh); ++ ++ /* Drop inline data, add block instead */ ++ iinfo->i_alloc_type = alloctype; ++ memset(iinfo->i_data + iinfo->i_lenEAttr, 0, iinfo->i_lenAlloc); ++ iinfo->i_lenAlloc = 0; ++ eloc.logicalBlockNum = *block; ++ eloc.partitionReferenceNum = ++ iinfo->i_location.partitionReferenceNum; ++ iinfo->i_lenExtents = inode->i_size; ++ epos.bh = NULL; ++ epos.block = iinfo->i_location; ++ epos.offset = udf_file_entry_alloc_offset(inode); ++ ret = udf_add_aext(inode, &epos, &eloc, inode->i_size, 0); ++ brelse(epos.bh); ++ if (ret < 0) { ++ brelse(dbh); ++ udf_free_blocks(inode->i_sb, inode, &eloc, 0, 1); ++ return ret; ++ } ++ mark_inode_dirty(inode); + +- block = dinfo->i_location.logicalBlockNum; ++ /* Now fixup tags in moved directory entries */ ++ for (ret = udf_fiiter_init(&iter, inode, 0); ++ !ret && iter.pos < inode->i_size; ++ ret = udf_fiiter_advance(&iter)) { ++ iter.fi.descTag.tagLocation = cpu_to_le32(*block); ++ if (iter.fi.lengthOfImpUse != cpu_to_le16(0)) ++ impuse = dbh->b_data + iter.pos + ++ sizeof(struct fileIdentDesc); ++ else ++ impuse = NULL; ++ udf_fiiter_write_fi(&iter, impuse); + } ++ brelse(dbh); ++ /* ++ * We don't expect the iteration to fail as the directory has been ++ * already verified to be correct ++ */ ++ WARN_ON_ONCE(ret); ++ udf_fiiter_release(&iter); ++ ++ return 0; ++} + +- while (f_pos < size) { +- fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &epos, &eloc, +- &elen, &offset); ++static int udf_fiiter_add_entry(struct inode *dir, struct dentry *dentry, ++ struct udf_fileident_iter *iter) ++{ ++ struct udf_inode_info *dinfo = UDF_I(dir); ++ int nfidlen, namelen = 0; ++ int ret; ++ int off, blksize = 1 << dir->i_blkbits; ++ udf_pblk_t block; ++ char name[UDF_NAME_LEN_CS0]; + +- if (!fi) { +- *err = -EIO; +- goto out_err; +- } ++ if (dentry) { ++ if (!dentry->d_name.len) ++ return -EINVAL; ++ namelen = udf_put_filename(dir->i_sb, dentry->d_name.name, ++ dentry->d_name.len, ++ name, UDF_NAME_LEN_CS0); ++ if (!namelen) ++ return -ENAMETOOLONG; ++ } ++ nfidlen = ALIGN(sizeof(struct fileIdentDesc) + namelen, UDF_NAME_PAD); + +- if ((cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) { +- if (udf_dir_entry_len(cfi) == nfidlen) { +- cfi->descTag.tagSerialNum = cpu_to_le16(1); +- cfi->fileVersionNum = cpu_to_le16(1); +- cfi->fileCharacteristics = 0; +- cfi->lengthFileIdent = namelen; +- cfi->lengthOfImpUse = cpu_to_le16(0); +- if (!udf_write_fi(dir, cfi, fi, fibh, NULL, +- name)) +- goto out_ok; +- else { +- *err = -EIO; +- goto out_err; +- } ++ for (ret = udf_fiiter_init(iter, dir, 0); ++ !ret && iter->pos < dir->i_size; ++ ret = udf_fiiter_advance(iter)) { ++ if (iter->fi.fileCharacteristics & FID_FILE_CHAR_DELETED) { ++ if (udf_dir_entry_len(&iter->fi) == nfidlen) { ++ iter->fi.descTag.tagSerialNum = cpu_to_le16(1); ++ iter->fi.fileVersionNum = cpu_to_le16(1); ++ iter->fi.fileCharacteristics = 0; ++ iter->fi.lengthFileIdent = namelen; ++ iter->fi.lengthOfImpUse = cpu_to_le16(0); ++ memcpy(iter->namebuf, name, namelen); ++ iter->name = iter->namebuf; ++ return 0; + } + } + } +- +-add: +- f_pos += nfidlen; +- +- if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB && +- sb->s_blocksize - fibh->eoffset < nfidlen) { +- brelse(epos.bh); +- epos.bh = NULL; +- fibh->soffset -= udf_ext0_offset(dir); +- fibh->eoffset -= udf_ext0_offset(dir); +- f_pos -= udf_ext0_offset(dir); +- if (fibh->sbh != fibh->ebh) +- brelse(fibh->ebh); +- brelse(fibh->sbh); +- fibh->sbh = fibh->ebh = +- udf_expand_dir_adinicb(dir, &block, err); +- if (!fibh->sbh) +- goto out_err; +- epos.block = dinfo->i_location; +- epos.offset = udf_file_entry_alloc_offset(dir); +- /* Load extent udf_expand_dir_adinicb() has created */ +- udf_current_aext(dir, &epos, &eloc, &elen, 1); ++ if (ret) { ++ udf_fiiter_release(iter); ++ return ret; + } +- +- /* Entry fits into current block? */ +- if (sb->s_blocksize - fibh->eoffset >= nfidlen) { +- fibh->soffset = fibh->eoffset; +- fibh->eoffset += nfidlen; +- if (fibh->sbh != fibh->ebh) { +- brelse(fibh->sbh); +- fibh->sbh = fibh->ebh; +- } +- +- if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { +- block = dinfo->i_location.logicalBlockNum; +- fi = (struct fileIdentDesc *) +- (dinfo->i_data + fibh->soffset - +- udf_ext0_offset(dir) + +- dinfo->i_lenEAttr); +- } else { +- block = eloc.logicalBlockNum + +- ((elen - 1) >> +- dir->i_sb->s_blocksize_bits); +- fi = (struct fileIdentDesc *) +- (fibh->sbh->b_data + fibh->soffset); +- } ++ if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB && ++ blksize - udf_ext0_offset(dir) - iter->pos < nfidlen) { ++ udf_fiiter_release(iter); ++ ret = udf_expand_dir_adinicb(dir, &block); ++ if (ret) ++ return ret; ++ ret = udf_fiiter_init(iter, dir, dir->i_size); ++ if (ret < 0) ++ return ret; ++ } ++ ++ /* Get blocknumber to use for entry tag */ ++ if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { ++ block = dinfo->i_location.logicalBlockNum; + } else { +- /* Round up last extent in the file */ +- elen = (elen + sb->s_blocksize - 1) & ~(sb->s_blocksize - 1); +- if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) +- epos.offset -= sizeof(struct short_ad); +- else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) +- epos.offset -= sizeof(struct long_ad); +- udf_write_aext(dir, &epos, &eloc, elen, 1); +- dinfo->i_lenExtents = (dinfo->i_lenExtents + sb->s_blocksize +- - 1) & ~(sb->s_blocksize - 1); +- +- fibh->soffset = fibh->eoffset - sb->s_blocksize; +- fibh->eoffset += nfidlen - sb->s_blocksize; +- if (fibh->sbh != fibh->ebh) { +- brelse(fibh->sbh); +- fibh->sbh = fibh->ebh; +- } +- +- block = eloc.logicalBlockNum + ((elen - 1) >> +- dir->i_sb->s_blocksize_bits); +- fibh->ebh = udf_bread(dir, +- f_pos >> dir->i_sb->s_blocksize_bits, 1, err); +- if (!fibh->ebh) +- goto out_err; +- /* Extents could have been merged, invalidate our position */ +- brelse(epos.bh); +- epos.bh = NULL; +- epos.block = dinfo->i_location; +- epos.offset = udf_file_entry_alloc_offset(dir); +- +- if (!fibh->soffset) { +- /* Find the freshly allocated block */ +- while (udf_next_aext(dir, &epos, &eloc, &elen, 1) == +- (EXT_RECORDED_ALLOCATED >> 30)) +- ; +- block = eloc.logicalBlockNum + ((elen - 1) >> +- dir->i_sb->s_blocksize_bits); +- brelse(fibh->sbh); +- fibh->sbh = fibh->ebh; +- fi = (struct fileIdentDesc *)(fibh->sbh->b_data); +- } else { +- fi = (struct fileIdentDesc *) +- (fibh->sbh->b_data + sb->s_blocksize + +- fibh->soffset); +- } ++ block = iter->eloc.logicalBlockNum + ++ ((iter->elen - 1) >> dir->i_blkbits); + } +- +- memset(cfi, 0, sizeof(struct fileIdentDesc)); +- if (UDF_SB(sb)->s_udfrev >= 0x0200) +- udf_new_tag((char *)cfi, TAG_IDENT_FID, 3, 1, block, ++ off = iter->pos & (blksize - 1); ++ if (!off) ++ off = blksize; ++ /* Entry fits into current block? */ ++ if (blksize - udf_ext0_offset(dir) - off >= nfidlen) ++ goto store_fi; ++ ++ ret = udf_fiiter_append_blk(iter); ++ if (ret) { ++ udf_fiiter_release(iter); ++ return ret; ++ } ++ ++ /* Entry will be completely in the new block? Update tag location... */ ++ if (!(iter->pos & (blksize - 1))) ++ block = iter->eloc.logicalBlockNum + ++ ((iter->elen - 1) >> dir->i_blkbits); ++store_fi: ++ memset(&iter->fi, 0, sizeof(struct fileIdentDesc)); ++ if (UDF_SB(dir->i_sb)->s_udfrev >= 0x0200) ++ udf_new_tag((char *)(&iter->fi), TAG_IDENT_FID, 3, 1, block, + sizeof(struct tag)); + else +- udf_new_tag((char *)cfi, TAG_IDENT_FID, 2, 1, block, ++ udf_new_tag((char *)(&iter->fi), TAG_IDENT_FID, 2, 1, block, + sizeof(struct tag)); +- cfi->fileVersionNum = cpu_to_le16(1); +- cfi->lengthFileIdent = namelen; +- cfi->lengthOfImpUse = cpu_to_le16(0); +- if (!udf_write_fi(dir, cfi, fi, fibh, NULL, name)) { +- dir->i_size += nfidlen; +- if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) +- dinfo->i_lenAlloc += nfidlen; +- else { +- /* Find the last extent and truncate it to proper size */ +- while (udf_next_aext(dir, &epos, &eloc, &elen, 1) == +- (EXT_RECORDED_ALLOCATED >> 30)) +- ; +- elen -= dinfo->i_lenExtents - dir->i_size; +- if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) +- epos.offset -= sizeof(struct short_ad); +- else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) +- epos.offset -= sizeof(struct long_ad); +- udf_write_aext(dir, &epos, &eloc, elen, 1); +- dinfo->i_lenExtents = dir->i_size; +- } +- +- mark_inode_dirty(dir); +- goto out_ok; ++ iter->fi.fileVersionNum = cpu_to_le16(1); ++ iter->fi.lengthFileIdent = namelen; ++ iter->fi.lengthOfImpUse = cpu_to_le16(0); ++ memcpy(iter->namebuf, name, namelen); ++ iter->name = iter->namebuf; ++ ++ dir->i_size += nfidlen; ++ if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { ++ dinfo->i_lenAlloc += nfidlen; + } else { +- *err = -EIO; +- goto out_err; ++ /* Truncate last extent to proper size */ ++ udf_fiiter_update_elen(iter, iter->elen - ++ (dinfo->i_lenExtents - dir->i_size)); + } ++ mark_inode_dirty(dir); + +-out_err: +- fi = NULL; +- if (fibh->sbh != fibh->ebh) +- brelse(fibh->ebh); +- brelse(fibh->sbh); +-out_ok: +- brelse(epos.bh); +- kfree(name); +- return fi; ++ return 0; + } + +-static int udf_delete_entry(struct inode *inode, struct fileIdentDesc *fi, +- struct udf_fileident_bh *fibh, +- struct fileIdentDesc *cfi) ++static void udf_fiiter_delete_entry(struct udf_fileident_iter *iter) + { +- cfi->fileCharacteristics |= FID_FILE_CHAR_DELETED; ++ iter->fi.fileCharacteristics |= FID_FILE_CHAR_DELETED; + +- if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT)) +- memset(&(cfi->icb), 0x00, sizeof(struct long_ad)); ++ if (UDF_QUERY_FLAG(iter->dir->i_sb, UDF_FLAG_STRICT)) ++ memset(&iter->fi.icb, 0x00, sizeof(struct long_ad)); + +- return udf_write_fi(inode, cfi, fi, fibh, NULL, NULL); ++ udf_fiiter_write_fi(iter, NULL); + } + + static int udf_add_nondir(struct dentry *dentry, struct inode *inode) + { + struct udf_inode_info *iinfo = UDF_I(inode); + struct inode *dir = d_inode(dentry->d_parent); +- struct udf_fileident_bh fibh; +- struct fileIdentDesc cfi, *fi; ++ struct udf_fileident_iter iter; + int err; + +- fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err); +- if (unlikely(!fi)) { ++ err = udf_fiiter_add_entry(dir, dentry, &iter); ++ if (err) { + inode_dec_link_count(inode); + discard_new_inode(inode); + return err; + } +- cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); +- cfi.icb.extLocation = cpu_to_lelb(iinfo->i_location); +- *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = ++ iter.fi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); ++ iter.fi.icb.extLocation = cpu_to_lelb(iinfo->i_location); ++ *(__le32 *)((struct allocDescImpUse *)iter.fi.icb.impUse)->impUse = + cpu_to_le32(iinfo->i_unique & 0x00000000FFFFFFFFUL); +- udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); ++ udf_fiiter_write_fi(&iter, NULL); + dir->i_ctime = dir->i_mtime = current_time(dir); + mark_inode_dirty(dir); +- if (fibh.sbh != fibh.ebh) +- brelse(fibh.ebh); +- brelse(fibh.sbh); ++ udf_fiiter_release(&iter); + d_instantiate_new(dentry, inode); + + return 0; +@@ -665,8 +421,7 @@ static int udf_mkdir(struct user_namespace *mnt_userns, struct inode *dir, + struct dentry *dentry, umode_t mode) + { + struct inode *inode; +- struct udf_fileident_bh fibh; +- struct fileIdentDesc cfi, *fi; ++ struct udf_fileident_iter iter; + int err; + struct udf_inode_info *dinfo = UDF_I(dir); + struct udf_inode_info *iinfo; +@@ -678,144 +433,82 @@ static int udf_mkdir(struct user_namespace *mnt_userns, struct inode *dir, + iinfo = UDF_I(inode); + inode->i_op = &udf_dir_inode_operations; + inode->i_fop = &udf_dir_operations; +- fi = udf_add_entry(inode, NULL, &fibh, &cfi, &err); +- if (!fi) { +- inode_dec_link_count(inode); ++ err = udf_fiiter_add_entry(inode, NULL, &iter); ++ if (err) { ++ clear_nlink(inode); + discard_new_inode(inode); +- goto out; ++ return err; + } + set_nlink(inode, 2); +- cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); +- cfi.icb.extLocation = cpu_to_lelb(dinfo->i_location); +- *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = ++ iter.fi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); ++ iter.fi.icb.extLocation = cpu_to_lelb(dinfo->i_location); ++ *(__le32 *)((struct allocDescImpUse *)iter.fi.icb.impUse)->impUse = + cpu_to_le32(dinfo->i_unique & 0x00000000FFFFFFFFUL); +- cfi.fileCharacteristics = ++ iter.fi.fileCharacteristics = + FID_FILE_CHAR_DIRECTORY | FID_FILE_CHAR_PARENT; +- udf_write_fi(inode, &cfi, fi, &fibh, NULL, NULL); +- brelse(fibh.sbh); ++ udf_fiiter_write_fi(&iter, NULL); ++ udf_fiiter_release(&iter); + mark_inode_dirty(inode); + +- fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err); +- if (!fi) { ++ err = udf_fiiter_add_entry(dir, dentry, &iter); ++ if (err) { + clear_nlink(inode); +- mark_inode_dirty(inode); + discard_new_inode(inode); +- goto out; ++ return err; + } +- cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); +- cfi.icb.extLocation = cpu_to_lelb(iinfo->i_location); +- *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = ++ iter.fi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); ++ iter.fi.icb.extLocation = cpu_to_lelb(iinfo->i_location); ++ *(__le32 *)((struct allocDescImpUse *)iter.fi.icb.impUse)->impUse = + cpu_to_le32(iinfo->i_unique & 0x00000000FFFFFFFFUL); +- cfi.fileCharacteristics |= FID_FILE_CHAR_DIRECTORY; +- udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); ++ iter.fi.fileCharacteristics |= FID_FILE_CHAR_DIRECTORY; ++ udf_fiiter_write_fi(&iter, NULL); ++ udf_fiiter_release(&iter); + inc_nlink(dir); + dir->i_ctime = dir->i_mtime = current_time(dir); + mark_inode_dirty(dir); + d_instantiate_new(dentry, inode); +- if (fibh.sbh != fibh.ebh) +- brelse(fibh.ebh); +- brelse(fibh.sbh); +- err = 0; + +-out: +- return err; ++ return 0; + } + + static int empty_dir(struct inode *dir) + { +- struct fileIdentDesc *fi, cfi; +- struct udf_fileident_bh fibh; +- loff_t f_pos; +- loff_t size = udf_ext0_offset(dir) + dir->i_size; +- udf_pblk_t block; +- struct kernel_lb_addr eloc; +- uint32_t elen; +- sector_t offset; +- struct extent_position epos = {}; +- struct udf_inode_info *dinfo = UDF_I(dir); +- +- f_pos = udf_ext0_offset(dir); +- fibh.soffset = fibh.eoffset = f_pos & (dir->i_sb->s_blocksize - 1); +- +- if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) +- fibh.sbh = fibh.ebh = NULL; +- else if (inode_bmap(dir, f_pos >> dir->i_sb->s_blocksize_bits, +- &epos, &eloc, &elen, &offset) == +- (EXT_RECORDED_ALLOCATED >> 30)) { +- block = udf_get_lb_pblock(dir->i_sb, &eloc, offset); +- if ((++offset << dir->i_sb->s_blocksize_bits) < elen) { +- if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) +- epos.offset -= sizeof(struct short_ad); +- else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) +- epos.offset -= sizeof(struct long_ad); +- } else +- offset = 0; +- +- fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block); +- if (!fibh.sbh) { +- brelse(epos.bh); ++ struct udf_fileident_iter iter; ++ int ret; ++ ++ for (ret = udf_fiiter_init(&iter, dir, 0); ++ !ret && iter.pos < dir->i_size; ++ ret = udf_fiiter_advance(&iter)) { ++ if (iter.fi.lengthFileIdent && ++ !(iter.fi.fileCharacteristics & FID_FILE_CHAR_DELETED)) { ++ udf_fiiter_release(&iter); + return 0; + } +- } else { +- brelse(epos.bh); +- return 0; + } +- +- while (f_pos < size) { +- fi = udf_fileident_read(dir, &f_pos, &fibh, &cfi, &epos, &eloc, +- &elen, &offset); +- if (!fi) { +- if (fibh.sbh != fibh.ebh) +- brelse(fibh.ebh); +- brelse(fibh.sbh); +- brelse(epos.bh); +- return 0; +- } +- +- if (cfi.lengthFileIdent && +- (cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) == 0) { +- if (fibh.sbh != fibh.ebh) +- brelse(fibh.ebh); +- brelse(fibh.sbh); +- brelse(epos.bh); +- return 0; +- } +- } +- +- if (fibh.sbh != fibh.ebh) +- brelse(fibh.ebh); +- brelse(fibh.sbh); +- brelse(epos.bh); ++ udf_fiiter_release(&iter); + + return 1; + } + + static int udf_rmdir(struct inode *dir, struct dentry *dentry) + { +- int retval; ++ int ret; + struct inode *inode = d_inode(dentry); +- struct udf_fileident_bh fibh; +- struct fileIdentDesc *fi, cfi; ++ struct udf_fileident_iter iter; + struct kernel_lb_addr tloc; + +- retval = -ENOENT; +- fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi); +- if (IS_ERR_OR_NULL(fi)) { +- if (fi) +- retval = PTR_ERR(fi); ++ ret = udf_fiiter_find_entry(dir, &dentry->d_name, &iter); ++ if (ret) + goto out; +- } + +- retval = -EIO; +- tloc = lelb_to_cpu(cfi.icb.extLocation); ++ ret = -EFSCORRUPTED; ++ tloc = lelb_to_cpu(iter.fi.icb.extLocation); + if (udf_get_lb_pblock(dir->i_sb, &tloc, 0) != inode->i_ino) + goto end_rmdir; +- retval = -ENOTEMPTY; ++ ret = -ENOTEMPTY; + if (!empty_dir(inode)) + goto end_rmdir; +- retval = udf_delete_entry(dir, fi, &fibh, &cfi); +- if (retval) +- goto end_rmdir; ++ udf_fiiter_delete_entry(&iter); + if (inode->i_nlink != 2) + udf_warn(inode->i_sb, "empty directory has nlink != 2 (%u)\n", + inode->i_nlink); +@@ -825,36 +518,26 @@ static int udf_rmdir(struct inode *dir, struct dentry *dentry) + inode->i_ctime = dir->i_ctime = dir->i_mtime = + current_time(inode); + mark_inode_dirty(dir); +- ++ ret = 0; + end_rmdir: +- if (fibh.sbh != fibh.ebh) +- brelse(fibh.ebh); +- brelse(fibh.sbh); +- ++ udf_fiiter_release(&iter); + out: +- return retval; ++ return ret; + } + + static int udf_unlink(struct inode *dir, struct dentry *dentry) + { +- int retval; ++ int ret; + struct inode *inode = d_inode(dentry); +- struct udf_fileident_bh fibh; +- struct fileIdentDesc *fi; +- struct fileIdentDesc cfi; ++ struct udf_fileident_iter iter; + struct kernel_lb_addr tloc; + +- retval = -ENOENT; +- fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi); +- +- if (IS_ERR_OR_NULL(fi)) { +- if (fi) +- retval = PTR_ERR(fi); ++ ret = udf_fiiter_find_entry(dir, &dentry->d_name, &iter); ++ if (ret) + goto out; +- } + +- retval = -EIO; +- tloc = lelb_to_cpu(cfi.icb.extLocation); ++ ret = -EFSCORRUPTED; ++ tloc = lelb_to_cpu(iter.fi.icb.extLocation); + if (udf_get_lb_pblock(dir->i_sb, &tloc, 0) != inode->i_ino) + goto end_unlink; + +@@ -863,22 +546,16 @@ static int udf_unlink(struct inode *dir, struct dentry *dentry) + inode->i_ino, inode->i_nlink); + set_nlink(inode, 1); + } +- retval = udf_delete_entry(dir, fi, &fibh, &cfi); +- if (retval) +- goto end_unlink; ++ udf_fiiter_delete_entry(&iter); + dir->i_ctime = dir->i_mtime = current_time(dir); + mark_inode_dirty(dir); + inode_dec_link_count(inode); + inode->i_ctime = dir->i_ctime; +- retval = 0; +- ++ ret = 0; + end_unlink: +- if (fibh.sbh != fibh.ebh) +- brelse(fibh.ebh); +- brelse(fibh.sbh); +- ++ udf_fiiter_release(&iter); + out: +- return retval; ++ return ret; + } + + static int udf_symlink(struct user_namespace *mnt_userns, struct inode *dir, +@@ -1038,27 +715,21 @@ static int udf_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry) + { + struct inode *inode = d_inode(old_dentry); +- struct udf_fileident_bh fibh; +- struct fileIdentDesc cfi, *fi; ++ struct udf_fileident_iter iter; + int err; + +- fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err); +- if (!fi) { ++ err = udf_fiiter_add_entry(dir, dentry, &iter); ++ if (err) + return err; +- } +- cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); +- cfi.icb.extLocation = cpu_to_lelb(UDF_I(inode)->i_location); ++ iter.fi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); ++ iter.fi.icb.extLocation = cpu_to_lelb(UDF_I(inode)->i_location); + if (UDF_SB(inode->i_sb)->s_lvid_bh) { +- *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = ++ *(__le32 *)((struct allocDescImpUse *)iter.fi.icb.impUse)->impUse = + cpu_to_le32(lvid_get_unique_id(inode->i_sb)); + } +- udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); +- if (UDF_I(dir)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) +- mark_inode_dirty(dir); ++ udf_fiiter_write_fi(&iter, NULL); ++ udf_fiiter_release(&iter); + +- if (fibh.sbh != fibh.ebh) +- brelse(fibh.ebh); +- brelse(fibh.sbh); + inc_nlink(inode); + inode->i_ctime = current_time(inode); + mark_inode_dirty(inode); +@@ -1079,78 +750,68 @@ static int udf_rename(struct user_namespace *mnt_userns, struct inode *old_dir, + { + struct inode *old_inode = d_inode(old_dentry); + struct inode *new_inode = d_inode(new_dentry); +- struct udf_fileident_bh ofibh, nfibh; +- struct fileIdentDesc *ofi = NULL, *nfi = NULL, *dir_fi = NULL; +- struct fileIdentDesc ocfi, ncfi; +- struct buffer_head *dir_bh = NULL; +- int retval = -ENOENT; ++ struct udf_fileident_iter oiter, niter, diriter; ++ bool has_diriter = false; ++ int retval; + struct kernel_lb_addr tloc; +- struct udf_inode_info *old_iinfo = UDF_I(old_inode); + + if (flags & ~RENAME_NOREPLACE) + return -EINVAL; + +- ofi = udf_find_entry(old_dir, &old_dentry->d_name, &ofibh, &ocfi); +- if (!ofi || IS_ERR(ofi)) { +- if (IS_ERR(ofi)) +- retval = PTR_ERR(ofi); +- goto end_rename; +- } +- +- if (ofibh.sbh != ofibh.ebh) +- brelse(ofibh.ebh); +- +- brelse(ofibh.sbh); +- tloc = lelb_to_cpu(ocfi.icb.extLocation); +- if (udf_get_lb_pblock(old_dir->i_sb, &tloc, 0) != old_inode->i_ino) +- goto end_rename; ++ retval = udf_fiiter_find_entry(old_dir, &old_dentry->d_name, &oiter); ++ if (retval) ++ return retval; + +- nfi = udf_find_entry(new_dir, &new_dentry->d_name, &nfibh, &ncfi); +- if (IS_ERR(nfi)) { +- retval = PTR_ERR(nfi); +- goto end_rename; +- } +- if (nfi && !new_inode) { +- if (nfibh.sbh != nfibh.ebh) +- brelse(nfibh.ebh); +- brelse(nfibh.sbh); +- nfi = NULL; ++ tloc = lelb_to_cpu(oiter.fi.icb.extLocation); ++ if (udf_get_lb_pblock(old_dir->i_sb, &tloc, 0) != old_inode->i_ino) { ++ retval = -ENOENT; ++ goto out_oiter; + } +- if (S_ISDIR(old_inode->i_mode)) { +- int offset = udf_ext0_offset(old_inode); + ++ if (S_ISDIR(old_inode->i_mode)) { + if (new_inode) { + retval = -ENOTEMPTY; + if (!empty_dir(new_inode)) +- goto end_rename; ++ goto out_oiter; + } +- retval = -EIO; +- if (old_iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { +- dir_fi = udf_get_fileident( +- old_iinfo->i_data - +- (old_iinfo->i_efe ? +- sizeof(struct extendedFileEntry) : +- sizeof(struct fileEntry)), +- old_inode->i_sb->s_blocksize, &offset); +- } else { +- dir_bh = udf_bread(old_inode, 0, 0, &retval); +- if (!dir_bh) +- goto end_rename; +- dir_fi = udf_get_fileident(dir_bh->b_data, +- old_inode->i_sb->s_blocksize, &offset); ++ retval = udf_fiiter_find_entry(old_inode, &dotdot_name, ++ &diriter); ++ if (retval == -ENOENT) { ++ udf_err(old_inode->i_sb, ++ "directory (ino %lu) has no '..' entry\n", ++ old_inode->i_ino); ++ retval = -EFSCORRUPTED; + } +- if (!dir_fi) +- goto end_rename; +- tloc = lelb_to_cpu(dir_fi->icb.extLocation); ++ if (retval) ++ goto out_oiter; ++ has_diriter = true; ++ tloc = lelb_to_cpu(diriter.fi.icb.extLocation); + if (udf_get_lb_pblock(old_inode->i_sb, &tloc, 0) != +- old_dir->i_ino) +- goto end_rename; ++ old_dir->i_ino) { ++ retval = -EFSCORRUPTED; ++ udf_err(old_inode->i_sb, ++ "directory (ino %lu) has parent entry pointing to another inode (%lu != %u)\n", ++ old_inode->i_ino, old_dir->i_ino, ++ udf_get_lb_pblock(old_inode->i_sb, &tloc, 0)); ++ goto out_oiter; ++ } + } +- if (!nfi) { +- nfi = udf_add_entry(new_dir, new_dentry, &nfibh, &ncfi, +- &retval); +- if (!nfi) +- goto end_rename; ++ ++ retval = udf_fiiter_find_entry(new_dir, &new_dentry->d_name, &niter); ++ if (retval && retval != -ENOENT) ++ goto out_oiter; ++ /* Entry found but not passed by VFS? */ ++ if (!retval && !new_inode) { ++ retval = -EFSCORRUPTED; ++ udf_fiiter_release(&niter); ++ goto out_oiter; ++ } ++ /* Entry not found? Need to add one... */ ++ if (retval) { ++ udf_fiiter_release(&niter); ++ retval = udf_fiiter_add_entry(new_dir, new_dentry, &niter); ++ if (retval) ++ goto out_oiter; + } + + /* +@@ -1163,14 +824,26 @@ static int udf_rename(struct user_namespace *mnt_userns, struct inode *old_dir, + /* + * ok, that's it + */ +- ncfi.fileVersionNum = ocfi.fileVersionNum; +- ncfi.fileCharacteristics = ocfi.fileCharacteristics; +- memcpy(&(ncfi.icb), &(ocfi.icb), sizeof(ocfi.icb)); +- udf_write_fi(new_dir, &ncfi, nfi, &nfibh, NULL, NULL); ++ niter.fi.fileVersionNum = oiter.fi.fileVersionNum; ++ niter.fi.fileCharacteristics = oiter.fi.fileCharacteristics; ++ memcpy(&(niter.fi.icb), &(oiter.fi.icb), sizeof(oiter.fi.icb)); ++ udf_fiiter_write_fi(&niter, NULL); ++ udf_fiiter_release(&niter); + +- /* The old fid may have moved - find it again */ +- ofi = udf_find_entry(old_dir, &old_dentry->d_name, &ofibh, &ocfi); +- udf_delete_entry(old_dir, ofi, &ofibh, &ocfi); ++ /* ++ * The old entry may have moved due to new entry allocation. Find it ++ * again. ++ */ ++ udf_fiiter_release(&oiter); ++ retval = udf_fiiter_find_entry(old_dir, &old_dentry->d_name, &oiter); ++ if (retval) { ++ udf_err(old_dir->i_sb, ++ "failed to find renamed entry again in directory (ino %lu)\n", ++ old_dir->i_ino); ++ } else { ++ udf_fiiter_delete_entry(&oiter); ++ udf_fiiter_release(&oiter); ++ } + + if (new_inode) { + new_inode->i_ctime = current_time(new_inode); +@@ -1181,13 +854,11 @@ static int udf_rename(struct user_namespace *mnt_userns, struct inode *old_dir, + mark_inode_dirty(old_dir); + mark_inode_dirty(new_dir); + +- if (dir_fi) { +- dir_fi->icb.extLocation = cpu_to_lelb(UDF_I(new_dir)->i_location); +- udf_update_tag((char *)dir_fi, udf_dir_entry_len(dir_fi)); +- if (old_iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) +- mark_inode_dirty(old_inode); +- else +- mark_buffer_dirty_inode(dir_bh, old_inode); ++ if (has_diriter) { ++ diriter.fi.icb.extLocation = ++ cpu_to_lelb(UDF_I(new_dir)->i_location); ++ udf_fiiter_write_fi(&diriter, NULL); ++ udf_fiiter_release(&diriter); + + inode_dec_link_count(old_dir); + if (new_inode) +@@ -1197,22 +868,11 @@ static int udf_rename(struct user_namespace *mnt_userns, struct inode *old_dir, + mark_inode_dirty(new_dir); + } + } +- +- if (ofi) { +- if (ofibh.sbh != ofibh.ebh) +- brelse(ofibh.ebh); +- brelse(ofibh.sbh); +- } +- +- retval = 0; +- +-end_rename: +- brelse(dir_bh); +- if (nfi) { +- if (nfibh.sbh != nfibh.ebh) +- brelse(nfibh.ebh); +- brelse(nfibh.sbh); +- } ++ return 0; ++out_oiter: ++ if (has_diriter) ++ udf_fiiter_release(&diriter); ++ udf_fiiter_release(&oiter); + + return retval; + } +@@ -1221,17 +881,15 @@ static struct dentry *udf_get_parent(struct dentry *child) + { + struct kernel_lb_addr tloc; + struct inode *inode = NULL; +- struct fileIdentDesc cfi; +- struct udf_fileident_bh fibh; +- +- if (!udf_find_entry(d_inode(child), &dotdot_name, &fibh, &cfi)) +- return ERR_PTR(-EACCES); ++ struct udf_fileident_iter iter; ++ int err; + +- if (fibh.sbh != fibh.ebh) +- brelse(fibh.ebh); +- brelse(fibh.sbh); ++ err = udf_fiiter_find_entry(d_inode(child), &dotdot_name, &iter); ++ if (err) ++ return ERR_PTR(err); + +- tloc = lelb_to_cpu(cfi.icb.extLocation); ++ tloc = lelb_to_cpu(iter.fi.icb.extLocation); ++ udf_fiiter_release(&iter); + inode = udf_iget(child->d_sb, &tloc); + if (IS_ERR(inode)) + return ERR_CAST(inode); +diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h +index 7e258f15b8ef52..f764b4d15094d4 100644 +--- a/fs/udf/udfdecl.h ++++ b/fs/udf/udfdecl.h +@@ -86,11 +86,22 @@ extern const struct address_space_operations udf_aops; + extern const struct address_space_operations udf_adinicb_aops; + extern const struct address_space_operations udf_symlink_aops; + +-struct udf_fileident_bh { +- struct buffer_head *sbh; +- struct buffer_head *ebh; +- int soffset; +- int eoffset; ++struct udf_fileident_iter { ++ struct inode *dir; /* Directory we are working with */ ++ loff_t pos; /* Logical position in a dir */ ++ struct buffer_head *bh[2]; /* Buffer containing 'pos' and possibly ++ * next buffer if entry straddles ++ * blocks */ ++ struct kernel_lb_addr eloc; /* Start of extent containing 'pos' */ ++ uint32_t elen; /* Length of extent containing 'pos' */ ++ sector_t loffset; /* Block offset of 'pos' within above ++ * extent */ ++ struct extent_position epos; /* Position after the above extent */ ++ struct fileIdentDesc fi; /* Copied directory entry */ ++ uint8_t *name; /* Pointer to entry name */ ++ uint8_t namebuf[UDF_NAME_LEN_CS0]; /* Storage for entry name in case ++ * the name is split between two blocks ++ */ + }; + + struct udf_vds_record { +@@ -121,19 +132,12 @@ struct inode *udf_find_metadata_inode_efe(struct super_block *sb, + u32 meta_file_loc, u32 partition_num); + + /* namei.c */ +-extern int udf_write_fi(struct inode *inode, struct fileIdentDesc *, +- struct fileIdentDesc *, struct udf_fileident_bh *, +- uint8_t *, uint8_t *); + static inline unsigned int udf_dir_entry_len(struct fileIdentDesc *cfi) + { + return ALIGN(sizeof(struct fileIdentDesc) + + le16_to_cpu(cfi->lengthOfImpUse) + cfi->lengthFileIdent, + UDF_NAME_PAD); + } +-static inline uint8_t *udf_get_fi_ident(struct fileIdentDesc *fi) +-{ +- return ((uint8_t *)(fi + 1)) + le16_to_cpu(fi->lengthOfImpUse); +-} + + /* file.c */ + extern long udf_ioctl(struct file *, unsigned int, unsigned long); +@@ -151,8 +155,6 @@ static inline struct inode *udf_iget(struct super_block *sb, + return __udf_iget(sb, ino, false); + } + extern int udf_expand_file_adinicb(struct inode *); +-extern struct buffer_head *udf_expand_dir_adinicb(struct inode *inode, +- udf_pblk_t *block, int *err); + extern struct buffer_head *udf_bread(struct inode *inode, udf_pblk_t block, + int create, int *err); + extern int udf_setsize(struct inode *, loff_t); +@@ -243,14 +245,13 @@ extern udf_pblk_t udf_new_block(struct super_block *sb, struct inode *inode, + uint16_t partition, uint32_t goal, int *err); + + /* directory.c */ +-extern struct fileIdentDesc *udf_fileident_read(struct inode *, loff_t *, +- struct udf_fileident_bh *, +- struct fileIdentDesc *, +- struct extent_position *, +- struct kernel_lb_addr *, uint32_t *, +- sector_t *); +-extern struct fileIdentDesc *udf_get_fileident(void *buffer, int bufsize, +- int *offset); ++int udf_fiiter_init(struct udf_fileident_iter *iter, struct inode *dir, ++ loff_t pos); ++int udf_fiiter_advance(struct udf_fileident_iter *iter); ++void udf_fiiter_release(struct udf_fileident_iter *iter); ++void udf_fiiter_write_fi(struct udf_fileident_iter *iter, uint8_t *impuse); ++void udf_fiiter_update_elen(struct udf_fileident_iter *iter, uint32_t new_elen); ++int udf_fiiter_append_blk(struct udf_fileident_iter *iter); + extern struct long_ad *udf_get_filelongad(uint8_t *, int, uint32_t *, int); + extern struct short_ad *udf_get_fileshortad(uint8_t *, int, uint32_t *, int); + +diff --git a/include/linux/fsl/enetc_mdio.h b/include/linux/fsl/enetc_mdio.h +index 2d920331486581..b90c4dc50b7dec 100644 +--- a/include/linux/fsl/enetc_mdio.h ++++ b/include/linux/fsl/enetc_mdio.h +@@ -48,7 +48,8 @@ static inline int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum) + static inline int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, + u16 value) + { return -EINVAL; } +-struct enetc_hw *enetc_hw_alloc(struct device *dev, void __iomem *port_regs) ++static inline struct enetc_hw *enetc_hw_alloc(struct device *dev, ++ void __iomem *port_regs) + { return ERR_PTR(-EINVAL); } + + #endif +diff --git a/include/linux/irqchip/arm-gic-v4.h b/include/linux/irqchip/arm-gic-v4.h +index 2c63375bbd43f4..bf9e0640288d1d 100644 +--- a/include/linux/irqchip/arm-gic-v4.h ++++ b/include/linux/irqchip/arm-gic-v4.h +@@ -58,10 +58,12 @@ struct its_vpe { + bool enabled; + bool group; + } sgi_config[16]; +- atomic_t vmapp_count; + }; + }; + ++ /* Track the VPE being mapped */ ++ atomic_t vmapp_count; ++ + /* + * Ensures mutual exclusion between affinity setting of the + * vPE and vLPI operations using vpe->col_idx. +diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c +index 8ed2c65529714f..f1ab0cd987273b 100644 +--- a/io_uring/io_uring.c ++++ b/io_uring/io_uring.c +@@ -56,6 +56,7 @@ + #include <linux/mm.h> + #include <linux/mman.h> + #include <linux/percpu.h> ++#include <linux/cpuset.h> + #include <linux/slab.h> + #include <linux/blkdev.h> + #include <linux/bvec.h> +@@ -1582,7 +1583,14 @@ static inline bool io_sqring_full(struct io_ring_ctx *ctx) + { + struct io_rings *r = ctx->rings; + +- return READ_ONCE(r->sq.tail) - ctx->cached_sq_head == ctx->sq_entries; ++ /* ++ * SQPOLL must use the actual sqring head, as using the cached_sq_head ++ * is race prone if the SQPOLL thread has grabbed entries but not yet ++ * committed them to the ring. For !SQPOLL, this doesn't matter, but ++ * since this helper is just used for SQPOLL sqring waits (or POLLOUT), ++ * just read the actual sqring head unconditionally. ++ */ ++ return READ_ONCE(r->sq.tail) - READ_ONCE(r->sq.head) == ctx->sq_entries; + } + + static inline unsigned int __io_cqring_events(struct io_ring_ctx *ctx) +@@ -8746,11 +8754,22 @@ static int io_sq_offload_create(struct io_ring_ctx *ctx, + return 0; + + if (p->flags & IORING_SETUP_SQ_AFF) { ++ cpumask_var_t allowed_mask; + int cpu = p->sq_thread_cpu; + + ret = -EINVAL; + if (cpu >= nr_cpu_ids || !cpu_online(cpu)) + goto err_sqpoll; ++ ret = -ENOMEM; ++ if (!alloc_cpumask_var(&allowed_mask, GFP_KERNEL)) ++ goto err_sqpoll; ++ ret = -EINVAL; ++ cpuset_cpus_allowed(current, allowed_mask); ++ if (!cpumask_test_cpu(cpu, allowed_mask)) { ++ free_cpumask_var(allowed_mask); ++ goto err_sqpoll; ++ } ++ free_cpumask_var(allowed_mask); + sqd->sq_cpu = cpu; + } else { + sqd->sq_cpu = -1; +diff --git a/kernel/time/posix-clock.c b/kernel/time/posix-clock.c +index 77c0c2370b6d1d..8127673bfc45e6 100644 +--- a/kernel/time/posix-clock.c ++++ b/kernel/time/posix-clock.c +@@ -299,6 +299,9 @@ static int pc_clock_settime(clockid_t id, const struct timespec64 *ts) + goto out; + } + ++ if (!timespec64_valid_strict(ts)) ++ return -EINVAL; ++ + if (cd.clk->ops.clock_settime) + err = cd.clk->ops.clock_settime(cd.clk, ts); + else +diff --git a/mm/secretmem.c b/mm/secretmem.c +index d1986ce2e7c779..624663a9480830 100644 +--- a/mm/secretmem.c ++++ b/mm/secretmem.c +@@ -234,7 +234,7 @@ SYSCALL_DEFINE1(memfd_secret, unsigned int, flags) + /* make sure local flags do not confict with global fcntl.h */ + BUILD_BUG_ON(SECRETMEM_FLAGS_MASK & O_CLOEXEC); + +- if (!secretmem_enable) ++ if (!secretmem_enable || !can_set_direct_map()) + return -ENOSYS; + + if (flags & ~(SECRETMEM_FLAGS_MASK | O_CLOEXEC)) +@@ -278,7 +278,7 @@ static int secretmem_init(void) + { + int ret = 0; + +- if (!secretmem_enable) ++ if (!secretmem_enable || !can_set_direct_map()) + return ret; + + secretmem_mnt = kern_mount(&secretmem_fs); +diff --git a/mm/swapfile.c b/mm/swapfile.c +index fec3e736a19483..c9b9053c796984 100644 +--- a/mm/swapfile.c ++++ b/mm/swapfile.c +@@ -2125,7 +2125,7 @@ static int unuse_mm(struct mm_struct *mm, unsigned int type, + + mmap_read_lock(mm); + for (vma = mm->mmap; vma; vma = vma->vm_next) { +- if (vma->anon_vma) { ++ if (vma->anon_vma && !is_vm_hugetlb_page(vma)) { + ret = unuse_vma(vma, type, frontswap, + fs_pages_to_unuse); + if (ret) +diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c +index b7498e890f0b03..9c9c855b9736e0 100644 +--- a/net/bluetooth/af_bluetooth.c ++++ b/net/bluetooth/af_bluetooth.c +@@ -779,6 +779,7 @@ static int __init bt_init(void) + bt_sysfs_cleanup(); + cleanup_led: + bt_leds_cleanup(); ++ debugfs_remove_recursive(bt_debugfs); + return err; + } + +diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c +index 2c9670c8320204..44eedae43eaa47 100644 +--- a/net/ipv4/tcp_output.c ++++ b/net/ipv4/tcp_output.c +@@ -2308,7 +2308,7 @@ static bool tcp_can_coalesce_send_queue_head(struct sock *sk, int len) + if (len <= skb->len) + break; + +- if (unlikely(TCP_SKB_CB(skb)->eor) || tcp_has_tx_tstamp(skb)) ++ if (tcp_has_tx_tstamp(skb) || !tcp_skb_can_collapse(skb, next)) + return false; + + len -= skb->len; +diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c +index f652982a106bae..c54b3be62c0abf 100644 +--- a/net/mac80211/cfg.c ++++ b/net/mac80211/cfg.c +@@ -511,6 +511,9 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, + sta->cipher_scheme = cs; + + err = ieee80211_key_link(key, sdata, sta); ++ /* KRACK protection, shouldn't happen but just silently accept key */ ++ if (err == -EALREADY) ++ err = 0; + + out_unlock: + mutex_unlock(&local->sta_mtx); +diff --git a/net/mac80211/key.c b/net/mac80211/key.c +index f695fc80088bc9..7b427e39831bd0 100644 +--- a/net/mac80211/key.c ++++ b/net/mac80211/key.c +@@ -843,7 +843,7 @@ int ieee80211_key_link(struct ieee80211_key *key, + */ + if (ieee80211_key_identical(sdata, old_key, key)) { + ieee80211_key_free_unused(key); +- ret = 0; ++ ret = -EALREADY; + goto out; + } + +diff --git a/net/mptcp/mib.c b/net/mptcp/mib.c +index c2fadfcfd6d6a4..3e773259fa8335 100644 +--- a/net/mptcp/mib.c ++++ b/net/mptcp/mib.c +@@ -15,6 +15,7 @@ static const struct snmp_mib mptcp_snmp_list[] = { + SNMP_MIB_ITEM("MPCapableACKRX", MPTCP_MIB_MPCAPABLEPASSIVEACK), + SNMP_MIB_ITEM("MPCapableFallbackACK", MPTCP_MIB_MPCAPABLEPASSIVEFALLBACK), + SNMP_MIB_ITEM("MPCapableFallbackSYNACK", MPTCP_MIB_MPCAPABLEACTIVEFALLBACK), ++ SNMP_MIB_ITEM("MPCapableEndpAttempt", MPTCP_MIB_MPCAPABLEENDPATTEMPT), + SNMP_MIB_ITEM("MPFallbackTokenInit", MPTCP_MIB_TOKENFALLBACKINIT), + SNMP_MIB_ITEM("MPTCPRetrans", MPTCP_MIB_RETRANSSEGS), + SNMP_MIB_ITEM("MPJoinNoTokenFound", MPTCP_MIB_JOINNOTOKEN), +@@ -26,6 +27,8 @@ static const struct snmp_mib mptcp_snmp_list[] = { + SNMP_MIB_ITEM("MPJoinAckRx", MPTCP_MIB_JOINACKRX), + SNMP_MIB_ITEM("MPJoinAckHMacFailure", MPTCP_MIB_JOINACKMAC), + SNMP_MIB_ITEM("DSSNotMatching", MPTCP_MIB_DSSNOMATCH), ++ SNMP_MIB_ITEM("DSSCorruptionFallback", MPTCP_MIB_DSSCORRUPTIONFALLBACK), ++ SNMP_MIB_ITEM("DSSCorruptionReset", MPTCP_MIB_DSSCORRUPTIONRESET), + SNMP_MIB_ITEM("InfiniteMapRx", MPTCP_MIB_INFINITEMAPRX), + SNMP_MIB_ITEM("DSSNoMatchTCP", MPTCP_MIB_DSSTCPMISMATCH), + SNMP_MIB_ITEM("DataCsumErr", MPTCP_MIB_DATACSUMERR), +diff --git a/net/mptcp/mib.h b/net/mptcp/mib.h +index 90025acdcf7245..0690db18fc95d2 100644 +--- a/net/mptcp/mib.h ++++ b/net/mptcp/mib.h +@@ -8,6 +8,7 @@ enum linux_mptcp_mib_field { + MPTCP_MIB_MPCAPABLEPASSIVEACK, /* Received third ACK with MP_CAPABLE */ + MPTCP_MIB_MPCAPABLEPASSIVEFALLBACK,/* Server-side fallback during 3-way handshake */ + MPTCP_MIB_MPCAPABLEACTIVEFALLBACK, /* Client-side fallback during 3-way handshake */ ++ MPTCP_MIB_MPCAPABLEENDPATTEMPT, /* Prohibited MPC to port-based endp */ + MPTCP_MIB_TOKENFALLBACKINIT, /* Could not init/allocate token */ + MPTCP_MIB_RETRANSSEGS, /* Segments retransmitted at the MPTCP-level */ + MPTCP_MIB_JOINNOTOKEN, /* Received MP_JOIN but the token was not found */ +@@ -19,6 +20,8 @@ enum linux_mptcp_mib_field { + MPTCP_MIB_JOINACKRX, /* Received an ACK + MP_JOIN */ + MPTCP_MIB_JOINACKMAC, /* HMAC was wrong on ACK + MP_JOIN */ + MPTCP_MIB_DSSNOMATCH, /* Received a new mapping that did not match the previous one */ ++ MPTCP_MIB_DSSCORRUPTIONFALLBACK,/* DSS corruption detected, fallback */ ++ MPTCP_MIB_DSSCORRUPTIONRESET, /* DSS corruption detected, MPJ subflow reset */ + MPTCP_MIB_INFINITEMAPRX, /* Received an infinite mapping */ + MPTCP_MIB_DSSTCPMISMATCH, /* DSS-mapping did not map with TCP's sequence numbers */ + MPTCP_MIB_DATACSUMERR, /* The data checksum fail */ +diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c +index e524171291bc77..a7a46d99d5a39e 100644 +--- a/net/mptcp/pm_netlink.c ++++ b/net/mptcp/pm_netlink.c +@@ -793,10 +793,10 @@ static void mptcp_pm_nl_rm_addr_or_subflow(struct mptcp_sock *msk, + i, rm_list->ids[i], subflow->local_id, subflow->remote_id); + spin_unlock_bh(&msk->pm.lock); + mptcp_subflow_shutdown(sk, ssk, how); ++ removed |= subflow->request_join; + mptcp_close_ssk(sk, ssk, subflow); + spin_lock_bh(&msk->pm.lock); + +- removed |= subflow->request_join; + msk->pm.subflows--; + if (rm_type == MPTCP_MIB_RMSUBFLOW) + __MPTCP_INC_STATS(sock_net(sk), rm_type); +@@ -991,6 +991,7 @@ static int mptcp_pm_nl_create_listen_socket(struct sock *sk, + goto out; + } + ++ WRITE_ONCE(mptcp_subflow_ctx(ssock->sk)->pm_listener, true); + err = kernel_listen(ssock, backlog); + if (err) { + pr_warn("kernel_listen error, err=%d", err); +diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c +index da2a1a150bc674..34c98596350e87 100644 +--- a/net/mptcp/protocol.c ++++ b/net/mptcp/protocol.c +@@ -554,6 +554,18 @@ static bool mptcp_check_data_fin(struct sock *sk) + return ret; + } + ++static void mptcp_dss_corruption(struct mptcp_sock *msk, struct sock *ssk) ++{ ++ if (READ_ONCE(msk->allow_infinite_fallback)) { ++ MPTCP_INC_STATS(sock_net(ssk), ++ MPTCP_MIB_DSSCORRUPTIONFALLBACK); ++ mptcp_do_fallback(ssk); ++ } else { ++ MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_DSSCORRUPTIONRESET); ++ mptcp_subflow_reset(ssk); ++ } ++} ++ + static bool __mptcp_move_skbs_from_subflow(struct mptcp_sock *msk, + struct sock *ssk, + unsigned int *bytes) +@@ -626,10 +638,12 @@ static bool __mptcp_move_skbs_from_subflow(struct mptcp_sock *msk, + moved += len; + seq += len; + +- if (WARN_ON_ONCE(map_remaining < len)) +- break; ++ if (unlikely(map_remaining < len)) ++ mptcp_dss_corruption(msk, ssk); + } else { +- WARN_ON_ONCE(!fin); ++ if (unlikely(!fin)) ++ mptcp_dss_corruption(msk, ssk); ++ + sk_eat_skb(ssk, skb); + done = true; + } +@@ -2472,6 +2486,7 @@ static void __mptcp_retrans(struct sock *sk) + dfrag->already_sent = max(dfrag->already_sent, info.sent); + tcp_push(ssk, 0, info.mss_now, tcp_sk(ssk)->nonagle, + info.size_goal); ++ WRITE_ONCE(msk->allow_infinite_fallback, false); + } + + release_sock(ssk); +@@ -2549,6 +2564,7 @@ static int __mptcp_init_sock(struct sock *sk) + msk->first = NULL; + inet_csk(sk)->icsk_sync_mss = mptcp_sync_mss; + WRITE_ONCE(msk->csum_enabled, mptcp_is_checksum_enabled(sock_net(sk))); ++ WRITE_ONCE(msk->allow_infinite_fallback, true); + msk->recovery = false; + + mptcp_pm_data_init(msk); +@@ -3299,6 +3315,7 @@ bool mptcp_finish_join(struct sock *ssk) + if (parent_sock && !ssk->sk_socket) + mptcp_sock_graft(ssk, parent_sock); + subflow->map_seq = READ_ONCE(msk->ack_seq); ++ WRITE_ONCE(msk->allow_infinite_fallback, false); + out: + mptcp_event(MPTCP_EVENT_SUB_ESTABLISHED, msk, ssk, GFP_ATOMIC); + return true; +diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h +index 9e0a5591d4e1f6..8f5e5a66babf35 100644 +--- a/net/mptcp/protocol.h ++++ b/net/mptcp/protocol.h +@@ -249,6 +249,7 @@ struct mptcp_sock { + bool rcv_fastclose; + bool use_64bit_ack; /* Set when we received a 64-bit DSN */ + bool csum_enabled; ++ bool allow_infinite_fallback; + spinlock_t join_list_lock; + int keepalive_cnt; + int keepalive_idle; +@@ -445,6 +446,7 @@ struct mptcp_subflow_context { + close_event_done : 1, /* has done the post-closed part */ + __unused : 11; + enum mptcp_data_avail data_avail; ++ bool pm_listener; /* a listener managed by the kernel PM? */ + u32 remote_nonce; + u64 thmac; + u32 local_nonce; +diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c +index e71082dd648426..d8b33e10750b6b 100644 +--- a/net/mptcp/subflow.c ++++ b/net/mptcp/subflow.c +@@ -129,6 +129,13 @@ static void subflow_add_reset_reason(struct sk_buff *skb, u8 reason) + } + } + ++static int subflow_reset_req_endp(struct request_sock *req, struct sk_buff *skb) ++{ ++ SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_MPCAPABLEENDPATTEMPT); ++ subflow_add_reset_reason(skb, MPTCP_RST_EPROHIBIT); ++ return -EPERM; ++} ++ + /* Init mptcp request socket. + * + * Returns an error code if a JOIN has failed and a TCP reset +@@ -160,6 +167,8 @@ static int subflow_check_req(struct request_sock *req, + if (opt_mp_capable) { + SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_MPCAPABLEPASSIVE); + ++ if (unlikely(listener->pm_listener)) ++ return subflow_reset_req_endp(req, skb); + if (opt_mp_join) + return 0; + } else if (opt_mp_join) { +@@ -167,6 +176,8 @@ static int subflow_check_req(struct request_sock *req, + + if (mp_opt.backup) + SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_JOINSYNBACKUPRX); ++ } else if (unlikely(listener->pm_listener)) { ++ return subflow_reset_req_endp(req, skb); + } + + if (opt_mp_capable && listener->request_mptcp) { +@@ -847,7 +858,7 @@ static bool skb_is_fully_mapped(struct sock *ssk, struct sk_buff *skb) + unsigned int skb_consumed; + + skb_consumed = tcp_sk(ssk)->copied_seq - TCP_SKB_CB(skb)->seq; +- if (WARN_ON_ONCE(skb_consumed >= skb->len)) ++ if (unlikely(skb_consumed >= skb->len)) + return true; + + return skb->len - skb_consumed <= subflow->map_data_len - +@@ -1152,7 +1163,7 @@ static bool subflow_can_fallback(struct mptcp_subflow_context *subflow) + else if (READ_ONCE(msk->csum_enabled)) + return !subflow->valid_csum_seen; + else +- return !subflow->fully_established; ++ return READ_ONCE(msk->allow_infinite_fallback); + } + + static bool subflow_check_data_avail(struct sock *ssk) +@@ -1219,7 +1230,8 @@ static bool subflow_check_data_avail(struct sock *ssk) + fallback: + /* RFC 8684 section 3.7. */ + if (subflow->send_mp_fail) { +- if (mptcp_has_another_subflow(ssk)) { ++ if (mptcp_has_another_subflow(ssk) || ++ !READ_ONCE(msk->allow_infinite_fallback)) { + while ((skb = skb_peek(&ssk->sk_receive_queue))) + sk_eat_skb(ssk, skb); + } +@@ -1481,6 +1493,7 @@ int __mptcp_subflow_connect(struct sock *sk, const struct mptcp_addr_info *loc, + /* discard the subflow socket */ + mptcp_sock_graft(ssk, sk->sk_socket); + iput(SOCK_INODE(sf)); ++ WRITE_ONCE(msk->allow_infinite_fallback, false); + return err; + + failed_unlink: +diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c +index 5b296cacb3896f..3d687d1f7bebb6 100644 +--- a/sound/pci/hda/patch_conexant.c ++++ b/sound/pci/hda/patch_conexant.c +@@ -307,6 +307,7 @@ enum { + CXT_FIXUP_HP_SPECTRE, + CXT_FIXUP_HP_GATE_MIC, + CXT_FIXUP_MUTE_LED_GPIO, ++ CXT_FIXUP_HP_ELITEONE_OUT_DIS, + CXT_FIXUP_HP_ZBOOK_MUTE_LED, + CXT_FIXUP_HEADSET_MIC, + CXT_FIXUP_HP_MIC_NO_PRESENCE, +@@ -324,6 +325,19 @@ static void cxt_fixup_stereo_dmic(struct hda_codec *codec, + spec->gen.inv_dmic_split = 1; + } + ++/* fix widget control pin settings */ ++static void cxt_fixup_update_pinctl(struct hda_codec *codec, ++ const struct hda_fixup *fix, int action) ++{ ++ if (action == HDA_FIXUP_ACT_PROBE) { ++ /* Unset OUT_EN for this Node pin, leaving only HP_EN. ++ * This is the value stored in the codec register after ++ * the correct initialization of the previous windows boot. ++ */ ++ snd_hda_set_pin_ctl_cache(codec, 0x1d, AC_PINCTL_HP_EN); ++ } ++} ++ + static void cxt5066_increase_mic_boost(struct hda_codec *codec, + const struct hda_fixup *fix, int action) + { +@@ -975,6 +989,10 @@ static const struct hda_fixup cxt_fixups[] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt_fixup_mute_led_gpio, + }, ++ [CXT_FIXUP_HP_ELITEONE_OUT_DIS] = { ++ .type = HDA_FIXUP_FUNC, ++ .v.func = cxt_fixup_update_pinctl, ++ }, + [CXT_FIXUP_HP_ZBOOK_MUTE_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt_fixup_hp_zbook_mute_led, +@@ -1065,6 +1083,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = { + SND_PCI_QUIRK(0x103c, 0x83b2, "HP EliteBook 840 G5", CXT_FIXUP_HP_DOCK), + SND_PCI_QUIRK(0x103c, 0x83b3, "HP EliteBook 830 G5", CXT_FIXUP_HP_DOCK), + SND_PCI_QUIRK(0x103c, 0x83d3, "HP ProBook 640 G4", CXT_FIXUP_HP_DOCK), ++ SND_PCI_QUIRK(0x103c, 0x83e5, "HP EliteOne 1000 G2", CXT_FIXUP_HP_ELITEONE_OUT_DIS), + SND_PCI_QUIRK(0x103c, 0x8402, "HP ProBook 645 G4", CXT_FIXUP_MUTE_LED_GPIO), + SND_PCI_QUIRK(0x103c, 0x8427, "HP ZBook Studio G5", CXT_FIXUP_HP_ZBOOK_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x844f, "HP ZBook Studio G5", CXT_FIXUP_HP_ZBOOK_MUTE_LED), +diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c +index d11c581ce9b9a0..243ef8fa400509 100644 +--- a/virt/kvm/kvm_main.c ++++ b/virt/kvm/kvm_main.c +@@ -3528,12 +3528,13 @@ void kvm_vcpu_on_spin(struct kvm_vcpu *me, bool yield_to_kernel_mode) + { + struct kvm *kvm = me->kvm; + struct kvm_vcpu *vcpu; +- int last_boosted_vcpu = me->kvm->last_boosted_vcpu; ++ int last_boosted_vcpu; + int yielded = 0; + int try = 3; + int pass; + int i; + ++ last_boosted_vcpu = READ_ONCE(kvm->last_boosted_vcpu); + kvm_vcpu_set_in_spin_loop(me, true); + /* + * We boost the priority of a VCPU that is runnable but not +@@ -3565,7 +3566,7 @@ void kvm_vcpu_on_spin(struct kvm_vcpu *me, bool yield_to_kernel_mode) + + yielded = kvm_vcpu_yield_to(vcpu); + if (yielded > 0) { +- kvm->last_boosted_vcpu = i; ++ WRITE_ONCE(kvm->last_boosted_vcpu, i); + break; + } else if (yielded < 0) { + try--;
