Skip to content

serial: adi_uart4: avoid double increment of icount.tx in DMA TX path#3228

Merged
qasim-ijaz merged 6 commits intoadsp-6.12.0-yfrom
uart-fix-icount-increment
Apr 3, 2026
Merged

serial: adi_uart4: avoid double increment of icount.tx in DMA TX path#3228
qasim-ijaz merged 6 commits intoadsp-6.12.0-yfrom
uart-fix-icount-increment

Conversation

@qasim-ijaz
Copy link
Copy Markdown

In adi_uart4_serial_dma_tx(), the driver updates uart->port.icount.tx
manually and then calls uart_xmit_advance(&uart->port, uart->tx_count).

However, uart_xmit_advance() already advances the xmit_fifo TX queue and
accounts transmitted bytes into icount.tx, so the function counts each
completed DMA transfer twice.

Fix this by dropping the explicit icount.tx update and rely on uart_xmit_advance()
for TX accounting.

Fixes: eab94da ("serial: Add UART driver for SC5xx SoCs")
Signed-off-by: Qasim Ijaz qasim.ijaz@analog.com

PR Type

  • Bug fix (a change that fixes an issue)
  • New feature (a change that adds new functionality)
  • Breaking change (a change that affects other repos or cause CIs to fail)

PR Checklist

  • I have conducted a self-review of my own code changes
  • I have compiled my changes, including the documentation
  • I have tested the changes on the relevant hardware
  • I have updated the documentation outside this repo accordingly
  • I have provided links for the relevant upstream lore

@qasim-ijaz qasim-ijaz requested review from a team, nunojsa, ozan956 and pamolloy April 1, 2026 01:33
@pamolloy
Copy link
Copy Markdown
Collaborator

pamolloy commented Apr 1, 2026

Looks like you may have committed with your GMail:

