Merge 19cbc75c56 ("Merge tag 'sound-7.1-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound") into android-mainline

Steps on the way to v7.1-rc3

Change-Id: Iec6f394d4eaee71a60904af1d0a50e5dca9608be
Signed-off-by: Carlos Llamas <cmllamas@google.com>
This commit is contained in:
Carlos Llamas committed 2026-05-26 23:54:05 +00:00
commit a04515d334
88 files changed
+1136 -836

No files matched your search

+1
View File
@@ -24658,6 +24658,7 @@ S: Maintained
F: fs/smb/client/smbdirect.*
F: fs/smb/smbdirect/
F: fs/smb/server/transport_rdma.*
F: include/linux/smbdirect.h
SMC91x ETHERNET DRIVER
M: Nicolas Pitre <nico@fluxnic.net>
+1 -1
View File
@@ -3,7 +3,7 @@ obj-y += mm/
obj-y += net/
obj-y += vdso/
obj-$(CONFIG_KVM) += kvm/
obj-$(subst m,y,$(CONFIG_KVM)) += kvm/
# for cleaning
subdir- += boot
+1
View File
@@ -220,6 +220,7 @@ menu "Kernel type and options"
choice
prompt "Kernel type"
default 64BIT # Keep existing behavior
config 32BIT
bool "32-bit kernel"
+2
View File
@@ -55,9 +55,11 @@ endif
ifdef CONFIG_32BIT
tool-archpref = $(32bit-tool-archpref)
UTS_MACHINE := loongarch32
cflags-y += $(call cc-option,-m32)
else
tool-archpref = $(64bit-tool-archpref)
UTS_MACHINE := loongarch64
cflags-y += $(call cc-option,-m64)
endif
ifneq ($(SUBARCH),$(ARCH))
@@ -20,3 +20,23 @@ asmlinkage void noinstr __no_stack_protector ret_from_kernel_thread(struct task_
struct pt_regs *regs,
int (*fn)(void *),
void *fn_arg);
struct kvm_run;
struct kvm_vcpu;
struct loongarch_fpu;
void kvm_exc_entry(void);
int kvm_enter_guest(struct kvm_run *run, struct kvm_vcpu *vcpu);
void kvm_save_fpu(struct loongarch_fpu *fpu);
void kvm_restore_fpu(struct loongarch_fpu *fpu);
#ifdef CONFIG_CPU_HAS_LSX
void kvm_save_lsx(struct loongarch_fpu *fpu);
void kvm_restore_lsx(struct loongarch_fpu *fpu);
#endif
#ifdef CONFIG_CPU_HAS_LASX
void kvm_save_lasx(struct loongarch_fpu *fpu);
void kvm_restore_lasx(struct loongarch_fpu *fpu);
#endif
-3
View File
@@ -87,7 +87,6 @@ struct kvm_context {
struct kvm_world_switch {
int (*exc_entry)(void);
int (*enter_guest)(struct kvm_run *run, struct kvm_vcpu *vcpu);
unsigned long page_order;
};
#define MAX_PGTABLE_LEVELS 4
@@ -359,8 +358,6 @@ void kvm_exc_entry(void);
int kvm_enter_guest(struct kvm_run *run, struct kvm_vcpu *vcpu);
extern unsigned long vpid_mask;
extern const unsigned long kvm_exception_size;
extern const unsigned long kvm_enter_guest_size;
extern struct kvm_world_switch *kvm_loongarch_ops;
#define SW_GCSR (1 << 0)
+1 -1
View File
@@ -69,7 +69,7 @@
9, 10, 11, 12, 13, 14, 15, 16, \
17, 18, 19, 20, 21, 22, 23, 24, \
25, 26, 27, 28, 29, 30, 31; \
.cfi_offset \num, SC_REGS + \num * SZREG; \
.cfi_offset \num, SC_REGS + \num * 8; \
.endr; \
\
nop; \
@@ -85,12 +85,6 @@ static __always_inline u64 __arch_get_hw_counter(s32 clock_mode,
return count;
}
static inline bool loongarch_vdso_hres_capable(void)
{
return true;
}
#define __arch_vdso_hres_capable loongarch_vdso_hres_capable
#endif /* CONFIG_GENERIC_GETTIMEOFDAY */
#endif /* !__ASSEMBLER__ */
+2 -1
View File
@@ -7,11 +7,12 @@ include $(srctree)/virt/kvm/Makefile.kvm
obj-$(CONFIG_KVM) += kvm.o
obj-y += switch.o
kvm-y += exit.o
kvm-y += interrupt.o
kvm-y += main.o
kvm-y += mmu.o
kvm-y += switch.o
kvm-y += timer.o
kvm-y += tlb.o
kvm-y += vcpu.o
+1
View File
@@ -390,6 +390,7 @@ int kvm_emu_mmio_read(struct kvm_vcpu *vcpu, larch_inst inst)
run->mmio.len = 8;
break;
default:
ret = EMULATE_FAIL;
break;
}
break;
+23 -11
View File
@@ -28,23 +28,29 @@ static unsigned int priority_to_irq[EXCCODE_INT_NUM] = {
static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
{
unsigned int irq = 0;
unsigned long old, new;
clear_bit(priority, &vcpu->arch.irq_pending);
if (priority < EXCCODE_INT_NUM)
irq = priority_to_irq[priority];
if (kvm_guest_has_msgint(&vcpu->arch) && (priority == INT_AVEC)) {
dmsintc_inject_irq(vcpu);
set_gcsr_estat(irq);
return 1;
}
switch (priority) {
case INT_AVEC:
if (!kvm_guest_has_msgint(&vcpu->arch))
break;
dmsintc_inject_irq(vcpu);
fallthrough;
case INT_TI:
case INT_IPI:
case INT_SWI0:
case INT_SWI1:
old = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL);
set_gcsr_estat(irq);
new = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL);
/* Inject TI if TVAL inverted */
if (new > old)
set_gcsr_estat(CPU_TIMER);
break;
case INT_HWI0 ... INT_HWI7:
@@ -61,22 +67,28 @@ static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
static int kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned int priority)
{
unsigned int irq = 0;
unsigned long old, new;
clear_bit(priority, &vcpu->arch.irq_clear);
if (priority < EXCCODE_INT_NUM)
irq = priority_to_irq[priority];
if (kvm_guest_has_msgint(&vcpu->arch) && (priority == INT_AVEC)) {
clear_gcsr_estat(irq);
return 1;
}
switch (priority) {
case INT_AVEC:
if (!kvm_guest_has_msgint(&vcpu->arch))
break;
fallthrough;
case INT_TI:
case INT_IPI:
case INT_SWI0:
case INT_SWI1:
old = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL);
clear_gcsr_estat(irq);
new = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL);
/* Inject TI if TVAL inverted */
if (new > old)
set_gcsr_estat(CPU_TIMER);
break;
case INT_HWI0 ... INT_HWI7:
+3 -32
View File
@@ -348,8 +348,7 @@ void kvm_arch_disable_virtualization_cpu(void)
static int kvm_loongarch_env_init(void)
{
int cpu, order, ret;
void *addr;
int cpu, ret;
struct kvm_context *context;
vmcs = alloc_percpu(struct kvm_context);
@@ -365,30 +364,8 @@ static int kvm_loongarch_env_init(void)
return -ENOMEM;
}
/*
* PGD register is shared between root kernel and kvm hypervisor.
* So world switch entry should be in DMW area rather than TLB area
* to avoid page fault reenter.
*
* In future if hardware pagetable walking is supported, we won't
* need to copy world switch code to DMW area.
*/
order = get_order(kvm_exception_size + kvm_enter_guest_size);
addr = (void *)__get_free_pages(GFP_KERNEL, order);
if (!addr) {
free_percpu(vmcs);
vmcs = NULL;
kfree(kvm_loongarch_ops);
kvm_loongarch_ops = NULL;
return -ENOMEM;
}
memcpy(addr, kvm_exc_entry, kvm_exception_size);
memcpy(addr + kvm_exception_size, kvm_enter_guest, kvm_enter_guest_size);
flush_icache_range((unsigned long)addr, (unsigned long)addr + kvm_exception_size + kvm_enter_guest_size);
kvm_loongarch_ops->exc_entry = addr;
kvm_loongarch_ops->enter_guest = addr + kvm_exception_size;
kvm_loongarch_ops->page_order = order;
kvm_loongarch_ops->exc_entry = (void *)kvm_exc_entry;
kvm_loongarch_ops->enter_guest = (void *)kvm_enter_guest;
vpid_mask = read_csr_gstat();
vpid_mask = (vpid_mask & CSR_GSTAT_GIDBIT) >> CSR_GSTAT_GIDBIT_SHIFT;
@@ -428,16 +405,10 @@ static int kvm_loongarch_env_init(void)
static void kvm_loongarch_env_exit(void)
{
unsigned long addr;
if (vmcs)
free_percpu(vmcs);
if (kvm_loongarch_ops) {
if (kvm_loongarch_ops->exc_entry) {
addr = (unsigned long)kvm_loongarch_ops->exc_entry;
free_pages(addr, kvm_loongarch_ops->page_order);
}
kfree(kvm_loongarch_ops);
}
+1 -1
View File
@@ -95,7 +95,7 @@ static int kvm_flush_pte(kvm_pte_t *pte, phys_addr_t addr, kvm_ptw_ctx *ctx)
else
kvm->stat.pages--;
*pte = ctx->invalid_entry;
kvm_set_pte(pte, ctx->invalid_entry);
return 1;
}
+16 -6
View File
@@ -4,9 +4,11 @@
*/
#include <linux/linkage.h>
#include <linux/kvm_types.h>
#include <asm/asm.h>
#include <asm/asmmacro.h>
#include <asm/loongarch.h>
#include <asm/page.h>
#include <asm/regdef.h>
#include <asm/unwind_hints.h>
@@ -100,11 +102,16 @@
* - is still in guest mode, such as pgd table/vmid registers etc,
* - will fix with hw page walk enabled in future
* load kvm_vcpu from reserved CSR KVM_VCPU_KS, and save a2 to KVM_TEMP_KS
*
* PGD register is shared between root kernel and kvm hypervisor.
* So world switch entry should be in DMW area rather than TLB area
* to avoid page fault re-enter.
*/
.text
.p2align PAGE_SHIFT
.cfi_sections .debug_frame
SYM_CODE_START(kvm_exc_entry)
UNWIND_HINT_UNDEFINED
UNWIND_HINT_END_OF_STACK
csrwr a2, KVM_TEMP_KS
csrrd a2, KVM_VCPU_KS
addi.d a2, a2, KVM_VCPU_ARCH
@@ -190,8 +197,8 @@ ret_to_host:
kvm_restore_host_gpr a2
jr ra
SYM_INNER_LABEL(kvm_exc_entry_end, SYM_L_LOCAL)
SYM_CODE_END(kvm_exc_entry)
EXPORT_SYMBOL_FOR_KVM(kvm_exc_entry)
/*
* int kvm_enter_guest(struct kvm_run *run, struct kvm_vcpu *vcpu)
@@ -215,8 +222,8 @@ SYM_FUNC_START(kvm_enter_guest)
/* Save kvm_vcpu to kscratch */
csrwr a1, KVM_VCPU_KS
kvm_switch_to_guest
SYM_INNER_LABEL(kvm_enter_guest_end, SYM_L_LOCAL)
SYM_FUNC_END(kvm_enter_guest)
EXPORT_SYMBOL_FOR_KVM(kvm_enter_guest)
SYM_FUNC_START(kvm_save_fpu)
fpu_save_csr a0 t1
@@ -224,6 +231,7 @@ SYM_FUNC_START(kvm_save_fpu)
fpu_save_cc a0 t1 t2
jr ra
SYM_FUNC_END(kvm_save_fpu)
EXPORT_SYMBOL_FOR_KVM(kvm_save_fpu)
SYM_FUNC_START(kvm_restore_fpu)
fpu_restore_double a0 t1
@@ -231,6 +239,7 @@ SYM_FUNC_START(kvm_restore_fpu)
fpu_restore_cc a0 t1 t2
jr ra
SYM_FUNC_END(kvm_restore_fpu)
EXPORT_SYMBOL_FOR_KVM(kvm_restore_fpu)
#ifdef CONFIG_CPU_HAS_LSX
SYM_FUNC_START(kvm_save_lsx)
@@ -239,6 +248,7 @@ SYM_FUNC_START(kvm_save_lsx)
lsx_save_data a0 t1
jr ra
SYM_FUNC_END(kvm_save_lsx)
EXPORT_SYMBOL_FOR_KVM(kvm_save_lsx)
SYM_FUNC_START(kvm_restore_lsx)
lsx_restore_data a0 t1
@@ -246,6 +256,7 @@ SYM_FUNC_START(kvm_restore_lsx)
fpu_restore_csr a0 t1 t2
jr ra
SYM_FUNC_END(kvm_restore_lsx)
EXPORT_SYMBOL_FOR_KVM(kvm_restore_lsx)
#endif
#ifdef CONFIG_CPU_HAS_LASX
@@ -255,6 +266,7 @@ SYM_FUNC_START(kvm_save_lasx)
lasx_save_data a0 t1
jr ra
SYM_FUNC_END(kvm_save_lasx)
EXPORT_SYMBOL_FOR_KVM(kvm_save_lasx)
SYM_FUNC_START(kvm_restore_lasx)
lasx_restore_data a0 t1
@@ -262,10 +274,8 @@ SYM_FUNC_START(kvm_restore_lasx)
fpu_restore_csr a0 t1 t2
jr ra
SYM_FUNC_END(kvm_restore_lasx)
EXPORT_SYMBOL_FOR_KVM(kvm_restore_lasx)
#endif
.section ".rodata"
SYM_DATA(kvm_exception_size, .quad kvm_exc_entry_end - kvm_exc_entry)
SYM_DATA(kvm_enter_guest_size, .quad kvm_enter_guest_end - kvm_enter_guest)
#ifdef CONFIG_CPU_HAS_LBT
STACK_FRAME_NON_STANDARD kvm_restore_fpu
+8 -2
View File
@@ -96,15 +96,21 @@ void kvm_restore_timer(struct kvm_vcpu *vcpu)
* and set CSR TVAL with -1
*/
write_gcsr_timertick(0);
__delay(2); /* Wait cycles until timer interrupt injected */
/*
* Writing CSR_TINTCLR_TI to LOONGARCH_CSR_TINTCLR will clear
* timer interrupt, and CSR TVAL keeps unchanged with -1, it
* avoids spurious timer interrupt
*/
if (!(estat & CPU_TIMER))
if (!(estat & CPU_TIMER)) {
__delay(2); /* Wait cycles until timer interrupt injected */
/* Write TVAL with max value if no TI shot */
estat = kvm_read_hw_gcsr(LOONGARCH_CSR_ESTAT);
if (!(estat & CPU_TIMER))
write_gcsr_timertick(CSR_TCFG_VAL);
gcsr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR);
}
return;
}
+1 -1
View File
@@ -125,7 +125,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
r = 1;
break;
case KVM_CAP_NR_VCPUS:
r = num_online_cpus();
r = min_t(unsigned int, num_online_cpus(), KVM_MAX_VCPUS);
break;
case KVM_CAP_MAX_VCPUS:
r = KVM_MAX_VCPUS;
+5
View File
@@ -61,11 +61,16 @@ static void acpi_release_root_info(struct acpi_pci_root_info *ci)
static int acpi_prepare_root_resources(struct acpi_pci_root_info *ci)
{
int status;
unsigned long long pci_h = 0;
struct resource_entry *entry, *tmp;
struct acpi_device *device = ci->bridge;
status = acpi_pci_probe_root_resources(ci);
if (status > 0) {
acpi_evaluate_integer(device->handle, "PCIH", NULL, &pci_h);
if (pci_h)
return status;
resource_list_for_each_entry_safe(entry, tmp, &ci->resources) {
if (entry->res->flags & IORESOURCE_MEM) {
entry->offset = ci->root->mcfg_addr & GENMASK_ULL(63, 40);
+3
View File
@@ -132,6 +132,9 @@ static void loongson_gpu_fixup_dma_hang(struct pci_dev *pdev, bool on)
crtc_reg = regbase;
crtc_offset = 0x400;
break;
default:
iounmap(regbase);
return;
}
for (i = 0; i < CRTC_NUM_MAX; i++, crtc_reg += crtc_offset) {
+2
View File
@@ -12,6 +12,8 @@ obj-vdso-$(CONFIG_GENERIC_GETTIMEOFDAY) += vgettimeofday.o
ccflags-vdso := \
$(filter -I%,$(KBUILD_CFLAGS)) \
$(filter -E%,$(KBUILD_CFLAGS)) \
$(filter -m32,$(KBUILD_CFLAGS)) \
$(filter -m64,$(KBUILD_CFLAGS)) \
$(filter -march=%,$(KBUILD_CFLAGS)) \
$(filter -m%-float,$(KBUILD_CFLAGS)) \
$(CLANG_FLAGS) \
+11 -5
View File
@@ -174,15 +174,21 @@ ifeq ($(KBUILD_EXTMOD),)
# this hack.
prepare: vdso_prepare
vdso_prepare: prepare0
$(if $(CONFIG_64BIT),$(Q)$(MAKE) \
$(build)=arch/parisc/kernel/vdso64 include/generated/vdso64-offsets.h)
$(if $(CONFIG_PA11)$(CONFIG_COMPAT),$(Q)$(MAKE) \
ifdef CONFIG_64BIT
$(Q)$(MAKE) $(build)=arch/parisc/kernel/vdso64 include/generated/vdso64-offsets.h
$(if $(CONFIG_COMPAT),$(Q)$(MAKE) \
$(build)=arch/parisc/kernel/vdso32 include/generated/vdso32-offsets.h)
else
$(Q)$(MAKE) $(build)=arch/parisc/kernel/vdso32 include/generated/vdso32-offsets.h
endif
endif
vdso-install-$(CONFIG_PA11) += arch/parisc/kernel/vdso32/vdso32.so
ifdef CONFIG_64BIT
vdso-install-y += arch/parisc/kernel/vdso64/vdso64.so
vdso-install-$(CONFIG_COMPAT) += arch/parisc/kernel/vdso32/vdso32.so
vdso-install-$(CONFIG_64BIT) += arch/parisc/kernel/vdso64/vdso64.so
else
vdso-install-y += arch/parisc/kernel/vdso32/vdso32.so
endif
install: KBUILD_IMAGE := vmlinux
zinstall: KBUILD_IMAGE := vmlinuz
+4 -3
View File
@@ -6,13 +6,14 @@
#ifdef CONFIG_64BIT
#include <generated/vdso64-offsets.h>
#define VDSO64_SYMBOL(tsk, name) ((tsk)->mm->context.vdso_base + (vdso64_offset_##name))
#endif
#if !defined(CONFIG_64BIT) || defined(CONFIG_COMPAT)
#include <generated/vdso32-offsets.h>
#endif
#define VDSO64_SYMBOL(tsk, name) ((tsk)->mm->context.vdso_base + (vdso64_offset_##name))
#define VDSO32_SYMBOL(tsk, name) ((tsk)->mm->context.vdso_base + (vdso32_offset_##name))
#else
#define VDSO32_SYMBOL(tsk, name) 0UL
#endif
#endif /* __ASSEMBLER__ */
+5 -2
View File
@@ -46,6 +46,9 @@ obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
# vdso
obj-y += vdso.o
obj-$(CONFIG_64BIT) += vdso64/
obj-$(CONFIG_PA11) += vdso32/
ifdef CONFIG_64BIT
obj-y += vdso64/
obj-$(CONFIG_COMPAT) += vdso32/
else
obj-y += vdso32/
endif
+10 -11
View File
@@ -41,9 +41,7 @@
const struct dma_map_ops *hppa_dma_ops __ro_after_init;
EXPORT_SYMBOL(hppa_dma_ops);
static struct device root = {
.init_name = "parisc",
};
static struct device *root;
static inline int check_dev(struct device *dev)
{
@@ -89,7 +87,7 @@ static int for_each_padev(int (*fn)(struct device *, void *), void * data)
.obj = data,
.fn = fn,
};
return device_for_each_child(&root, &recurse_data, descend_children);
return device_for_each_child(root, &recurse_data, descend_children);
}
/**
@@ -290,7 +288,7 @@ const struct parisc_device *
find_pa_parent_type(const struct parisc_device *padev, int type)
{
const struct device *dev = &padev->dev;
while (dev != &root) {
while (dev != root) {
struct parisc_device *candidate = to_parisc_device(dev);
if (candidate->id.hw_type == type)
return candidate;
@@ -319,7 +317,7 @@ static void get_node_path(struct device *dev, struct hardware_path *path)
dev = dev->parent;
}
while (dev != &root) {
while (dev != root) {
if (dev_is_pci(dev)) {
unsigned int devfn = to_pci_dev(dev)->devfn;
path->bc[i--] = PCI_SLOT(devfn) | (PCI_FUNC(devfn)<< 5);
@@ -482,7 +480,7 @@ static struct parisc_device * __init alloc_tree_node(
static struct parisc_device *create_parisc_device(struct hardware_path *modpath)
{
int i;
struct device *parent = &root;
struct device *parent = root;
for (i = 0; i < 6; i++) {
if (modpath->bc[i] == -1)
continue;
@@ -755,7 +753,7 @@ parse_tree_node(struct device *parent, int index, struct hardware_path *modpath)
struct device *hwpath_to_device(struct hardware_path *modpath)
{
int i;
struct device *parent = &root;
struct device *parent = root;
for (i = 0; i < 6; i++) {
if (modpath->bc[i] == -1)
continue;
@@ -880,7 +878,7 @@ void __init walk_central_bus(void)
{
walk_native_bus(CENTRAL_BUS_ADDR,
CENTRAL_BUS_ADDR + (MAX_NATIVE_DEVICES * NATIVE_DEVICE_OFFSET),
&root);
root);
}
static __init void print_parisc_device(struct parisc_device *dev)
@@ -907,9 +905,10 @@ void __init init_parisc_bus(void)
{
if (bus_register(&parisc_bus_type))
panic("Could not register PA-RISC bus type\n");
if (device_register(&root))
root = root_device_register("parisc");
if (IS_ERR(root))
panic("Could not register PA-RISC root device\n");
get_device(&root);
}
static __init void qemu_header(void)
+2 -1
View File
@@ -137,7 +137,8 @@ extern void __init efi_dump_pagetable(void);
extern void __init efi_apply_memmap_quirks(void);
extern int __init efi_reuse_config(u64 tables, int nr_tables);
extern void efi_delete_dummy_variable(void);
extern void efi_crash_gracefully_on_page_fault(unsigned long phys_addr);
extern void efi_crash_gracefully_on_page_fault(unsigned long phys_addr,
const struct pt_regs *regs);
extern void efi_unmap_boot_services(void);
void arch_efi_call_virt_setup(void);
+1 -1
View File
@@ -686,7 +686,7 @@ page_fault_oops(struct pt_regs *regs, unsigned long error_code,
* avoid hanging the system.
*/
if (IS_ENABLED(CONFIG_EFI))
efi_crash_gracefully_on_page_fault(address);
efi_crash_gracefully_on_page_fault(address, regs);
/* Only not-present faults should be handled by KFENCE. */
if (!(error_code & X86_PF_PROT) &&
+11 -2
View File
@@ -761,7 +761,8 @@ int efi_capsule_setup_info(struct capsule_info *cap_info, void *kbuff,
* @return: Returns, if the page fault is not handled. This function
* will never return if the page fault is handled successfully.
*/
void efi_crash_gracefully_on_page_fault(unsigned long phys_addr)
void efi_crash_gracefully_on_page_fault(unsigned long phys_addr,
const struct pt_regs *regs)
{
if (!IS_ENABLED(CONFIG_X86_64))
return;
@@ -770,7 +771,7 @@ void efi_crash_gracefully_on_page_fault(unsigned long phys_addr)
* If we get an interrupt/NMI while processing an EFI runtime service
* then this is a regular OOPS, not an EFI failure.
*/
if (in_interrupt())
if (!in_task())
return;
/*
@@ -810,6 +811,14 @@ void efi_crash_gracefully_on_page_fault(unsigned long phys_addr)
return;
}
/*
* The API does not permit entering a kernel mode FPU section with
* interrupts enabled and leaving it with interrupts disabled. So
* re-enable interrupts now if they were enabled when the page fault
* occurred.
*/
local_irq_restore(regs->flags);
/*
* Before calling EFI Runtime Service, the kernel has switched the
* calling process to efi_mm. Hence, switch back to task_mm.
+3 -1
View File
@@ -60,8 +60,10 @@ static int efi_pstore_open(struct pstore_info *psi)
return err;
psi->data = kzalloc(record_size, GFP_KERNEL);
if (!psi->data)
if (!psi->data) {
efivar_unlock();
return -ENOMEM;
}
return 0;
}
+1 -1
View File
@@ -66,7 +66,7 @@ KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__
lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \
file.o mem.o random.o randomalloc.o pci.o \
skip_spaces.o lib-cmdline.o lib-ctype.o \
alignedmem.o relocate.o printk.o vsprintf.o
alignedmem.o printk.o vsprintf.o
# include the stub's libfdt dependencies from lib/ when needed
libfdt-deps := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c \
-7
View File
@@ -1104,13 +1104,6 @@ efi_status_t efi_allocate_pages_aligned(unsigned long size, unsigned long *addr,
efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align,
unsigned long *addr, unsigned long min);
efi_status_t efi_relocate_kernel(unsigned long *image_addr,
unsigned long image_size,
unsigned long alloc_size,
unsigned long preferred_addr,
unsigned long alignment,
unsigned long min_addr);
efi_status_t efi_parse_options(char const *cmdline);
void efi_parse_option_graphics(char *option);
@@ -14,6 +14,86 @@ extern int kernel_asize;
extern int kernel_fsize;
extern int kernel_entry;
/**
* efi_relocate_kernel() - copy memory area
* @image_addr: pointer to address of memory area to copy
* @image_size: size of memory area to copy
* @alloc_size: minimum size of memory to allocate, must be greater or
* equal to image_size
* @preferred_addr: preferred target address
* @alignment: minimum alignment of the allocated memory area. It
* should be a power of two.
* @min_addr: minimum target address
*
* Copy a memory area to a newly allocated memory area aligned according
* to @alignment but at least EFI_ALLOC_ALIGN. If the preferred address
* is not available, the allocated address will not be below @min_addr.
* On exit, @image_addr is updated to the target copy address that was used.
*
* This function is used to copy the Linux kernel verbatim. It does not apply
* any relocation changes.
*
* Return: status code
*/
static
efi_status_t efi_relocate_kernel(unsigned long *image_addr,
unsigned long image_size,
unsigned long alloc_size,
unsigned long preferred_addr,
unsigned long alignment,
unsigned long min_addr)
{
unsigned long cur_image_addr;
unsigned long new_addr = 0;
efi_status_t status;
unsigned long nr_pages;
efi_physical_addr_t efi_addr = preferred_addr;
if (!image_addr || !image_size || !alloc_size)
return EFI_INVALID_PARAMETER;
if (alloc_size < image_size)
return EFI_INVALID_PARAMETER;
cur_image_addr = *image_addr;
/*
* The EFI firmware loader could have placed the kernel image
* anywhere in memory, but the kernel has restrictions on the
* max physical address it can run at. Some architectures
* also have a preferred address, so first try to relocate
* to the preferred address. If that fails, allocate as low
* as possible while respecting the required alignment.
*/
nr_pages = round_up(alloc_size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
EFI_LOADER_DATA, nr_pages, &efi_addr);
new_addr = efi_addr;
/*
* If preferred address allocation failed allocate as low as
* possible.
*/
if (status != EFI_SUCCESS) {
status = efi_low_alloc_above(alloc_size, alignment, &new_addr,
min_addr);
}
if (status != EFI_SUCCESS) {
efi_err("Failed to allocate usable memory for kernel.\n");
return status;
}
/*
* We know source/dest won't overlap since both memory ranges
* have been allocated by UEFI, so we can safely use memcpy.
*/
memcpy((void *)new_addr, (void *)cur_image_addr, image_size);
efi_cache_sync_image(new_addr, image_size);
/* Return the new address of the relocated image. */
*image_addr = new_addr;
return status;
}
efi_status_t handle_kernel_image(unsigned long *image_addr,
unsigned long *image_size,
unsigned long *reserve_addr,
+5
View File
@@ -18,6 +18,11 @@ efi_status_t check_platform_features(void)
return EFI_SUCCESS;
}
void efi_cache_sync_image(unsigned long image_base, unsigned long alloc_size)
{
asm volatile ("ibar 0" ::: "memory");
}
struct exit_boot_struct {
efi_memory_desc_t *runtime_map;
int runtime_entry_count;
+82
View File
@@ -124,3 +124,85 @@ void efi_free(unsigned long size, unsigned long addr)
nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
efi_bs_call(free_pages, addr, nr_pages);
}
/**
* efi_low_alloc_above() - allocate pages at or above given address
* @size: size of the memory area to allocate
* @align: minimum alignment of the allocated memory area. It should
* a power of two.
* @addr: on exit the address of the allocated memory
* @min: minimum address to used for the memory allocation
*
* Allocate at the lowest possible address that is not below @min as
* EFI_LOADER_DATA. The allocated pages are aligned according to @align but at
* least EFI_ALLOC_ALIGN. The first allocated page will not below the address
* given by @min.
*
* Return: status code
*/
efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align,
unsigned long *addr, unsigned long min)
{
struct efi_boot_memmap *map __free(efi_pool) = NULL;
efi_status_t status;
unsigned long nr_pages;
int i;
status = efi_get_memory_map(&map, false);
if (status != EFI_SUCCESS)
return status;
/*
* Enforce minimum alignment that EFI or Linux requires when
* requesting a specific address. We are doing page-based (or
* larger) allocations, and both the address and size must meet
* alignment constraints.
*/
if (align < EFI_ALLOC_ALIGN)
align = EFI_ALLOC_ALIGN;
size = round_up(size, EFI_ALLOC_ALIGN);
nr_pages = size / EFI_PAGE_SIZE;
for (i = 0; i < map->map_size / map->desc_size; i++) {
efi_memory_desc_t *desc;
unsigned long m = (unsigned long)map->map;
u64 start, end;
desc = efi_memdesc_ptr(m, map->desc_size, i);
if (desc->type != EFI_CONVENTIONAL_MEMORY)
continue;
if (desc->attribute & EFI_MEMORY_HOT_PLUGGABLE)
continue;
if (efi_soft_reserve_enabled() &&
(desc->attribute & EFI_MEMORY_SP))
continue;
if (desc->num_pages < nr_pages)
continue;
start = desc->phys_addr;
end = start + desc->num_pages * EFI_PAGE_SIZE;
if (start < min)
start = min;
start = round_up(start, align);
if ((start + size) > end)
continue;
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
EFI_LOADER_DATA, nr_pages, &start);
if (status == EFI_SUCCESS) {
*addr = start;
break;
}
}
if (i == map->map_size / map->desc_size)
return EFI_NOT_FOUND;
return EFI_SUCCESS;
}
-166
View File
@@ -1,166 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/efi.h>
#include <asm/efi.h>
#include "efistub.h"
/**
* efi_low_alloc_above() - allocate pages at or above given address
* @size: size of the memory area to allocate
* @align: minimum alignment of the allocated memory area. It should
* a power of two.
* @addr: on exit the address of the allocated memory
* @min: minimum address to used for the memory allocation
*
* Allocate at the lowest possible address that is not below @min as
* EFI_LOADER_DATA. The allocated pages are aligned according to @align but at
* least EFI_ALLOC_ALIGN. The first allocated page will not below the address
* given by @min.
*
* Return: status code
*/
efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align,
unsigned long *addr, unsigned long min)
{
struct efi_boot_memmap *map __free(efi_pool) = NULL;
efi_status_t status;
unsigned long nr_pages;
int i;
status = efi_get_memory_map(&map, false);
if (status != EFI_SUCCESS)
return status;
/*
* Enforce minimum alignment that EFI or Linux requires when
* requesting a specific address. We are doing page-based (or
* larger) allocations, and both the address and size must meet
* alignment constraints.
*/
if (align < EFI_ALLOC_ALIGN)
align = EFI_ALLOC_ALIGN;
size = round_up(size, EFI_ALLOC_ALIGN);
nr_pages = size / EFI_PAGE_SIZE;
for (i = 0; i < map->map_size / map->desc_size; i++) {
efi_memory_desc_t *desc;
unsigned long m = (unsigned long)map->map;
u64 start, end;
desc = efi_memdesc_ptr(m, map->desc_size, i);
if (desc->type != EFI_CONVENTIONAL_MEMORY)
continue;
if (desc->attribute & EFI_MEMORY_HOT_PLUGGABLE)
continue;
if (efi_soft_reserve_enabled() &&
(desc->attribute & EFI_MEMORY_SP))
continue;
if (desc->num_pages < nr_pages)
continue;
start = desc->phys_addr;
end = start + desc->num_pages * EFI_PAGE_SIZE;
if (start < min)
start = min;
start = round_up(start, align);
if ((start + size) > end)
continue;
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
EFI_LOADER_DATA, nr_pages, &start);
if (status == EFI_SUCCESS) {
*addr = start;
break;
}
}
if (i == map->map_size / map->desc_size)
return EFI_NOT_FOUND;
return EFI_SUCCESS;
}
/**
* efi_relocate_kernel() - copy memory area
* @image_addr: pointer to address of memory area to copy
* @image_size: size of memory area to copy
* @alloc_size: minimum size of memory to allocate, must be greater or
* equal to image_size
* @preferred_addr: preferred target address
* @alignment: minimum alignment of the allocated memory area. It
* should be a power of two.
* @min_addr: minimum target address
*
* Copy a memory area to a newly allocated memory area aligned according
* to @alignment but at least EFI_ALLOC_ALIGN. If the preferred address
* is not available, the allocated address will not be below @min_addr.
* On exit, @image_addr is updated to the target copy address that was used.
*
* This function is used to copy the Linux kernel verbatim. It does not apply
* any relocation changes.
*
* Return: status code
*/
efi_status_t efi_relocate_kernel(unsigned long *image_addr,
unsigned long image_size,
unsigned long alloc_size,
unsigned long preferred_addr,
unsigned long alignment,
unsigned long min_addr)
{
unsigned long cur_image_addr;
unsigned long new_addr = 0;
efi_status_t status;
unsigned long nr_pages;
efi_physical_addr_t efi_addr = preferred_addr;
if (!image_addr || !image_size || !alloc_size)
return EFI_INVALID_PARAMETER;
if (alloc_size < image_size)
return EFI_INVALID_PARAMETER;
cur_image_addr = *image_addr;
/*
* The EFI firmware loader could have placed the kernel image
* anywhere in memory, but the kernel has restrictions on the
* max physical address it can run at. Some architectures
* also have a preferred address, so first try to relocate
* to the preferred address. If that fails, allocate as low
* as possible while respecting the required alignment.
*/
nr_pages = round_up(alloc_size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
EFI_LOADER_DATA, nr_pages, &efi_addr);
new_addr = efi_addr;
/*
* If preferred address allocation failed allocate as low as
* possible.
*/
if (status != EFI_SUCCESS) {
status = efi_low_alloc_above(alloc_size, alignment, &new_addr,
min_addr);
}
if (status != EFI_SUCCESS) {
efi_err("Failed to allocate usable memory for kernel.\n");
return status;
}
/*
* We know source/dest won't overlap since both memory ranges
* have been allocated by UEFI, so we can safely use memcpy.
*/
memcpy((void *)new_addr, (void *)cur_image_addr, image_size);
/* Return the new address of the relocated image. */
*image_addr = new_addr;
return status;
}
+8 -4
View File
@@ -193,8 +193,7 @@ static int __init lasi_init_chip(struct parisc_device *dev)
ret = request_irq(lasi->gsc_irq.irq, gsc_asic_intr, 0, "lasi", lasi);
if (ret < 0) {
kfree(lasi);
return ret;
goto err_free;
}
/* enable IRQ's for devices below LASI */
@@ -203,8 +202,7 @@ static int __init lasi_init_chip(struct parisc_device *dev)
/* Done init'ing, register this driver */
ret = gsc_common_setup(dev, lasi);
if (ret) {
kfree(lasi);
return ret;
goto err_irq;
}
gsc_fixup_irqs(dev, lasi, lasi_choose_irq);
@@ -214,6 +212,12 @@ static int __init lasi_init_chip(struct parisc_device *dev)
SYS_OFF_PRIO_DEFAULT, lasi_power_off, lasi);
return ret;
err_irq:
free_irq(lasi->gsc_irq.irq, lasi);
err_free:
kfree(lasi);
return ret;
}
static struct parisc_device_id lasi_tbl[] __initdata = {
+2 -4
View File
@@ -543,10 +543,8 @@ static void __init register_led_regions(void)
static int __init startup_leds(void)
{
if (platform_device_register(&platform_leds)) {
pr_info("LED: failed to register LEDs\n");
platform_device_put(&platform_leds);
}
if (platform_device_register(&platform_leds))
printk(KERN_INFO "LED: failed to register LEDs\n");
register_led_regions();
return 0;
}
@@ -359,6 +359,7 @@ cros_typec_register_thunderbolt(struct cros_typec_port *port,
}
INIT_WORK(&adata->work, cros_typec_altmode_work);
mutex_init(&adata->lock);
adata->alt = alt;
adata->port = port;
adata->ap_mode_entry = true;
+3
View File
@@ -411,6 +411,9 @@ int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id,
obj = aout.pointer;
if (!obj) {
if (min_size != 0)
return -ENOMSG;
out->length = 0;
out->data = ZERO_SIZE_PTR;
+1 -1
View File
@@ -243,7 +243,7 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst
},
{
.matches = { DMI_MATCH(DMI_BOARD_NAME, "8D41") },
.driver_data = (void *)&victus_s_thermal_params,
.driver_data = (void *)&omen_v1_no_ec_thermal_params,
},
{
.matches = { DMI_MATCH(DMI_BOARD_NAME, "8D87") },
+2
View File
@@ -349,6 +349,8 @@ static int lwmi_om_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
*/
if (!relax_fan_constraint)
raw = val / LWMI_FAN_DIV * LWMI_FAN_DIV;
else
raw = val;
err = lwmi_om_fan_get_set(priv, channel, &raw, true);
if (err)
+9 -1
View File
@@ -3089,6 +3089,7 @@ static const struct bus_type genpd_bus_type = {
static void genpd_dev_pm_detach(struct device *dev, bool power_off)
{
struct generic_pm_domain *pd;
bool is_virt_dev;
unsigned int i;
int ret = 0;
@@ -3098,6 +3099,13 @@ static void genpd_dev_pm_detach(struct device *dev, bool power_off)
dev_dbg(dev, "removing from PM domain %s\n", pd->name);
/* Check if the device was created by genpd at attach. */
is_virt_dev = dev->bus == &genpd_bus_type;
/* Disable runtime PM if we enabled it at attach. */
if (is_virt_dev)
pm_runtime_disable(dev);
/* Drop the default performance state */
if (dev_gpd_data(dev)->default_pstate) {
dev_pm_genpd_set_performance_state(dev, 0);
@@ -3123,7 +3131,7 @@ static void genpd_dev_pm_detach(struct device *dev, bool power_off)
genpd_queue_power_off_work(pd);
/* Unregister the device if it was created by genpd. */
if (dev->bus == &genpd_bus_type)
if (is_virt_dev)
device_unregister(dev);
}
+7 -3
View File
@@ -1015,6 +1015,7 @@ static int scpsys_get_bus_protection_legacy(struct device *dev, struct scpsys *s
struct device_node *node, *smi_np;
int num_regmaps = 0, i, j;
struct regmap *regmap[3];
int ret = 0;
/*
* Legacy code retrieves a maximum of three bus protection handles:
@@ -1065,11 +1066,14 @@ static int scpsys_get_bus_protection_legacy(struct device *dev, struct scpsys *s
if (node) {
regmap[2] = syscon_regmap_lookup_by_phandle(node, "mediatek,infracfg-nao");
num_regmaps++;
of_node_put(node);
if (IS_ERR(regmap[2]))
return dev_err_probe(dev, PTR_ERR(regmap[2]),
if (IS_ERR(regmap[2])) {
ret = dev_err_probe(dev, PTR_ERR(regmap[2]),
"%pOF: failed to get infracfg regmap\n",
node);
of_node_put(node);
return ret;
}
of_node_put(node);
} else {
regmap[2] = NULL;
}
+1 -4
View File
@@ -191,13 +191,10 @@ static const struct dentry_operations efivarfs_d_ops = {
static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name)
{
struct qstr q = QSTR(name);
struct dentry *d;
struct qstr q;
int err;
q.name = name;
q.len = strlen(name);
err = efivarfs_d_hash(parent, &q);
if (err)
return ERR_PTR(err);
+2 -1
View File
@@ -9,7 +9,6 @@
#include "cifs_debug.h"
#include "cifsproto.h"
#include "smb2proto.h"
#include "../smbdirect/public.h"
/* Port numbers for SMBD transport */
#define SMB_PORT 445
@@ -558,3 +557,5 @@ void smbd_debug_proc_show(struct TCP_Server_Info *server, struct seq_file *m)
server->rdma_readwrite_threshold,
m);
}
MODULE_IMPORT_NS("SMBDIRECT");
+1 -1
View File
@@ -12,7 +12,7 @@
#include "cifsglob.h"
#include "../smbdirect/smbdirect.h"
#include <linux/smbdirect.h>
extern int rdma_readwrite_threshold;
extern int smbd_max_frmr_depth;
+90 -15
View File
@@ -79,6 +79,85 @@ static int create_proc_clients(void) { return 0; }
static void delete_proc_clients(void) {}
#endif
static struct workqueue_struct *ksmbd_conn_wq;
int ksmbd_conn_wq_init(void)
{
ksmbd_conn_wq = alloc_workqueue("ksmbd-conn-release",
WQ_UNBOUND | WQ_MEM_RECLAIM, 0);
if (!ksmbd_conn_wq)
return -ENOMEM;
return 0;
}
void ksmbd_conn_wq_destroy(void)
{
if (ksmbd_conn_wq) {
destroy_workqueue(ksmbd_conn_wq);
ksmbd_conn_wq = NULL;
}
}
/*
* __ksmbd_conn_release_work() - perform the final, once-per-struct cleanup
* of a ksmbd_conn whose refcount has just dropped to zero.
*
* This is the common release path used by ksmbd_conn_put() for the embedded
* state that outlives the connection thread: async_ida and the attached
* transport (which owns the socket and iov for TCP). Called from a workqueue
* so that sleep-allowed teardown (sock_release -> tcp_close ->
* lock_sock_nested) never runs from an RCU softirq callback (free_opinfo_rcu)
* or any other non-sleeping putter context.
*/
static void __ksmbd_conn_release_work(struct work_struct *work)
{
struct ksmbd_conn *conn =
container_of(work, struct ksmbd_conn, release_work);
ida_destroy(&conn->async_ida);
conn->transport->ops->free_transport(conn->transport);
kfree(conn);
}
/**
* ksmbd_conn_get() - take a reference on @conn and return it.
*
* @conn: connection instance to get a reference to
*
* Returns @conn unchanged so callers can write
* "fp->conn = ksmbd_conn_get(work->conn);" in one expression. Returns NULL
* if @conn is NULL.
*/
struct ksmbd_conn *ksmbd_conn_get(struct ksmbd_conn *conn)
{
if (!conn)
return NULL;
atomic_inc(&conn->refcnt);
return conn;
}
/**
* ksmbd_conn_put() - drop a reference and, if it was the last, queue the
* release onto ksmbd_conn_wq so it runs from process context.
*
* @conn: connection instance to put a reference to
*
* Callable from any context including RCU softirq callbacks and non-sleeping
* locks; the actual release is deferred to the workqueue. ksmbd_conn_wq is
* created in ksmbd_server_init() before any conn can be allocated and is
* destroyed in ksmbd_server_exit() after rcu_barrier(), so it is always
* non-NULL while a conn reference is held.
*/
void ksmbd_conn_put(struct ksmbd_conn *conn)
{
if (!conn)
return;
if (atomic_dec_and_test(&conn->refcnt))
queue_work(ksmbd_conn_wq, &conn->release_work);
}
/**
* ksmbd_conn_free() - free resources of the connection instance
*
@@ -93,23 +172,19 @@ void ksmbd_conn_free(struct ksmbd_conn *conn)
hash_del(&conn->hlist);
up_write(&conn_list_lock);
/*
* request_buf / preauth_info / mechToken are only ever accessed by the
* connection handler thread that owns @conn. ksmbd_conn_free() is
* called from the transport free_transport() path when that thread is
* exiting, so it is safe to release them unconditionally even when
* ksmbd_conn_put() below is not the final putter (oplock / ksmbd_file
* holders only retain the conn pointer, not these per-thread buffers).
*/
xa_destroy(&conn->sessions);
kvfree(conn->request_buf);
kfree(conn->preauth_info);
kfree(conn->mechToken);
if (atomic_dec_and_test(&conn->refcnt)) {
/*
* async_ida is embedded in struct ksmbd_conn, so pair
* ida_destroy() with the final kfree() rather than with
* the unconditional field teardown above. This keeps
* the IDA valid for the entire lifetime of the struct,
* even while other refcount holders (oplock / vfs
* durable handles) still reference the connection.
*/
ida_destroy(&conn->async_ida);
conn->transport->ops->free_transport(conn->transport);
kfree(conn);
}
ksmbd_conn_put(conn);
}
/**
@@ -136,6 +211,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)
conn->um = ERR_PTR(-EOPNOTSUPP);
if (IS_ERR(conn->um))
conn->um = NULL;
INIT_WORK(&conn->release_work, __ksmbd_conn_release_work);
atomic_set(&conn->req_running, 0);
atomic_set(&conn->r_count, 0);
atomic_set(&conn->refcnt, 1);
@@ -512,8 +588,7 @@ void ksmbd_conn_r_count_dec(struct ksmbd_conn *conn)
if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q))
wake_up(&conn->r_count_q);
if (atomic_dec_and_test(&conn->refcnt))
kfree(conn);
ksmbd_conn_put(conn);
}
int ksmbd_conn_transport_init(void)
+6
View File
@@ -16,6 +16,7 @@
#include <linux/kthread.h>
#include <linux/nls.h>
#include <linux/unicode.h>
#include <linux/workqueue.h>
#include "smb_common.h"
#include "ksmbd_work.h"
@@ -120,6 +121,7 @@ struct ksmbd_conn {
bool binding;
atomic_t refcnt;
bool is_aapl;
struct work_struct release_work;
};
struct ksmbd_conn_ops {
@@ -164,6 +166,10 @@ void ksmbd_conn_wait_idle(struct ksmbd_conn *conn);
int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id);
struct ksmbd_conn *ksmbd_conn_alloc(void);
void ksmbd_conn_free(struct ksmbd_conn *conn);
struct ksmbd_conn *ksmbd_conn_get(struct ksmbd_conn *conn);
void ksmbd_conn_put(struct ksmbd_conn *conn);
int ksmbd_conn_wq_init(void);
void ksmbd_conn_wq_destroy(void);
bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c);
int ksmbd_conn_write(struct ksmbd_work *work);
int ksmbd_conn_rdma_read(struct ksmbd_conn *conn,
+8 -4
View File
@@ -167,7 +167,10 @@ static struct ksmbd_share_config *share_config_request(struct ksmbd_work *work,
share->path = kstrndup(ksmbd_share_config_path(resp), path_len,
KSMBD_DEFAULT_GFP);
if (share->path) {
if (!share->path) {
ret = -ENOMEM;
} else {
ret = 0;
share->path_sz = strlen(share->path);
while (share->path_sz > 1 &&
share->path[share->path_sz - 1] == '/')
@@ -179,9 +182,10 @@ static struct ksmbd_share_config *share_config_request(struct ksmbd_work *work,
share->force_directory_mode = resp->force_directory_mode;
share->force_uid = resp->force_uid;
share->force_gid = resp->force_gid;
ret = parse_veto_list(share,
KSMBD_SHARE_CONFIG_VETO_LIST(resp),
resp->veto_list_sz);
if (!ret)
ret = parse_veto_list(share,
KSMBD_SHARE_CONFIG_VETO_LIST(resp),
resp->veto_list_sz);
if (!ret && share->path) {
if (__ksmbd_override_fsids(work, share)) {
kill_share(share);
+2 -5
View File
@@ -30,7 +30,6 @@ static DEFINE_RWLOCK(lease_list_lock);
static struct oplock_info *alloc_opinfo(struct ksmbd_work *work,
u64 id, __u16 Tid)
{
struct ksmbd_conn *conn = work->conn;
struct ksmbd_session *sess = work->sess;
struct oplock_info *opinfo;
@@ -39,7 +38,7 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work,
return NULL;
opinfo->sess = sess;
opinfo->conn = conn;
opinfo->conn = ksmbd_conn_get(work->conn);
opinfo->level = SMB2_OPLOCK_LEVEL_NONE;
opinfo->op_state = OPLOCK_STATE_NONE;
opinfo->pending_break = 0;
@@ -50,7 +49,6 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work,
init_waitqueue_head(&opinfo->oplock_brk);
atomic_set(&opinfo->refcount, 1);
atomic_set(&opinfo->breaking_cnt, 0);
atomic_inc(&opinfo->conn->refcnt);
return opinfo;
}
@@ -132,8 +130,7 @@ static void __free_opinfo(struct oplock_info *opinfo)
{
if (opinfo->is_lease)
free_lease(opinfo);
if (opinfo->conn && atomic_dec_and_test(&opinfo->conn->refcnt))
kfree(opinfo->conn);
ksmbd_conn_put(opinfo->conn);
kfree(opinfo);
}
+12
View File
@@ -596,8 +596,14 @@ static int __init ksmbd_server_init(void)
if (ret)
goto err_crypto_destroy;
ret = ksmbd_conn_wq_init();
if (ret)
goto err_workqueue_destroy;
return 0;
err_workqueue_destroy:
ksmbd_workqueue_destroy();
err_crypto_destroy:
ksmbd_crypto_destroy();
err_release_inode_hash:
@@ -623,6 +629,12 @@ static void __exit ksmbd_server_exit(void)
{
ksmbd_server_shutdown();
rcu_barrier();
/*
* ksmbd_conn_put() defers the final release onto ksmbd_conn_wq,
* so drain it after rcu_barrier() has fired any pending RCU
* callbacks that may have queued a release.
*/
ksmbd_conn_wq_destroy();
ksmbd_release_inode_hash();
}
+4 -2
View File
@@ -3767,8 +3767,10 @@ err_out1:
err_out2:
if (!rc) {
ksmbd_update_fstate(&work->sess->file_table, fp, FP_INITED);
rc = ksmbd_iov_pin_rsp(work, (void *)rsp, iov_len);
rc = ksmbd_update_fstate(&work->sess->file_table, fp,
FP_INITED);
if (!rc)
rc = ksmbd_iov_pin_rsp(work, (void *)rsp, iov_len);
}
if (rc) {
if (rc == -EINVAL)
+52 -14
View File
@@ -1068,7 +1068,26 @@ static void smb_set_ace(struct smb_ace *ace, const struct smb_sid *sid, u8 type,
ace->flags = flags;
ace->access_req = access_req;
smb_copy_sid(&ace->sid, sid);
ace->size = cpu_to_le16(1 + 1 + 2 + 4 + 1 + 1 + 6 + (sid->num_subauth * 4));
ace->size = cpu_to_le16(1 + 1 + 2 + 4 + 1 + 1 + 6 +
(ace->sid.num_subauth * 4));
}
static int smb_append_inherited_ace(struct smb_ace **ace, int *nt_size,
u16 *ace_cnt, const struct smb_sid *sid,
u8 type, u8 flags, __le32 access_req)
{
int ace_size;
smb_set_ace(*ace, sid, type, flags, access_req);
ace_size = le16_to_cpu((*ace)->size);
/* pdacl->size is __le16 and includes struct smb_acl. */
if (check_add_overflow(*nt_size, ace_size, nt_size) ||
*nt_size > U16_MAX - (int)sizeof(struct smb_acl))
return -EINVAL;
(*ace_cnt)++;
*ace = (struct smb_ace *)((char *)*ace + ace_size);
return 0;
}
int smb_inherit_dacl(struct ksmbd_conn *conn,
@@ -1157,6 +1176,12 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
CIFS_SID_BASE_SIZE)
break;
if (parent_aces->sid.num_subauth > SID_MAX_SUB_AUTHORITIES ||
pace_size < offsetof(struct smb_ace, sid) +
CIFS_SID_BASE_SIZE +
sizeof(__le32) * parent_aces->sid.num_subauth)
break;
aces_size -= pace_size;
flags = parent_aces->flags;
@@ -1186,22 +1211,24 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
}
if (is_dir && creator && flags & CONTAINER_INHERIT_ACE) {
smb_set_ace(aces, psid, parent_aces->type, inherited_flags,
parent_aces->access_req);
nt_size += le16_to_cpu(aces->size);
ace_cnt++;
aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size));
rc = smb_append_inherited_ace(&aces, &nt_size, &ace_cnt,
psid, parent_aces->type,
inherited_flags,
parent_aces->access_req);
if (rc)
goto free_aces_base;
flags |= INHERIT_ONLY_ACE;
psid = creator;
} else if (is_dir && !(parent_aces->flags & NO_PROPAGATE_INHERIT_ACE)) {
psid = &parent_aces->sid;
}
smb_set_ace(aces, psid, parent_aces->type, flags | inherited_flags,
parent_aces->access_req);
nt_size += le16_to_cpu(aces->size);
aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size));
ace_cnt++;
rc = smb_append_inherited_ace(&aces, &nt_size, &ace_cnt, psid,
parent_aces->type,
flags | inherited_flags,
parent_aces->access_req);
if (rc)
goto free_aces_base;
pass:
parent_aces = (struct smb_ace *)((char *)parent_aces + pace_size);
}
@@ -1211,7 +1238,7 @@ pass:
struct smb_acl *pdacl;
struct smb_sid *powner_sid = NULL, *pgroup_sid = NULL;
int powner_sid_size = 0, pgroup_sid_size = 0, pntsd_size;
int pntsd_alloc_size;
size_t pntsd_alloc_size;
if (parent_pntsd->osidoffset) {
powner_sid = (struct smb_sid *)((char *)parent_pntsd +
@@ -1224,8 +1251,19 @@ pass:
pgroup_sid_size = 1 + 1 + 6 + (pgroup_sid->num_subauth * 4);
}
pntsd_alloc_size = sizeof(struct smb_ntsd) + powner_sid_size +
pgroup_sid_size + sizeof(struct smb_acl) + nt_size;
if (check_add_overflow(sizeof(struct smb_ntsd),
(size_t)powner_sid_size,
&pntsd_alloc_size) ||
check_add_overflow(pntsd_alloc_size,
(size_t)pgroup_sid_size,
&pntsd_alloc_size) ||
check_add_overflow(pntsd_alloc_size, sizeof(struct smb_acl),
&pntsd_alloc_size) ||
check_add_overflow(pntsd_alloc_size, (size_t)nt_size,
&pntsd_alloc_size)) {
rc = -EINVAL;
goto free_aces_base;
}
pntsd = kzalloc(pntsd_alloc_size, KSMBD_DEFAULT_GFP);
if (!pntsd) {
+2 -1
View File
@@ -18,7 +18,6 @@
#include "smb_common.h"
#include "../common/smb2status.h"
#include "transport_rdma.h"
#include "../smbdirect/public.h"
#define SMB_DIRECT_PORT_IWARP 5445
@@ -540,3 +539,5 @@ static const struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops = {
.rdma_write = smb_direct_rdma_write,
.free_transport = smb_direct_free_transport,
};
MODULE_IMPORT_NS("SMBDIRECT");
+1 -1
View File
@@ -25,6 +25,6 @@ static inline void init_smbd_max_io_size(unsigned int sz) { }
static inline unsigned int get_smbd_max_read_write_size(struct ksmbd_transport *kt) { return 0; }
#endif
#include "../smbdirect/smbdirect.h"
#include <linux/smbdirect.h>
#endif /* __KSMBD_TRANSPORT_RDMA_H__ */
+284 -57
View File
@@ -418,6 +418,14 @@ static void __ksmbd_remove_durable_fd(struct ksmbd_file *fp)
return;
idr_remove(global_ft.idr, fp->persistent_id);
/*
* Clear persistent_id so a later __ksmbd_close_fd() that runs from a
* delayed putter (e.g. when a concurrent ksmbd_lookup_fd_inode()
* walker held the final reference) does not re-issue idr_remove() on
* an id that idr_alloc_cyclic() may have already handed out to a new
* durable handle.
*/
fp->persistent_id = KSMBD_NO_FID;
}
static void ksmbd_remove_durable_fd(struct ksmbd_file *fp)
@@ -431,13 +439,13 @@ static void ksmbd_remove_durable_fd(struct ksmbd_file *fp)
static void __ksmbd_remove_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)
{
if (!has_file_id(fp->volatile_id))
return;
down_write(&fp->f_ci->m_lock);
list_del_init(&fp->node);
up_write(&fp->f_ci->m_lock);
if (!has_file_id(fp->volatile_id))
return;
write_lock(&ft->lock);
idr_remove(ft->idr, fp->volatile_id);
write_unlock(&ft->lock);
@@ -475,6 +483,17 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)
kfree(smb_lock);
}
/*
* Drop fp's strong reference on conn (taken in ksmbd_open_fd() /
* ksmbd_reopen_durable_fd()). Durable fps that reached the
* scavenger have already had fp->conn cleared by session_fd_check(),
* in which case there is nothing to drop here.
*/
if (fp->conn) {
ksmbd_conn_put(fp->conn);
fp->conn = NULL;
}
if (ksmbd_stream_fd(fp))
kfree(fp->stream.name);
kfree(fp->owner.name);
@@ -510,6 +529,20 @@ static struct ksmbd_file *__ksmbd_lookup_fd(struct ksmbd_file_table *ft,
static void __put_fd_final(struct ksmbd_work *work, struct ksmbd_file *fp)
{
/*
* Detached durable fp -- session_fd_check() cleared fp->conn at
* preserve, so this fp is no longer tracked by any conn's
* stats.open_files_count. This happens when
* ksmbd_scavenger_dispose_dh() hands the final close off to an
* m_fp_list walker (e.g. ksmbd_lookup_fd_inode()) whose work->conn
* is unrelated to the conn that originally opened the handle; close
* via the NULL-ft path so we do not underflow that unrelated
* counter.
*/
if (!fp->conn) {
__ksmbd_close_fd(NULL, fp);
return;
}
__ksmbd_close_fd(&work->sess->file_table, fp);
atomic_dec(&work->conn->stats.open_files_count);
}
@@ -752,7 +785,14 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp)
atomic_set(&fp->refcount, 1);
fp->filp = filp;
fp->conn = work->conn;
/*
* fp owns a strong reference on fp->conn for as long as fp->conn is
* non-NULL, so session_fd_check() and __ksmbd_close_fd() never
* dereference a dangling pointer. Paired with ksmbd_conn_put() in
* session_fd_check() (durable preserve), in __ksmbd_close_fd()
* (final close), and on the error paths below.
*/
fp->conn = ksmbd_conn_get(work->conn);
fp->tcon = work->tcon;
fp->volatile_id = KSMBD_NO_FID;
fp->persistent_id = KSMBD_NO_FID;
@@ -774,19 +814,64 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp)
return fp;
err_out:
/* fp->conn was set and refcounted before every branch here. */
ksmbd_conn_put(fp->conn);
kmem_cache_free(filp_cache, fp);
return ERR_PTR(ret);
}
void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp,
unsigned int state)
/**
* ksmbd_update_fstate() - update an fp state under the file-table lock
* @ft: file table that publishes @fp's volatile id
* @fp: file pointer to update
* @state: new state
*
* Return: 0 on success. The FP_NEW -> FP_INITED transition is special:
* -ENOENT if teardown already unpublished @fp by advancing the state or
* clearing the volatile id. Other state updates preserve the historical
* fire-and-forget behavior.
*/
int ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp,
unsigned int state)
{
int ret;
if (!fp)
return;
return -ENOENT;
write_lock(&ft->lock);
fp->f_state = state;
if (state == FP_INITED &&
(fp->f_state != FP_NEW || !has_file_id(fp->volatile_id))) {
ret = -ENOENT;
} else {
fp->f_state = state;
ret = 0;
}
write_unlock(&ft->lock);
return ret;
}
/*
* ksmbd_mark_fp_closed() - mark fp closed under ft->lock and return how many
* refs the teardown path owns.
*
* FP_INITED has a normal idr-owned reference, so teardown owns both that
* reference and the transient lookup reference. FP_NEW is still owned by the
* in-flight opener/reopener, which will drop the original reference after
* ksmbd_update_fstate(..., FP_INITED) observes the cleared volatile id.
* FP_CLOSED on entry means an earlier ksmbd_close_fd() already consumed the
* idr-owned ref.
*/
static int ksmbd_mark_fp_closed(struct ksmbd_file *fp)
{
if (fp->f_state == FP_INITED) {
set_close_state_blocked_works(fp);
fp->f_state = FP_CLOSED;
return 2;
}
return 1;
}
static int
@@ -794,7 +879,8 @@ __close_file_table_ids(struct ksmbd_session *sess,
struct ksmbd_tree_connect *tcon,
bool (*skip)(struct ksmbd_tree_connect *tcon,
struct ksmbd_file *fp,
struct ksmbd_user *user))
struct ksmbd_user *user),
bool skip_preserves_fp)
{
struct ksmbd_file_table *ft = &sess->file_table;
struct ksmbd_file *fp;
@@ -802,32 +888,120 @@ __close_file_table_ids(struct ksmbd_session *sess,
int num = 0;
while (1) {
int n_to_drop;
write_lock(&ft->lock);
fp = idr_get_next(ft->idr, &id);
if (!fp) {
write_unlock(&ft->lock);
break;
}
if (skip(tcon, fp, sess->user) ||
!atomic_dec_and_test(&fp->refcount)) {
if (!atomic_inc_not_zero(&fp->refcount)) {
id++;
write_unlock(&ft->lock);
continue;
}
set_close_state_blocked_works(fp);
idr_remove(ft->idr, fp->volatile_id);
fp->volatile_id = KSMBD_NO_FID;
write_unlock(&ft->lock);
if (skip_preserves_fp) {
/*
* Session teardown: skip() is session_fd_check(),
* which may sleep and mutates fp->conn / fp->tcon /
* fp->volatile_id when it chooses to preserve fp
* for durable reconnect. Unpublish fp from the
* session idr here, under ft->lock, so that
* __ksmbd_lookup_fd() through this session cannot
* grant a new ksmbd_fp_get() reference to an fp
* whose fields are about to be rewritten outside
* the lock. Durable reconnect still reaches fp via
* global_ft.
*/
idr_remove(ft->idr, id);
fp->volatile_id = KSMBD_NO_FID;
write_unlock(&ft->lock);
if (skip(tcon, fp, sess->user)) {
/*
* session_fd_check() has converted fp to
* durable-preserve state and cleared its
* per-conn fields. fp is already unpublished
* above; the original idr-owned ref keeps it
* alive for the durable scavenger. Drop only
* the transient ref. atomic_dec() is safe --
* atomic_inc_not_zero() succeeded on a
* positive value and we added one more, so
* refcount cannot be zero here.
*/
atomic_dec(&fp->refcount);
id++;
continue;
}
/*
* Keep the close-state decision under the same lock
* observed by ksmbd_update_fstate(), which is how an
* in-flight FP_NEW opener learns that teardown has
* cleared its volatile id.
*/
write_lock(&ft->lock);
n_to_drop = ksmbd_mark_fp_closed(fp);
write_unlock(&ft->lock);
} else {
/*
* Tree teardown: skip() is tree_conn_fd_check(), a
* cheap pointer compare that doesn't sleep and has
* no side effects, so keep the skip decision plus
* the unpublish-and-mark-closed sequence atomic
* under ft->lock. fps belonging to other tree
* connects (skip() == true) stay fully published in
* the session idr with no lock window.
*/
if (skip(tcon, fp, sess->user)) {
atomic_dec(&fp->refcount);
write_unlock(&ft->lock);
id++;
continue;
}
idr_remove(ft->idr, id);
fp->volatile_id = KSMBD_NO_FID;
n_to_drop = ksmbd_mark_fp_closed(fp);
write_unlock(&ft->lock);
}
/*
* fp->volatile_id is already cleared to prevent stale idr
* removal from a deferred final close. Remove fp from
* m_fp_list here because __ksmbd_remove_fd() will skip the
* list unlink when volatile_id is KSMBD_NO_FID.
*/
down_write(&fp->f_ci->m_lock);
list_del_init(&fp->node);
up_write(&fp->f_ci->m_lock);
__ksmbd_close_fd(ft, fp);
num++;
/*
* Drop the references this iteration owns:
*
* n_to_drop == 2: we observed FP_INITED and committed
* the FP_CLOSED transition ourselves, so we own the
* transient (+1) and the still-intact idr-owned ref.
*
* n_to_drop == 1: either a prior ksmbd_close_fd()
* already consumed the idr-owned ref, or fp was still
* FP_NEW and the in-flight opener/reopener must keep
* the original reference until ksmbd_update_fstate()
* observes the cleared volatile id.
*
* If we end up as the final putter, finalize fp and
* account the open_files_count decrement via the caller's
* atomic_sub(num, ...). Otherwise the remaining user's
* ksmbd_fd_put() reaches __put_fd_final(), which does its
* own atomic_dec(&open_files_count), so we must not count
* this fp here -- doing so would double-decrement the
* connection-wide counter.
*/
if (atomic_sub_and_test(n_to_drop, &fp->refcount)) {
__ksmbd_close_fd(NULL, fp);
num++;
}
id++;
}
@@ -881,24 +1055,37 @@ static bool ksmbd_durable_scavenger_alive(void)
return true;
}
static void ksmbd_scavenger_dispose_dh(struct list_head *head)
static void ksmbd_scavenger_dispose_dh(struct ksmbd_file *fp)
{
while (!list_empty(head)) {
struct ksmbd_file *fp;
/*
* Durable-preserved fp can remain linked on f_ci->m_fp_list for
* share-mode checks. Unlink it before final close; fp->node is not
* available as a scavenger-private list node because re-adding it to
* another list corrupts m_fp_list.
*/
down_write(&fp->f_ci->m_lock);
list_del_init(&fp->node);
up_write(&fp->f_ci->m_lock);
fp = list_first_entry(head, struct ksmbd_file, node);
list_del_init(&fp->node);
/*
* Drop both the durable lifetime reference and the transient reference
* taken by the scavenger under global_ft.lock. If a concurrent
* ksmbd_lookup_fd_inode() (or any other m_fp_list walker) snatched fp
* before the unlink above, that holder owns the final close via
* ksmbd_fd_put() -> __ksmbd_close_fd(). Otherwise the scavenger is
* the last putter and finalises fp here.
*/
if (atomic_sub_and_test(2, &fp->refcount))
__ksmbd_close_fd(NULL, fp);
}
}
static int ksmbd_durable_scavenger(void *dummy)
{
struct ksmbd_file *fp = NULL;
struct ksmbd_file *expired_fp;
unsigned int id;
unsigned int min_timeout = 1;
bool found_fp_timeout;
LIST_HEAD(scavenger_list);
unsigned long remaining_jiffies;
__module_get(THIS_MODULE);
@@ -908,8 +1095,6 @@ static int ksmbd_durable_scavenger(void *dummy)
if (try_to_freeze())
continue;
found_fp_timeout = false;
remaining_jiffies = wait_event_timeout(dh_wq,
ksmbd_durable_scavenger_alive() == false,
__msecs_to_jiffies(min_timeout));
@@ -918,23 +1103,39 @@ static int ksmbd_durable_scavenger(void *dummy)
else
min_timeout = DURABLE_HANDLE_MAX_TIMEOUT;
write_lock(&global_ft.lock);
idr_for_each_entry(global_ft.idr, fp, id) {
if (!fp->durable_timeout)
continue;
do {
expired_fp = NULL;
found_fp_timeout = false;
if (atomic_read(&fp->refcount) > 1 ||
fp->conn)
continue;
found_fp_timeout = true;
if (fp->durable_scavenger_timeout <=
jiffies_to_msecs(jiffies)) {
__ksmbd_remove_durable_fd(fp);
list_add(&fp->node, &scavenger_list);
} else {
write_lock(&global_ft.lock);
idr_for_each_entry(global_ft.idr, fp, id) {
unsigned long durable_timeout;
if (!fp->durable_timeout)
continue;
if (atomic_read(&fp->refcount) > 1 ||
fp->conn)
continue;
found_fp_timeout = true;
if (fp->durable_scavenger_timeout <=
jiffies_to_msecs(jiffies)) {
__ksmbd_remove_durable_fd(fp);
/*
* Take a transient reference so fp
* cannot be freed by an in-flight
* ksmbd_lookup_fd_inode() that found
* it through f_ci->m_fp_list while we
* drop global_ft.lock and reach the
* m_fp_list unlink in
* ksmbd_scavenger_dispose_dh().
*/
atomic_inc(&fp->refcount);
expired_fp = fp;
break;
}
durable_timeout =
fp->durable_scavenger_timeout -
jiffies_to_msecs(jiffies);
@@ -942,10 +1143,11 @@ static int ksmbd_durable_scavenger(void *dummy)
if (min_timeout > durable_timeout)
min_timeout = durable_timeout;
}
}
write_unlock(&global_ft.lock);
write_unlock(&global_ft.lock);
ksmbd_scavenger_dispose_dh(&scavenger_list);
if (expired_fp)
ksmbd_scavenger_dispose_dh(expired_fp);
} while (expired_fp);
if (found_fp_timeout == false)
break;
@@ -1062,25 +1264,35 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon,
if (!is_reconnectable(fp))
return false;
if (fp->f_state != FP_INITED)
return false;
if (WARN_ON_ONCE(!fp->conn))
return false;
if (ksmbd_vfs_copy_durable_owner(fp, user))
return false;
/*
* fp owns a strong reference on fp->conn (taken in ksmbd_open_fd()
* / ksmbd_reopen_durable_fd()), so conn stays valid for the whole
* body of this function regardless of any op->conn puts below.
*/
conn = fp->conn;
ci = fp->f_ci;
down_write(&ci->m_lock);
list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) {
if (op->conn != conn)
continue;
if (op->conn && atomic_dec_and_test(&op->conn->refcnt))
kfree(op->conn);
ksmbd_conn_put(op->conn);
op->conn = NULL;
}
up_write(&ci->m_lock);
list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) {
spin_lock(&fp->conn->llist_lock);
spin_lock(&conn->llist_lock);
list_del_init(&smb_lock->clist);
spin_unlock(&fp->conn->llist_lock);
spin_unlock(&conn->llist_lock);
}
fp->conn = NULL;
@@ -1091,6 +1303,8 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon,
fp->durable_scavenger_timeout =
jiffies_to_msecs(jiffies) + fp->durable_timeout;
/* Drop fp's own reference on conn. */
ksmbd_conn_put(conn);
return true;
}
@@ -1098,7 +1312,8 @@ void ksmbd_close_tree_conn_fds(struct ksmbd_work *work)
{
int num = __close_file_table_ids(work->sess,
work->tcon,
tree_conn_fd_check);
tree_conn_fd_check,
false);
atomic_sub(num, &work->conn->stats.open_files_count);
}
@@ -1107,7 +1322,8 @@ void ksmbd_close_session_fds(struct ksmbd_work *work)
{
int num = __close_file_table_ids(work->sess,
work->tcon,
session_fd_check);
session_fd_check,
true);
atomic_sub(num, &work->conn->stats.open_files_count);
}
@@ -1178,15 +1394,27 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp)
old_f_state = fp->f_state;
fp->f_state = FP_NEW;
/*
* Initialize fp's connection binding before publishing fp into the
* session's file table. If __open_id() is ordered first, a
* concurrent teardown that iterates the table can observe a valid
* volatile_id with fp->conn == NULL and preserve a
* partially-initialized fp. fp owns a strong reference on the new
* conn (see ksmbd_open_fd()); undo it on __open_id() failure.
*/
fp->conn = ksmbd_conn_get(conn);
fp->tcon = work->tcon;
__open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID);
if (!has_file_id(fp->volatile_id)) {
fp->conn = NULL;
fp->tcon = NULL;
ksmbd_conn_put(conn);
fp->f_state = old_f_state;
return -EBADF;
}
fp->conn = conn;
fp->tcon = work->tcon;
list_for_each_entry(smb_lock, &fp->lock_list, flist) {
spin_lock(&conn->llist_lock);
list_add_tail(&smb_lock->clist, &conn->lock_list);
@@ -1198,8 +1426,7 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp)
list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) {
if (op->conn)
continue;
op->conn = fp->conn;
atomic_inc(&op->conn->refcnt);
op->conn = ksmbd_conn_get(fp->conn);
}
up_write(&ci->m_lock);
@@ -1228,7 +1455,7 @@ void ksmbd_destroy_file_table(struct ksmbd_session *sess)
if (!ft->idr)
return;
__close_file_table_ids(sess, NULL, session_fd_check);
__close_file_table_ids(sess, NULL, session_fd_check, true);
idr_destroy(ft->idr);
kfree(ft->idr);
ft->idr = NULL;
+2 -2
View File
@@ -172,8 +172,8 @@ int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode);
int ksmbd_init_global_file_table(void);
void ksmbd_free_global_file_table(void);
void ksmbd_set_fd_limit(unsigned long limit);
void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp,
unsigned int state);
int ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp,
unsigned int state);
bool ksmbd_vfs_compare_durable_owner(struct ksmbd_file *fp,
struct ksmbd_user *user);
+1 -1
View File
@@ -854,4 +854,4 @@ struct smbdirect_socket *smbdirect_socket_accept(struct smbdirect_socket *lsc,
return nsc;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_accept);
EXPORT_SYMBOL_GPL(smbdirect_socket_accept);
+2 -2
View File
@@ -60,7 +60,7 @@ int smbdirect_connect(struct smbdirect_socket *sc, const struct sockaddr *dst)
*/
return 0;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connect);
EXPORT_SYMBOL_GPL(smbdirect_connect);
static int smbdirect_connect_setup_connection(struct smbdirect_socket *sc)
{
@@ -922,4 +922,4 @@ int smbdirect_connect_sync(struct smbdirect_socket *sc,
return 0;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connect_sync);
EXPORT_SYMBOL_GPL(smbdirect_connect_sync);
+8 -8
View File
@@ -706,7 +706,7 @@ bool smbdirect_connection_is_connected(struct smbdirect_socket *sc)
return false;
return true;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_is_connected);
EXPORT_SYMBOL_GPL(smbdirect_connection_is_connected);
int smbdirect_connection_wait_for_connected(struct smbdirect_socket *sc)
{
@@ -779,7 +779,7 @@ int smbdirect_connection_wait_for_connected(struct smbdirect_socket *sc)
return 0;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_wait_for_connected);
EXPORT_SYMBOL_GPL(smbdirect_connection_wait_for_connected);
void smbdirect_connection_idle_timer_work(struct work_struct *work)
{
@@ -958,7 +958,7 @@ release_credit:
return ret;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_send_batch_flush);
EXPORT_SYMBOL_GPL(smbdirect_connection_send_batch_flush);
struct smbdirect_send_batch *
smbdirect_init_send_batch_storage(struct smbdirect_send_batch_storage *storage,
@@ -976,7 +976,7 @@ smbdirect_init_send_batch_storage(struct smbdirect_send_batch_storage *storage,
return batch;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_init_send_batch_storage);
EXPORT_SYMBOL_GPL(smbdirect_init_send_batch_storage);
static int smbdirect_connection_wait_for_send_bcredit(struct smbdirect_socket *sc,
struct smbdirect_send_batch *batch)
@@ -1263,7 +1263,7 @@ lcredit_failed:
bcredit_failed:
return ret;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_send_single_iter);
EXPORT_SYMBOL_GPL(smbdirect_connection_send_single_iter);
int smbdirect_connection_send_wait_zero_pending(struct smbdirect_socket *sc)
{
@@ -1288,7 +1288,7 @@ int smbdirect_connection_send_wait_zero_pending(struct smbdirect_socket *sc)
return 0;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_send_wait_zero_pending);
EXPORT_SYMBOL_GPL(smbdirect_connection_send_wait_zero_pending);
int smbdirect_connection_send_iter(struct smbdirect_socket *sc,
struct iov_iter *iter,
@@ -1373,7 +1373,7 @@ int smbdirect_connection_send_iter(struct smbdirect_socket *sc,
return total_count;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_send_iter);
EXPORT_SYMBOL_GPL(smbdirect_connection_send_iter);
static void smbdirect_connection_send_io_done(struct ib_cq *cq, struct ib_wc *wc)
{
@@ -1937,7 +1937,7 @@ read_rfc1002_done:
goto again;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_recvmsg);
EXPORT_SYMBOL_GPL(smbdirect_connection_recvmsg);
static bool smbdirect_map_sges_single_page(struct smbdirect_map_sges *state,
struct page *page, size_t off, size_t len)
+1 -1
View File
@@ -85,4 +85,4 @@ void smbdirect_connection_legacy_debug_proc_show(struct smbdirect_socket *sc,
atomic_read(&sc->mr_io.ready.count),
atomic_read(&sc->mr_io.used.count));
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_legacy_debug_proc_show);
EXPORT_SYMBOL_GPL(smbdirect_connection_legacy_debug_proc_show);
+1 -1
View File
@@ -238,7 +238,7 @@ u8 smbdirect_netdev_rdma_capable_node_type(struct net_device *netdev)
return RDMA_NODE_UNSPECIFIED;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_netdev_rdma_capable_node_type);
EXPORT_SYMBOL_GPL(smbdirect_netdev_rdma_capable_node_type);
__init int smbdirect_devices_init(void)
{
+2 -2
View File
@@ -6,11 +6,11 @@
#ifndef __FS_SMB_COMMON_SMBDIRECT_INTERNAL_H__
#define __FS_SMB_COMMON_SMBDIRECT_INTERNAL_H__
#define DEFAULT_SYMBOL_NAMESPACE "SMBDIRECT"
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include "smbdirect.h"
#include <linux/smbdirect.h>
#include "pdu.h"
#include "public.h"
#include <linux/mutex.h>
+1 -1
View File
@@ -90,7 +90,7 @@ int smbdirect_socket_listen(struct smbdirect_socket *sc, int backlog)
*/
return 0;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_listen);
EXPORT_SYMBOL_GPL(smbdirect_socket_listen);
static int smbdirect_new_rdma_event_handler(struct rdma_cm_id *new_id,
struct rdma_cm_event *event)
+15 -12
View File
@@ -269,7 +269,7 @@ smbdirect_connection_register_mr_io(struct smbdirect_socket *sc,
{
const struct smbdirect_socket_parameters *sp = &sc->parameters;
struct smbdirect_mr_io *mr;
int ret, num_pages;
int ret, num_pages, num_mapped;
struct ib_reg_wr *reg_wr;
num_pages = iov_iter_npages(iter, sp->max_frmr_depth + 1);
@@ -300,19 +300,22 @@ smbdirect_connection_register_mr_io(struct smbdirect_socket *sc,
num_pages, iov_iter_count(iter), sp->max_frmr_depth);
smbdirect_iter_to_sgt(iter, &mr->sgt, sp->max_frmr_depth);
ret = ib_dma_map_sg(sc->ib.dev, mr->sgt.sgl, mr->sgt.nents, mr->dir);
if (!ret) {
num_mapped = ib_dma_map_sg(sc->ib.dev, mr->sgt.sgl, mr->sgt.nents, mr->dir);
if (!num_mapped) {
smbdirect_log_rdma_mr(sc, SMBDIRECT_LOG_ERR,
"ib_dma_map_sg num_pages=%u dir=%x ret=%d (%1pe)\n",
num_pages, mr->dir, ret, SMBDIRECT_DEBUG_ERR_PTR(ret));
"ib_dma_map_sg num_pages=%u dir=%x num_mapped=%d\n",
num_pages, mr->dir, num_mapped);
ret = -EIO;
goto dma_map_error;
}
ret = ib_map_mr_sg(mr->mr, mr->sgt.sgl, mr->sgt.nents, NULL, PAGE_SIZE);
if (ret != mr->sgt.nents) {
ret = ib_map_mr_sg(mr->mr, mr->sgt.sgl, num_mapped, NULL, PAGE_SIZE);
if (ret != num_mapped) {
smbdirect_log_rdma_mr(sc, SMBDIRECT_LOG_ERR,
"ib_map_mr_sg failed ret = %d nents = %u\n",
ret, mr->sgt.nents);
"ib_map_mr_sg failed ret = %d num_mapped = %u\n",
ret, num_mapped);
if (ret >= 0)
ret = -EIO;
goto map_mr_error;
}
@@ -380,7 +383,7 @@ dma_map_error:
mutex_unlock(&mr->mutex);
return NULL;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_register_mr_io);
EXPORT_SYMBOL_GPL(smbdirect_connection_register_mr_io);
void smbdirect_mr_io_fill_buffer_descriptor(struct smbdirect_mr_io *mr,
struct smbdirect_buffer_descriptor_v1 *v1)
@@ -397,7 +400,7 @@ void smbdirect_mr_io_fill_buffer_descriptor(struct smbdirect_mr_io *mr,
}
mutex_unlock(&mr->mutex);
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_mr_io_fill_buffer_descriptor);
EXPORT_SYMBOL_GPL(smbdirect_mr_io_fill_buffer_descriptor);
/*
* Deregister a MR after I/O is done
@@ -490,4 +493,4 @@ put_kref:
if (!kref_put(&mr->kref, smbdirect_mr_io_free_locked))
mutex_unlock(&mr->mutex);
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_deregister_mr_io);
EXPORT_SYMBOL_GPL(smbdirect_connection_deregister_mr_io);
+1 -1
View File
@@ -252,4 +252,4 @@ free_msg:
kfree(msg);
goto out;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_rdma_xmit);
EXPORT_SYMBOL_GPL(smbdirect_connection_rdma_xmit);
-52
View File
@@ -1,52 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2025 Stefan Metzmacher
*/
#ifndef __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_H__
#define __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_H__
#include <linux/types.h>
/* SMB-DIRECT buffer descriptor V1 structure [MS-SMBD] 2.2.3.1 */
struct smbdirect_buffer_descriptor_v1 {
__le64 offset;
__le32 token;
__le32 length;
} __packed;
/*
* Connection parameters mostly from [MS-SMBD] 3.1.1.1
*
* These are setup and negotiated at the beginning of a
* connection and remain constant unless explicitly changed.
*
* Some values are important for the upper layer.
*/
struct smbdirect_socket_parameters {
__u64 flags;
#define SMBDIRECT_FLAG_PORT_RANGE_ONLY_IB ((__u64)0x1)
#define SMBDIRECT_FLAG_PORT_RANGE_ONLY_IW ((__u64)0x2)
__u32 resolve_addr_timeout_msec;
__u32 resolve_route_timeout_msec;
__u32 rdma_connect_timeout_msec;
__u32 negotiate_timeout_msec;
__u16 initiator_depth; /* limited to U8_MAX */
__u16 responder_resources; /* limited to U8_MAX */
__u16 recv_credit_max;
__u16 send_credit_target;
__u32 max_send_size;
__u32 max_fragmented_send_size;
__u32 max_recv_size;
__u32 max_fragmented_recv_size;
__u32 max_read_write_size;
__u32 max_frmr_depth;
__u32 keepalive_interval_msec;
__u32 keepalive_timeout_msec;
} __packed;
#define SMBDIRECT_FLAG_PORT_RANGE_MASK ( \
SMBDIRECT_FLAG_PORT_RANGE_ONLY_IB | \
SMBDIRECT_FLAG_PORT_RANGE_ONLY_IW)
#endif /* __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_H__ */
+10 -10
View File
@@ -20,7 +20,7 @@ bool smbdirect_frwr_is_supported(const struct ib_device_attr *attrs)
return false;
return true;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_frwr_is_supported);
EXPORT_SYMBOL_GPL(smbdirect_frwr_is_supported);
static void smbdirect_socket_cleanup_work(struct work_struct *work);
@@ -107,7 +107,7 @@ init_failed:
alloc_failed:
return ret;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_create_kern);
EXPORT_SYMBOL_GPL(smbdirect_socket_create_kern);
int smbdirect_socket_init_accepting(struct rdma_cm_id *id, struct smbdirect_socket *sc)
{
@@ -148,7 +148,7 @@ init_failed:
alloc_failed:
return ret;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_create_accepting);
EXPORT_SYMBOL_GPL(smbdirect_socket_create_accepting);
int smbdirect_socket_set_initial_parameters(struct smbdirect_socket *sc,
const struct smbdirect_socket_parameters *sp)
@@ -189,14 +189,14 @@ int smbdirect_socket_set_initial_parameters(struct smbdirect_socket *sc,
return 0;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_set_initial_parameters);
EXPORT_SYMBOL_GPL(smbdirect_socket_set_initial_parameters);
const struct smbdirect_socket_parameters *
smbdirect_socket_get_current_parameters(struct smbdirect_socket *sc)
{
return &sc->parameters;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_get_current_parameters);
EXPORT_SYMBOL_GPL(smbdirect_socket_get_current_parameters);
int smbdirect_socket_set_kernel_settings(struct smbdirect_socket *sc,
enum ib_poll_context poll_ctx,
@@ -220,7 +220,7 @@ int smbdirect_socket_set_kernel_settings(struct smbdirect_socket *sc,
return 0;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_set_kernel_settings);
EXPORT_SYMBOL_GPL(smbdirect_socket_set_kernel_settings);
void smbdirect_socket_set_logging(struct smbdirect_socket *sc,
void *private_ptr,
@@ -240,7 +240,7 @@ void smbdirect_socket_set_logging(struct smbdirect_socket *sc,
sc->logging.needed = needed;
sc->logging.vaprintf = vaprintf;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_set_logging);
EXPORT_SYMBOL_GPL(smbdirect_socket_set_logging);
static void smbdirect_socket_wake_up_all(struct smbdirect_socket *sc)
{
@@ -663,13 +663,13 @@ int smbdirect_socket_bind(struct smbdirect_socket *sc, struct sockaddr *addr)
return 0;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_bind);
EXPORT_SYMBOL_GPL(smbdirect_socket_bind);
void smbdirect_socket_shutdown(struct smbdirect_socket *sc)
{
smbdirect_socket_schedule_cleanup_lvl(sc, SMBDIRECT_LOG_INFO, -ESHUTDOWN);
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_shutdown);
EXPORT_SYMBOL_GPL(smbdirect_socket_shutdown);
static void smbdirect_socket_release_disconnect(struct kref *kref)
{
@@ -712,7 +712,7 @@ void smbdirect_socket_release(struct smbdirect_socket *sc)
*/
kref_put(&sc->refs.destroy, smbdirect_socket_release_destroy);
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_release);
EXPORT_SYMBOL_GPL(smbdirect_socket_release);
int smbdirect_socket_wait_for_credits(struct smbdirect_socket *sc,
enum smbdirect_socket_status expected_status,
@@ -3,18 +3,56 @@
* Copyright (C) 2025, Stefan Metzmacher
*/
#ifndef __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_PUBLIC_H__
#define __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_PUBLIC_H__
#ifndef __LINUX_SMBDIRECT_H__
#define __LINUX_SMBDIRECT_H__
struct smbdirect_buffer_descriptor_v1;
struct smbdirect_socket_parameters;
#include <linux/types.h>
/* SMB-DIRECT buffer descriptor V1 structure [MS-SMBD] 2.2.3.1 */
struct smbdirect_buffer_descriptor_v1 {
__le64 offset;
__le32 token;
__le32 length;
} __packed;
/*
* Connection parameters mostly from [MS-SMBD] 3.1.1.1
*
* These are setup and negotiated at the beginning of a
* connection and remain constant unless explicitly changed.
*
* Some values are important for the upper layer.
*/
struct smbdirect_socket_parameters {
__u64 flags;
#define SMBDIRECT_FLAG_PORT_RANGE_ONLY_IB ((__u64)0x1)
#define SMBDIRECT_FLAG_PORT_RANGE_ONLY_IW ((__u64)0x2)
__u32 resolve_addr_timeout_msec;
__u32 resolve_route_timeout_msec;
__u32 rdma_connect_timeout_msec;
__u32 negotiate_timeout_msec;
__u16 initiator_depth; /* limited to U8_MAX */
__u16 responder_resources; /* limited to U8_MAX */
__u16 recv_credit_max;
__u16 send_credit_target;
__u32 max_send_size;
__u32 max_fragmented_send_size;
__u32 max_recv_size;
__u32 max_fragmented_recv_size;
__u32 max_read_write_size;
__u32 max_frmr_depth;
__u32 keepalive_interval_msec;
__u32 keepalive_timeout_msec;
} __packed;
#define SMBDIRECT_FLAG_PORT_RANGE_MASK ( \
SMBDIRECT_FLAG_PORT_RANGE_ONLY_IB | \
SMBDIRECT_FLAG_PORT_RANGE_ONLY_IW)
struct smbdirect_socket;
struct smbdirect_send_batch;
struct smbdirect_mr_io;
#define __SMBDIRECT_EXPORT_SYMBOL__(__sym) EXPORT_SYMBOL_FOR_MODULES(__sym, "cifs,ksmbd")
#include <rdma/rw.h>
u8 smbdirect_netdev_rdma_capable_node_type(struct net_device *netdev);
@@ -145,4 +183,4 @@ void smbdirect_connection_legacy_debug_proc_show(struct smbdirect_socket *sc,
unsigned int rdma_readwrite_threshold,
struct seq_file *m);
#endif /* __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_PUBLIC_H__ */
#endif /* __LINUX_SMBDIRECT_H__ */
+6 -2
View File
@@ -148,9 +148,11 @@ EXPORT_SYMBOL_GPL(snd_fasync_helper);
void snd_kill_fasync(struct snd_fasync *fasync, int signal, int poll)
{
if (!fasync || !fasync->on)
if (!fasync)
return;
guard(spinlock_irqsave)(&snd_fasync_lock);
if (!fasync->on)
return;
fasync->signal = signal;
fasync->poll = poll;
list_move(&fasync->list, &snd_fasync_list);
@@ -163,8 +165,10 @@ void snd_fasync_free(struct snd_fasync *fasync)
if (!fasync)
return;
scoped_guard(spinlock_irq, &snd_fasync_lock)
scoped_guard(spinlock_irq, &snd_fasync_lock) {
fasync->on = 0;
list_del_init(&fasync->list);
}
flush_work(&snd_fasync_work);
kfree(fasync);
+1 -1
View File
@@ -1253,7 +1253,7 @@ static int snd_seq_ioctl_set_client_info(struct snd_seq_client *client,
if (client->user_pversion >= SNDRV_PROTOCOL_VERSION(1, 0, 3))
client->midi_version = client_info->midi_version;
memcpy(client->event_filter, client_info->event_filter, 32);
client->group_filter = client_info->group_filter;
client->group_filter = client_info->group_filter & SND_SEQ_GROUP_FILTER_MASK;
/* notify the change */
snd_seq_system_client_ev_client_change(client->number);
+4 -1
View File
@@ -14,6 +14,9 @@
/* client manager */
#define SND_SEQ_GROUP_FILTER_MASK GENMASK(SNDRV_UMP_MAX_GROUPS, 0)
#define SND_SEQ_GROUP_FILTER_GROUPS GENMASK(SNDRV_UMP_MAX_GROUPS, 1)
struct snd_seq_user_client {
struct file *file; /* file struct of client */
/* ... */
@@ -40,7 +43,7 @@ struct snd_seq_client {
int number; /* client number */
unsigned int filter; /* filter flags */
DECLARE_BITMAP(event_filter, 256);
unsigned short group_filter;
unsigned int group_filter;
snd_use_lock_t use_lock;
int event_lost;
/* ports */
+1 -1
View File
@@ -369,7 +369,7 @@ static void setup_client_group_filter(struct seq_ump_client *client)
cptr = snd_seq_kernel_client_get(client->seq_client);
if (!cptr)
return;
filter = ~(1U << 0); /* always allow groupless messages */
filter = SND_SEQ_GROUP_FILTER_GROUPS; /* always allow groupless messages */
for (p = 0; p < SNDRV_UMP_MAX_GROUPS; p++) {
if (client->ump->groups[p].active)
filter &= ~(1U << (p + 1));
+3 -3
View File
@@ -679,9 +679,9 @@ static ssize_t pattern_read(struct file *file, char __user *u_buff, size_t len,
return 0;
if (copy_to_user(u_buff, patt_buf->buf + *off, to_read))
to_read = 0;
else
*off += to_read;
return -EFAULT;
*off += to_read;
return to_read;
}
+1
View File
@@ -73,6 +73,7 @@ static long tscm_hwdep_read_queue(struct snd_tscm *tscm, char __user *buf,
length = rounddown(remained, sizeof(*entries));
if (length == 0)
break;
tail_pos = head_pos + length / sizeof(*entries);
spin_unlock_irq(&tscm->lock);
if (copy_to_user(pos, &entries[head_pos], length))
+52 -1
View File
@@ -1669,6 +1669,21 @@ static void alc295_fixup_hp_mute_led_coefbit11(struct hda_codec *codec,
}
}
/* Override wrong pin to NID 0x1b (F.32 BIOS reports 0x18 via DMI OEM string)
* on HP pavilion 15-cs1xxx laptops
*/
static void alc295_fixup_hp_pavilion_mute_led_1b(struct hda_codec *codec,
const struct hda_fixup *fix,
int action)
{
struct alc_spec *spec = codec->spec;
alc269_fixup_hp_mute_led(codec, fix, action);
if (action == HDA_FIXUP_ACT_PRE_PROBE)
spec->mute_led_nid = 0x1b;
}
static void alc233_fixup_lenovo_coef_micmute_led(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
@@ -3390,6 +3405,19 @@ static void alc256_fixup_mic_no_presence_and_resume(struct hda_codec *codec,
}
}
static void alc256_fixup_xiaomi_pro15_resume(struct hda_codec *codec,
const struct hda_fixup *fix,
int action)
{
/*
* On the Xiaomi Mi Laptop Pro 15 (TM1905, SSID 1d72:1905) the ALC256
* codec sets coefficient 0x10 bit 9 to 1 after S3 resume, silencing
* the internal speaker. Bluetooth and HDMI audio are unaffected.
* Clear the bit so the speaker keeps working across suspend cycles.
*/
alc_update_coef_idx(codec, 0x10, 1<<9, 0);
}
static void alc256_decrease_headphone_amp_val(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
@@ -3857,6 +3885,7 @@ enum {
ALC290_FIXUP_SUBWOOFER,
ALC290_FIXUP_SUBWOOFER_HSJACK,
ALC295_FIXUP_HP_MUTE_LED_COEFBIT11,
ALC295_FIXUP_HP_PAVILION_MUTE_LED_1B,
ALC269_FIXUP_THINKPAD_ACPI,
ALC269_FIXUP_LENOVO_XPAD_ACPI,
ALC269_FIXUP_DMIC_THINKPAD_ACPI,
@@ -4052,6 +4081,7 @@ enum {
ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE,
ALC233_FIXUP_NO_AUDIO_JACK,
ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME,
ALC256_FIXUP_XIAOMI_PRO15_RESUME,
ALC285_FIXUP_LEGION_Y9000X_SPEAKERS,
ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE,
ALC287_FIXUP_LEGION_16ACHG6,
@@ -4123,6 +4153,7 @@ enum {
ALC245_FIXUP_CS35L41_I2C_2_MUTE_LED,
ALC236_FIXUP_HP_DMIC,
ALC256_FIXUP_HONOR_MRB_XXX_M1020_AUDIO,
ALC245_FIXUP_HP_ENVY_X360_15_FH0XXX,
};
/* A special fixup for Lenovo C940 and Yoga Duet 7;
@@ -5700,6 +5731,10 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc295_fixup_hp_mute_led_coefbit11,
},
[ALC295_FIXUP_HP_PAVILION_MUTE_LED_1B] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc295_fixup_hp_pavilion_mute_led_1b,
},
[ALC298_FIXUP_SAMSUNG_AMP] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc298_fixup_samsung_amp,
@@ -6240,6 +6275,10 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
},
[ALC256_FIXUP_XIAOMI_PRO15_RESUME] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc256_fixup_xiaomi_pro15_resume,
},
[ALC287_FIXUP_LEGION_16ACHG6] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc287_fixup_legion_16achg6_speakers,
@@ -6675,6 +6714,12 @@ static const struct hda_fixup alc269_fixups[] = {
{ 0x1b, 0x90170110 },
{ }
}
},
[ALC245_FIXUP_HP_ENVY_X360_15_FH0XXX] = {
.type = HDA_FIXUP_FUNC,
.v.func = cs35l41_fixup_i2c_two,
.chained = true,
.chain_id = ALC245_FIXUP_HP_X360_MUTE_LEDS
}
};
@@ -6906,6 +6951,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8537, "HP ProBook 440 G6", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
SND_PCI_QUIRK(0x103c, 0x8548, "HP EliteBook x360 830 G6", ALC285_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x854a, "HP EliteBook 830 G6", ALC285_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x856a, "HP Pavilion 15-cs1xxx", ALC295_FIXUP_HP_PAVILION_MUTE_LED_1B),
SND_PCI_QUIRK(0x103c, 0x85c6, "HP Pavilion x360 Convertible 14-dy1xxx", ALC295_FIXUP_HP_MUTE_LED_COEFBIT11),
SND_PCI_QUIRK(0x103c, 0x85de, "HP Envy x360 13-ar0xxx", ALC285_FIXUP_HP_ENVY_X360),
SND_PCI_QUIRK(0x103c, 0x8603, "HP Omen 17-cb0xxx", ALC285_FIXUP_HP_MUTE_LED),
@@ -7097,7 +7143,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8be6, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8be7, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8be8, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8be9, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8be9, "HP Envy x360 2-in-1 Laptop 15-fh0xxx", ALC245_FIXUP_HP_ENVY_X360_15_FH0XXX),
SND_PCI_QUIRK(0x103c, 0x8bf0, "HP", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8c15, "HP Spectre x360 2-in-1 Laptop 14-eu0xxx", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX),
SND_PCI_QUIRK(0x103c, 0x8c16, "HP Spectre x360 2-in-1 Laptop 16-aa0xxx", ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX),
@@ -7629,6 +7675,10 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x3801, "Lenovo Yoga9 14IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
HDA_CODEC_QUIRK(0x17aa, 0x3802, "DuetITL 2021", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo Yoga Pro 9 14IRP8", ALC287_FIXUP_TAS2781_I2C),
/* Yoga Pro 9 16IMH9 shares PCI SSID 17aa:3811 with Legion S7 15IMH05;
* use codec SSID to distinguish them
*/
HDA_CODEC_QUIRK(0x17aa, 0x38d6, "Lenovo Yoga Pro 9 16IMH9", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x3811, "Legion S7 15IMH05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS),
SND_PCI_QUIRK(0x17aa, 0x3813, "Legion 7i 15IMHG05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS),
SND_PCI_QUIRK(0x17aa, 0x3818, "Lenovo C940 / Yoga Duet 7", ALC298_FIXUP_LENOVO_C940_DUET7),
@@ -7774,6 +7824,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC),
SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1d72, 0x1901, "RedmiBook 14", ALC256_FIXUP_ASUS_HEADSET_MIC),
SND_PCI_QUIRK(0x1d72, 0x1905, "Xiaomi Mi Laptop Pro 15", ALC256_FIXUP_XIAOMI_PRO15_RESUME),
SND_PCI_QUIRK(0x1d72, 0x1945, "Redmi G", ALC256_FIXUP_ASUS_HEADSET_MIC),
SND_PCI_QUIRK(0x1d72, 0x1947, "RedmiBook Air", ALC255_FIXUP_XIAOMI_HEADSET_MIC),
SND_PCI_QUIRK(0x1e39, 0xca14, "MEDION NM14LNL", ALC233_FIXUP_MEDION_MTL_SPK),
+21
View File
@@ -59,6 +59,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "HP Laptop 15-fc0xxx"),
}
},
{
.driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
DMI_MATCH(DMI_PRODUCT_NAME, "OMEN Gaming Laptop 16-ap0xxx"),
}
},
{
.driver_data = &acp6x_card,
.matches = {
@@ -479,6 +486,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 15 B7ED"),
}
},
{
.driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."),
DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 15 C7VE"),
}
},
{
.driver_data = &acp6x_card,
.matches = {
@@ -668,6 +682,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
DMI_MATCH(DMI_BOARD_NAME, "8EE4"),
}
},
{
.driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
DMI_MATCH(DMI_BOARD_NAME, "8E35"),
}
},
{
.driver_data = &acp6x_card,
.matches = {
+3 -301
View File
@@ -60,19 +60,6 @@
low before proceeding with the configuration sequence */
#define AB8500_ANC_SM_DELAY 2000
#define AB8500_FILTER_CONTROL(xname, xcount, xmin, xmax) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
.info = filter_control_info, \
.get = filter_control_get, .put = filter_control_put, \
.private_value = (unsigned long)&(struct filter_control) \
{.count = xcount, .min = xmin, .max = xmax} }
struct filter_control {
long min, max;
unsigned int count;
long value[128];
};
/* Sidetone states */
static const char * const enum_sid_state[] = {
"Unconfigured",
@@ -85,45 +72,13 @@ enum sid_state {
SID_FIR_CONFIGURED = 2,
};
static const char * const enum_anc_state[] = {
"Unconfigured",
"Apply FIR and IIR",
"FIR and IIR are configured",
"Apply FIR",
"FIR is configured",
"Apply IIR",
"IIR is configured"
};
enum anc_state {
ANC_UNCONFIGURED = 0,
ANC_APPLY_FIR_IIR = 1,
ANC_FIR_IIR_CONFIGURED = 2,
ANC_APPLY_FIR = 3,
ANC_FIR_CONFIGURED = 4,
ANC_APPLY_IIR = 5,
ANC_IIR_CONFIGURED = 6
};
/* Analog microphones */
enum amic_idx {
AMIC_IDX_1A,
AMIC_IDX_1B,
AMIC_IDX_2
};
/* Private data for AB8500 device-driver */
struct ab8500_codec_drvdata {
struct regmap *regmap;
struct mutex ctrl_lock;
/* Sidetone */
long *sid_fir_values;
enum sid_state sid_status;
/* ANC */
long *anc_fir_values;
long *anc_iir_values;
enum anc_state anc_status;
};
static inline const char *amic_micbias_str(enum amic_micbias micbias)
@@ -1024,89 +979,6 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes_mic2_vamicx[] = {
{"MIC2 V-AMICx Enable", NULL, "V-AMIC2"},
};
/* ANC FIR-coefficients configuration sequence */
static void anc_fir(struct snd_soc_component *component,
unsigned int bnk, unsigned int par, unsigned int val)
{
if (par == 0 && bnk == 0)
snd_soc_component_update_bits(component, AB8500_ANCCONF1,
BIT(AB8500_ANCCONF1_ANCFIRUPDATE),
BIT(AB8500_ANCCONF1_ANCFIRUPDATE));
snd_soc_component_write(component, AB8500_ANCCONF5, val >> 8 & 0xff);
snd_soc_component_write(component, AB8500_ANCCONF6, val & 0xff);
if (par == AB8500_ANC_FIR_COEFFS - 1 && bnk == 1)
snd_soc_component_update_bits(component, AB8500_ANCCONF1,
BIT(AB8500_ANCCONF1_ANCFIRUPDATE), 0);
}
/* ANC IIR-coefficients configuration sequence */
static void anc_iir(struct snd_soc_component *component, unsigned int bnk,
unsigned int par, unsigned int val)
{
if (par == 0) {
if (bnk == 0) {
snd_soc_component_update_bits(component, AB8500_ANCCONF1,
BIT(AB8500_ANCCONF1_ANCIIRINIT),
BIT(AB8500_ANCCONF1_ANCIIRINIT));
usleep_range(AB8500_ANC_SM_DELAY, AB8500_ANC_SM_DELAY*2);
snd_soc_component_update_bits(component, AB8500_ANCCONF1,
BIT(AB8500_ANCCONF1_ANCIIRINIT), 0);
usleep_range(AB8500_ANC_SM_DELAY, AB8500_ANC_SM_DELAY*2);
} else {
snd_soc_component_update_bits(component, AB8500_ANCCONF1,
BIT(AB8500_ANCCONF1_ANCIIRUPDATE),
BIT(AB8500_ANCCONF1_ANCIIRUPDATE));
}
} else if (par > 3) {
snd_soc_component_write(component, AB8500_ANCCONF7, 0);
snd_soc_component_write(component, AB8500_ANCCONF8, val >> 16 & 0xff);
}
snd_soc_component_write(component, AB8500_ANCCONF7, val >> 8 & 0xff);
snd_soc_component_write(component, AB8500_ANCCONF8, val & 0xff);
if (par == AB8500_ANC_IIR_COEFFS - 1 && bnk == 1)
snd_soc_component_update_bits(component, AB8500_ANCCONF1,
BIT(AB8500_ANCCONF1_ANCIIRUPDATE), 0);
}
/* ANC IIR-/FIR-coefficients configuration sequence */
static void anc_configure(struct snd_soc_component *component,
bool apply_fir, bool apply_iir)
{
struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev);
unsigned int bnk, par, val;
dev_dbg(component->dev, "%s: Enter.\n", __func__);
if (apply_fir)
snd_soc_component_update_bits(component, AB8500_ANCCONF1,
BIT(AB8500_ANCCONF1_ENANC), 0);
snd_soc_component_update_bits(component, AB8500_ANCCONF1,
BIT(AB8500_ANCCONF1_ENANC), BIT(AB8500_ANCCONF1_ENANC));
if (apply_fir)
for (bnk = 0; bnk < AB8500_NR_OF_ANC_COEFF_BANKS; bnk++)
for (par = 0; par < AB8500_ANC_FIR_COEFFS; par++) {
val = snd_soc_component_read(component,
drvdata->anc_fir_values[par]);
anc_fir(component, bnk, par, val);
}
if (apply_iir)
for (bnk = 0; bnk < AB8500_NR_OF_ANC_COEFF_BANKS; bnk++)
for (par = 0; par < AB8500_ANC_IIR_COEFFS; par++) {
val = snd_soc_component_read(component,
drvdata->anc_iir_values[par]);
anc_iir(component, bnk, par, val);
}
dev_dbg(component->dev, "%s: Exit.\n", __func__);
}
/*
* Control-events
*/
@@ -1130,7 +1002,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol,
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev);
unsigned int param, sidconf, val;
unsigned int param, sidconf;
int status = 1;
dev_dbg(component->dev, "%s: Enter\n", __func__);
@@ -1159,9 +1031,8 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol,
snd_soc_component_write(component, AB8500_SIDFIRADR, 0);
for (param = 0; param < AB8500_SID_FIR_COEFFS; param++) {
val = snd_soc_component_read(component, drvdata->sid_fir_values[param]);
snd_soc_component_write(component, AB8500_SIDFIRCOEF1, val >> 8 & 0xff);
snd_soc_component_write(component, AB8500_SIDFIRCOEF2, val & 0xff);
snd_soc_component_write(component, AB8500_SIDFIRCOEF1, 0);
snd_soc_component_write(component, AB8500_SIDFIRCOEF2, 0);
}
snd_soc_component_update_bits(component, AB8500_SIDFIRADR,
@@ -1180,136 +1051,6 @@ out:
return status;
}
static int anc_status_control_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev);
mutex_lock(&drvdata->ctrl_lock);
ucontrol->value.enumerated.item[0] = drvdata->anc_status;
mutex_unlock(&drvdata->ctrl_lock);
return 0;
}
static int anc_status_control_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev);
struct device *dev = component->dev;
bool apply_fir, apply_iir;
unsigned int req;
int status;
dev_dbg(dev, "%s: Enter.\n", __func__);
mutex_lock(&drvdata->ctrl_lock);
req = ucontrol->value.enumerated.item[0];
if (req >= ARRAY_SIZE(enum_anc_state)) {
status = -EINVAL;
goto cleanup;
}
if (req != ANC_APPLY_FIR_IIR && req != ANC_APPLY_FIR &&
req != ANC_APPLY_IIR) {
dev_err(dev, "%s: ERROR: Unsupported status to set '%s'!\n",
__func__, enum_anc_state[req]);
status = -EINVAL;
goto cleanup;
}
apply_fir = req == ANC_APPLY_FIR || req == ANC_APPLY_FIR_IIR;
apply_iir = req == ANC_APPLY_IIR || req == ANC_APPLY_FIR_IIR;
status = snd_soc_dapm_force_enable_pin(dapm, "ANC Configure Input");
if (status < 0) {
dev_err(dev,
"%s: ERROR: Failed to enable power (status = %d)!\n",
__func__, status);
goto cleanup;
}
snd_soc_dapm_sync(dapm);
anc_configure(component, apply_fir, apply_iir);
if (apply_fir) {
if (drvdata->anc_status == ANC_IIR_CONFIGURED)
drvdata->anc_status = ANC_FIR_IIR_CONFIGURED;
else if (drvdata->anc_status != ANC_FIR_IIR_CONFIGURED)
drvdata->anc_status = ANC_FIR_CONFIGURED;
}
if (apply_iir) {
if (drvdata->anc_status == ANC_FIR_CONFIGURED)
drvdata->anc_status = ANC_FIR_IIR_CONFIGURED;
else if (drvdata->anc_status != ANC_FIR_IIR_CONFIGURED)
drvdata->anc_status = ANC_IIR_CONFIGURED;
}
status = snd_soc_dapm_disable_pin(dapm, "ANC Configure Input");
snd_soc_dapm_sync(dapm);
cleanup:
mutex_unlock(&drvdata->ctrl_lock);
if (status < 0)
dev_err(dev, "%s: Unable to configure ANC! (status = %d)\n",
__func__, status);
dev_dbg(dev, "%s: Exit.\n", __func__);
return (status < 0) ? status : 1;
}
static int filter_control_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct filter_control *fc =
(struct filter_control *)kcontrol->private_value;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = fc->count;
uinfo->value.integer.min = fc->min;
uinfo->value.integer.max = fc->max;
return 0;
}
static int filter_control_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct ab8500_codec_drvdata *drvdata = snd_soc_component_get_drvdata(component);
struct filter_control *fc =
(struct filter_control *)kcontrol->private_value;
unsigned int i;
mutex_lock(&drvdata->ctrl_lock);
for (i = 0; i < fc->count; i++)
ucontrol->value.integer.value[i] = fc->value[i];
mutex_unlock(&drvdata->ctrl_lock);
return 0;
}
static int filter_control_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct ab8500_codec_drvdata *drvdata = snd_soc_component_get_drvdata(component);
struct filter_control *fc =
(struct filter_control *)kcontrol->private_value;
unsigned int i;
mutex_lock(&drvdata->ctrl_lock);
for (i = 0; i < fc->count; i++)
fc->value[i] = ucontrol->value.integer.value[i];
mutex_unlock(&drvdata->ctrl_lock);
return 0;
}
/*
* Controls - Non-DAPM ASoC
*/
@@ -1597,7 +1338,6 @@ static SOC_ENUM_SINGLE_DECL(soc_enum_bfifomast,
static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_sidstate, enum_sid_state);
/* ANC */
static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_ancstate, enum_anc_state);
static struct snd_kcontrol_new ab8500_ctrls[] = {
/* Charge pump */
@@ -1873,8 +1613,6 @@ static struct snd_kcontrol_new ab8500_ctrls[] = {
AB8500_FIFOCONF6_BFIFOSAMPLE_MAX, 0),
/* ANC */
SOC_ENUM_EXT("ANC Status", soc_enum_ancstate,
anc_status_control_get, anc_status_control_put),
SOC_SINGLE_XR_SX("ANC Warp Delay Shift",
AB8500_ANCCONF2, 1, AB8500_ANCCONF2_SHIFT,
AB8500_ANCCONF2_MIN, AB8500_ANCCONF2_MAX, 0),
@@ -1895,21 +1633,6 @@ static struct snd_kcontrol_new ab8500_ctrls[] = {
AB8500_SIDFIRADR, AB8500_SIDFIRADR_FIRSIDSET, 0),
};
static struct snd_kcontrol_new ab8500_filter_controls[] = {
AB8500_FILTER_CONTROL("ANC FIR Coefficients", AB8500_ANC_FIR_COEFFS,
AB8500_ANC_FIR_COEFF_MIN, AB8500_ANC_FIR_COEFF_MAX),
AB8500_FILTER_CONTROL("ANC IIR Coefficients", AB8500_ANC_IIR_COEFFS,
AB8500_ANC_IIR_COEFF_MIN, AB8500_ANC_IIR_COEFF_MAX),
AB8500_FILTER_CONTROL("Sidetone FIR Coefficients",
AB8500_SID_FIR_COEFFS, AB8500_SID_FIR_COEFF_MIN,
AB8500_SID_FIR_COEFF_MAX)
};
enum ab8500_filter {
AB8500_FILTER_ANC_FIR = 0,
AB8500_FILTER_ANC_IIR = 1,
AB8500_FILTER_SID_FIR = 2,
};
/*
* Extended interface for codec-driver
*/
@@ -2454,7 +2177,6 @@ static int ab8500_codec_probe(struct snd_soc_component *component)
struct device_node *np = dev->of_node;
struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(dev);
struct ab8500_codec_platform_data codec_pdata;
struct filter_control *fc;
int status;
dev_dbg(dev, "%s: Enter.\n", __func__);
@@ -2486,25 +2208,6 @@ static int ab8500_codec_probe(struct snd_soc_component *component)
snd_soc_component_write(component, AB8500_SHORTCIRCONF,
BIT(AB8500_SHORTCIRCONF_HSZCDDIS));
/* Add filter controls */
status = snd_soc_add_component_controls(component, ab8500_filter_controls,
ARRAY_SIZE(ab8500_filter_controls));
if (status < 0) {
dev_err(dev,
"%s: failed to add ab8500 filter controls (%d).\n",
__func__, status);
return status;
}
fc = (struct filter_control *)
ab8500_filter_controls[AB8500_FILTER_ANC_FIR].private_value;
drvdata->anc_fir_values = (long *)fc->value;
fc = (struct filter_control *)
ab8500_filter_controls[AB8500_FILTER_ANC_IIR].private_value;
drvdata->anc_iir_values = (long *)fc->value;
fc = (struct filter_control *)
ab8500_filter_controls[AB8500_FILTER_SID_FIR].private_value;
drvdata->sid_fir_values = (long *)fc->value;
snd_soc_dapm_disable_pin(dapm, "ANC Configure Input");
mutex_init(&drvdata->ctrl_lock);
@@ -2538,7 +2241,6 @@ static int ab8500_codec_driver_probe(struct platform_device *pdev)
if (!drvdata)
return -ENOMEM;
drvdata->sid_status = SID_UNCONFIGURED;
drvdata->anc_status = ANC_UNCONFIGURED;
dev_set_drvdata(&pdev->dev, drvdata);
drvdata->regmap = devm_regmap_init(&pdev->dev, NULL, &pdev->dev,
+6 -5
View File
@@ -851,9 +851,11 @@ out_sync:
err:
regcache_cache_only(cs35l56_base->regmap, true);
regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
cs35l56_hibernate_seq,
ARRAY_SIZE(cs35l56_hibernate_seq));
if (cs35l56_base->can_hibernate) {
regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
cs35l56_hibernate_seq,
ARRAY_SIZE(cs35l56_hibernate_seq));
}
return ret;
}
@@ -1728,8 +1730,7 @@ int cs35l56_read_onchip_spkid(struct cs35l56_base *cs35l56_base)
ret = regmap_read(regmap, CS35L56_GPIO_STATUS1, &val);
if (ret) {
dev_err(cs35l56_base->dev, "GPIO%d status read failed: %d\n",
cs35l56_base->onchip_spkid_gpios[i] + 1, ret);
dev_err(cs35l56_base->dev, "GPIO status read failed: %d\n", ret);
return ret;
}
+8 -3
View File
@@ -1956,9 +1956,9 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56)
goto err;
}
ret = devm_snd_soc_register_component(cs35l56->base.dev,
&soc_component_dev_cs35l56,
cs35l56_dai, ARRAY_SIZE(cs35l56_dai));
ret = snd_soc_register_component(cs35l56->base.dev,
&soc_component_dev_cs35l56,
cs35l56_dai, ARRAY_SIZE(cs35l56_dai));
if (ret < 0) {
dev_err_probe(cs35l56->base.dev, ret, "Register codec failed\n");
goto err;
@@ -1970,6 +1970,9 @@ err:
gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
if (cs35l56->dsp_wq)
destroy_workqueue(cs35l56->dsp_wq);
return ret;
}
EXPORT_SYMBOL_NS_GPL(cs35l56_common_probe, "SND_SOC_CS35L56_CORE");
@@ -2057,6 +2060,8 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_init, "SND_SOC_CS35L56_CORE");
void cs35l56_remove(struct cs35l56_private *cs35l56)
{
snd_soc_unregister_component(cs35l56->base.dev);
cs35l56->base.init_done = false;
/*
+27 -8
View File
@@ -684,18 +684,33 @@ static int tas2764_read_die_temp(struct tas2764_priv *tas2764, long *result)
* As per datasheet, subtract 93 from raw value to get degrees
* Celsius. hwmon wants millidegrees.
*
* NOTE: The chip will initialise the TAS2764_TEMP register to
* 2.6 *C to avoid triggering temperature protection. Since the
* ADC is powered down during software shutdown, this value will
* persist until the chip is fully powered up (e.g. the PCM it's
* attached to is opened). The ADC will power down again when
* the chip is put back into software shutdown, with the last
* value sampled persisting in the ADC's register.
* NOTE: The TAS2764 datasheet mentions initialising TAS2764_TEMP
* such that the temperature is 2.6 *C, however the register
* is actually initialised to 0. The ADC is also powered down during
* software shutdown. The last sampled temperature will persist
* in the register while the amp is in this power state.
*/
if (reg == 0)
return -ENODATA;
*result = (reg - 93) * 1000;
return 0;
}
static int tas2764_hwmon_is_fault(struct tas2764_priv *tas2764, long *result)
{
int ret;
long temp;
ret = tas2764_read_die_temp(tas2764, &temp);
if (ret == -ENODATA) {
*result = true;
return 0;
}
return ret;
}
static umode_t tas2764_hwmon_is_visible(const void *data,
enum hwmon_sensor_types type, u32 attr,
int channel)
@@ -705,6 +720,7 @@ static umode_t tas2764_hwmon_is_visible(const void *data,
switch (attr) {
case hwmon_temp_input:
case hwmon_temp_fault:
return 0444;
default:
break;
@@ -724,6 +740,9 @@ static int tas2764_hwmon_read(struct device *dev,
case hwmon_temp_input:
ret = tas2764_read_die_temp(tas2764, val);
break;
case hwmon_temp_fault:
ret = tas2764_hwmon_is_fault(tas2764, val);
break;
default:
ret = -EOPNOTSUPP;
break;
@@ -733,7 +752,7 @@ static int tas2764_hwmon_read(struct device *dev,
}
static const struct hwmon_channel_info *const tas2764_hwmon_info[] = {
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_FAULT),
NULL
};
+22 -1
View File
@@ -633,10 +633,27 @@ static int tas2770_read_die_temp(struct tas2770_priv *tas2770, long *result)
* value read back from its registers will be the last value sampled
* before entering software shutdown.
*/
if (reading == 0)
return -ENODATA;
*result = (reading - (93 * 16)) * 1000 / 16;
return 0;
}
static int tas2770_hwmon_is_fault(struct tas2770_priv *tas2770, long *result)
{
int ret;
long temp;
ret = tas2770_read_die_temp(tas2770, &temp);
if (ret == -ENODATA) {
*result = true;
return 0;
}
return ret;
}
static umode_t tas2770_hwmon_is_visible(const void *data,
enum hwmon_sensor_types type, u32 attr,
int channel)
@@ -646,6 +663,7 @@ static umode_t tas2770_hwmon_is_visible(const void *data,
switch (attr) {
case hwmon_temp_input:
case hwmon_temp_fault:
return 0444;
default:
break;
@@ -665,6 +683,9 @@ static int tas2770_hwmon_read(struct device *dev,
case hwmon_temp_input:
ret = tas2770_read_die_temp(tas2770, val);
break;
case hwmon_temp_fault:
ret = tas2770_hwmon_is_fault(tas2770, val);
break;
default:
ret = -EOPNOTSUPP;
break;
@@ -674,7 +695,7 @@ static int tas2770_hwmon_read(struct device *dev,
}
static const struct hwmon_channel_info *const tas2770_hwmon_info[] = {
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_FAULT),
NULL
};
+47 -10
View File
@@ -45,6 +45,34 @@ struct wm_adsp_fw_find_test_params {
/* Dummy struct firmware to return from wm_adsp_request_firmware_files */
static const struct firmware wm_adsp_find_test_dummy_firmware;
static void wm_adsp_fw_find_test_release_firmware_files_stub(struct wm_adsp_fw_files *fw)
{
/*
* fw->wmfw.firmware and fw->coeff.firmware allocated by this KUnit
* test are dummies not allocated by the real request_firmware() call
* so they must not be passed to release_firmware().
* This function replaces wm_adsp_release_firmware_files().
*/
if (!fw)
return;
kfree(fw->wmfw.filename);
kfree(fw->coeff.filename);
fw->wmfw.firmware = NULL;
fw->coeff.firmware = NULL;
fw->wmfw.filename = NULL;
fw->coeff.filename = NULL;
}
static void wm_adsp_free_found_fw(struct kunit *test)
{
struct wm_adsp_fw_find_test *priv = test->priv;
wm_adsp_fw_find_test_release_firmware_files_stub(&priv->found_fw);
}
/* Simple lookup of a filename in a list of names */
static int wm_adsp_fw_find_test_firmware_request_simple_stub(const struct firmware **firmware,
const char *filename,
@@ -97,9 +125,14 @@ static void wm_adsp_fw_find_test_pick_file(struct kunit *test)
kunit_activate_static_stub(test,
wm_adsp_firmware_request,
wm_adsp_fw_find_test_firmware_request_simple_stub);
kunit_activate_static_stub(test,
wm_adsp_release_firmware_files,
wm_adsp_fw_find_test_release_firmware_files_stub);
ret = wm_adsp_request_firmware_files(dsp, &priv->found_fw);
kunit_deactivate_static_stub(test, wm_adsp_firmware_request);
kunit_deactivate_static_stub(test, wm_adsp_release_firmware_files);
KUNIT_EXPECT_EQ_MSG(test, ret,
(params->expect_wmfw || params->expect_bin) ? 0 : -ENOENT,
"%s\n", priv->searched_fw_files);
@@ -173,10 +206,13 @@ static void wm_adsp_fw_find_test_search_order(struct kunit *test)
kunit_activate_static_stub(test,
wm_adsp_firmware_request,
wm_adsp_fw_find_test_firmware_request_stub);
kunit_activate_static_stub(test,
wm_adsp_release_firmware_files,
wm_adsp_fw_find_test_release_firmware_files_stub);
wm_adsp_request_firmware_files(dsp, &priv->found_fw);
kunit_deactivate_static_stub(test, wm_adsp_firmware_request);
kunit_deactivate_static_stub(test, wm_adsp_release_firmware_files);
KUNIT_EXPECT_STREQ(test, priv->searched_fw_files, params->expected_searches);
@@ -201,6 +237,7 @@ static void wm_adsp_fw_find_test_find_firmware_byindex(struct kunit *test)
dsp->cs_dsp.name = "cs1234";
dsp->part = "dsp1";
for (dsp->fw = 0;; dsp->fw++) {
fw_name = wm_adsp_get_fwf_name_by_index(dsp->fw);
if (!fw_name)
@@ -209,14 +246,22 @@ static void wm_adsp_fw_find_test_find_firmware_byindex(struct kunit *test)
kunit_activate_static_stub(test,
wm_adsp_firmware_request,
wm_adsp_fw_find_test_firmware_request_stub);
kunit_activate_static_stub(test,
wm_adsp_release_firmware_files,
wm_adsp_fw_find_test_release_firmware_files_stub);
wm_adsp_request_firmware_files(dsp, &priv->found_fw);
kunit_deactivate_static_stub(test, wm_adsp_firmware_request);
kunit_deactivate_static_stub(test, wm_adsp_release_firmware_files);
KUNIT_EXPECT_NOT_NULL_MSG(test,
strstr(priv->searched_fw_files, fw_name),
"fw#%d Did not find '%s' in '%s'\n",
dsp->fw, fw_name, priv->searched_fw_files);
wm_adsp_free_found_fw(test);
memset(priv->searched_fw_files, 0, sizeof(priv->searched_fw_files));
}
}
@@ -255,15 +300,7 @@ static int wm_adsp_fw_find_test_case_init(struct kunit *test)
static void wm_adsp_fw_find_test_case_exit(struct kunit *test)
{
struct wm_adsp_fw_find_test *priv = test->priv;
/*
* priv->found_wmfw_firmware and priv->found_bin_firmware are
* dummies not allocated by the real request_firmware() call they
* must not be passed to release_firmware().
*/
kfree(priv->found_fw.wmfw.filename);
kfree(priv->found_fw.coeff.filename);
wm_adsp_free_found_fw(test);
}
static void wm_adsp_fw_find_test_param_desc(const struct wm_adsp_fw_find_test_params *param,
+13 -4
View File
@@ -228,10 +228,14 @@ static int fsl_xcvr_capds_put(struct snd_kcontrol *kcontrol,
{
struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
int changed;
memcpy(xcvr->cap_ds, ucontrol->value.bytes.data, FSL_XCVR_CAPDS_SIZE);
changed = memcmp(xcvr->cap_ds, ucontrol->value.bytes.data,
sizeof(xcvr->cap_ds)) != 0;
memcpy(xcvr->cap_ds, ucontrol->value.bytes.data,
sizeof(xcvr->cap_ds));
return 0;
return changed;
}
static struct snd_kcontrol_new fsl_xcvr_earc_capds_kctl = {
@@ -1040,10 +1044,15 @@ static int fsl_xcvr_tx_cs_put(struct snd_kcontrol *kcontrol,
{
struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
int changed;
memcpy(xcvr->tx_iec958.status, ucontrol->value.iec958.status, 24);
changed = memcmp(xcvr->tx_iec958.status,
ucontrol->value.iec958.status,
sizeof(xcvr->tx_iec958.status)) != 0;
memcpy(xcvr->tx_iec958.status, ucontrol->value.iec958.status,
sizeof(xcvr->tx_iec958.status));
return 0;
return changed;
}
static struct snd_kcontrol_new fsl_xcvr_rx_ctls[] = {
+1
View File
@@ -1608,6 +1608,7 @@ int asoc_sdw_get_dai_type(u32 type)
switch (type) {
case SDCA_FUNCTION_TYPE_SMART_AMP:
case SDCA_FUNCTION_TYPE_SIMPLE_AMP:
case SDCA_FUNCTION_TYPE_COMPANION_AMP:
return SOC_SDW_DAI_TYPE_AMP;
case SDCA_FUNCTION_TYPE_SMART_MIC:
case SDCA_FUNCTION_TYPE_SIMPLE_MIC:
+3 -4
View File
@@ -148,10 +148,6 @@ static int spacemit_i2s_hw_params(struct snd_pcm_substream *substream,
u32 val;
int ret;
val = readl(i2s->base + SSCR);
if (val & SSCR_SSE)
return 0;
dma_data = &i2s->playback_dma_data;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
@@ -199,6 +195,9 @@ static int spacemit_i2s_hw_params(struct snd_pcm_substream *substream,
}
val = readl(i2s->base + SSCR);
if (val & SSCR_SSE)
return 0;
val &= ~SSCR_DW_32BYTE;
val |= data_width;
writel(val, i2s->base + SSCR);
+1
View File
@@ -592,6 +592,7 @@ static __u32 reverse_bytes(__u32 b, int len)
fallthrough;
case 2:
b = ((b & 0xaaaaaaaa) >> 1) | ((b & 0x55555555) << 1);
fallthrough;
case 1:
case 0:
break;
+11 -4
View File
@@ -208,11 +208,18 @@ static bool uac_clock_source_is_valid_quirk(struct snd_usb_audio *chip,
}
/*
* MOTU MicroBook IIc
* Sample rate changes takes more than 2 seconds for this device. Clock
* validity request returns false during that period.
* Quirk for older MOTU AVB / hybrid interfaces
*
* These devices take more than 2 seconds to switch sample rate or
* clock source. During this period the clock validity request
* returns false, causing ALSA to fail prematurely.
*
* Affected models (all use vendor 0x07fd):
* - MicroBook IIc → 0x0004
* - 1248, 624, 8A, UltraLite AVB, 8M, 16A, ... → 0x0005
*/
if (chip->usb_id == USB_ID(0x07fd, 0x0004)) {
if (chip->usb_id == USB_ID(0x07fd, 0x0004) || /* MicroBook IIc */
chip->usb_id == USB_ID(0x07fd, 0x0005)) { /* 1248 / 624 / 8A / UltraLite AVB / ... */
count = 0;
while ((!ret) && (count < 50)) {
+5 -4
View File
@@ -227,7 +227,7 @@ static void kill_midi_urbs(struct snd_usb_midi2_endpoint *ep, bool suspending)
if (!ep)
return;
if (suspending)
ep->suspended = ep->running;
atomic_set(&ep->suspended, atomic_read(&ep->running));
atomic_set(&ep->running, 0);
for (i = 0; i < ep->num_urbs; i++) {
if (!ep->urbs[i].urb)
@@ -1188,10 +1188,11 @@ void snd_usb_midi_v2_suspend_all(struct snd_usb_audio *chip)
static void resume_midi2_endpoint(struct snd_usb_midi2_endpoint *ep)
{
ep->running = ep->suspended;
if (ep->direction == STR_IN)
atomic_set(&ep->running, atomic_read(&ep->suspended));
atomic_set(&ep->suspended, 0);
if (ep->direction == STR_IN || atomic_read(&ep->running))
submit_io_urbs(ep);
/* FIXME: does it all? */
}
void snd_usb_midi_v2_resume_all(struct snd_usb_audio *chip)
+7
View File
@@ -2277,6 +2277,9 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_ALIGN_TRANSFER),
DEVICE_FLG(0x05e1, 0x0480, /* Hauppauge Woodbury */
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
DEVICE_FLG(0x05fc, 0x0231, /* JBL Pebbles */
QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL | QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL |
QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x0624, 0x3d3f, /* AB13X USB Audio */
QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY),
DEVICE_FLG(0x0644, 0x8043, /* TEAC UD-501/UD-501V2/UD-503/NT-503 */
@@ -2366,6 +2369,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_IGNORE_CTL_ERROR),
DEVICE_FLG(0x152a, 0x880a, /* NeuralDSP Quad Cortex */
0), /* Doesn't have the vendor quirk which would otherwise apply */
DEVICE_FLG(0x1532, 0x055e, /* Razer Nommo V2 X */
QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
DEVICE_FLG(0x154e, 0x1002, /* Denon DCD-1500RE */
QUIRK_FLAG_ITF_USB_DSD_DAC | QUIRK_FLAG_CTL_MSG_DELAY),
DEVICE_FLG(0x154e, 0x1003, /* Denon DA-300USB */
@@ -2458,6 +2463,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
DEVICE_FLG(0x2b53, 0x0031, /* Fiero SC-01 (firmware v1.1.0) */
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
DEVICE_FLG(0x2b73, 0x0047, /* AlphaTheta EUPHONIA */
QUIRK_FLAG_PLAYBACK_FIRST | QUIRK_FLAG_GENERIC_IMPLICIT_FB),
DEVICE_FLG(0x2d95, 0x8011, /* VIVO USB-C HEADSET */
QUIRK_FLAG_CTL_MSG_DELAY_1M),
DEVICE_FLG(0x2d95, 0x8021, /* VIVO USB-C-XE710 HEADSET */