summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/powerpc/kernel/iommu.c2
-rw-r--r--arch/powerpc/kvm/book3s_hv_nested.c2
-rw-r--r--arch/powerpc/kvm/book3s_hv_uvmem.c5
-rw-r--r--arch/x86/mm/mem_encrypt.c3
-rw-r--r--drivers/gpio/gpio-virtuser.c3
-rw-r--r--drivers/gpio/gpiolib-of.c6
-rw-r--r--drivers/gpio/gpiolib-shared.c9
-rw-r--r--drivers/power/sequencing/Kconfig3
-rw-r--r--drivers/power/sequencing/core.c11
-rw-r--r--include/sound/soc.h3
-rw-r--r--include/sound/soc_sdw_utils.h2
-rw-r--r--include/trace/events/dma.h2
-rw-r--r--kernel/dma/coherent.c3
-rw-r--r--kernel/dma/swiotlb.c4
-rw-r--r--kernel/sched/ext/ext.c142
-rw-r--r--kernel/sched/ext/idle.c11
-rw-r--r--kernel/sched/ext/internal.h12
-rw-r--r--kernel/sched/ext/sub.c12
-rw-r--r--sound/core/init.c3
-rw-r--r--sound/core/pcm_timer.c7
-rw-r--r--sound/hda/codecs/realtek/alc269.c11
-rw-r--r--sound/hda/common/controller.c2
-rw-r--r--sound/soc/amd/acp/acp-sdw-legacy-mach.c14
-rw-r--r--sound/soc/amd/acp/acp-sdw-sof-mach.c23
-rw-r--r--sound/soc/codecs/Kconfig4
-rw-r--r--sound/soc/codecs/adau1977-i2c.c9
-rw-r--r--sound/soc/codecs/adau1977-spi.c12
-rw-r--r--sound/soc/codecs/cs-amp-lib.c2
-rw-r--r--sound/soc/codecs/hdmi-codec.c6
-rw-r--r--sound/soc/codecs/rt712-sdca-dmic.c14
-rw-r--r--sound/soc/codecs/rt712-sdca-sdw.c8
-rw-r--r--sound/soc/codecs/rt712-sdca.c86
-rw-r--r--sound/soc/codecs/rt712-sdca.h18
-rw-r--r--sound/soc/codecs/rt721-sdca-sdw.c3
-rw-r--r--sound/soc/codecs/rt721-sdca.c17
-rw-r--r--sound/soc/codecs/wm_adsp.c5
-rw-r--r--sound/soc/intel/boards/sof_es8336.c9
-rw-r--r--sound/soc/sdw_utils/soc_sdw_cs_amp.c46
-rw-r--r--sound/soc/sdw_utils/soc_sdw_utils.c24
-rw-r--r--sound/soc/soc-pcm.c18
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.h4
-rw-r--r--sound/usb/6fire/pcm.c12
-rw-r--r--sound/usb/bcd2000/bcd2000.c33
-rw-r--r--sound/usb/card.h1
-rw-r--r--sound/usb/endpoint.c21
-rw-r--r--sound/usb/implicit.c1
-rw-r--r--sound/usb/mixer_maps.c13
-rw-r--r--sound/virtio/virtio_card.c4
-rw-r--r--tools/sched_ext/include/scx/common.bpf.h1
-rw-r--r--tools/sched_ext/scx_qmap.bpf.c162
-rw-r--r--tools/sched_ext/scx_qmap.h3
51 files changed, 603 insertions, 228 deletions
diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c
index ee1b5cb557c9..1ae8384637b5 100644
--- a/arch/powerpc/kernel/iommu.c
+++ b/arch/powerpc/kernel/iommu.c
@@ -1076,7 +1076,7 @@ int iommu_tce_check_ioba(unsigned long page_shift,
if (ioba < offset)
return -EINVAL;
- if ((ioba + 1) > (offset + size))
+ if ((ioba + npages < ioba) || (ioba - offset + npages > size))
return -EINVAL;
return 0;
diff --git a/arch/powerpc/kvm/book3s_hv_nested.c b/arch/powerpc/kvm/book3s_hv_nested.c
index 22e616662255..a6ff42d7666c 100644
--- a/arch/powerpc/kvm/book3s_hv_nested.c
+++ b/arch/powerpc/kvm/book3s_hv_nested.c
@@ -1204,8 +1204,10 @@ static void kvmhv_emulate_tlbie_all_lpid(struct kvm_vcpu *vcpu, int ric)
spin_lock(&kvm->mmu_lock);
idr_for_each_entry(&kvm->arch.kvm_nested_guest_idr, gp, lpid) {
+ ++gp->refcnt;
spin_unlock(&kvm->mmu_lock);
kvmhv_emulate_tlbie_lpid(vcpu, gp, ric);
+ kvmhv_put_nested(gp);
spin_lock(&kvm->mmu_lock);
}
spin_unlock(&kvm->mmu_lock);
diff --git a/arch/powerpc/kvm/book3s_hv_uvmem.c b/arch/powerpc/kvm/book3s_hv_uvmem.c
index 5fbb95d90e99..463aef870c4e 100644
--- a/arch/powerpc/kvm/book3s_hv_uvmem.c
+++ b/arch/powerpc/kvm/book3s_hv_uvmem.c
@@ -779,8 +779,11 @@ static int kvmppc_svm_page_in(struct vm_area_struct *vma,
if (spage) {
ret = uv_page_in(kvm->arch.lpid, pfn << page_shift,
gpa, 0, page_shift);
- if (ret)
+ if (ret) {
+ unlock_page(dpage);
+ put_page(dpage);
goto out_finalize;
+ }
}
}
diff --git a/arch/x86/mm/mem_encrypt.c b/arch/x86/mm/mem_encrypt.c
index 95bae74fdab2..3aefdef5bcb0 100644
--- a/arch/x86/mm/mem_encrypt.c
+++ b/arch/x86/mm/mem_encrypt.c
@@ -13,6 +13,7 @@
#include <linux/cc_platform.h>
#include <linux/mem_encrypt.h>
#include <linux/virtio_anchor.h>
+#include <linux/iommu-dma.h>
#include <asm/sev.h>
@@ -30,7 +31,7 @@ bool force_dma_unencrypted(struct device *dev)
* device does not support DMA to addresses that include the
* encryption mask.
*/
- if (cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT)) {
+ if (cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT) && !use_dma_iommu(dev)) {
u64 dma_enc_mask = DMA_BIT_MASK(__ffs64(sme_me_mask));
u64 dma_dev_mask = min_not_zero(dev->coherent_dma_mask,
dev->bus_dma_limit);
diff --git a/drivers/gpio/gpio-virtuser.c b/drivers/gpio/gpio-virtuser.c
index 449fb1aed24b..70ddccccc1c4 100644
--- a/drivers/gpio/gpio-virtuser.c
+++ b/drivers/gpio/gpio-virtuser.c
@@ -692,7 +692,8 @@ static int gpio_virtuser_interrupts_set(void *data, u64 val)
atomic_set(&ld->irq, irq);
} else {
irq = atomic_xchg(&ld->irq, 0);
- free_irq(irq, ld);
+ if (irq)
+ free_irq(irq, ld);
}
return 0;
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index 940b566946ce..f36e4b171fa7 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -788,13 +788,13 @@ static int of_gpio_notify(struct notifier_block *nb, unsigned long action,
if (!of_property_read_bool(rd->dn, "gpio-hog"))
return NOTIFY_DONE; /* not for us */
- if (of_node_test_and_set_flag(rd->dn, OF_POPULATED))
- return NOTIFY_DONE;
-
gdev = of_find_gpio_device_by_node(rd->dn->parent);
if (!gdev)
return NOTIFY_DONE; /* not for us */
+ if (of_node_test_and_set_flag(rd->dn, OF_POPULATED))
+ return NOTIFY_DONE;
+
ret = gpiochip_add_hog(gpio_device_get_chip(gdev), of_fwnode_handle(rd->dn));
if (ret < 0) {
pr_err("%s: failed to add hogs for %pOF\n", __func__,
diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c
index 495bd3d0ddf0..5f9623e40b0f 100644
--- a/drivers/gpio/gpiolib-shared.c
+++ b/drivers/gpio/gpiolib-shared.c
@@ -261,10 +261,13 @@ static int gpio_shared_of_traverse(struct device_node *curr)
con_id[con_id_len - suffix_len] = '\0';
}
- ref = gpio_shared_make_ref(fwnode_handle_get(of_fwnode_handle(curr)),
- con_id, args.args[1]);
- if (!ref)
+ struct fwnode_handle *curr_fwnode =
+ fwnode_handle_get(of_fwnode_handle(curr));
+ ref = gpio_shared_make_ref(curr_fwnode, con_id, args.args[1]);
+ if (!ref) {
+ fwnode_handle_put(curr_fwnode);
return -ENOMEM;
+ }
if (!list_empty(&entry->refs))
pr_debug("GPIO %u at %s is shared by multiple firmware nodes\n",
diff --git a/drivers/power/sequencing/Kconfig b/drivers/power/sequencing/Kconfig
index 1c5f5820f5b7..226c62704d9b 100644
--- a/drivers/power/sequencing/Kconfig
+++ b/drivers/power/sequencing/Kconfig
@@ -29,7 +29,8 @@ config POWER_SEQUENCING_QCOM_WCN
config POWER_SEQUENCING_TH1520_GPU
tristate "T-HEAD TH1520 GPU power sequencing driver"
- depends on (ARCH_THEAD && AUXILIARY_BUS) || COMPILE_TEST
+ depends on ARCH_THEAD || COMPILE_TEST
+ select AUXILIARY_BUS
help
Say Y here to enable the power sequencing driver for the TH1520 SoC
GPU. This driver handles the complex clock and reset sequence
diff --git a/drivers/power/sequencing/core.c b/drivers/power/sequencing/core.c
index 0cb71efbb268..3076b3879af9 100644
--- a/drivers/power/sequencing/core.c
+++ b/drivers/power/sequencing/core.c
@@ -101,6 +101,7 @@ static struct pwrseq_unit *pwrseq_unit_new(const struct pwrseq_unit_data *data)
}
kref_init(&unit->ref);
+ INIT_LIST_HEAD(&unit->list);
INIT_LIST_HEAD(&unit->deps);
unit->enable = data->enable;
unit->disable = data->disable;
@@ -504,10 +505,6 @@ pwrseq_device_register(const struct pwrseq_config *config)
*/
device_initialize(&pwrseq->dev);
- ret = dev_set_name(&pwrseq->dev, "pwrseq.%d", pwrseq->id);
- if (ret)
- goto err_put_pwrseq;
-
pwrseq->owner = config->owner ?: THIS_MODULE;
pwrseq->match = config->match;
@@ -516,6 +513,10 @@ pwrseq_device_register(const struct pwrseq_config *config)
INIT_LIST_HEAD(&pwrseq->targets);
INIT_LIST_HEAD(&pwrseq->units);
+ ret = dev_set_name(&pwrseq->dev, "pwrseq.%d", pwrseq->id);
+ if (ret)
+ goto err_put_pwrseq;
+
ret = pwrseq_setup_targets(config->targets, pwrseq);
if (ret)
goto err_put_pwrseq;
@@ -912,6 +913,8 @@ int pwrseq_enable(struct pwrseq_desc *desc)
if (!ret)
desc->powered_on = true;
}
+ if (ret)
+ return ret;
if (target->post_enable) {
ret = target->post_enable(pwrseq);
diff --git a/include/sound/soc.h b/include/sound/soc.h
index f46b2bc2a022..5afc34b147b5 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -699,7 +699,8 @@ struct snd_soc_dai_link_component {
struct snd_soc_dai_link_ch_map {
unsigned int cpu;
unsigned int codec;
- unsigned int ch_mask;
+ unsigned int cpu_ch_mask;
+ unsigned int codec_ch_mask;
};
struct snd_soc_dai_link {
diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h
index 9b28e9aef4f1..9fbb69b9052d 100644
--- a/include/sound/soc_sdw_utils.h
+++ b/include/sound/soc_sdw_utils.h
@@ -250,8 +250,6 @@ int asoc_sdw_cs_amp_init(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_links,
struct asoc_sdw_codec_info *info,
bool playback);
-int asoc_sdw_cs_spk_feedback_rtd_init(struct snd_soc_pcm_runtime *rtd,
- struct snd_soc_dai *dai);
int asoc_sdw_cs35l56_volume_limit(struct snd_soc_card *card, const char *name_prefix);
/* MAXIM codec support */
diff --git a/include/trace/events/dma.h b/include/trace/events/dma.h
index 9df02c1511de..b06d8f99922d 100644
--- a/include/trace/events/dma.h
+++ b/include/trace/events/dma.h
@@ -134,7 +134,7 @@ DECLARE_EVENT_CLASS(dma_alloc_class,
TP_fast_assign(
__assign_str(device);
__entry->virt_addr = virt_addr;
- __entry->dma_addr = dma_addr;
+ __entry->dma_addr = virt_addr ? dma_addr : 0;
__entry->size = size;
__entry->flags = flags;
__entry->dir = dir;
diff --git a/kernel/dma/coherent.c b/kernel/dma/coherent.c
index 45bbae947f4b..4d0266893bcc 100644
--- a/kernel/dma/coherent.c
+++ b/kernel/dma/coherent.c
@@ -352,8 +352,7 @@ static int rmem_dma_device_init(struct reserved_mem *rmem, struct device *dev)
min_not_zero(dev->coherent_dma_mask, dev->bus_dma_limit))
dev_warn(dev, "reserved memory is beyond device's set DMA address range\n");
- dma_assign_coherent_memory(dev, mem);
- return 0;
+ return dma_assign_coherent_memory(dev, mem);
}
static void rmem_dma_device_release(struct reserved_mem *rmem,
diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index ded7016a46a7..aa2f1c4588b9 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -1019,7 +1019,6 @@ static void swiotlb_bounce(struct device *dev, phys_addr_t tlb_addr, size_t size
int index = (tlb_addr - mem->start) >> IO_TLB_SHIFT;
phys_addr_t orig_addr = mem->slots[index].orig_addr;
size_t alloc_size = mem->slots[index].alloc_size;
- unsigned long pfn = PFN_DOWN(orig_addr);
unsigned char *vaddr = mem->vaddr + tlb_addr - mem->start;
int tlb_offset;
@@ -1052,7 +1051,8 @@ static void swiotlb_bounce(struct device *dev, phys_addr_t tlb_addr, size_t size
size = alloc_size;
}
- if (PageHighMem(pfn_to_page(pfn))) {
+ if (PhysHighMem(orig_addr)) {
+ unsigned long pfn = PFN_DOWN(orig_addr);
unsigned int offset = orig_addr & ~PAGE_MASK;
struct page *page;
unsigned int sz = 0;
diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c
index 51de1d8b72a1..3219f0da0fe4 100644
--- a/kernel/sched/ext/ext.c
+++ b/kernel/sched/ext/ext.c
@@ -2919,7 +2919,7 @@ static inline void maybe_queue_balance_callback(struct rq *rq)
static enum scx_dsp_verdict dispatch_one(struct rq *rq, struct task_struct *prev)
{
- struct scx_sched *sch = scx_root_protected_live();
+ struct scx_sched *root_sch = scx_root_protected_live();
enum scx_dsp_verdict verdict;
s32 cpu = cpu_of(rq);
@@ -2928,7 +2928,7 @@ static enum scx_dsp_verdict dispatch_one(struct rq *rq, struct task_struct *prev
scx_process_sync_ecaps(rq, prev);
- if ((sch->ops.flags & SCX_OPS_HAS_CPU_PREEMPT) &&
+ if ((root_sch->ops.flags & SCX_OPS_HAS_CPU_PREEMPT) &&
unlikely(rq->scx.cpu_released)) {
/*
* If the previous sched_class for the current CPU was not SCX,
@@ -2936,8 +2936,8 @@ static enum scx_dsp_verdict dispatch_one(struct rq *rq, struct task_struct *prev
* core. This callback complements ->cpu_release(), which is
* emitted in switch_class().
*/
- if (sch->ops.cpu_acquire)
- SCX_CALL_OP(sch, cpu_acquire, rq, cpu, NULL);
+ if (root_sch->ops.cpu_acquire)
+ SCX_CALL_OP(root_sch, cpu_acquire, rq, cpu, NULL);
rq->scx.cpu_released = false;
}
@@ -2955,7 +2955,7 @@ static enum scx_dsp_verdict dispatch_one(struct rq *rq, struct task_struct *prev
* test.
*/
if ((prev->scx.flags & SCX_TASK_QUEUED) && prev->scx.slice &&
- !scx_bypassing(sch, cpu)) {
+ !scx_bypassing(scx_task_sched(prev), cpu)) {
verdict = SCX_DSP_PREV;
goto has_tasks;
}
@@ -2967,20 +2967,25 @@ static enum scx_dsp_verdict dispatch_one(struct rq *rq, struct task_struct *prev
goto has_tasks;
}
- verdict = scx_dispatch_sched(sch, rq, prev, false);
+ verdict = scx_dispatch_sched(root_sch, rq, prev, false);
if (verdict != SCX_DSP_NONE)
goto has_tasks;
/*
- * Didn't find another task to run. Keep running @prev unless
- * %SCX_OPS_ENQ_LAST is in effect.
+ * Didn't find another task to run. Keep running @prev unless its own
+ * scheduler set %SCX_OPS_ENQ_LAST and takes the enqueue instead, see
+ * put_prev_task_scx(). Read the scheduler here as the dispatch above
+ * may have dropped the rq lock while @prev changed class or scheduler.
*/
- if ((prev->scx.flags & SCX_TASK_QUEUED) &&
- (!(sch->ops.flags & SCX_OPS_ENQ_LAST) || scx_bypassing(sch, cpu)) &&
- scx_task_can_stay_on_cpu(rq, prev)) {
- __scx_add_event(sch, SCX_EV_DISPATCH_KEEP_LAST, 1);
- verdict = SCX_DSP_PREV;
- goto has_tasks;
+ if (prev->scx.flags & SCX_TASK_QUEUED) {
+ struct scx_sched *prev_sch = scx_task_sched(prev);
+
+ if ((!(prev_sch->ops.flags & SCX_OPS_ENQ_LAST) ||
+ scx_bypassing(prev_sch, cpu)) && scx_task_can_stay_on_cpu(rq, prev)) {
+ __scx_add_event(prev_sch, SCX_EV_DISPATCH_KEEP_LAST, 1);
+ verdict = SCX_DSP_PREV;
+ goto has_tasks;
+ }
}
rq->scx.flags &= ~SCX_RQ_IN_DISPATCH;
return SCX_DSP_NONE;
@@ -3665,8 +3670,20 @@ static void handle_hotplug(struct rq *rq, bool online)
s16 *tbl = rcu_dereference_check(scx_cpu_to_cid_tbl,
lockdep_is_cpus_held());
- if (tbl)
+ if (tbl) {
+ struct scx_sched *pos;
+
cpu_or_cid = tbl[cpu];
+
+ guard(raw_spinlock_irqsave)(&scx_sched_lock);
+ list_for_each_entry(pos, &scx_sched_all, all) {
+ struct scx_cmask *mask = pos->online_cmask;
+
+ if (mask)
+ __assign_bit(cpu_or_cid, (unsigned long *)mask->bits,
+ online);
+ }
+ }
}
if (online && SCX_HAS_OP(sch, cpu_online))
@@ -4766,7 +4783,8 @@ int scx_tg_online(struct task_group *tg)
{ .weight = tg->scx.weight,
.bw_period_us = tg->scx.bw_period_us,
.bw_quota_us = tg->scx.bw_quota_us,
- .bw_burst_us = tg->scx.bw_burst_us };
+ .bw_burst_us = tg->scx.bw_burst_us,
+ .sched_idle = tg->scx.idle };
ret = SCX_CALL_OP_RET(sch, cgroup_init,
NULL, tg->css.cgroup, &args);
@@ -4932,7 +4950,8 @@ void scx_group_set_idle(struct task_group *tg, bool idle)
percpu_down_read(&scx_cgroup_ops_rwsem);
sch = scx_tg_knob_sched(tg);
- if (scx_cgroup_enabled && sch && SCX_HAS_OP(sch, cgroup_set_idle))
+ if (scx_cgroup_enabled && sch && SCX_HAS_OP(sch, cgroup_set_idle) &&
+ tg->scx.idle != idle)
SCX_CALL_OP(sch, cgroup_set_idle, NULL, tg_cgrp(tg), idle);
/* Update the task group's idle state */
@@ -5187,6 +5206,7 @@ static int scx_cgroup_init(struct scx_sched *sch)
.bw_period_us = tg->scx.bw_period_us,
.bw_quota_us = tg->scx.bw_quota_us,
.bw_burst_us = tg->scx.bw_burst_us,
+ .sched_idle = tg->scx.idle,
};
ret = SCX_CALL_OP_RET(sch, cgroup_init, NULL, css->cgroup, &args);
@@ -5272,12 +5292,17 @@ static void free_exit_info(struct scx_exit_info *ei);
static const char *scx_exit_reason(enum scx_exit_kind kind);
static bool scx_claim_exit(struct scx_sched *sch, enum scx_exit_kind kind);
-s32 scx_set_cmask_scratch_alloc(struct scx_sched *sch)
+s32 scx_alloc_kern_arena_objs(struct scx_sched *sch)
{
size_t size = struct_size_t(struct scx_cmask, bits,
SCX_CMASK_NR_WORDS(num_possible_cpus()));
+ struct scx_cmask *online;
+ struct scx_cmask_ref ref;
int cpu;
+ /* hotplug stays excluded until the online mask is published */
+ lockdep_assert_cpus_held();
+
if (!sch->is_cid_type || !sch->arena_pool)
return 0;
@@ -5293,15 +5318,28 @@ s32 scx_set_cmask_scratch_alloc(struct scx_sched *sch)
return -ENOMEM;
scx_cmask_init(*slot, 0, num_possible_cpus());
}
+
+ /* pack the online mask alongside the scratch masks */
+ online = scx_arena_alloc(sch, size);
+ if (!online)
+ return -ENOMEM;
+
+ scoped_guard(rcu) {
+ scx_cmask_ref_init_kern(sch, online, 0, num_possible_cpus(), &ref);
+ scx_cmask_ref_from_cpumask(&ref, cpu_active_mask);
+ }
+ sch->online_cmask = online;
+
return 0;
}
-static void scx_set_cmask_scratch_free(struct scx_sched *sch)
+static void scx_free_kern_arena_objs(struct scx_sched *sch)
{
size_t size = struct_size_t(struct scx_cmask, bits,
SCX_CMASK_NR_WORDS(num_possible_cpus()));
int cpu;
+ scx_arena_free(sch, sch->online_cmask, size);
if (!sch->set_cmask_scratch)
return;
@@ -5388,7 +5426,7 @@ static void scx_sched_free_rcu_work(struct work_struct *work)
rhashtable_free_and_destroy(&sch->dsq_hash, NULL, NULL);
free_exit_info(sch->exit_info);
- scx_set_cmask_scratch_free(sch);
+ scx_free_kern_arena_objs(sch);
scx_arena_pool_destroy(sch);
if (sch->arena_map)
bpf_map_put(sch->arena_map);
@@ -7508,22 +7546,24 @@ static void scx_root_enable_workfn(struct kthread_work *work)
#ifdef CONFIG_EXT_SUB_SCHED
cgroup_get(cgrp);
#endif
+ /*
+ * Transition to ENABLING to arm the disable path. Allocation failure
+ * still unwinds locally. Full disabling on failure applies only after
+ * scx_alloc_and_add_sched() succeeds.
+ */
+ WARN_ON_ONCE(scx_set_enable_state(SCX_ENABLING) != SCX_DISABLED);
+ WARN_ON_ONCE(scx_root);
+
sch = scx_alloc_and_add_sched(cmd, cgrp, NULL);
if (IS_ERR(sch)) {
ret = PTR_ERR(sch);
+ WARN_ON_ONCE(scx_set_enable_state(SCX_DISABLED) != SCX_ENABLING);
goto err_free_tid_hash;
}
if (sch->is_cid_type)
static_branch_enable(&__scx_is_cid_type);
- /*
- * Transition to ENABLING and clear exit info to arm the disable path.
- * Failure triggers full disabling from here on.
- */
- WARN_ON_ONCE(scx_set_enable_state(SCX_ENABLING) != SCX_DISABLED);
- WARN_ON_ONCE(scx_root);
-
atomic_long_set(&scx_nr_rejected, 0);
for_each_possible_cpu(cpu) {
@@ -7591,7 +7631,7 @@ static void scx_root_enable_workfn(struct kthread_work *work)
goto err_disable;
}
- ret = scx_set_cmask_scratch_alloc(sch);
+ ret = scx_alloc_kern_arena_objs(sch);
if (ret) {
cpus_read_unlock();
goto err_disable;
@@ -8946,10 +8986,17 @@ __bpf_kfunc void scx_bpf_dsq_insert_vtime(struct task_struct *p, u64 dsq_id,
#ifdef CONFIG_EXT_SUB_SCHED
/*
* Disallow if any sub-scheds are attached. There is no way to tell
- * which scheduler called us, just error out @p's scheduler.
+ * which scheduler called us, so error out @p's scheduler -- read it
+ * under RCU as @p's locks aren't necessarily held here. @p may be a
+ * task past sched_ext_dead() or an idle task, in which case its
+ * scheduler can't be determined and there is nothing obviously wrong
+ * to report; just refuse the call.
*/
if (unlikely(!list_empty(&sch->children))) {
- scx_error(scx_task_sched(p), "__scx_bpf_dsq_insert_vtime() must be used");
+ struct scx_sched *tsch = scx_task_sched_rcu(p);
+
+ if (tsch)
+ scx_error(tsch, "__scx_bpf_dsq_insert_vtime() must be used");
return;
}
#endif
@@ -10321,7 +10368,8 @@ __bpf_kfunc u32 scx_bpf_nr_cids(void)
* hotplug, which lets schedulers treat [0, nr_online_cids) as the online
* range. Schedulers that prefer to handle hotplug without a restart should
* install a custom mapping via scx_bpf_cid_override() and track onlining
- * through the ops.cid_online / ops.cid_offline callbacks.
+ * through the ops.cid_online / ops.cid_offline callbacks, starting from the
+ * mask scx_bpf_online_cmask() returns.
*/
__bpf_kfunc u32 scx_bpf_nr_online_cids(void)
{
@@ -10329,6 +10377,37 @@ __bpf_kfunc u32 scx_bpf_nr_online_cids(void)
}
/**
+ * scx_bpf_online_cmask - Return the online cid mask in the scheduler arena
+ * @aux: implicit BPF argument to access bpf_prog_aux hidden from BPF progs
+ *
+ * Return a kernel-maintained cmask covering [0, scx_bpf_nr_cids()), or NULL if
+ * the calling program is not associated with a live cid-form scheduler or the
+ * mask is not allocated yet, as in ops.init_cids(). Treat the mask as read-only
+ * even though arena memory stays writable by the BPF scheduler. The mask
+ * follows the SCX hotplug notifications: a cid's bit is updated before
+ * ops.cid_online/offline() runs for it. The pointer is valid from ops.init()
+ * through ops.exit(). Root ops.init() runs with hotplug excluded. Other
+ * contexts can observe concurrent updates.
+ */
+__bpf_kfunc const void *scx_bpf_online_cmask(const struct bpf_prog_aux *aux)
+{
+ struct scx_sched *sch;
+ struct scx_cmask *online;
+
+ guard(rcu)();
+
+ sch = scx_prog_sched(aux);
+ if (unlikely(!sch))
+ return NULL;
+ online = sch->online_cmask;
+ if (unlikely(!online))
+ return NULL;
+
+ /* BPF rebases by the low 32 bits, like __arena callback args */
+ return (void *)((unsigned long)online - sch->arena_kern_base);
+}
+
+/**
* scx_bpf_this_cid - Return the cid of the CPU this program is running on
*
* cid-addressed equivalent of bpf_get_smp_processor_id() for scx programs.
@@ -10691,6 +10770,7 @@ BTF_ID_FLAGS(func, scx_bpf_nr_node_ids)
BTF_ID_FLAGS(func, scx_bpf_nr_cpu_ids)
BTF_ID_FLAGS(func, scx_bpf_nr_cids)
BTF_ID_FLAGS(func, scx_bpf_nr_online_cids)
+BTF_ID_FLAGS(func, scx_bpf_online_cmask, KF_IMPLICIT_ARGS | KF_ARENA_RET)
BTF_ID_FLAGS(func, scx_bpf_this_cid)
BTF_ID_FLAGS(func, scx_bpf_get_possible_cpumask, KF_ACQUIRE)
BTF_ID_FLAGS(func, scx_bpf_get_online_cpumask, KF_ACQUIRE)
diff --git a/kernel/sched/ext/idle.c b/kernel/sched/ext/idle.c
index d2973fb3af6d..aa9fb6de0ad6 100644
--- a/kernel/sched/ext/idle.c
+++ b/kernel/sched/ext/idle.c
@@ -1142,10 +1142,17 @@ __bpf_kfunc s32 scx_bpf_select_cpu_and(struct task_struct *p, s32 prev_cpu, u64
#ifdef CONFIG_EXT_SUB_SCHED
/*
* Disallow if any sub-scheds are attached. There is no way to tell
- * which scheduler called us, just error out @p's scheduler.
+ * which scheduler called us, so error out @p's scheduler -- read it
+ * under RCU as @p's locks aren't necessarily held here. @p may be a
+ * task past sched_ext_dead() or an idle task, in which case its
+ * scheduler can't be determined and there is nothing obviously wrong
+ * to report; just refuse the call.
*/
if (unlikely(!list_empty(&sch->children))) {
- scx_error(scx_task_sched(p), "__scx_bpf_select_cpu_and() must be used");
+ struct scx_sched *tsch = scx_task_sched_rcu(p);
+
+ if (tsch)
+ scx_error(tsch, "__scx_bpf_select_cpu_and() must be used");
return -EINVAL;
}
#endif
diff --git a/kernel/sched/ext/internal.h b/kernel/sched/ext/internal.h
index 0967b99a4948..3464e0f113c1 100644
--- a/kernel/sched/ext/internal.h
+++ b/kernel/sched/ext/internal.h
@@ -259,6 +259,9 @@ struct scx_cgroup_init_args {
u64 bw_period_us;
u64 bw_quota_us;
u64 bw_burst_us;
+
+ /* whether the cgroup is configured SCHED_IDLE via cpu.idle */
+ bool sched_idle;
};
enum scx_cpu_preempt_reason {
@@ -569,6 +572,12 @@ struct sched_ext_ops {
*
* Specify the %SCX_OPS_KEEP_BUILTIN_IDLE flag to keep the built-in idle
* tracking.
+ *
+ * Only actual transitions are reported. A CPU that is claimed with an
+ * idle pick and kicked but dispatches no task returns to idle without a
+ * transition. A scheduler tracking idle CPUs itself must restore the
+ * idle state from ops.dispatch() when it returns without the next task
+ * to run.
*/
void (*update_idle)(s32 cpu, bool idle);
@@ -1552,6 +1561,7 @@ struct scx_sched {
* and passes it to the callback's __arena argument.
*/
struct scx_cmask * __percpu *set_cmask_scratch;
+ struct scx_cmask *online_cmask;
DECLARE_BITMAP(has_op, SCX_OPI_END);
@@ -2078,7 +2088,7 @@ void scx_disable_and_exit_task(struct scx_sched *sch, struct task_struct *p);
void scx_cgroup_lock(void);
void scx_cgroup_unlock(void);
#endif
-s32 scx_set_cmask_scratch_alloc(struct scx_sched *sch);
+s32 scx_alloc_kern_arena_objs(struct scx_sched *sch);
void scx_disable_bypass_dsp(struct scx_sched *sch);
void scx_bypass(struct scx_sched *sch, bool bypass);
s32 scx_link_sched(struct scx_sched *sch);
diff --git a/kernel/sched/ext/sub.c b/kernel/sched/ext/sub.c
index 9e7040482bde..34e642a1a403 100644
--- a/kernel/sched/ext/sub.c
+++ b/kernel/sched/ext/sub.c
@@ -1361,6 +1361,7 @@ static s32 scx_cgroup_claim_subtree(struct scx_sched *sch)
.bw_period_us = tg->scx.bw_period_us,
.bw_quota_us = tg->scx.bw_quota_us,
.bw_burst_us = tg->scx.bw_burst_us,
+ .sched_idle = tg->scx.idle,
};
if (tg->scx.sched != parent ||
@@ -1464,6 +1465,7 @@ static void scx_cgroup_return_subtree(struct scx_sched *sch)
.bw_period_us = tg->scx.bw_period_us,
.bw_quota_us = tg->scx.bw_quota_us,
.bw_burst_us = tg->scx.bw_burst_us,
+ .sched_idle = tg->scx.idle,
};
/* the first pass must have transferred everything */
@@ -1803,6 +1805,12 @@ void scx_sub_enable_workfn(struct kthread_work *work)
goto err_disable;
}
+ scoped_guard(cpus_read_lock) {
+ ret = scx_alloc_kern_arena_objs(sch);
+ if (ret)
+ goto err_disable;
+ }
+
if (sch->ops.init) {
ret = SCX_CALL_OP_RET(sch, init, NULL);
if (ret) {
@@ -1813,10 +1821,6 @@ void scx_sub_enable_workfn(struct kthread_work *work)
sch->exit_info->flags |= SCX_EFLAG_INITIALIZED;
}
- ret = scx_set_cmask_scratch_alloc(sch);
- if (ret)
- goto err_disable;
-
struct scx_sub_attach_args sub_attach_args = {
.ops = &sch->ops,
.cgroup_path = sch->cgrp_path,
diff --git a/sound/core/init.c b/sound/core/init.c
index 9693e646b3bb..1bcb6a2e7550 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -310,7 +310,7 @@ static int snd_card_init(struct snd_card *card, struct device *parent,
kfree(card); /* manually free here, as no destructor called */
return err;
}
- card->dev = parent;
+ card->dev = get_device(parent);
card->number = idx;
WARN_ON(IS_MODULE(CONFIG_SND) && !module);
card->module = module;
@@ -603,6 +603,7 @@ static int snd_card_do_free(struct snd_card *card)
dev_warn(card->dev, "unable to free card info\n");
/* Not fatal error */
}
+ put_device(card->dev);
if (card->release_completion)
complete(card->release_completion);
if (!managed)
diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c
index ab0e5bd70f8f..18bedd66435d 100644
--- a/sound/core/pcm_timer.c
+++ b/sound/core/pcm_timer.c
@@ -111,12 +111,15 @@ void snd_pcm_timer_init(struct snd_pcm_substream *substream)
snd_pcm_direction_name(substream->stream),
tid.card, tid.device, tid.subdevice);
timer->hw = snd_pcm_timer;
+ /* Set before registering: a concurrent reader can invoke our hw
+ * callbacks as soon as the timer is on the global list.
+ */
+ timer->private_data = substream;
+ timer->private_free = snd_pcm_timer_free;
if (snd_device_register(timer->card, timer) < 0) {
snd_device_free(timer->card, timer);
return;
}
- timer->private_data = substream;
- timer->private_free = snd_pcm_timer_free;
substream->timer = timer;
}
diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c
index 3abee617e86e..5127a111c59c 100644
--- a/sound/hda/codecs/realtek/alc269.c
+++ b/sound/hda/codecs/realtek/alc269.c
@@ -4303,6 +4303,7 @@ enum {
ALC287_FIXUP_LEGION_16ACHG6,
ALC287_FIXUP_CS35L41_I2C_2,
ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED,
+ ALC287_FIXUP_CS35L41_I2C_2_HP_MUTE_LEDS,
ALC287_FIXUP_CS35L41_I2C_4,
ALC245_FIXUP_CS35L41_SPI_1,
ALC245_FIXUP_CS35L41_SPI_2,
@@ -6614,6 +6615,12 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC285_FIXUP_HP_MUTE_LED,
},
+ [ALC287_FIXUP_CS35L41_I2C_2_HP_MUTE_LEDS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_i2c_two,
+ .chained = true,
+ .chain_id = ALC245_FIXUP_HP_X360_MUTE_LEDS,
+ },
[ALC287_FIXUP_CS35L41_I2C_4] = {
.type = HDA_FIXUP_FUNC,
.v.func = cs35l41_fixup_i2c_four,
@@ -7358,6 +7365,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8158, "HP", ALC256_FIXUP_HP_HEADSET_MIC),
SND_PCI_QUIRK(0x103c, 0x820d, "HP Pavilion 15", ALC295_FIXUP_HP_X360),
SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC),
+ SND_PCI_QUIRK(0x103c, 0x8259, "HP OMEN 15-ax202nf", ALC269_FIXUP_HP_MUTE_LED_MIC3),
SND_PCI_QUIRK(0x103c, 0x827e, "HP x360", ALC295_FIXUP_HP_X360),
SND_PCI_QUIRK(0x103c, 0x827f, "HP x360", ALC269_FIXUP_HP_MUTE_LED_MIC3),
SND_PCI_QUIRK(0x103c, 0x82bf, "HP G3 mini", ALC221_FIXUP_HP_MIC_NO_PRESENCE),
@@ -7568,6 +7576,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8b96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
SND_PCI_QUIRK(0x103c, 0x8ba9, "HP Omen 16-wd0xxx", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8bb1, "HP Victus 15-fa1xxx (MB 8BB1)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8bb3, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8bb4, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8bb6, "HP Laptop 15-fd0039nt", ALC236_FIXUP_HP_15_FD0XXX),
@@ -7660,7 +7669,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8d92, "HP ZBook Firefly 16 G12", ALC285_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8dcd, "HP Victus 15-fa2xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8d9b, "HP 17 Turbine OmniBook 7 UMA", ALC287_FIXUP_CS35L41_I2C_2),
- SND_PCI_QUIRK(0x103c, 0x8d9c, "HP 17 Turbine OmniBook 7 DIS", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8d9c, "HP 17 Turbine OmniBook 7 DIS", ALC287_FIXUP_CS35L41_I2C_2_HP_MUTE_LEDS),
SND_PCI_QUIRK(0x103c, 0x8d9d, "HP 17 Turbine OmniBook X UMA", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8d9e, "HP 17 Turbine OmniBook X DIS", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8d9f, "HP 14 Cadet (x360)", ALC287_FIXUP_CS35L41_I2C_2),
diff --git a/sound/hda/common/controller.c b/sound/hda/common/controller.c
index afec5c5546ec..18dae022b324 100644
--- a/sound/hda/common/controller.c
+++ b/sound/hda/common/controller.c
@@ -586,11 +586,11 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
snd_hda_codec_pcm_get(apcm->info);
mutex_lock(&chip->open_mutex);
azx_dev = azx_assign_device(chip, substream);
- trace_azx_pcm_open(chip, azx_dev);
if (azx_dev == NULL) {
err = -EBUSY;
goto unlock;
}
+ trace_azx_pcm_open(chip, azx_dev);
runtime->private_data = azx_dev;
runtime->hw = azx_pcm_hw;
diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c
index 6eac42bac855..1a05d4288a46 100644
--- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c
+++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c
@@ -205,9 +205,19 @@ static int create_sdw_dailink(struct snd_soc_card *card,
return -EINVAL;
}
+ if (!soc_end->link_mask) {
+ dev_err(dev, "invalid zero link_mask\n");
+ return -EINVAL;
+ }
+ if ((ffs(soc_end->link_mask) - 1) >= amd_ctx->max_sdw_links) {
+ dev_err(dev, "link_id %d exceeds max_sdw_links %d\n",
+ ffs(soc_end->link_mask) - 1, amd_ctx->max_sdw_links);
+ return -EINVAL;
+ }
+
switch (amd_ctx->acp_rev) {
case ACP63_PCI_REV:
- ret = get_acp63_cpu_pin_id(ffs(soc_end->link_mask - 1),
+ ret = get_acp63_cpu_pin_id(ffs(soc_end->link_mask) - 1,
*be_id, &cpu_pin_id, dev);
if (ret)
return ret;
@@ -215,7 +225,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
case ACP70_PCI_REV:
case ACP71_PCI_REV:
case ACP72_PCI_REV:
- ret = get_acp70_cpu_pin_id(ffs(soc_end->link_mask - 1),
+ ret = get_acp70_cpu_pin_id(ffs(soc_end->link_mask) - 1,
*be_id, &cpu_pin_id, dev);
if (ret)
return ret;
diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c
index a9cd1f335167..ec3e1f5f1052 100644
--- a/sound/soc/amd/acp/acp-sdw-sof-mach.c
+++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c
@@ -121,9 +121,18 @@ static int create_sdw_dailink(struct snd_soc_card *card,
return -EINVAL;
}
+ if (!sof_end->link_mask) {
+ dev_err(dev, "invalid zero link_mask\n");
+ return -EINVAL;
+ }
+ if ((ffs(sof_end->link_mask) - 1) >= amd_ctx->max_sdw_links) {
+ dev_err(dev, "link_id %d exceeds max_sdw_links %d\n",
+ ffs(sof_end->link_mask) - 1, amd_ctx->max_sdw_links);
+ return -EINVAL;
+ }
switch (amd_ctx->acp_rev) {
case ACP63_PCI_REV:
- ret = get_acp63_cpu_pin_id(ffs(sof_end->link_mask - 1),
+ ret = get_acp63_cpu_pin_id(ffs(sof_end->link_mask) - 1,
*be_id, &cpu_pin_id, dev);
if (ret)
return ret;
@@ -131,7 +140,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
case ACP70_PCI_REV:
case ACP71_PCI_REV:
case ACP72_PCI_REV:
- ret = get_acp70_cpu_pin_id(ffs(sof_end->link_mask - 1),
+ ret = get_acp70_cpu_pin_id(ffs(sof_end->link_mask) - 1,
*be_id, &cpu_pin_id, dev);
if (ret)
return ret;
@@ -277,6 +286,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
int num_devs = 0;
int num_ends = 0;
int num_aux = 0;
+ int num_confs;
int num_links;
int be_id = 0;
int ret;
@@ -287,6 +297,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
return ret;
}
+ num_confs = num_ends;
/* One per DAI link, worst case is a DAI link for every endpoint */
struct asoc_sdw_dailink *sof_dais __free(kfree) =
kzalloc_objs(*sof_dais, num_ends);
@@ -303,7 +314,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
if (!sof_aux)
return -ENOMEM;
- ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_devs);
+ ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_confs);
if (ret < 0)
return ret;
@@ -315,7 +326,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
dev_dbg(dev, "sdw %d, dmic %d", sdw_be_num, dmic_num);
- codec_conf = devm_kcalloc(dev, num_devs, sizeof(*codec_conf), GFP_KERNEL);
+ codec_conf = devm_kcalloc(dev, num_confs, sizeof(*codec_conf), GFP_KERNEL);
if (!codec_conf)
return -ENOMEM;
@@ -326,7 +337,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
return -ENOMEM;
card->codec_conf = codec_conf;
- card->num_configs = num_devs;
+ card->num_configs = num_confs;
card->dai_link = dai_links;
card->num_links = num_links;
card->aux_dev = sof_aux;
@@ -379,7 +390,7 @@ static int mc_probe(struct platform_device *pdev)
ctx->private = amd_ctx;
card = &ctx->card;
card->dev = &pdev->dev;
- card->name = "amd-soundwire";
+ card->name = "amd-sdw";
card->owner = THIS_MODULE;
card->late_probe = asoc_sdw_card_late_probe;
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index f9a47e262a77..d88593c2bac8 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -524,13 +524,13 @@ config SND_SOC_ADAU1977
tristate
config SND_SOC_ADAU1977_SPI
- tristate
+ tristate "Analog Devices ADAU1977/ADAU1978/ADAU1979 CODEC - SPI"
depends on SPI_MASTER
select SND_SOC_ADAU1977
select REGMAP_SPI
config SND_SOC_ADAU1977_I2C
- tristate
+ tristate "Analog Devices ADAU1977/ADAU1978/ADAU1979 CODEC - I2C"
depends on I2C
select SND_SOC_ADAU1977
select REGMAP_I2C
diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c
index d1c6c4ddf506..5a11cafdff36 100644
--- a/sound/soc/codecs/adau1977-i2c.c
+++ b/sound/soc/codecs/adau1977-i2c.c
@@ -34,9 +34,18 @@ static const struct i2c_device_id adau1977_i2c_ids[] = {
};
MODULE_DEVICE_TABLE(i2c, adau1977_i2c_ids);
+static const struct of_device_id adau1977_i2c_of_match[] = {
+ { .compatible = "adi,adau1977" },
+ { .compatible = "adi,adau1978" },
+ { .compatible = "adi,adau1979" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adau1977_i2c_of_match);
+
static struct i2c_driver adau1977_i2c_driver = {
.driver = {
.name = "adau1977",
+ .of_match_table = adau1977_i2c_of_match,
},
.probe = adau1977_i2c_probe,
.id_table = adau1977_i2c_ids,
diff --git a/sound/soc/codecs/adau1977-spi.c b/sound/soc/codecs/adau1977-spi.c
index 878cde9d1014..c98da5ba9e9e 100644
--- a/sound/soc/codecs/adau1977-spi.c
+++ b/sound/soc/codecs/adau1977-spi.c
@@ -53,18 +53,18 @@ static const struct spi_device_id adau1977_spi_ids[] = {
};
MODULE_DEVICE_TABLE(spi, adau1977_spi_ids);
-static const struct of_device_id adau1977_spi_of_match[] __maybe_unused = {
- { .compatible = "adi,adau1977" },
- { .compatible = "adi,adau1978" },
- { .compatible = "adi,adau1979" },
- { },
+static const struct of_device_id adau1977_spi_of_match[] = {
+ { .compatible = "adi,adau1977" },
+ { .compatible = "adi,adau1978" },
+ { .compatible = "adi,adau1979" },
+ { },
};
MODULE_DEVICE_TABLE(of, adau1977_spi_of_match);
static struct spi_driver adau1977_spi_driver = {
.driver = {
.name = "adau1977",
- .of_match_table = of_match_ptr(adau1977_spi_of_match),
+ .of_match_table = adau1977_spi_of_match,
},
.probe = adau1977_spi_probe,
.id_table = adau1977_spi_ids,
diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c
index 41a9a5b005c6..9bc19d2e1639 100644
--- a/sound/soc/codecs/cs-amp-lib.c
+++ b/sound/soc/codecs/cs-amp-lib.c
@@ -317,6 +317,8 @@ static void *cs_amp_alloc_get_efi_variable(efi_char16_t *name,
unsigned long size = 0;
status = cs_amp_get_efi_variable(name, guid, NULL, &size, NULL);
+ if (status == EFI_SUCCESS)
+ return ERR_PTR(-ENOENT);
if (status != EFI_BUFFER_TOO_SMALL)
return ERR_PTR(cs_amp_convert_efi_status(status));
diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c
index bc2c22436ba6..7aa50c5bd3df 100644
--- a/sound/soc/codecs/hdmi-codec.c
+++ b/sound/soc/codecs/hdmi-codec.c
@@ -426,10 +426,14 @@ static int hdmi_codec_iec958_default_put(struct snd_kcontrol *kcontrol,
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
+ if (!memcmp(hcp->iec_status, ucontrol->value.iec958.status,
+ sizeof(hcp->iec_status)))
+ return 0;
+
memcpy(hcp->iec_status, ucontrol->value.iec958.status,
sizeof(hcp->iec_status));
- return 0;
+ return 1;
}
static int hdmi_codec_iec958_mask_get(struct snd_kcontrol *kcontrol,
diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c
index 8860d81134e7..a9f3aa4e143a 100644
--- a/sound/soc/codecs/rt712-sdca-dmic.c
+++ b/sound/soc/codecs/rt712-sdca-dmic.c
@@ -13,6 +13,7 @@
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
+#include <sound/sdw.h>
#include <sound/tlv.h>
#include "rt712-sdca.h"
#include "rt712-sdca-dmic.h"
@@ -632,10 +633,10 @@ static int rt712_sdca_dmic_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
- struct sdw_stream_config stream_config;
+ struct sdw_stream_config stream_config = {0};
struct sdw_port_config port_config;
struct sdw_stream_runtime *sdw_stream;
- int retval, num_channels;
+ int retval;
unsigned int sampling_rate;
dev_dbg(dai->dev, "%s %s", __func__, dai->name);
@@ -647,13 +648,8 @@ static int rt712_sdca_dmic_hw_params(struct snd_pcm_substream *substream,
if (!rt712->slave)
return -EINVAL;
- stream_config.frame_rate = params_rate(params);
- stream_config.ch_count = params_channels(params);
- stream_config.bps = snd_pcm_format_width(params_format(params));
- stream_config.direction = SDW_DATA_DIR_TX;
-
- num_channels = params_channels(params);
- port_config.ch_mask = GENMASK(num_channels - 1, 0);
+ /* SoundWire specific configuration */
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
port_config.num = 2;
retval = sdw_stream_add_slave(rt712->slave, &stream_config,
diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c
index c50e74e20a88..edba0367d9ce 100644
--- a/sound/soc/codecs/rt712-sdca-sdw.c
+++ b/sound/soc/codecs/rt712-sdca-sdw.c
@@ -18,12 +18,16 @@
static bool rt712_sdca_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
+ case 0x004d:
case 0x201a ... 0x201f:
case 0x2029 ... 0x202a:
case 0x202d ... 0x2034:
case 0x2230 ... 0x2232:
case 0x2f01 ... 0x2f0a:
case 0x2f35 ... 0x2f36:
+ case 0x2f3a:
+ case 0x2f3d:
+ case 0x2f41:
case 0x2f50:
case 0x2f54:
case 0x2f58 ... 0x2f5d:
@@ -48,6 +52,7 @@ static bool rt712_sdca_readable_register(struct device *dev, unsigned int reg)
static bool rt712_sdca_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
+ case 0x004d:
case 0x201b:
case 0x201c:
case 0x201d:
@@ -56,6 +61,9 @@ static bool rt712_sdca_volatile_register(struct device *dev, unsigned int reg)
case 0x2230:
case 0x2f01:
case 0x2f35:
+ case 0x2f3a:
+ case 0x2f3d:
+ case 0x2f41:
case 0x320c:
case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0):
case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ...
diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c
index eda87eb9ab66..38052cb19790 100644
--- a/sound/soc/codecs/rt712-sdca.c
+++ b/sound/soc/codecs/rt712-sdca.c
@@ -73,14 +73,57 @@ static int rt712_sdca_index_update_bits(struct rt712_sdca_priv *rt712,
return rt712_sdca_index_write(rt712, nid, reg, tmp);
}
+static void rt712_sdca_clk_patch(struct rt712_sdca_priv *rt712)
+{
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0000);
+ regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x03);
+ usleep_range(1000, 1100);
+ regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x02);
+ usleep_range(1000, 1100);
+ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x0080, 0x0000);
+ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x001f, 0x0017);
+ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF3, 0x0010, 0x0000);
+ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF1, 0x0081, 0x0001);
+ regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x03);
+ usleep_range(1000, 1100);
+ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF1, 0x0081, 0x0081);
+ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x0080, 0x0080);
+ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x001f, 0x0000);
+ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF3, 0x0010, 0x0010);
+ usleep_range(1000, 1100);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0081);
+}
+
+static void rt712_sdca_clk_patch2(struct rt712_sdca_priv *rt712)
+{
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_REG, 0x49, 0x0800,
+ 0x0000);
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_REG, 0x49, 0xf000,
+ 0x0000);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0000);
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_ANALOG_CTL, 0x0c, 0xc000,
+ 0xc000);
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_ANALOG_CTL, 0x00, 0xc000,
+ 0xc000);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0081);
+ regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x02);
+ usleep_range(1000, 1100);
+ regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x03);
+ usleep_range(1000, 1100);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0000);
+}
+
static int rt712_sdca_calibration(struct rt712_sdca_priv *rt712)
{
unsigned int val, loop_rc = 0, loop_dc = 0;
struct device *dev;
struct regmap *regmap = rt712->regmap;
+ unsigned int clk_base;
int chk_cnt = 100;
int ret = 0;
+ regmap_read(rt712->regmap, RT712_SDW_ROOT_CLK, &clk_base);
+
mutex_lock(&rt712->calibrate_mutex);
dev = regmap_get_device(regmap);
@@ -109,8 +152,35 @@ static int rt712_sdca_calibration(struct rt712_sdca_priv *rt712)
if (ret < 0)
goto _cali_fail_;
}
- if (loop_dc == chk_cnt)
- dev_err(dev, "%s, calibration time-out!\n", __func__);
+
+ if (loop_dc == chk_cnt) {
+ if (clk_base == RT712_CLK_FREQ_24_576MHZ) {
+ rt712_sdca_clk_patch(rt712);
+ rt712_sdca_clk_patch2(rt712);
+ }
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_FSM_CTL, 0x4100);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_CALI,
+ RT712_DAC_DC_CALI_CTL1, 0x7883);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_CALI,
+ RT712_DAC_DC_CALI_CTL1, 0xf893);
+ rt712_sdca_index_read(rt712, RT712_VENDOR_CALI,
+ RT712_DAC_DC_CALI_CTL1, &val);
+
+ for (loop_dc = 0; loop_dc < chk_cnt &&
+ (val & RT712_DAC_DC_CALI_TRIGGER); loop_dc++) {
+ usleep_range(10000, 11000);
+ ret = rt712_sdca_index_read(rt712, RT712_VENDOR_CALI,
+ RT712_DAC_DC_CALI_CTL1, &val);
+
+ if (ret < 0)
+ goto _cali_fail_;
+ }
+
+ if (loop_dc == chk_cnt)
+ dev_err(dev, "%s, calibration time-out!\n", __func__);
+ else
+ dev_dbg(dev, "%s, calibration success!\n", __func__);
+ }
if (loop_dc == chk_cnt || loop_rc == chk_cnt)
ret = -ETIMEDOUT;
@@ -1759,9 +1829,13 @@ static void rt712_sdca_va_io_init(struct rt712_sdca_priv *rt712)
static void rt712_sdca_vb_io_init(struct rt712_sdca_priv *rt712)
{
- int ret = 0;
unsigned int jack_func_status, mic_func_status, amp_func_status;
struct device *dev = &rt712->slave->dev;
+ unsigned int clk_base;
+ int ret = 0;
+
+ regmap_read(rt712->regmap, RT712_SDW_ROOT_CLK, &clk_base);
+ dev_dbg(dev, "%s clk_base=%x", __func__, clk_base);
regmap_read(rt712->regmap,
SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), &jack_func_status);
@@ -1773,6 +1847,12 @@ static void rt712_sdca_vb_io_init(struct rt712_sdca_priv *rt712)
__func__, jack_func_status, mic_func_status, amp_func_status);
rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_JD_CTL3, 0x7778);
+
+ if (clk_base == RT712_CLK_FREQ_24_576MHZ) {
+ rt712_sdca_clk_patch(rt712);
+ rt712_sdca_clk_patch2(rt712);
+ }
+
/* DMIC */
if ((mic_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt712->first_hw_init)) {
rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_DMIC2_FU_IT_FLOAT_CTL, 0x1526);
diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h
index 46740281a5c1..6229fe341bb5 100644
--- a/sound/soc/codecs/rt712-sdca.h
+++ b/sound/soc/codecs/rt712-sdca.h
@@ -162,6 +162,16 @@ struct rt712_dmic_kctrl_priv {
#define RT712_EAPD_HIGH 0x2
#define RT712_EAPD_LOW 0x0
+/* SDW clock root frequency */
+#define RT712_SDW_ROOT_CLK 0x004d
+#define RT712_SDW_SCALE_CLK0 0x0062
+#define RT712_SDW_SCALE_CLK1 0x0072
+
+/* PLL2 config */
+#define RT712_PLL2_CONF1 0x2f3a
+#define RT712_PLL2_CONF2 0x2f3d
+#define RT712_PLL2_CONF3 0x2f41
+
/* RC Calibration register */
#define RT712_RC_CAL 0x3201
@@ -254,6 +264,14 @@ enum rt712_sdca_version {
RT712_VB,
};
+enum {
+ RT712_CLK_FREQ_19_2_MHZ = 1,
+ RT712_CLK_FREQ_24MHZ = 2,
+ RT712_CLK_FREQ_24_576MHZ = 3,
+ RT712_CLK_FREQ_22_5792MHZ = 4,
+};
+
+
int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave);
int rt712_sdca_init(struct device *dev, struct regmap *regmap,
struct regmap *mbq_regmap, struct sdw_slave *slave);
diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c
index eae7d662efae..910583162d3e 100644
--- a/sound/soc/codecs/rt721-sdca-sdw.c
+++ b/sound/soc/codecs/rt721-sdca-sdw.c
@@ -70,6 +70,7 @@ static bool rt721_sdca_mbq_readable_register(struct device *dev, unsigned int re
case 0x0310100:
case 0x2000000 ... 0x2000003:
case 0x2000013:
+ case 0x2000026:
case 0x200002c:
case 0x200003c:
case 0x2000046:
@@ -142,6 +143,7 @@ static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int re
case 0x200000d:
case 0x2000019:
case 0x2000020:
+ case 0x2000026:
case 0x200002c:
case 0x2000030:
case 0x2000046:
@@ -155,6 +157,7 @@ static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int re
case 0x5810039:
case 0x5b10018:
case 0x5b10019:
+ case 0x6100006:
return true;
default:
return false;
diff --git a/sound/soc/codecs/rt721-sdca.c b/sound/soc/codecs/rt721-sdca.c
index a9479d0e4941..738644018fb9 100644
--- a/sound/soc/codecs/rt721-sdca.c
+++ b/sound/soc/codecs/rt721-sdca.c
@@ -1497,6 +1497,15 @@ int rt721_sdca_init(struct device *dev, struct regmap *regmap,
&soc_sdca_dev_rt721, rt721_sdca_dai, ARRAY_SIZE(rt721_sdca_dai));
}
+static void rt721_sdca_reset(struct rt721_sdca_priv *rt721)
+{
+ rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_VENDOR_REG,
+ RT721_VD_HIDDEN_CTRL, RT721_HIDDEN_REG_SW_RESET,
+ RT721_HIDDEN_REG_SW_RESET);
+ rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_HDA_LEGACY_RESET_CTL, 0x1, 0x1);
+}
+
int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave)
{
struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev);
@@ -1530,9 +1539,17 @@ int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave)
}
pm_runtime_get_noresume(&slave->dev);
+
+ if (!rt721->first_hw_init)
+ rt721_sdca_reset(rt721);
+
rt721_sdca_dmic_preset(rt721);
rt721_sdca_amp_preset(rt721);
rt721_sdca_jack_preset(rt721);
+
+ if (rt721->hs_jack && (!rt721->first_hw_init))
+ rt721_sdca_jack_init(rt721);
+
if (rt721->first_hw_init) {
regcache_cache_bypass(rt721->regmap, false);
regcache_mark_dirty(rt721->regmap);
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 90c24c4b318e..b8f3035af3ba 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -775,9 +775,12 @@ static int wm_adsp_request_firmware_file(struct wm_adsp *dsp,
s++;
}
+ adsp_dbg(dsp, "Try '%s'\n", fw->filename);
ret = wm_adsp_firmware_request(&fw->firmware, fw->filename, cs_dsp->dev);
if (ret < 0) {
- adsp_dbg(dsp, "Failed to request '%s': %d\n", fw->filename, ret);
+ if (ret != -ENOENT)
+ adsp_dbg(dsp, "Failed to request '%s': %d\n", fw->filename, ret);
+
kfree(fw->filename);
fw->filename = NULL;
if (ret != -ENOENT)
diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c
index 9b016136c639..f1e62c2e79fe 100644
--- a/sound/soc/intel/boards/sof_es8336.c
+++ b/sound/soc/intel/boards/sof_es8336.c
@@ -360,6 +360,15 @@ static const struct dmi_system_id sof_es8336_quirk_table[] = {
.driver_data = (void *)(SOF_ES8336_HEADPHONE_GPIO |
SOC_ES8336_HEADSET_MIC1)
},
+ {
+ .callback = sof_es8336_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "NDZ-WXX9"),
+ },
+ .driver_data = (void *)(SOF_ES8336_HEADPHONE_GPIO |
+ SOC_ES8336_HEADSET_MIC1)
+ },
{}
};
diff --git a/sound/soc/sdw_utils/soc_sdw_cs_amp.c b/sound/soc/sdw_utils/soc_sdw_cs_amp.c
index 325ab7230481..6e21ef8f87e2 100644
--- a/sound/soc/sdw_utils/soc_sdw_cs_amp.c
+++ b/sound/soc/sdw_utils/soc_sdw_cs_amp.c
@@ -14,7 +14,6 @@
#include <sound/soc-dai.h>
#include <sound/soc_sdw_utils.h>
-#define CS_AMP_CHANNELS_PER_AMP 4
#define CS35L56_SPK_VOLUME_0DB 400 /* 0dB Max */
int asoc_sdw_cs35l56_volume_limit(struct snd_soc_card *card, const char *name_prefix)
@@ -64,51 +63,6 @@ int asoc_sdw_cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai
}
EXPORT_SYMBOL_NS(asoc_sdw_cs_spk_rtd_init, "SND_SOC_SDW_UTILS");
-int asoc_sdw_cs_spk_feedback_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
-{
- const struct snd_soc_dai_link *dai_link = rtd->dai_link;
- const struct snd_soc_dai_link_ch_map *ch_map;
- const struct snd_soc_dai_link_component *codec_dlc;
- struct snd_soc_dai *codec_dai;
- u8 ch_slot[8] = {};
- unsigned int amps_per_bus, ch_per_amp, mask;
- int i, ret;
-
- WARN_ON(dai_link->num_cpus > ARRAY_SIZE(ch_slot));
-
- /*
- * CS35L56 has 4 TX channels. When the capture is aggregated the
- * same bus slots will be allocated to all the amps on a bus. Only
- * one amp on that bus can be transmitting in each slot so divide
- * the available 4 slots between all the amps on a bus.
- */
- amps_per_bus = dai_link->num_codecs / dai_link->num_cpus;
- if ((amps_per_bus == 0) || (amps_per_bus > CS_AMP_CHANNELS_PER_AMP)) {
- dev_err(rtd->card->dev, "Illegal num_codecs:%u / num_cpus:%u\n",
- dai_link->num_codecs, dai_link->num_cpus);
- return -EINVAL;
- }
-
- ch_per_amp = CS_AMP_CHANNELS_PER_AMP / amps_per_bus;
-
- for_each_rtd_ch_maps(rtd, i, ch_map) {
- codec_dlc = snd_soc_link_to_codec(rtd->dai_link, i);
- codec_dai = snd_soc_find_dai(codec_dlc);
- mask = GENMASK(ch_per_amp - 1, 0) << ch_slot[ch_map->cpu];
-
- ret = snd_soc_dai_set_tdm_slot(codec_dai, 0, mask, 4, 32);
- if (ret < 0) {
- dev_err(rtd->card->dev, "Failed to set TDM slot:%d\n", ret);
- return ret;
- }
-
- ch_slot[ch_map->cpu] += ch_per_amp;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_NS(asoc_sdw_cs_spk_feedback_rtd_init, "SND_SOC_SDW_UTILS");
-
int asoc_sdw_cs_amp_init(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_links,
struct asoc_sdw_codec_info *info,
diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c
index a66dcc02fb59..d2eeef4931c6 100644
--- a/sound/soc/sdw_utils/soc_sdw_utils.c
+++ b/sound/soc/sdw_utils/soc_sdw_utils.c
@@ -811,7 +811,6 @@ struct asoc_sdw_codec_info codec_info_list[] = {
.dai_name = "cs35l56-sdw1c",
.dai_type = SOC_SDW_DAI_TYPE_AMP,
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
- .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init,
},
},
.dai_num = 2,
@@ -840,7 +839,6 @@ struct asoc_sdw_codec_info codec_info_list[] = {
.dai_name = "cs35l56-sdw1c",
.dai_type = SOC_SDW_DAI_TYPE_AMP,
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
- .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init,
},
},
.dai_num = 2,
@@ -869,7 +867,6 @@ struct asoc_sdw_codec_info codec_info_list[] = {
.dai_name = "cs35l56-sdw1c",
.dai_type = SOC_SDW_DAI_TYPE_AMP,
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
- .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init,
},
},
.dai_num = 2,
@@ -898,7 +895,6 @@ struct asoc_sdw_codec_info codec_info_list[] = {
.dai_name = "cs35l56-sdw1c",
.dai_type = SOC_SDW_DAI_TYPE_AMP,
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
- .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init,
},
},
.dai_num = 2,
@@ -1565,7 +1561,7 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai_link_ch_map *ch_maps;
int ch = params_channels(params);
- unsigned int ch_mask;
+ unsigned int cpu_ch_mask, codec_ch_mask;
int num_codecs;
int step;
int i;
@@ -1575,8 +1571,9 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream,
/* Identical data will be sent to all codecs in playback */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- ch_mask = GENMASK(ch - 1, 0);
+ cpu_ch_mask = GENMASK(ch - 1, 0);
step = 0;
+ codec_ch_mask = 0;
} else {
num_codecs = rtd->dai_link->num_codecs;
@@ -1586,17 +1583,24 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- ch_mask = GENMASK(ch / num_codecs - 1, 0);
- step = hweight_long(ch_mask);
+ cpu_ch_mask = GENMASK(ch / num_codecs - 1, 0);
+ step = hweight_long(cpu_ch_mask);
+ codec_ch_mask = cpu_ch_mask;
}
/*
* The captured data will be combined from each cpu DAI if the dai
* link has more than one codec DAIs. Set codec channel mask and
* ASoC will set the corresponding channel numbers for each cpu dai.
+ *
+ * sdw_stream_add_slave() assigns different payload offsets to each
+ * codec in a capture stream, so that the same channels on each
+ * codec map to different channels on the CPU.
*/
- for_each_link_ch_maps(rtd->dai_link, i, ch_maps)
- ch_maps->ch_mask = ch_mask << (i * step);
+ for_each_link_ch_maps(rtd->dai_link, i, ch_maps) {
+ ch_maps->cpu_ch_mask = cpu_ch_mask << (i * step);
+ ch_maps->codec_ch_mask = codec_ch_mask;
+ }
return 0;
}
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 0e49290a8c90..3137c091bdb8 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -1206,7 +1206,9 @@ static int __soc_pcm_hw_params(struct snd_pcm_substream *substream,
goto out;
for_each_rtd_codec_dais(rtd, i, codec_dai) {
- unsigned int tdm_mask = snd_soc_dai_tdm_mask_get(codec_dai, substream->stream);
+ unsigned int ch_mask = snd_soc_dai_tdm_mask_get(codec_dai, substream->stream);
+ struct snd_soc_dai_link_ch_map *ch_maps;
+ int j;
/*
* Skip CODECs which don't support the current stream type,
@@ -1228,9 +1230,15 @@ static int __soc_pcm_hw_params(struct snd_pcm_substream *substream,
/* copy params for each codec */
tmp_params = *params;
- /* fixup params based on TDM slot masks */
- if (tdm_mask)
- soc_pcm_codec_params_fixup(&tmp_params, tdm_mask);
+ /* fixup params based on TDM or ch_map masks */
+ if (!ch_mask) {
+ for_each_rtd_ch_maps(rtd, j, ch_maps)
+ if (ch_maps->codec == i)
+ ch_mask |= ch_maps->codec_ch_mask;
+ }
+
+ if (ch_mask)
+ soc_pcm_codec_params_fixup(&tmp_params, ch_mask);
ret = snd_soc_dai_hw_params(codec_dai, substream,
&tmp_params);
@@ -1264,7 +1272,7 @@ static int __soc_pcm_hw_params(struct snd_pcm_substream *substream,
*/
for_each_rtd_ch_maps(rtd, j, ch_maps)
if (ch_maps->cpu == i)
- ch_mask |= ch_maps->ch_mask;
+ ch_mask |= ch_maps->cpu_ch_mask;
/* fixup cpu channel number */
if (ch_mask)
diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h
index 2bf2699bdc49..c66ef455e138 100644
--- a/sound/soc/ux500/ux500_msp_i2s.h
+++ b/sound/soc/ux500/ux500_msp_i2s.h
@@ -147,8 +147,8 @@ enum msp_direction {
#define RCKPOL_MASK BIT(0)
#define TCKPOL_MASK BIT(0)
#define SPICKM_MASK (BIT(1) | BIT(0))
-#define MSP_RX_CLKPOL_BIT(n) ((n & RCKPOL_MASK) << RCKPOL_SHIFT)
-#define MSP_TX_CLKPOL_BIT(n) ((n & TCKPOL_MASK) << TCKPOL_SHIFT)
+#define MSP_RX_CLKPOL_BIT(n) (((n) & RCKPOL_MASK) << RCKPOL_SHIFT)
+#define MSP_TX_CLKPOL_BIT(n) (((n) & TCKPOL_MASK) << TCKPOL_SHIFT)
#define P1ELEN_SHIFT 0
#define P1FLEN_SHIFT 3
diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c
index 21789db6657d..0285d79ace0f 100644
--- a/sound/usb/6fire/pcm.c
+++ b/sound/usb/6fire/pcm.c
@@ -335,11 +335,19 @@ static void usb6fire_pcm_in_urb_handler(struct urb *usb_urb)
/* setup out urb structure */
for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) {
+ unsigned int frames = 0;
+
isoc_out = &out_urb->instance->iso_frame_desc[i];
isoc_in = &in_urb->instance->iso_frame_desc[i];
+ if (isoc_in->actual_length > 4)
+ frames = (isoc_in->actual_length - 4)
+ / (rt->in_n_analog << 2);
+ frames = min_t(unsigned int, frames,
+ (rt->out_packet_size - 4)
+ / (rt->out_n_analog << 2));
+
isoc_out->offset = total_length;
- isoc_out->length = (isoc_in->actual_length - 4) / (rt->in_n_analog << 2)
- * (rt->out_n_analog << 2) + 4;
+ isoc_out->length = frames * (rt->out_n_analog << 2) + 4;
isoc_out->status = 0;
total_length += isoc_out->length;
}
diff --git a/sound/usb/bcd2000/bcd2000.c b/sound/usb/bcd2000/bcd2000.c
index c5c542d17ccc..2bd49bf82748 100644
--- a/sound/usb/bcd2000/bcd2000.c
+++ b/sound/usb/bcd2000/bcd2000.c
@@ -43,6 +43,7 @@ struct bcd2000 {
struct usb_interface *intf;
int card_index;
+ spinlock_t midi_lock;
int midi_out_active;
struct snd_rawmidi *rmidi;
struct snd_rawmidi_substream *midi_receive_substream;
@@ -90,6 +91,8 @@ static void bcd2000_midi_input_trigger(struct snd_rawmidi_substream *substream,
int up)
{
struct bcd2000 *bcd2k = substream->rmidi->private_data;
+
+ guard(spinlock_irqsave)(&bcd2k->midi_lock);
bcd2k->midi_receive_substream = up ? substream : NULL;
}
@@ -195,6 +198,8 @@ static void bcd2000_midi_output_trigger(struct snd_rawmidi_substream *substream,
{
struct bcd2000 *bcd2k = substream->rmidi->private_data;
+ guard(spinlock_irqsave)(&bcd2k->midi_lock);
+
if (up) {
bcd2k->midi_out_substream = substream;
/* check if there is data userspace wants to send */
@@ -219,6 +224,7 @@ static void bcd2000_output_complete(struct urb *urb)
return;
/* check if there is more data userspace wants to send */
+ guard(spinlock_irqsave)(&bcd2k->midi_lock);
bcd2000_midi_send(bcd2k);
}
@@ -234,6 +240,8 @@ static void bcd2000_input_complete(struct urb *urb)
if (!bcd2k || urb->status == -ESHUTDOWN)
return;
+ guard(spinlock_irqsave)(&bcd2k->midi_lock);
+
if (urb->actual_length > 0)
bcd2000_midi_handle_input(bcd2k, urb->transfer_buffer,
urb->actual_length);
@@ -348,16 +356,26 @@ static int bcd2000_init_midi(struct bcd2000 *bcd2k)
return 0;
}
+static void bcd2000_midi_free(struct bcd2000 *bcd2k,
+ struct urb **urb_p)
+{
+ struct urb *urb = *urb_p;
+
+ if (!urb)
+ return;
+
+ usb_poison_urb(urb);
+ scoped_guard(spinlock_irq, &bcd2k->midi_lock)
+ *urb_p = NULL;
+
+ usb_free_urb(urb);
+}
+
static void bcd2000_free_usb_related_resources(struct bcd2000 *bcd2k,
struct usb_interface *interface)
{
- usb_poison_urb(bcd2k->midi_out_urb);
- usb_poison_urb(bcd2k->midi_in_urb);
-
- usb_free_urb(bcd2k->midi_out_urb);
- usb_free_urb(bcd2k->midi_in_urb);
- bcd2k->midi_out_urb = NULL;
- bcd2k->midi_in_urb = NULL;
+ bcd2000_midi_free(bcd2k, &bcd2k->midi_out_urb);
+ bcd2000_midi_free(bcd2k, &bcd2k->midi_in_urb);
if (bcd2k->intf) {
usb_set_intfdata(bcd2k->intf, NULL);
@@ -393,6 +411,7 @@ static int bcd2000_probe(struct usb_interface *interface,
bcd2k->card = card;
bcd2k->card_index = card_index;
bcd2k->intf = interface;
+ spin_lock_init(&bcd2k->midi_lock);
snd_card_set_dev(card, &interface->dev);
diff --git a/sound/usb/card.h b/sound/usb/card.h
index e34d92d576a2..8299ac241c60 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -116,6 +116,7 @@ struct snd_usb_endpoint {
unsigned int phase; /* phase accumulator */
unsigned int maxpacksize; /* max packet size in bytes */
unsigned int maxframesize; /* max packet size in frames */
+ unsigned int max_urb_packs; /* packets allocated per data URB */
unsigned int max_urb_frames; /* max URB size in frames */
unsigned int curpacksize; /* current packet size in bytes (for capture) */
unsigned int curframesize; /* current packet size in frames (for capture) */
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 0835943d7b0e..879d0451536e 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -449,7 +449,9 @@ static void push_back_to_ready_list(struct snd_usb_endpoint *ep,
struct snd_urb_ctx *ctx)
{
guard(spinlock_irqsave)(&ep->lock);
- list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
+ /* ctx may still be linked: a stale completion racing a stop/restart. */
+ if (list_empty(&ctx->ready_list))
+ list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
}
/*
@@ -492,9 +494,10 @@ int snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep,
/* copy over the length information */
if (implicit_fb) {
- ctx->packets = packet->packets;
+ ctx->packets = min_t(int, packet->packets,
+ ep->max_urb_packs);
memcpy(ctx->packet_size, packet->packet_size,
- packet->packets * sizeof(packet->packet_size[0]));
+ ctx->packets * sizeof(packet->packet_size[0]));
}
/* call the data handler to fill in playback data */
@@ -1036,6 +1039,7 @@ void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep)
*/
static int stop_urbs(struct snd_usb_endpoint *ep, bool force, bool keep_pending)
{
+ struct snd_urb_ctx *ctx, *n;
unsigned int i;
if (!force && atomic_read(&ep->running))
@@ -1045,7 +1049,9 @@ static int stop_urbs(struct snd_usb_endpoint *ep, bool force, bool keep_pending)
return 0;
scoped_guard(spinlock_irqsave, &ep->lock) {
- INIT_LIST_HEAD(&ep->ready_playback_urbs);
+ /* Unlink each ctx; INIT_LIST_HEAD() alone would leave them looking linked. */
+ list_for_each_entry_safe(ctx, n, &ep->ready_playback_urbs, ready_list)
+ list_del_init(&ctx->ready_list);
ep->next_packet_head = 0;
ep->next_packet_queued = 0;
}
@@ -1242,15 +1248,16 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep)
ep->nurbs = min(max_urbs, urbs_per_period * ep->cur_buffer_periods);
}
+ if (fmt->fmt_type == UAC_FORMAT_TYPE_II)
+ urb_packs++; /* for transfer delimiter */
+ ep->max_urb_packs = urb_packs;
+
/* allocate and initialize data urbs */
for (i = 0; i < ep->nurbs; i++) {
struct snd_urb_ctx *u = &ep->urb[i];
u->index = i;
u->ep = ep;
u->packets = urb_packs;
-
- if (fmt->fmt_type == UAC_FORMAT_TYPE_II)
- u->packets++; /* for transfer delimiter */
u->buffer_size = maxsize * u->packets;
u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
if (!u->urb)
diff --git a/sound/usb/implicit.c b/sound/usb/implicit.c
index 77f06da93151..bd4d569a8190 100644
--- a/sound/usb/implicit.c
+++ b/sound/usb/implicit.c
@@ -76,6 +76,7 @@ static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = {
/* Implicit feedback quirk table for capture: only FIXED type */
static const struct snd_usb_implicit_fb_match capture_implicit_fb_quirks[] = {
+ IMPLICIT_FB_FIXED_DEV(0x1397, 0x0004, 0x01, 1), /* Behringer FCA1616 */
{} /* terminator */
};
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
index 69093c666282..41454cb403d0 100644
--- a/sound/usb/mixer_maps.c
+++ b/sound/usb/mixer_maps.c
@@ -532,6 +532,15 @@ static const struct usbmix_name_map audient_id24_map[] = {
};
/*
+ * The GC553Pro returns no data for GET_CUR on its advertised mute control.
+ * SET_CUR succeeds but does not mute capture, so skip the control entirely.
+ */
+static const struct usbmix_name_map avermedia_gc553pro_map[] = {
+ { 3, NULL, UAC_FU_MUTE },
+ {}
+};
+
+/*
* Control map entries
*/
@@ -578,6 +587,10 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = {
.selector_map = c400_selectors,
},
{
+ .id = USB_ID(0x07ca, 0x1553),
+ .map = avermedia_gc553pro_map,
+ },
+ {
.id = USB_ID(0x08bb, 0x2702),
.map = linex_map,
},
diff --git a/sound/virtio/virtio_card.c b/sound/virtio/virtio_card.c
index 647190f4d5af..6f35276416fe 100644
--- a/sound/virtio/virtio_card.c
+++ b/sound/virtio/virtio_card.c
@@ -354,8 +354,8 @@ static void virtsnd_remove(struct virtio_device *vdev)
if (snd->card)
snd_card_free(snd->card);
- vdev->config->del_vqs(vdev);
virtio_reset_device(vdev);
+ vdev->config->del_vqs(vdev);
for (i = 0; snd->substreams && i < snd->nsubstreams; ++i) {
struct virtio_pcm_substream *vss = &snd->substreams[i];
@@ -383,8 +383,8 @@ static int virtsnd_freeze(struct virtio_device *vdev)
virtsnd_disable_event_vq(snd);
virtsnd_ctl_msg_cancel_all(snd);
- vdev->config->del_vqs(vdev);
virtio_reset_device(vdev);
+ vdev->config->del_vqs(vdev);
for (i = 0; i < snd->nsubstreams; ++i)
cancel_work_sync(&snd->substreams[i].elapsed_period);
diff --git a/tools/sched_ext/include/scx/common.bpf.h b/tools/sched_ext/include/scx/common.bpf.h
index 76f5e025e107..2ddb01a059fd 100644
--- a/tools/sched_ext/include/scx/common.bpf.h
+++ b/tools/sched_ext/include/scx/common.bpf.h
@@ -113,6 +113,7 @@ s32 scx_bpf_this_cid(void) __ksym __weak;
struct task_struct *scx_bpf_cid_curr(s32 cid) __ksym __weak;
u32 scx_bpf_nr_cids(void) __ksym __weak;
u32 scx_bpf_nr_online_cids(void) __ksym __weak;
+const void __arena *scx_bpf_online_cmask(void) __ksym __weak;
u32 scx_bpf_cidperf_cap(s32 cid) __ksym __weak;
u32 scx_bpf_cidperf_cur(s32 cid) __ksym __weak;
s32 scx_bpf_cidperf_set(s32 cid, u32 perf) __ksym __weak;
diff --git a/tools/sched_ext/scx_qmap.bpf.c b/tools/sched_ext/scx_qmap.bpf.c
index 9f6e61d7ca07..67b7c01cae55 100644
--- a/tools/sched_ext/scx_qmap.bpf.c
+++ b/tools/sched_ext/scx_qmap.bpf.c
@@ -24,6 +24,9 @@
* time-share that stays self-local.
* self - The excl cpus the node kept for itself, plus all of held_shared.
* owner - Who holds a cid - a child slot, CID_SELF, or CID_NONE.
+ * avail - Cpus whose caps are in effect, per ops.sub_ecaps_updated().
+ * usable - self AND avail. Placement decisions use this: self is the
+ * delegation split and can run ahead of what the cpus honor.
*
* The scheduler splits its held-excl cpus among self and the children in
* proportion to each node's cpu.weight, handing each the floor of its share as
@@ -208,8 +211,8 @@ static int qmap_spin_lock(struct bpf_res_spin_lock *lock)
}
/*
- * Try prev_cid, then scan cpus_allowed AND idle_cids AND self_cids round-robin
- * from prev_cid + 1. Atomic claim retries on race; bounded by
+ * Try prev_cid, then scan cpus_allowed AND idle_cids AND usable_cids
+ * round-robin from prev_cid + 1. Atomic claim retries on race; bounded by
* IDLE_PICK_RETRIES to keep the verifier's insn budget in check.
*/
#define IDLE_PICK_RETRIES 16
@@ -221,7 +224,7 @@ static s32 pick_direct_dispatch_cid(struct task_struct *p, s32 prev_cid,
s32 cid;
u32 i;
- if (cmask_test(prev_cid, &qa.self_cids.mask) &&
+ if (cmask_test(prev_cid, &qa.usable_cids.mask) &&
cmask_test_and_clear(prev_cid, &qa.idle_cids.mask))
return prev_cid;
@@ -229,7 +232,7 @@ static s32 pick_direct_dispatch_cid(struct task_struct *p, s32 prev_cid,
bpf_for(i, 0, IDLE_PICK_RETRIES) {
cid = cmask_next_and2_set_wrap(&taskc->cpus_allowed,
&qa.idle_cids.mask,
- &qa.self_cids.mask, cid + 1);
+ &qa.usable_cids.mask, cid + 1);
barrier_var(cid);
if (cid >= nr_cids)
return -1;
@@ -358,8 +361,8 @@ s32 BPF_STRUCT_OPS(qmap_select_cid, struct task_struct *p,
}
/*
- * A received time-shared cid is held ENQ_IMMED-only, so inserts must set
- * SCX_ENQ_IMMED.
+ * A received time-shared cid is held ENQ_IMMED-only, so inserts meant to run
+ * there must set SCX_ENQ_IMMED.
*/
static u64 needs_immed(s32 cid)
{
@@ -444,9 +447,11 @@ void BPF_STRUCT_OPS(qmap_enqueue, struct task_struct *p, u64 enq_flags)
* didn't grant them or we delegated them to children - would starve in
* SHARED/FIFO since we only pull from those on self cids.
*
- * Force it onto its first allowed cid's local DSQ. If we hold that cid
- * it runs. Otherwise the insert carries SCX_ENQ_RESCUE and the kernel
- * diverts the task to its rescue path.
+ * Force it onto its first allowed cid's local DSQ with SCX_ENQ_RESCUE.
+ * If we hold ENQ on that cid it runs. Otherwise the kernel diverts the
+ * task to its rescue path. IMMED would turn the insert into a legal
+ * placement on a time-shared cid and the kernel would bounce it back
+ * here instead of rescuing it.
*/
if (!cmask_intersects(&taskc->cpus_allowed, &qa.self_cids.mask)) {
s32 c = cmask_next_set_wrap(&taskc->cpus_allowed, 0);
@@ -455,7 +460,7 @@ void BPF_STRUCT_OPS(qmap_enqueue, struct task_struct *p, u64 enq_flags)
taskc->force_local = false;
__sync_fetch_and_add(&qa.nr_rescue_dsp, 1);
scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL_ON | c, slice_ns,
- enq_flags | needs_immed(c) | SCX_ENQ_RESCUE);
+ enq_flags | SCX_ENQ_RESCUE);
return;
}
}
@@ -540,7 +545,7 @@ void BPF_STRUCT_OPS(qmap_enqueue, struct task_struct *p, u64 enq_flags)
scx_bpf_dsq_insert(p, SHARED_DSQ, 0, enq_flags);
cid = cmask_next_and2_set_wrap(&taskc->cpus_allowed,
&qa.idle_cids.mask,
- &qa.self_cids.mask, 0);
+ &qa.usable_cids.mask, 0);
if (cid < scx_bpf_nr_cids())
scx_bpf_kick_cid(cid, SCX_KICK_IDLE);
return;
@@ -618,7 +623,7 @@ static bool scan_shared_dsq(bool from_timer)
if (c >= 0 && c < scx_bpf_nr_cids()) {
__sync_fetch_and_add(&qa.nr_rescue_dsp, 1);
scx_bpf_dsq_move(BPF_FOR_EACH_ITER, p, SCX_DSQ_LOCAL_ON | c,
- needs_immed(c) | SCX_ENQ_RESCUE);
+ SCX_ENQ_RESCUE);
}
continue;
}
@@ -644,22 +649,27 @@ static bool scan_shared_dsq(bool from_timer)
if (!(taskc = lookup_task_ctx(p)))
return false;
- /* only run highpri tasks on cids this node holds, not delegated ones */
+ /* only run highpri tasks on cids this node can use right now */
if (cmask_test(this_cid, &taskc->cpus_allowed) &&
- cmask_test(this_cid, &qa.self_cids.mask))
+ cmask_test(this_cid, &qa.usable_cids.mask))
cid = this_cid;
else
cid = cmask_next_and_set_wrap(&taskc->cpus_allowed,
- &qa.self_cids.mask,
+ &qa.usable_cids.mask,
this_cid + 1);
if (cid >= nr_cids) {
- /* stranded after the cull - rescue it from here */
- s32 c = cmask_next_set_wrap(&taskc->cpus_allowed, 0);
+ s32 c;
+
+ /* self cids lack caps in effect yet, leave it queued */
+ if (cmask_intersects(&taskc->cpus_allowed, &qa.self_cids.mask))
+ continue;
+ /* stranded after the cull - rescue it from here */
+ c = cmask_next_set_wrap(&taskc->cpus_allowed, 0);
if (c >= 0 && c < nr_cids) {
__sync_fetch_and_add(&qa.nr_rescue_dsp, 1);
scx_bpf_dsq_move(BPF_FOR_EACH_ITER, p, SCX_DSQ_LOCAL_ON | c,
- needs_immed(c) | SCX_ENQ_RESCUE);
+ SCX_ENQ_RESCUE);
}
continue;
}
@@ -808,10 +818,10 @@ void BPF_STRUCT_OPS(qmap_dispatch, s32 cid, struct task_struct *prev)
batch--;
cpuc->dsp_cnt--;
if (!batch || !scx_bpf_dispatch_nr_slots()) {
- if (scan_shared_dsq(false))
+ if (scan_shared_dsq(false) ||
+ scx_bpf_dsq_move_to_local(SHARED_DSQ, needs_immed(cid)))
return;
- scx_bpf_dsq_move_to_local(SHARED_DSQ, needs_immed(cid));
- return;
+ goto prev;
}
if (!cpuc->dsp_cnt)
break;
@@ -822,10 +832,14 @@ void BPF_STRUCT_OPS(qmap_dispatch, s32 cid, struct task_struct *prev)
if (scan_shared_dsq(false))
return;
-
+prev:
/*
* No other tasks. @prev will keep running. Update its core_sched_seq as
* if the task were enqueued and dispatched immediately.
+ *
+ * No @prev to keep running means the CPU goes idle. If its claim was
+ * never used, that is not a transition and ops.update_idle() stays
+ * silent. Restore the claim here.
*/
if (prev) {
taskc = lookup_task_ctx(prev);
@@ -834,6 +848,8 @@ void BPF_STRUCT_OPS(qmap_dispatch, s32 cid, struct task_struct *prev)
taskc->core_sched_seq =
qa.core_sched_tail_seqs[weight_to_idx(prev->scx.weight)]++;
+ } else {
+ cmask_set(cid, &qa.idle_cids.mask);
}
}
@@ -1113,7 +1129,7 @@ void BPF_STRUCT_OPS(qmap_update_idle, s32 cid, bool idle)
/*
* The kernel delivers update_idle() for every cid this node holds
* SCX_CAP_BASE on. Track every cid's idle state regardless of
- * delegation: the direct-dispatch pick masks idle_cids with self_cids
+ * delegation: the direct-dispatch pick masks idle_cids with usable_cids
* at selection, so a cid already idle when it returns to self needs no
* reseed here.
*/
@@ -1285,11 +1301,16 @@ struct {
__type(value, struct round_robin_timer);
} round_robin_timer SEC(".maps");
+enum part_pending_flags {
+ PART_REFRESH = BIT_U64(0),
+ PART_REDISTRIBUTE = BIT_U64(1),
+};
+
/*
* Partition update synchronization. qa.part can be written from concurrent
* contexts. This single-runner guard admits one writer at a time without
* holding a lock across the grant/revoke kfuncs. part_pending coalesces
- * repartition requests that arrive while it is held.
+ * refresh and repartition requests that arrive while it is held.
*
* They live in .bss, not the arena: rr_advance() runs from a bpf_timer
* callback, where the verifier rejects atomic ops on arena memory.
@@ -1538,6 +1559,19 @@ static __noinline void account_alloc(void)
}
/*
+ * usable_cids = self_cids & avail_cids. The inputs have separate writers,
+ * apply_partition() and qmap_sub_ecaps_updated(), so the result is rebuilt in
+ * full under the partition guard, in scratch first so that readers never see
+ * self_cids alone.
+ */
+static void refresh_usable(void)
+{
+ cmask_copy(&qa.usable_scratch.mask, &qa.self_cids.mask);
+ cmask_and(&qa.usable_scratch.mask, &qa.avail_cids.mask);
+ cmask_copy(&qa.usable_cids.mask, &qa.usable_scratch.mask);
+}
+
+/*
* apply_partition - execute the plan compute_partition() built
*
* Turn the owner map into the per-child, shared and self cmasks and issue the
@@ -1559,6 +1593,7 @@ __noinline void apply_partition(void)
/* no excl cpu: run own tasks on the held shares, evict children */
if (!qa.part.nr_excl) {
cmask_copy(&qa.self_cids.mask, &qa.held_shared.mask);
+ refresh_usable();
bpf_for(i, 0, MAX_SUB_SCHEDS)
if (qa.sub_sched_ctxs[i].cgroup_id)
scx_bpf_sub_kill(qa.sub_sched_ctxs[i].cgroup_id,
@@ -1596,6 +1631,7 @@ __noinline void apply_partition(void)
else if (o == CID_SELF)
cmask_set(cid, &qa.self_cids.mask);
}
+ refresh_usable();
/*
* Apply each child's exclusive cids as a delta against its previous
@@ -1643,33 +1679,46 @@ __noinline void apply_partition(void)
}
}
-/*
- * Recompute the split off the node's held caps and apply it. The contexts this
- * runs from (the sub-sched and cgroup callbacks, the rr timer) are not
- * serialized by the kernel, so a single runner does the work. A caller that
- * finds the guard held leaves part_pending set; the holder drains it before
- * releasing, with the rr timer as a backstop.
+/**
+ * execute_partition - Run pending partition updates
+ *
+ * The rr timer is the backstop if the loop reaches its iteration limit.
*/
-static void redistribute(void)
+static void execute_partition(void)
{
+ u64 pending;
s32 i;
- __sync_fetch_and_or(&part_pending, 1);
+ bpf_for(i, 0, 1024) {
+ if (!part_try_start())
+ break;
- if (!part_try_start())
- return;
+ pending = __sync_fetch_and_and(&part_pending, 0);
+ if (pending & PART_REDISTRIBUTE) {
+ /* charge elapsed time before repartitioning */
+ account_alloc();
+ compute_partition();
+ apply_partition();
+ } else if (pending & PART_REFRESH) {
+ refresh_usable();
+ }
- bpf_for(i, 0, 1024) {
- __sync_fetch_and_and(&part_pending, 0);
- /* charge elapsed time to the current partition before rebuilding it */
- account_alloc();
- compute_partition();
- apply_partition();
+ /*
+ * Requests are published before trying the guard. Releasing it
+ * before checking pending work ensures a racing request is
+ * either observed here or handled by a caller that acquires the
+ * guard.
+ */
+ part_end();
if (!__sync_fetch_and_or(&part_pending, 0))
break;
}
+}
- part_end();
+static void redistribute(void)
+{
+ __sync_fetch_and_or(&part_pending, PART_REDISTRIBUTE);
+ execute_partition();
}
/*
@@ -1683,6 +1732,7 @@ int flush_alloc(void *ctx)
if (part_try_start()) {
account_alloc();
part_end();
+ execute_partition();
}
return 0;
}
@@ -1740,9 +1790,7 @@ static void rr_advance(void)
part_end();
- /* a resplit queued while we held the guard supersedes this rotation */
- if (__sync_fetch_and_or(&part_pending, 0))
- redistribute();
+ execute_partition();
}
/* advance the time-shared cid pool every round_robin_ns */
@@ -1837,8 +1885,11 @@ s32 BPF_STRUCT_OPS_SLEEPABLE(qmap_init)
cmask_init(&qa.rr_cids.mask, 0, nr_cids);
cmask_init(&qa.prev_rr_cids.mask, 0, nr_cids);
cmask_init(&qa.self_cids.mask, 0, nr_cids);
+ cmask_init(&qa.avail_cids.mask, 0, nr_cids);
+ cmask_init(&qa.usable_cids.mask, 0, nr_cids);
cmask_init(&qa.to_revoke_cids.mask, 0, nr_cids);
cmask_init(&qa.to_grant_cids.mask, 0, nr_cids);
+ cmask_init(&qa.usable_scratch.mask, 0, nr_cids);
cmask_init(&qa.held_excl.mask, 0, nr_cids);
cmask_init(&qa.held_shared.mask, 0, nr_cids);
@@ -1852,14 +1903,16 @@ s32 BPF_STRUCT_OPS_SLEEPABLE(qmap_init)
}
/*
- * The root starts holding every cid. qmap_sub_ecaps_updated() maintains
- * per-cid shared state as effective caps settle, and redistribute()
- * rebuilds owner and self from held caps. A non-root node starts with
- * nothing.
+ * The root starts holding every cid and gets no ecaps notifications, so
+ * its avail set is fixed here. qmap_sub_ecaps_updated() maintains the
+ * per-cid state as effective caps settle, and redistribute() rebuilds
+ * owner and self from held caps. A non-root node starts with nothing.
*/
bpf_for(i, 0, nr_cids) {
if (!sub_cgroup_id) {
cmask_set(i, &qa.self_cids.mask);
+ cmask_set(i, &qa.avail_cids.mask);
+ cmask_set(i, &qa.usable_cids.mask);
qa.part.cid_owner[i] = CID_SELF;
} else {
qa.part.cid_owner[i] = CID_NONE;
@@ -2000,12 +2053,19 @@ void BPF_STRUCT_OPS(qmap_sub_ecaps_updated, s32 cid, u64 before, u64 after)
{
/*
* Effective caps updated. Track which cids hold shared caps so a self
- * task placed there enqueues IMMED.
+ * task placed there enqueues IMMED, and which cids have ENQ_IMMED in
+ * effect at all (avail, see the header comment).
*/
- if (after & SCX_CAP_ENQ_IMMED)
+ if (after & SCX_CAP_ENQ_IMMED) {
qa.cid_shared[cid] = (after & SCX_CAP_ENQ) ? 0 : 1;
- else
+ cmask_set(cid, &qa.avail_cids.mask);
+ } else {
qa.cid_shared[cid] = 0;
+ cmask_clear(cid, &qa.avail_cids.mask);
+ }
+
+ __sync_fetch_and_or(&part_pending, PART_REFRESH);
+ execute_partition();
}
SCX_OPS_CID_DEFINE(qmap_ops,
diff --git a/tools/sched_ext/scx_qmap.h b/tools/sched_ext/scx_qmap.h
index c78d61806b39..e95fffcf7b23 100644
--- a/tools/sched_ext/scx_qmap.h
+++ b/tools/sched_ext/scx_qmap.h
@@ -165,12 +165,15 @@ struct qmap_arena {
/* bpf-internal cmasks (embedded, see struct qmap_cmask) */
struct qmap_cmask self_cids; /* cids this node runs its own tasks on */
+ struct qmap_cmask avail_cids; /* cids with caps in effect on the cpu */
+ struct qmap_cmask usable_cids; /* self_cids & avail_cids, placeable right now */
struct qmap_cmask idle_cids; /* idle state of all cids regardless of delegation */
struct qmap_cmask rr_cids; /* the shared pool, as a mask for grant/revoke */
/* scratch cmasks */
struct qmap_cmask to_revoke_cids; /* delta cids to revoke */
struct qmap_cmask to_grant_cids; /* delta cids to grant */
+ struct qmap_cmask usable_scratch; /* refresh_usable() build area */
struct qmap_cmask prev_rr_cids; /* previous shared pool, to clear stale grants */
struct qmap_cmask held_excl; /* cids held excl (ENQ): delegatable */
struct qmap_cmask held_shared; /* cids held shared (ENQ_IMMED only): self-local */