Commit sha: [c7436ac](https://github.com/analogdevicesinc/linux/pull/3228/commits/c7436ac15f777ff5aea1afe070637af905f9e6b5), Author: Qasim, Committer: GitHub; Expected "Qasim [qasdev00@gmail.com](mailto:qasdev00@gmail.com)", but got "Qasim Ijaz [qasim.ijaz@analog.com](mailto:qasim.ijaz@analog.com)".

If you have a work directory and personal directory on our machine you can do something like:

~/.gitconfig

[includeIf "gitdir:~/repos/work/adi/"]
	path = ~/repos/work/adi/.gitconfig

~/repos/work/adi/.gitconfig

[user]
	email = foo.bar@analog.com

@pamolloy pamolloy added this to ADSP Apr 1, 2026
@pamolloy
Copy link
Copy Markdown
Collaborator

pamolloy commented Apr 1, 2026

c7436ac15 serial: adi_uart4: avoid double increment of icount.tx in DMA TX path
drivers/tty/serial/adi_uart4.c
#14:
Warning: checkpatch: Prefer a maximum 75 chars per line (possible unwrapped commit description?)
Fix this by dropping the explicit icount.tx update and rely on uart_xmit_advance()

https://github.com/analogdevicesinc/linux/actions/runs/23827385771/job/69453339504?pr=3228#step:9:23

@pamolloy
Copy link
Copy Markdown
Collaborator

pamolloy commented Apr 1, 2026

It is important to also explain the impact of the bug. From Claude:

  1. Inflated statistics: Any tool reading serial port statistics would see 2x the actual transmitted bytes
    - setserial -a /dev/ttySC0 would show wrong numbers
    - Custom monitoring tools using ioctl(fd, TIOCGICOUNT, ...) would get bad data
    - Bandwidth accounting and diagnostics would be incorrect
  2. Debugging confusion: When investigating serial port issues, developers would see:
    TX bytes: 2048 (actual: 1024)
    RX bytes: 512 (correct)
  3. This mismatch could lead to wild goose chases about "why is TX 4x higher than RX?"
  4. Counter overflow: Since it's a __u32, the counter wraps at 4GB. With double counting, it wraps at 2GB of actual data, potentially breaking applications that rely on the counter not wrapping frequently.
  1. Nobody actively monitors these counters
    - Most embedded systems just care that the serial console works
    - The bug doesn't affect actual data transmission at all
    - Serial ports are typically "set and forget"
  2. The statistics are rarely checked
    - TIOCGICOUNT is an obscure ioctl that few applications use
    - Unlike network interfaces (ifconfig, ip stats), serial port stats aren't routinely displayed
    - Most serial debugging focuses on data content, not byte counts

How did you test this change?

Comment thread drivers/tty/serial/adi_uart4.c Outdated
@pamolloy
Copy link
Copy Markdown
Collaborator

pamolloy commented Apr 1, 2026

This made me curious about the non-DMA path (from Claude):

So yes, the adi_uart4 driver has another opportunity for cleanup - the PIO path should use uart_fifo_get() instead of manually calling kfifo_get() + icount.tx++. This would make it consistent with the DMA path's use of uart_xmit_advance().

@qasim-ijaz qasim-ijaz force-pushed the uart-fix-icount-increment branch 2 times, most recently from 31d9cc3 to 9901041 Compare April 1, 2026 11:46
@qasim-ijaz
Copy link
Copy Markdown
Author

It is important to also explain the impact of the bug. From Claude:

  1. Inflated statistics: Any tool reading serial port statistics would see 2x the actual transmitted bytes
    • setserial -a /dev/ttySC0 would show wrong numbers
    • Custom monitoring tools using ioctl(fd, TIOCGICOUNT, ...) would get bad data
    • Bandwidth accounting and diagnostics would be incorrect
  2. Debugging confusion: When investigating serial port issues, developers would see:
    TX bytes: 2048 (actual: 1024)
    RX bytes: 512 (correct)
  3. This mismatch could lead to wild goose chases about "why is TX 4x higher than RX?"
  4. Counter overflow: Since it's a __u32, the counter wraps at 4GB. With double counting, it wraps at 2GB of actual data, potentially breaking applications that rely on the counter not wrapping frequently.
  1. Nobody actively monitors these counters
    • Most embedded systems just care that the serial console works
    • The bug doesn't affect actual data transmission at all
    • Serial ports are typically "set and forget"
  2. The statistics are rarely checked
    • TIOCGICOUNT is an obscure ioctl that few applications use
    • Unlike network interfaces (ifconfig, ip stats), serial port stats aren't routinely displayed
    • Most serial debugging focuses on data content, not byte counts

How did you test this change?

Hi @pamolloy, thanks for the extensive review!

I’ve updated the commit message to better explain the impact.

This does not affect actual data transmission, but it does make
icount.tx incorrect, so TX statistics reported by diagnostics/tools can
be inflated and become misleading during UART debugging.

I tested this change on both the ADSP-SC594 and ADSP-SC598 and verified
normal UART operation after the fix. The root cause is also clear from
serial core since "uart_xmit_advance()" is documented and implemented to
account transmitted bytes in icount.tx, so the extra manual increment
in adi_uart4_serial_dma_tx() causes double counting.

Link: https://elixir.bootlin.com/linux/v6.12/source/include/linux/serial_core.h#L826

@qasim-ijaz
Copy link
Copy Markdown
Author

c7436ac15 serial: adi_uart4: avoid double increment of icount.tx in DMA TX path
drivers/tty/serial/adi_uart4.c
#14:
Warning: checkpatch: Prefer a maximum 75 chars per line (possible unwrapped commit description?)
Fix this by dropping the explicit icount.tx update and rely on uart_xmit_advance()

https://github.com/analogdevicesinc/linux/actions/runs/23827385771/job/69453339504?pr=3228#step:9:23

Should be fixed now but seems like the file isn't being compiled during checks for some reason?

@gastmaier
Copy link
Copy Markdown
Collaborator

Does this driver really technically depends on a specific architecture

depends on ARCH_SC59X = y || ARCH_SC58X = y || ARCH_SC57X = y || ARCH_SC59X_64 = y

Or all those conditionals are being used to say "HEY! This is only works with my SOC!"?
Nevertheless, at least add || COMPILE_TEST so we can compile test the driver.

Ideally simplify the conditionals to represent the technical dependency, not "what this driver support at the moment".

Also, at sound/soc/adi/Kconfig, this logic is ARCH_SC57X || ARCH_SC58X || ARCH_SC59X || ARCH_SC59X_64, why is here different?

@pamolloy
Copy link
Copy Markdown
Collaborator

pamolloy commented Apr 1, 2026

Hi @pamolloy, thanks for the extensive review!

I’ve updated the commit message to better explain the impact.

Thanks!

This does not affect actual data transmission, but it does make icount.tx incorrect, so TX statistics reported by diagnostics/tools can be inflated and become misleading during UART debugging.

I tested this change on both the ADSP-SC594 and ADSP-SC598 and verified normal UART operation after the fix. The root cause is also clear from serial core since "uart_xmit_advance()" is documented and implemented to account transmitted bytes in icount.tx, so the extra manual increment in adi_uart4_serial_dma_tx() causes double counting.

Can you also explain what the "Kernel serial diagnostics" are and what userspace tools use them? Enough so someone else can reproduce it relatively easily. I assume this would be useful if we were using the UART to transmit generic data and not using it as a console. When used as a console these statistics certainly aren't necessary.

@qasim-ijaz
Copy link
Copy Markdown
Author

qasim-ijaz commented Apr 1, 2026

Hi @pamolloy, thanks for the extensive review!
I’ve updated the commit message to better explain the impact.

Thanks!

This does not affect actual data transmission, but it does make icount.tx incorrect, so TX statistics reported by diagnostics/tools can be inflated and become misleading during UART debugging.
I tested this change on both the ADSP-SC594 and ADSP-SC598 and verified normal UART operation after the fix. The root cause is also clear from serial core since "uart_xmit_advance()" is documented and implemented to account transmitted bytes in icount.tx, so the extra manual increment in adi_uart4_serial_dma_tx() causes double counting.

Can you also explain what the "Kernel serial diagnostics" are and what userspace tools use them? Enough so someone else can reproduce it relatively easily. I assume this would be useful if we were using the UART to transmit generic data and not using it as a console. When used as a console these statistics certainly aren't necessary.

Sure, so an example would be something like /proc/tty/driver/adi-uart4 this directly shows you various pieces of info such as the rx and tx counts the driver has completed:

root@adsp-sc598-som-ezkit:~# cat /proc/tty/driver/adi-uart4 
serinfo:1.0 driver revision:
0: uart:ADI-UART4 mmio:0x00000000 irq:0 tx:19594 rx:25 RTS|CTS|DTR|DSR|CD

To easily reproduce and view changes to the tx and rx you can boot up Linux on a ADSP-SC589 or ADSP-SC594 (or whatever). Then in a separate terminal ssh into the board. Then in the ssh session view the above. Then in the serial console type a single character, move back to the ssh session and view the data again, it should have increased now.

I did this exact experiment, so this is the count beforehand:

root@adsp-sc598-som-ezkit:~# cat /proc/tty/driver/adi-uart4
serinfo:1.0 driver revision:
0: uart:ADI-UART4 mmio:0x00000000 irq:0 tx:19594 rx:25 RTS|CTS|DTR|DSR|CD
root@adsp-sc598-som-ezkit:~# 

...Then type a character in the serial console (i pressed 'a' once) and view the counters again in the ssh session:

root@adsp-sc598-som-ezkit:~# cat /proc/tty/driver/adi-uart4
serinfo:1.0 driver revision:
0: uart:ADI-UART4 mmio:0x00000000 irq:0 tx:19596 rx:26 RTS|CTS|DTR|DSR|CD

As you can see the TX counter has gone to 19596 (should be 19595) which illustrates the double increment
once from "uart->port.icount.tx += uart->tx_count;" and another time from the xmit_advance call. Does that make sense?

@qasim-ijaz
Copy link
Copy Markdown
Author

Does this driver really technically depends on a specific architecture

depends on ARCH_SC59X = y || ARCH_SC58X = y || ARCH_SC57X = y || ARCH_SC59X_64 = y

Or all those conditionals are being used to say "HEY! This is only works with my SOC!"? Nevertheless, at least add || COMPILE_TEST so we can compile test the driver.

Ideally simplify the conditionals to represent the technical dependency, not "what this driver support at the moment".

Also, at sound/soc/adi/Kconfig, this logic is ARCH_SC57X || ARCH_SC58X || ARCH_SC59X || ARCH_SC59X_64, why is here different?

Thank for your review. I will look into this.

Comment thread drivers/tty/serial/adi_uart4.c Outdated
module_exit(adi_uart4_serial_exit);

MODULE_DESCRIPTION("ADI UART4 serial driver");
MODULE_LICENSE("GPL");
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be at the end of the file

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be fixed now.

Comment thread drivers/tty/serial/adi_uart4.c Outdated
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to address the coccicheck warning as well:

coccicheck: 3-8  No need to set .owner here. The core will do it. 

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, I have fixed this.

@qasim-ijaz
Copy link
Copy Markdown
Author

Hi @pamolloy, thanks for the extensive review!
I’ve updated the commit message to better explain the impact.

Thanks!

This does not affect actual data transmission, but it does make icount.tx incorrect, so TX statistics reported by diagnostics/tools can be inflated and become misleading during UART debugging.
I tested this change on both the ADSP-SC594 and ADSP-SC598 and verified normal UART operation after the fix. The root cause is also clear from serial core since "uart_xmit_advance()" is documented and implemented to account transmitted bytes in icount.tx, so the extra manual increment in adi_uart4_serial_dma_tx() causes double counting.

Can you also explain what the "Kernel serial diagnostics" are and what userspace tools use them? Enough so someone else can reproduce it relatively easily. I assume this would be useful if we were using the UART to transmit generic data and not using it as a console. When used as a console these statistics certainly aren't necessary.

Sure, so an example would be something like /proc/tty/driver/adi-uart4 this directly shows you various pieces of info such as the rx and tx counts the driver has completed:

root@adsp-sc598-som-ezkit:~# cat /proc/tty/driver/adi-uart4 
serinfo:1.0 driver revision:
0: uart:ADI-UART4 mmio:0x00000000 irq:0 tx:19594 rx:25 RTS|CTS|DTR|DSR|CD

To easily reproduce and view changes to the tx and rx you can boot up Linux on a ADSP-SC589 or ADSP-SC594 (or whatever). Then in a separate terminal ssh into the board. Then in the ssh session view the above. Then in the serial console type a single character, move back to the ssh session and view the data again, it should have increased now.

I did this exact experiment, so this is the count beforehand:

root@adsp-sc598-som-ezkit:~# cat /proc/tty/driver/adi-uart4
serinfo:1.0 driver revision:
0: uart:ADI-UART4 mmio:0x00000000 irq:0 tx:19594 rx:25 RTS|CTS|DTR|DSR|CD
root@adsp-sc598-som-ezkit:~# 

...Then type a character in the serial console (i pressed 'a' once) and view the counters again in the ssh session:

root@adsp-sc598-som-ezkit:~# cat /proc/tty/driver/adi-uart4
serinfo:1.0 driver revision:
0: uart:ADI-UART4 mmio:0x00000000 irq:0 tx:19596 rx:26 RTS|CTS|DTR|DSR|CD

As you can see the TX counter has gone to 19596 (should be 19595) which illustrates the double increment once from "uart->port.icount.tx += uart->tx_count;" and another time from the xmit_advance call. Does that make sense?

@pamolloy maybe i should add this info to the commit message? could be a good addition

@pamolloy
Copy link
Copy Markdown
Collaborator

pamolloy commented Apr 2, 2026

As you can see the TX counter has gone to 19596 (should be 19595) which illustrates the double increment once from "uart->port.icount.tx += uart->tx_count;" and another time from the xmit_advance call. Does that make sense?

@pamolloy maybe i should add this info to the commit message? could be a good addition

Definitely, in this case where we're committing into our own tree and this commit will get squashed before mainlining.

If this patch were submitted mainline some of the above information would be nice to include in a cover letter (to show you've tested it), although most of the context isn't required for a mainline review since it'll get reviewed by subsystem maintainers. Of course within ADI we can't specialize in all subsystems so it is really helpful to include in the PR description in that case. 😄

We also have the System Level doctools documentation, which is a good place to document things, especially if it is applicable to our customers.

@qasim-ijaz qasim-ijaz force-pushed the uart-fix-icount-increment branch from 095f7ff to cb28d13 Compare April 2, 2026 19:11
In adi_uart4_serial_dma_tx(), the driver updates uart->port.icount.tx
manually and then calls uart_xmit_advance(&uart->port, uart->tx_count).

However, uart_xmit_advance() already advances the xmit_fifo TX queue and
accounts transmitted bytes into icount.tx, so the function counts each
completed DMA transfer twice.

This does not affect data transmission, but it makes TX accounting
incorrect. Kernel serial diagnostics that report icount.tx will
show inflated TX byte counts, which could make it harder to
debug UART related bugs/issues in the future.

An example of this is /proc/tty/driver/adi-uart4 which directly shows
you various pieces of information such as the TX and RX counts associated
with the driver:

	root@adsp-sc598-som-ezkit:~# cat /proc/tty/driver/adi-uart4
	serinfo:1.0 driver revision:
	0: uart:ADI-UART4 irq:0 tx:19594 rx:25

To reproduce and view changes to the TX and RX counts you can boot
up Linux on an ADSP-SC589 or ADSP-SC594. Then in a separate
terminal SSH into the board. Then in the SSH session view the
above. Then in the serial console type a single character, move
back to the SSH session and view the data again, it should
have increased now.

I did this exact experiment, so this is the count beforehand:

	root@adsp-sc598-som-ezkit:~# cat /proc/tty/driver/adi-uart4
	serinfo:1.0 driver revision:
	0: uart:ADI-UART4 irq:0 tx:19594 rx:25

Then type a character in the serial console (i pressed 'a' once)
and view the counters again in the SSH session:

	root@adsp-sc598-som-ezkit:~# cat /proc/tty/driver/adi-uart4
	serinfo:1.0 driver revision:
	0: uart:ADI-UART4 irq:0 tx:19596 rx:26

As you can see the TX counter has gone to 19596 (should be 19595)
which illustrates the double increment once from
"uart->port.icount.tx += uart->tx_count;" and another time from
the uart_xmit_advance() call.

Fix this by dropping the explicit icount.tx update and relying on
uart_xmit_advance() for TX accounting.

Fixes: eab94da ("serial: Add UART driver for SC5xx SoCs")
Signed-off-by: Qasim Ijaz <qasim.ijaz@analog.com>
adi_uart4_serial_remove() frees "uart" before checking and releasing
the DMA channels stored in the same structure.

Move the DMA channel release before "kfree(uart)" so the remove path
does not dereference freed memory.

Fixes: eab94da ("serial: Add UART driver for SC5xx SoCs")
Signed-off-by: Qasim Ijaz <qasim.ijaz@analog.com>
Add COMPILE_TEST to SERIAL_ADI_UART4 so CI build jobs can build the driver.

Signed-off-by: Qasim Ijaz <qasim.ijaz@analog.com>
Add the missing MODULE_LICENSE() declaration so CI jobs can build the driver.

Signed-off-by: Qasim Ijaz <qasim.ijaz@analog.com>
Remove the explicit .owner assignment from adi_uart4_serial_driver
to address the coccicheck warning:
"No need to set .owner here. The core will do it."

Signed-off-by: Qasim Ijaz <qasim.ijaz@analog.com>
@qasim-ijaz qasim-ijaz force-pushed the uart-fix-icount-increment branch from a56d53b to 054247a Compare April 2, 2026 20:55
…llback

During the DMA TX completion callback adi_uart4_serial_dma_tx(), the
driver checks whether to call uart_write_wakeup() before advancing the
xmit_fifo TX queue with uart_xmit_advance().

uart_write_wakeup() notifies the tty layer that the driver can accept
more output and wakes tasks blocked in the tty write path. For ttySC0,
that allows a blocked writer to resume and eventually queue more bytes
into the TX buffer (xmit_fifo).

Because this check is done before xmit_fifo is advanced, the wakeup
decision uses stale xmit_fifo state. This becomes problematic when a
completed DMA transfer reduces the pending TX queue below
WAKEUP_CHARS, because blocked writers can miss their wakeup and remain
stalled. This includes the case where the DMA completion drains the
queue completely.

Missing this wakeup can stop further output from being queued and make
the serial console appear to hang or freeze.

I was able to reproduce the issue on both the ADSP-SC594 and ADSP-SC598
by running 'top' on the serial console and repeatedly refreshing the
display. When the issue triggers, the userspace writer ('top') ends up
blocked in the tty write path, for example:

    wait_woken
    n_tty_write
    file_tty_write
    tty_write
    vfs_write
    ksys_write

To fix this issue, the driver should first advance xmit_fifo via
uart_xmit_advance() and only then evaluate whether uart_write_wakeup()
is needed based on the post completion FIFO state. Furthermore
drop the "if (!kfifo_is_empty(&tport->xmit_fifo)" check, since an empty
xmit_fifo after uart_xmit_advance() means the pending TX queue
has dropped to 0, which is still below WAKEUP_CHARS and is a
valid case for uart_write_wakeup().

Tested-by: Arturs Artamonovs <arturs.artamonovs@analog.com>
Fixes: eab94da ("serial: Add UART driver for SC5xx SoCs")
Signed-off-by: Qasim Ijaz <qasim.ijaz@analog.com>
@qasim-ijaz qasim-ijaz merged commit 8037588 into adsp-6.12.0-y Apr 3, 2026
14 checks passed
@qasim-ijaz qasim-ijaz deleted the uart-fix-icount-increment branch April 3, 2026 08:26
@github-project-automation github-project-automation Bot moved this to Done in ADSP Apr 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants