From: Thomas Gleixner <tglx(a)linutronix.de>
Luigi reported that retriggering a posted MSI interrupt does not work
correctly.
The reason is that the retrigger happens at the vector domain by sending an
IPI to the actual vector on the target CPU. That works correctly exactly
once because the posted MSI interrupt chip does not issue an EOI as that's
only required for the posted MSI notification vector itself.
As a consequence the vector becomes stale in the ISR, which not only
affects this vector but also any lower priority vector in the affected
APIC because the ISR bit is not cleared.
Luigi proposed to set the vector in the remap PIR bitmap and raise the
posted MSI notification vector. That works, but that still does not cure a
related problem:
If there is ever a stray interrupt on such a vector, then the related
APIC ISR bit becomes stale due to the lack of EOI as described above.
Unlikely to happen, but if it happens it's not debuggable at all.
So instead of playing games with the PIR, this can be actually solved
for both cases by:
1) Keeping track of the posted interrupt vector handler state
2) Implementing a posted MSI specific irq_ack() callback which checks that
state. If the posted vector handler is inactive it issues an EOI,
otherwise it delegates that to the posted handler.
This is correct versus affinity changes and concurrent events on the posted
vector as the actual handler invocation is serialized through the interrupt
descriptor lock.
Fixes: ed1e48ea4370 ("iommu/vt-d: Enable posted mode for device MSIs")
Reported-by: Luigi Rizzo <lrizzo(a)google.com>
Signed-off-by: Thomas Gleixner <tglx(a)linutronix.de>
Tested-by: Luigi Rizzo <lrizzo(a)google.com>
Cc: stable(a)vger.kernel.org
Closes: https://lore.kernel.org/lkml/20251124104836.3685533-1-lrizzo@google.com
---
V2: Use __this_cpu...() - Luigi
---
arch/x86/include/asm/irq_remapping.h | 7 +++++++
arch/x86/kernel/irq.c | 23 +++++++++++++++++++++++
drivers/iommu/intel/irq_remapping.c | 8 ++++----
3 files changed, 34 insertions(+), 4 deletions(-)
--- a/arch/x86/include/asm/irq_remapping.h
+++ b/arch/x86/include/asm/irq_remapping.h
@@ -87,4 +87,11 @@ static inline void panic_if_irq_remap(co
}
#endif /* CONFIG_IRQ_REMAP */
+
+#ifdef CONFIG_X86_POSTED_MSI
+void intel_ack_posted_msi_irq(struct irq_data *irqd);
+#else
+#define intel_ack_posted_msi_irq NULL
+#endif
+
#endif /* __X86_IRQ_REMAPPING_H */
--- a/arch/x86/kernel/irq.c
+++ b/arch/x86/kernel/irq.c
@@ -396,6 +396,7 @@ DEFINE_IDTENTRY_SYSVEC_SIMPLE(sysvec_kvm
/* Posted Interrupt Descriptors for coalesced MSIs to be posted */
DEFINE_PER_CPU_ALIGNED(struct pi_desc, posted_msi_pi_desc);
+static DEFINE_PER_CPU_CACHE_HOT(bool, posted_msi_handler_active);
void intel_posted_msi_init(void)
{
@@ -413,6 +414,25 @@ void intel_posted_msi_init(void)
this_cpu_write(posted_msi_pi_desc.ndst, destination);
}
+void intel_ack_posted_msi_irq(struct irq_data *irqd)
+{
+ irq_move_irq(irqd);
+
+ /*
+ * Handle the rare case that irq_retrigger() raised the actual
+ * assigned vector on the target CPU, which means that it was not
+ * invoked via the posted MSI handler below. In that case APIC EOI
+ * is required as otherwise the ISR entry becomes stale and lower
+ * priority interrupts are never going to be delivered after that.
+ *
+ * If the posted handler invoked the device interrupt handler then
+ * the EOI would be premature because it would acknowledge the
+ * posted vector.
+ */
+ if (unlikely(!__this_cpu_read(posted_msi_handler_active)))
+ apic_eoi();
+}
+
static __always_inline bool handle_pending_pir(unsigned long *pir, struct pt_regs *regs)
{
unsigned long pir_copy[NR_PIR_WORDS];
@@ -445,6 +465,8 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_posted_msi
pid = this_cpu_ptr(&posted_msi_pi_desc);
+ /* Mark the handler active for intel_ack_posted_msi_irq() */
+ __this_cpu_write(posted_msi_handler_active, true);
inc_irq_stat(posted_msi_notification_count);
irq_enter();
@@ -473,6 +495,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_posted_msi
apic_eoi();
irq_exit();
+ __this_cpu_write(posted_msi_handler_active, false);
set_irq_regs(old_regs);
}
#endif /* X86_POSTED_MSI */
--- a/drivers/iommu/intel/irq_remapping.c
+++ b/drivers/iommu/intel/irq_remapping.c
@@ -1303,17 +1303,17 @@ static struct irq_chip intel_ir_chip = {
* irq_enter();
* handle_edge_irq()
* irq_chip_ack_parent()
- * irq_move_irq(); // No EOI
+ * intel_ack_posted_msi_irq(); // No EOI
* handle_irq_event()
* driver_handler()
* handle_edge_irq()
* irq_chip_ack_parent()
- * irq_move_irq(); // No EOI
+ * intel_ack_posted_msi_irq(); // No EOI
* handle_irq_event()
* driver_handler()
* handle_edge_irq()
* irq_chip_ack_parent()
- * irq_move_irq(); // No EOI
+ * intel_ack_posted_msi_irq(); // No EOI
* handle_irq_event()
* driver_handler()
* apic_eoi()
@@ -1322,7 +1322,7 @@ static struct irq_chip intel_ir_chip = {
*/
static struct irq_chip intel_ir_chip_post_msi = {
.name = "INTEL-IR-POST",
- .irq_ack = irq_move_irq,
+ .irq_ack = intel_ack_posted_msi_irq,
.irq_set_affinity = intel_ir_set_affinity,
.irq_compose_msi_msg = intel_ir_compose_msi_msg,
.irq_set_vcpu_affinity = intel_ir_set_vcpu_affinity,
(Cc: stable(a)vger.kernel.org)
On Wed Dec 10, 2025 at 12:25 PM CET, Marko Turk wrote:
> MMIO backend of PCI Bar always assumes little-endian devices and
> will convert to CPU endianness automatically. Remove the u32::from_le
> conversion which would cause a bug on big-endian machines.
>
> Signed-off-by: Marko Turk <mt(a)markoturk.info>
> Fixes: 685376d18e9a ("samples: rust: add Rust PCI sample driver")
Applied to driver-core-linus, thanks!
On 12/13/25 11:09 AM, Sasha Levin wrote:
> This is a note to let you know that I've just added the patch titled
>
> clk: qcom: rpmh: Define RPMH_IPA_CLK on QCS615
>
> to the 6.17-stable tree which can be found at:
> http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=sum…
>
> The filename of the patch is:
> clk-qcom-rpmh-define-rpmh_ipa_clk-on-qcs615.patch
> and it can be found in the queue-6.17 subdirectory.
>
> If you, or anyone else, feels it should not be added to the stable tree,
> please let <stable(a)vger.kernel.org> know about it.
Please drop from all queues, this depends on
cbabc73e85be ("interconnect: qcom: qcs615: Drop IP0 interconnects")
and has no visible effect today (the IP block this resource drives is not
enabled), however potentially handling it in two places could cause issues
Konrad
In 'normal' controls the mc->min is the minimum value the register can
have, the mc->max is the maximum (the steps between are max - min).
SX types are defined differently: mc->min is the minimum value and the
mc->max is the steps.
The max parameter of soc_mixer_reg_to_ctl() is the number of steps in
either type.
To have correct register value range in clamp the maximum value that needs
to be used is mc->min + max, which will be equal to mc->max for 'normal'
controls and mc->min + mc->max for SX ones.
The original clamp broke SX controls and rendered some of them impossible
to even set, like the cs42l43's Headphone Digital Volume, where the
min is smaller than the max (min=283, max=229 - 229 steps starting from
val 283).
The soc_mixer_ctl_to_reg() correctly uses the max parameter instead of
mc->max, so storing the value was correct.
Fixes: a0ce874cfaaa ("ASoC: ops: improve snd_soc_get_volsw")
Cc: stable(a)vger.kernel.org
Signed-off-by: Peter Ujfalusi <peter.ujfalusi(a)linux.intel.com>
---
sound/soc/soc-ops.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c
index ce86978c158d..6a18c56a9746 100644
--- a/sound/soc/soc-ops.c
+++ b/sound/soc/soc-ops.c
@@ -148,7 +148,7 @@ static int soc_mixer_reg_to_ctl(struct soc_mixer_control *mc, unsigned int reg_v
if (mc->sign_bit)
val = sign_extend32(val, mc->sign_bit);
- val = clamp(val, mc->min, mc->max);
+ val = clamp(val, mc->min, mc->min + max);
val -= mc->min;
if (mc->invert)
--
2.52.0
From: Claudiu Beznea <claudiu.beznea.uj(a)bp.renesas.com>
The driver updates struct sci_port::tx_cookie to zero right before the TX
work is scheduled, or to -EINVAL when DMA is disabled.
dma_async_is_complete(), called through dma_cookie_status() (and possibly
through dmaengine_tx_status()), considers cookies valid only if they have
values greater than or equal to 1.
Passing zero or -EINVAL to dmaengine_tx_status() before any TX DMA
transfer has started leads to an incorrect TX status being reported, as the
cookie is invalid for the DMA subsystem. This may cause long wait times
when the serial device is opened for configuration before any TX activity
has occurred.
Check that the TX cookie is valid before passing it to
dmaengine_tx_status().
Fixes: 7cc0e0a43a91 ("serial: sh-sci: Check if TX data was written to device in .tx_empty()")
Cc: stable(a)vger.kernel.org
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj(a)bp.renesas.com>
---
drivers/tty/serial/sh-sci.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 53edbf1d8963..fbfe5575bd3c 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -1914,7 +1914,7 @@ static void sci_dma_check_tx_occurred(struct sci_port *s)
struct dma_tx_state state;
enum dma_status status;
- if (!s->chan_tx)
+ if (!s->chan_tx || s->cookie_tx <= 0)
return;
status = dmaengine_tx_status(s->chan_tx, s->cookie_tx, &state);
--
2.43.0
Originally, the notification for connector change will be enabled after
the first read of the connector status. Therefore, if the event happens
during this window, it will be missing and make the status unsynced.
Get the connector status only after enabling the notification for
connector change to ensure the status is synced.
Fixes: c1b0bc2dabfa ("usb: typec: Add support for UCSI interface")
Cc: stable(a)vger.kernel.org # v4.13+
Signed-off-by: Hsin-Te Yuan <yuanhsinte(a)chromium.org>
---
Changes in v6:
- Free the locks in error path.
- Link to v5: https://lore.kernel.org/r/20251205-ucsi-v5-1-488eb89bc9b8@chromium.org
Changes in v5:
- Hold the lock of each connector during the initialization to avoid
race condition between initialization and other event handler
- Add Fixes tag
- Link to v4: https://lore.kernel.org/r/20251125-ucsi-v4-1-8c94568ddaa5@chromium.org
Changes in v4:
- Handle a single connector in ucsi_init_port() and call it in a loop
- Link to v3: https://lore.kernel.org/r/20251121-ucsi-v3-1-b1047ca371b8@chromium.org
Changes in v3:
- Seperate the status checking part into a new function called
ucsi_init_port() and call it after enabling the notifications
- Link to v2: https://lore.kernel.org/r/20251118-ucsi-v2-1-d314d50333e2@chromium.org
Changes in v2:
- Remove unnecessary braces.
- Link to v1: https://lore.kernel.org/r/20251117-ucsi-v1-1-1dcbc5ea642b@chromium.org
---
drivers/usb/typec/ucsi/ucsi.c | 131 +++++++++++++++++++++++-------------------
1 file changed, 73 insertions(+), 58 deletions(-)
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index 3f568f790f39b0271667e80816270274b8dd3008..3a0471fa4cc980c0512bc71776e3984e6cd2cdb7 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -1560,11 +1560,70 @@ static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con)
return NULL;
}
+static void ucsi_init_port(struct ucsi *ucsi, struct ucsi_connector *con)
+{
+ enum usb_role u_role = USB_ROLE_NONE;
+ int ret;
+
+ /* Get the status */
+ ret = ucsi_get_connector_status(con, false);
+ if (ret) {
+ dev_err(ucsi->dev, "con%d: failed to get status\n", con->num);
+ return;
+ }
+
+ if (ucsi->ops->connector_status)
+ ucsi->ops->connector_status(con);
+
+ switch (UCSI_CONSTAT(con, PARTNER_TYPE)) {
+ case UCSI_CONSTAT_PARTNER_TYPE_UFP:
+ case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP:
+ u_role = USB_ROLE_HOST;
+ fallthrough;
+ case UCSI_CONSTAT_PARTNER_TYPE_CABLE:
+ typec_set_data_role(con->port, TYPEC_HOST);
+ break;
+ case UCSI_CONSTAT_PARTNER_TYPE_DFP:
+ u_role = USB_ROLE_DEVICE;
+ typec_set_data_role(con->port, TYPEC_DEVICE);
+ break;
+ default:
+ break;
+ }
+
+ /* Check if there is already something connected */
+ if (UCSI_CONSTAT(con, CONNECTED)) {
+ typec_set_pwr_role(con->port, UCSI_CONSTAT(con, PWR_DIR));
+ ucsi_register_partner(con);
+ ucsi_pwr_opmode_change(con);
+ ucsi_port_psy_changed(con);
+ if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE)
+ ucsi_get_partner_identity(con);
+ if (con->ucsi->cap.features & UCSI_CAP_CABLE_DETAILS)
+ ucsi_check_cable(con);
+ }
+
+ /* Only notify USB controller if partner supports USB data */
+ if (!(UCSI_CONSTAT(con, PARTNER_FLAG_USB)))
+ u_role = USB_ROLE_NONE;
+
+ ret = usb_role_switch_set_role(con->usb_role_sw, u_role);
+ if (ret)
+ dev_err(ucsi->dev, "con:%d: failed to set usb role:%d\n",
+ con->num, u_role);
+
+ if (con->partner && UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD) {
+ ucsi_register_device_pdos(con);
+ ucsi_get_src_pdos(con);
+ ucsi_check_altmodes(con);
+ ucsi_check_connector_capability(con);
+ }
+}
+
static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con)
{
struct typec_capability *cap = &con->typec_cap;
enum typec_accessory *accessory = cap->accessory;
- enum usb_role u_role = USB_ROLE_NONE;
u64 command;
char *name;
int ret;
@@ -1659,62 +1718,6 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con)
goto out;
}
- /* Get the status */
- ret = ucsi_get_connector_status(con, false);
- if (ret) {
- dev_err(ucsi->dev, "con%d: failed to get status\n", con->num);
- goto out;
- }
-
- if (ucsi->ops->connector_status)
- ucsi->ops->connector_status(con);
-
- switch (UCSI_CONSTAT(con, PARTNER_TYPE)) {
- case UCSI_CONSTAT_PARTNER_TYPE_UFP:
- case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP:
- u_role = USB_ROLE_HOST;
- fallthrough;
- case UCSI_CONSTAT_PARTNER_TYPE_CABLE:
- typec_set_data_role(con->port, TYPEC_HOST);
- break;
- case UCSI_CONSTAT_PARTNER_TYPE_DFP:
- u_role = USB_ROLE_DEVICE;
- typec_set_data_role(con->port, TYPEC_DEVICE);
- break;
- default:
- break;
- }
-
- /* Check if there is already something connected */
- if (UCSI_CONSTAT(con, CONNECTED)) {
- typec_set_pwr_role(con->port, UCSI_CONSTAT(con, PWR_DIR));
- ucsi_register_partner(con);
- ucsi_pwr_opmode_change(con);
- ucsi_port_psy_changed(con);
- if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE)
- ucsi_get_partner_identity(con);
- if (con->ucsi->cap.features & UCSI_CAP_CABLE_DETAILS)
- ucsi_check_cable(con);
- }
-
- /* Only notify USB controller if partner supports USB data */
- if (!(UCSI_CONSTAT(con, PARTNER_FLAG_USB)))
- u_role = USB_ROLE_NONE;
-
- ret = usb_role_switch_set_role(con->usb_role_sw, u_role);
- if (ret) {
- dev_err(ucsi->dev, "con:%d: failed to set usb role:%d\n",
- con->num, u_role);
- ret = 0;
- }
-
- if (con->partner && UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD) {
- ucsi_register_device_pdos(con);
- ucsi_get_src_pdos(con);
- ucsi_check_altmodes(con);
- ucsi_check_connector_capability(con);
- }
-
trace_ucsi_register_port(con->num, con);
out:
@@ -1823,16 +1826,28 @@ static int ucsi_init(struct ucsi *ucsi)
goto err_unregister;
}
+ /* Delay other interactions with each connector until ucsi_init_port is done */
+ for (i = 0; i < ucsi->cap.num_connectors; i++)
+ mutex_lock(&connector[i].lock);
+
/* Enable all supported notifications */
ntfy = ucsi_get_supported_notifications(ucsi);
command = UCSI_SET_NOTIFICATION_ENABLE | ntfy;
ret = ucsi_send_command(ucsi, command, NULL, 0);
- if (ret < 0)
+ if (ret < 0) {
+ for (i = 0; i < ucsi->cap.num_connectors; i++)
+ mutex_unlock(&connector[i].lock);
goto err_unregister;
+ }
ucsi->connector = connector;
ucsi->ntfy = ntfy;
+ for (i = 0; i < ucsi->cap.num_connectors; i++) {
+ ucsi_init_port(ucsi, &connector[i]);
+ mutex_unlock(&connector[i].lock);
+ }
+
mutex_lock(&ucsi->ppm_lock);
ret = ucsi->ops->read_cci(ucsi, &cci);
mutex_unlock(&ucsi->ppm_lock);
---
base-commit: 2061f18ad76ecaddf8ed17df81b8611ea88dbddd
change-id: 20251117-ucsi-c2dfe8c006d7
Best regards,
--
Hsin-Te Yuan <yuanhsinte(a)chromium.org>