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:
commit
a04515d334
88 files changed
+1136
-836
No files matched your search
@@ -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>
|
||||
|
||||
@@ -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
|
||||
@@ -220,6 +220,7 @@ menu "Kernel type and options"
|
||||
|
||||
choice
|
||||
prompt "Kernel type"
|
||||
default 64BIT # Keep existing behavior
|
||||
|
||||
config 32BIT
|
||||
bool "32-bit kernel"
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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) &&
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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") },
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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");
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
@@ -252,4 +252,4 @@ free_msg:
|
||||
kfree(msg);
|
||||
goto out;
|
||||
}
|
||||
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_rdma_xmit);
|
||||
EXPORT_SYMBOL_GPL(smbdirect_connection_rdma_xmit);
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
/*
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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[] = {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in new issue
Block a user