2024-04-05 06:08:48

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 00/15] tty: serial: switch from circ_buf to kfifo

This series switches tty serial layer to use kfifo instead of circ_buf.

The reasoning can be found in the switching patch in this series:
"""
Switch from struct circ_buf to proper kfifo. kfifo provides much better
API, esp. when wrap-around of the buffer needs to be taken into account.
Look at pl011_dma_tx_refill() or cpm_uart_tx_pump() changes for example.

Kfifo API can also fill in scatter-gather DMA structures, so it easier
for that use case too. Look at lpuart_dma_tx() for example. Note that
not all drivers can be converted to that (like atmel_serial), they
handle DMA specially.

Note that usb-serial uses kfifo for TX for ages.
"""

Cc: Al Cooper <[email protected]>
Cc: Alexander Shiyan <[email protected]>
Cc: Alexandre Belloni <[email protected]>
Cc: Alexandre Torgue <[email protected]>
Cc: Alim Akhtar <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: "Aneesh Kumar K.V" <[email protected]>
Cc: AngeloGioacchino Del Regno <[email protected]>
Cc: Baolin Wang <[email protected]>
Cc: Baruch Siach <[email protected]>
Cc: Bjorn Andersson <[email protected]>
Cc: Claudiu Beznea <[email protected]>
Cc: "David S. Miller" <[email protected]>
Cc: Fabio Estevam <[email protected]>
Cc: Hammer Hsieh <[email protected]>
Cc: "Christian König" <[email protected]>
Cc: Christophe Leroy <[email protected]>
Cc: Chunyan Zhang <[email protected]>
Cc: Jerome Brunet <[email protected]>
Cc: Jonathan Hunter <[email protected]>
Cc: Kevin Hilman <[email protected]>
Cc: Konrad Dybcio <[email protected]>
Cc: Krzysztof Kozlowski <[email protected]>
Cc: Kumaravel Thiagarajan <[email protected]>
Cc: Laxman Dewangan <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: "Maciej W. Rozycki" <[email protected]>
Cc: Manivannan Sadhasivam <[email protected]>
Cc: Martin Blumenstingl <[email protected]>
Cc: Matthias Brugger <[email protected]>
Cc: Maxime Coquelin <[email protected]>
Cc: Michael Ellerman <[email protected]>
Cc: Michal Simek <[email protected]>
Cc: "Naveen N. Rao" <[email protected]>
Cc: Neil Armstrong <[email protected]>
Cc: Nicolas Ferre <[email protected]>
Cc: Nicholas Piggin <[email protected]>
Cc: Orson Zhai <[email protected]>
Cc: "Pali Rohár" <[email protected]>
Cc: Patrice Chotard <[email protected]>
Cc: Peter Korsgaard <[email protected]>
Cc: Richard Genoud <[email protected]>
Cc: Russell King <[email protected]>
Cc: Sascha Hauer <[email protected]>
Cc: Shawn Guo <[email protected]>
Cc: Stefani Seibold <[email protected]>
Cc: Sumit Semwal <[email protected]>
Cc: Taichi Sugaya <[email protected]>
Cc: Takao Orito <[email protected]>
Cc: Tharun Kumar P <[email protected]>
Cc: Thierry Reding <[email protected]>
Cc: Timur Tabi <[email protected]>
Cc: Vineet Gupta <[email protected]>

Jiri Slaby (SUSE) (15):
kfifo: drop __kfifo_dma_out_finish_r()
kfifo: introduce and use kfifo_skip_count()
kfifo: add kfifo_out_linear{,_ptr}()
kfifo: remove support for physically non-contiguous memory
kfifo: rename l to len_to_end in setup_sgl()
kfifo: pass offset to setup_sgl_buf() instead of a pointer
kfifo: add kfifo_dma_out_prepare_mapped()
kfifo: fix typos in kernel-doc
tty: 8250_dma: use dmaengine_prep_slave_sg()
tty: 8250_omap: use dmaengine_prep_slave_sg()
tty: msm_serial: use dmaengine_prep_slave_sg()
tty: serial: switch from circ_buf to kfifo
tty: atmel_serial: use single DMA mapping for TX
tty: atmel_serial: define macro for RX size
tty: atmel_serial: use single DMA mapping for RX

drivers/tty/serial/8250/8250_bcm7271.c | 14 +--
drivers/tty/serial/8250/8250_core.c | 3 +-
drivers/tty/serial/8250/8250_dma.c | 31 +++--
drivers/tty/serial/8250/8250_exar.c | 5 +-
drivers/tty/serial/8250/8250_mtk.c | 2 +-
drivers/tty/serial/8250/8250_omap.c | 48 +++++---
drivers/tty/serial/8250/8250_pci1xxxx.c | 50 ++++----
drivers/tty/serial/8250/8250_port.c | 22 ++--
drivers/tty/serial/amba-pl011.c | 46 +++-----
drivers/tty/serial/ar933x_uart.c | 15 ++-
drivers/tty/serial/arc_uart.c | 8 +-
drivers/tty/serial/atmel_serial.c | 150 +++++++++++-------------
drivers/tty/serial/clps711x.c | 12 +-
drivers/tty/serial/cpm_uart.c | 20 ++--
drivers/tty/serial/digicolor-usart.c | 12 +-
drivers/tty/serial/dz.c | 13 +-
drivers/tty/serial/fsl_linflexuart.c | 17 +--
drivers/tty/serial/fsl_lpuart.c | 39 +++---
drivers/tty/serial/icom.c | 25 +---
drivers/tty/serial/imx.c | 54 ++++-----
drivers/tty/serial/ip22zilog.c | 26 ++--
drivers/tty/serial/jsm/jsm_cls.c | 29 ++---
drivers/tty/serial/jsm/jsm_neo.c | 38 ++----
drivers/tty/serial/max3100.c | 14 +--
drivers/tty/serial/max310x.c | 35 +++---
drivers/tty/serial/men_z135_uart.c | 26 ++--
drivers/tty/serial/meson_uart.c | 11 +-
drivers/tty/serial/milbeaut_usio.c | 15 +--
drivers/tty/serial/msm_serial.c | 114 +++++++++---------
drivers/tty/serial/mvebu-uart.c | 8 +-
drivers/tty/serial/mxs-auart.c | 23 +---
drivers/tty/serial/pch_uart.c | 21 ++--
drivers/tty/serial/pic32_uart.c | 15 ++-
drivers/tty/serial/pmac_zilog.c | 24 ++--
drivers/tty/serial/qcom_geni_serial.c | 36 +++---
drivers/tty/serial/rda-uart.c | 17 +--
drivers/tty/serial/samsung_tty.c | 54 +++++----
drivers/tty/serial/sb1250-duart.c | 13 +-
drivers/tty/serial/sc16is7xx.c | 40 +++----
drivers/tty/serial/sccnxp.c | 16 ++-
drivers/tty/serial/serial-tegra.c | 43 ++++---
drivers/tty/serial/serial_core.c | 56 ++++-----
drivers/tty/serial/serial_port.c | 2 +-
drivers/tty/serial/sh-sci.c | 51 ++++----
drivers/tty/serial/sprd_serial.c | 20 ++--
drivers/tty/serial/st-asc.c | 4 +-
drivers/tty/serial/stm32-usart.c | 52 ++++----
drivers/tty/serial/sunhv.c | 35 +++---
drivers/tty/serial/sunplus-uart.c | 16 +--
drivers/tty/serial/sunsab.c | 30 ++---
drivers/tty/serial/sunsu.c | 15 +--
drivers/tty/serial/sunzilog.c | 27 ++---
drivers/tty/serial/tegra-tcu.c | 10 +-
drivers/tty/serial/timbuart.c | 17 ++-
drivers/tty/serial/uartlite.c | 13 +-
drivers/tty/serial/ucc_uart.c | 20 ++--
drivers/tty/serial/xilinx_uartps.c | 20 ++--
drivers/tty/serial/zs.c | 13 +-
include/linux/kfifo.h | 143 ++++++++++++++++------
include/linux/serial_core.h | 49 +++++---
lib/kfifo.c | 107 +++++++++--------
61 files changed, 944 insertions(+), 960 deletions(-)

--
2.44.0



2024-04-05 06:09:14

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 02/15] kfifo: introduce and use kfifo_skip_count()

kfifo_skip_count() is an extended version of kfifo_skip(), accepting
also count. This will be useful in the serial code later.

Now, it can be used to implement both kfifo_skip() and
kfifo_dma_out_finish(). In the latter, 'len' only needs to be divided by
'type' size (as it was until now).

And stop using statement expressions when the return value is cast to
'void'. Use classic 'do {} while (0)' instead.

Note: perhaps we should skip 'count' records for the 'recsize' case, but
the original (kfifo_dma_out_finish()) used to skip only one record. So
this is kept unchanged and 'count' is still ignored in the recsize case.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
Cc: Stefani Seibold <[email protected]>
Cc: Andrew Morton <[email protected]>
---

Notes:
[v2] rename __tmp to ___tmp to fix initialization in the nested macro

include/linux/kfifo.h | 31 +++++++++++++++----------------
1 file changed, 15 insertions(+), 16 deletions(-)

diff --git a/include/linux/kfifo.h b/include/linux/kfifo.h
index bc7a1f5bb0ce..8f3369ec528b 100644
--- a/include/linux/kfifo.h
+++ b/include/linux/kfifo.h
@@ -304,19 +304,25 @@ __kfifo_uint_must_check_helper( \
)

/**
- * kfifo_skip - skip output data
+ * kfifo_skip_count - skip output data
* @fifo: address of the fifo to be used
+ * @count: count of data to skip
*/
-#define kfifo_skip(fifo) \
-(void)({ \
+#define kfifo_skip_count(fifo, count) do { \
typeof((fifo) + 1) __tmp = (fifo); \
const size_t __recsize = sizeof(*__tmp->rectype); \
struct __kfifo *__kfifo = &__tmp->kfifo; \
if (__recsize) \
__kfifo_skip_r(__kfifo, __recsize); \
else \
- __kfifo->out++; \
-})
+ __kfifo->out += (count); \
+} while(0)
+
+/**
+ * kfifo_skip - skip output data
+ * @fifo: address of the fifo to be used
+ */
+#define kfifo_skip(fifo) kfifo_skip_count(fifo, 1)

/**
* kfifo_peek_len - gets the size of the next fifo record
@@ -790,17 +796,10 @@ __kfifo_int_must_check_helper( \
* Note that with only one concurrent reader and one concurrent
* writer, you don't need extra locking to use these macros.
*/
-#define kfifo_dma_out_finish(fifo, len) \
-(void)({ \
- typeof((fifo) + 1) __tmp = (fifo); \
- unsigned int __len = (len); \
- const size_t __recsize = sizeof(*__tmp->rectype); \
- struct __kfifo *__kfifo = &__tmp->kfifo; \
- if (__recsize) \
- __kfifo_skip_r(__kfifo, __recsize); \
- else \
- __kfifo->out += __len / sizeof(*__tmp->type); \
-})
+#define kfifo_dma_out_finish(fifo, len) do { \
+ typeof((fifo) + 1) ___tmp = (fifo); \
+ kfifo_skip_count(___tmp, (len) / sizeof(*___tmp->type)); \
+} while (0)

/**
* kfifo_out_peek - gets some data from the fifo
--
2.44.0


2024-04-05 06:09:25

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 01/15] kfifo: drop __kfifo_dma_out_finish_r()

It is the same as __kfifo_skip_r(), so:
* drop __kfifo_dma_out_finish_r() completely, and
* replace its (only) use by __kfifo_skip_r().

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
Cc: Stefani Seibold <[email protected]>
Cc: Andrew Morton <[email protected]>
---
include/linux/kfifo.h | 4 +---
lib/kfifo.c | 8 --------
2 files changed, 1 insertion(+), 11 deletions(-)

diff --git a/include/linux/kfifo.h b/include/linux/kfifo.h
index 0b35a41440ff..bc7a1f5bb0ce 100644
--- a/include/linux/kfifo.h
+++ b/include/linux/kfifo.h
@@ -797,7 +797,7 @@ __kfifo_int_must_check_helper( \
const size_t __recsize = sizeof(*__tmp->rectype); \
struct __kfifo *__kfifo = &__tmp->kfifo; \
if (__recsize) \
- __kfifo_dma_out_finish_r(__kfifo, __recsize); \
+ __kfifo_skip_r(__kfifo, __recsize); \
else \
__kfifo->out += __len / sizeof(*__tmp->type); \
})
@@ -879,8 +879,6 @@ extern void __kfifo_dma_in_finish_r(struct __kfifo *fifo,
extern unsigned int __kfifo_dma_out_prepare_r(struct __kfifo *fifo,
struct scatterlist *sgl, int nents, unsigned int len, size_t recsize);

-extern void __kfifo_dma_out_finish_r(struct __kfifo *fifo, size_t recsize);
-
extern unsigned int __kfifo_len_r(struct __kfifo *fifo, size_t recsize);

extern void __kfifo_skip_r(struct __kfifo *fifo, size_t recsize);
diff --git a/lib/kfifo.c b/lib/kfifo.c
index 12f5a347aa13..958099cc4914 100644
--- a/lib/kfifo.c
+++ b/lib/kfifo.c
@@ -582,11 +582,3 @@ unsigned int __kfifo_dma_out_prepare_r(struct __kfifo *fifo,
}
EXPORT_SYMBOL(__kfifo_dma_out_prepare_r);

-void __kfifo_dma_out_finish_r(struct __kfifo *fifo, size_t recsize)
-{
- unsigned int len;
-
- len = __kfifo_peek_n(fifo, recsize);
- fifo->out += len + recsize;
-}
-EXPORT_SYMBOL(__kfifo_dma_out_finish_r);
--
2.44.0


2024-04-05 06:09:25

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 03/15] kfifo: add kfifo_out_linear{,_ptr}()

These are helpers which are going to be used in the serial layer. We
need a wrapper around kfifo which provides us with a tail (sometimes
"tail" offset, sometimes a pointer) to the kfifo data. And which returns
count of available data -- but not larger than to the end of the buffer
(hence _linear in the names). I.e. something like CIRC_CNT_TO_END() in
the legacy circ_buf.

This patch adds such two helpers.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
Cc: Stefani Seibold <[email protected]>
Cc: Andrew Morton <[email protected]>
---

Notes:
[v2]
* added Andrew's fix -- missing right parenthesis
* simplify __kfifo_out_linear() -- min3() results can be returned
directly.

include/linux/kfifo.h | 63 +++++++++++++++++++++++++++++++++++++++++++
lib/kfifo.c | 26 ++++++++++++++++++
2 files changed, 89 insertions(+)

diff --git a/include/linux/kfifo.h b/include/linux/kfifo.h
index 8f3369ec528b..3def70e1a3e3 100644
--- a/include/linux/kfifo.h
+++ b/include/linux/kfifo.h
@@ -827,6 +827,63 @@ __kfifo_uint_must_check_helper( \
}) \
)

+/**
+ * kfifo_out_linear - gets a tail of/offset to available data
+ * @fifo: address of the fifo to be used
+ * @tail: pointer to an unsigned int to store the value of tail
+ * @n: max. number of elements to point at
+ *
+ * This macro obtains the offset (tail) to the available data in the fifo
+ * buffer and returns the
+ * numbers of elements available. It returns the available count till the end
+ * of data or till the end of the buffer. So that it can be used for linear
+ * data processing (like memcpy() of (@fifo->data + @tail) with count
+ * returned).
+ *
+ * Note that with only one concurrent reader and one concurrent
+ * writer, you don't need extra locking to use these macro.
+ */
+#define kfifo_out_linear(fifo, tail, n) \
+__kfifo_uint_must_check_helper( \
+({ \
+ typeof((fifo) + 1) __tmp = (fifo); \
+ unsigned int *__tail = (tail); \
+ unsigned long __n = (n); \
+ const size_t __recsize = sizeof(*__tmp->rectype); \
+ struct __kfifo *__kfifo = &__tmp->kfifo; \
+ (__recsize) ? \
+ __kfifo_out_linear_r(__kfifo, __tail, __n, __recsize) : \
+ __kfifo_out_linear(__kfifo, __tail, __n); \
+}) \
+)
+
+/**
+ * kfifo_out_linear_ptr - gets a pointer to the available data
+ * @fifo: address of the fifo to be used
+ * @ptr: pointer to data to store the pointer to tail
+ * @n: max. number of elements to point at
+ *
+ * Similarly to kfifo_out_linear(), this macro obtains the pointer to the
+ * available data in the fifo buffer and returns the numbers of elements
+ * available. It returns the available count till the end of available data or
+ * till the end of the buffer. So that it can be used for linear data
+ * processing (like memcpy() of @ptr with count returned).
+ *
+ * Note that with only one concurrent reader and one concurrent
+ * writer, you don't need extra locking to use these macro.
+ */
+#define kfifo_out_linear_ptr(fifo, ptr, n) \
+__kfifo_uint_must_check_helper( \
+({ \
+ typeof((fifo) + 1) ___tmp = (fifo); \
+ unsigned int ___tail; \
+ unsigned int ___n = kfifo_out_linear(___tmp, &___tail, (n)); \
+ *(ptr) = ___tmp->kfifo.data + ___tail * kfifo_esize(___tmp); \
+ ___n; \
+}) \
+)
+
+
extern int __kfifo_alloc(struct __kfifo *fifo, unsigned int size,
size_t esize, gfp_t gfp_mask);

@@ -856,6 +913,9 @@ extern unsigned int __kfifo_dma_out_prepare(struct __kfifo *fifo,
extern unsigned int __kfifo_out_peek(struct __kfifo *fifo,
void *buf, unsigned int len);

+extern unsigned int __kfifo_out_linear(struct __kfifo *fifo,
+ unsigned int *tail, unsigned int n);
+
extern unsigned int __kfifo_in_r(struct __kfifo *fifo,
const void *buf, unsigned int len, size_t recsize);

@@ -885,6 +945,9 @@ extern void __kfifo_skip_r(struct __kfifo *fifo, size_t recsize);
extern unsigned int __kfifo_out_peek_r(struct __kfifo *fifo,
void *buf, unsigned int len, size_t recsize);

+extern unsigned int __kfifo_out_linear_r(struct __kfifo *fifo,
+ unsigned int *tail, unsigned int n, size_t recsize);
+
extern unsigned int __kfifo_max_r(unsigned int len, size_t recsize);

#endif
diff --git a/lib/kfifo.c b/lib/kfifo.c
index 958099cc4914..a36bfdbdb17d 100644
--- a/lib/kfifo.c
+++ b/lib/kfifo.c
@@ -163,6 +163,19 @@ unsigned int __kfifo_out_peek(struct __kfifo *fifo,
}
EXPORT_SYMBOL(__kfifo_out_peek);

+unsigned int __kfifo_out_linear(struct __kfifo *fifo,
+ unsigned int *tail, unsigned int n)
+{
+ unsigned int size = fifo->mask + 1;
+ unsigned int off = fifo->out & fifo->mask;
+
+ if (tail)
+ *tail = off;
+
+ return min3(n, fifo->in - fifo->out, size - off);
+}
+EXPORT_SYMBOL(__kfifo_out_linear);
+
unsigned int __kfifo_out(struct __kfifo *fifo,
void *buf, unsigned int len)
{
@@ -473,6 +486,19 @@ unsigned int __kfifo_out_peek_r(struct __kfifo *fifo, void *buf,
}
EXPORT_SYMBOL(__kfifo_out_peek_r);

+unsigned int __kfifo_out_linear_r(struct __kfifo *fifo,
+ unsigned int *tail, unsigned int n, size_t recsize)
+{
+ if (fifo->in == fifo->out)
+ return 0;
+
+ if (tail)
+ *tail = fifo->out + recsize;
+
+ return min(n, __kfifo_peek_n(fifo, recsize));
+}
+EXPORT_SYMBOL(__kfifo_out_linear_r);
+
unsigned int __kfifo_out_r(struct __kfifo *fifo, void *buf,
unsigned int len, size_t recsize)
{
--
2.44.0


2024-04-05 06:09:38

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 04/15] kfifo: remove support for physically non-contiguous memory

First, there is no such user. The only user of this interface is
caam_rng_fill_async() and that uses kfifo_alloc() -> kmalloc().

Second, the implementation does not allow anything else than direct
mapping and kmalloc() (due to virt_to_phys()), anyway.

Therefore, there is no point in having this dead (and complex) code in
the kernel.

Note the setup_sgl_buf() function now boils down to simple sg_set_buf().
That is called twice from setup_sgl() to take care of kfifo buffer
wrap-around.

setup_sgl_buf() will be extended shortly, so keeping it in place.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Stefani Seibold <[email protected]>
---
lib/kfifo.c | 38 +++++---------------------------------
1 file changed, 5 insertions(+), 33 deletions(-)

diff --git a/lib/kfifo.c b/lib/kfifo.c
index a36bfdbdb17d..d5830960ab87 100644
--- a/lib/kfifo.c
+++ b/lib/kfifo.c
@@ -305,43 +305,15 @@ int __kfifo_to_user(struct __kfifo *fifo, void __user *to,
}
EXPORT_SYMBOL(__kfifo_to_user);

-static int setup_sgl_buf(struct scatterlist *sgl, void *buf,
- int nents, unsigned int len)
+static unsigned int setup_sgl_buf(struct scatterlist *sgl, void *buf,
+ int nents, unsigned int len)
{
- int n;
- unsigned int l;
- unsigned int off;
- struct page *page;
-
- if (!nents)
+ if (!nents || !len)
return 0;

- if (!len)
- return 0;
+ sg_set_buf(sgl, buf, len);

- n = 0;
- page = virt_to_page(buf);
- off = offset_in_page(buf);
- l = 0;
-
- while (len >= l + PAGE_SIZE - off) {
- struct page *npage;
-
- l += PAGE_SIZE;
- buf += PAGE_SIZE;
- npage = virt_to_page(buf);
- if (page_to_phys(page) != page_to_phys(npage) - l) {
- sg_set_page(sgl, page, l - off, off);
- sgl = sg_next(sgl);
- if (++n == nents || sgl == NULL)
- return n;
- page = npage;
- len -= l - off;
- l = off = 0;
- }
- }
- sg_set_page(sgl, page, len, off);
- return n + 1;
+ return 1;
}

static unsigned int setup_sgl(struct __kfifo *fifo, struct scatterlist *sgl,
--
2.44.0


2024-04-05 06:09:58

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 05/15] kfifo: rename l to len_to_end in setup_sgl()

So that one can make any sense of the name.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
Cc: Stefani Seibold <[email protected]>
Cc: Andrew Morton <[email protected]>
---
lib/kfifo.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/lib/kfifo.c b/lib/kfifo.c
index d5830960ab87..61e35550aea5 100644
--- a/lib/kfifo.c
+++ b/lib/kfifo.c
@@ -321,7 +321,7 @@ static unsigned int setup_sgl(struct __kfifo *fifo, struct scatterlist *sgl,
{
unsigned int size = fifo->mask + 1;
unsigned int esize = fifo->esize;
- unsigned int l;
+ unsigned int len_to_end;
unsigned int n;

off &= fifo->mask;
@@ -330,10 +330,10 @@ static unsigned int setup_sgl(struct __kfifo *fifo, struct scatterlist *sgl,
size *= esize;
len *= esize;
}
- l = min(len, size - off);
+ len_to_end = min(len, size - off);

- n = setup_sgl_buf(sgl, fifo->data + off, nents, l);
- n += setup_sgl_buf(sgl + n, fifo->data, nents - n, len - l);
+ n = setup_sgl_buf(sgl, fifo->data + off, nents, len_to_end);
+ n += setup_sgl_buf(sgl + n, fifo->data, nents - n, len - len_to_end);

return n;
}
--
2.44.0


2024-04-05 06:10:05

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 06/15] kfifo: pass offset to setup_sgl_buf() instead of a pointer

As a preparatory for dma addresses filling, we need the data offset
instead of virtual pointer in setup_sgl_buf(). So pass the former
instead the latter.

And pointer to fifo is needed in setup_sgl_buf() now too.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
Cc: Stefani Seibold <[email protected]>
Cc: Andrew Morton <[email protected]>
---
lib/kfifo.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/lib/kfifo.c b/lib/kfifo.c
index 61e35550aea5..3a249ce4f281 100644
--- a/lib/kfifo.c
+++ b/lib/kfifo.c
@@ -305,9 +305,12 @@ int __kfifo_to_user(struct __kfifo *fifo, void __user *to,
}
EXPORT_SYMBOL(__kfifo_to_user);

-static unsigned int setup_sgl_buf(struct scatterlist *sgl, void *buf,
- int nents, unsigned int len)
+static unsigned int setup_sgl_buf(struct __kfifo *fifo, struct scatterlist *sgl,
+ unsigned int data_offset, int nents,
+ unsigned int len)
{
+ const void *buf = fifo->data + data_offset;
+
if (!nents || !len)
return 0;

@@ -332,8 +335,8 @@ static unsigned int setup_sgl(struct __kfifo *fifo, struct scatterlist *sgl,
}
len_to_end = min(len, size - off);

- n = setup_sgl_buf(sgl, fifo->data + off, nents, len_to_end);
- n += setup_sgl_buf(sgl + n, fifo->data, nents - n, len - len_to_end);
+ n = setup_sgl_buf(fifo, sgl, off, nents, len_to_end);
+ n += setup_sgl_buf(fifo, sgl + n, 0, nents - n, len - len_to_end);

return n;
}
--
2.44.0


2024-04-05 06:10:21

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 07/15] kfifo: add kfifo_dma_out_prepare_mapped()

When the kfifo buffer is already dma-mapped, one cannot use the kfifo
API to fill in an SG list.

Add kfifo_dma_in_prepare_mapped() which allows exactly this. A mapped
dma_addr_t is passed and it is filled into provided sgl too. Including
the dma_len.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
Cc: Stefani Seibold <[email protected]>
Cc: Andrew Morton <[email protected]>
---
include/linux/kfifo.h | 37 +++++++++++++++++++++++++------------
lib/kfifo.c | 34 ++++++++++++++++++++++------------
2 files changed, 47 insertions(+), 24 deletions(-)

diff --git a/include/linux/kfifo.h b/include/linux/kfifo.h
index 3def70e1a3e3..c7cc25b2808b 100644
--- a/include/linux/kfifo.h
+++ b/include/linux/kfifo.h
@@ -36,6 +36,7 @@
* to lock the reader.
*/

+#include <linux/dma-mapping.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/stddef.h>
@@ -709,11 +710,12 @@ __kfifo_int_must_check_helper( \
)

/**
- * kfifo_dma_in_prepare - setup a scatterlist for DMA input
+ * kfifo_dma_in_prepare_mapped - setup a scatterlist for DMA input
* @fifo: address of the fifo to be used
* @sgl: pointer to the scatterlist array
* @nents: number of entries in the scatterlist array
* @len: number of elements to transfer
+ * @dma: mapped dma address to fill into @sgl
*
* This macro fills a scatterlist for DMA input.
* It returns the number entries in the scatterlist array.
@@ -721,7 +723,7 @@ __kfifo_int_must_check_helper( \
* Note that with only one concurrent reader and one concurrent
* writer, you don't need extra locking to use these macros.
*/
-#define kfifo_dma_in_prepare(fifo, sgl, nents, len) \
+#define kfifo_dma_in_prepare_mapped(fifo, sgl, nents, len, dma) \
({ \
typeof((fifo) + 1) __tmp = (fifo); \
struct scatterlist *__sgl = (sgl); \
@@ -730,10 +732,14 @@ __kfifo_int_must_check_helper( \
const size_t __recsize = sizeof(*__tmp->rectype); \
struct __kfifo *__kfifo = &__tmp->kfifo; \
(__recsize) ? \
- __kfifo_dma_in_prepare_r(__kfifo, __sgl, __nents, __len, __recsize) : \
- __kfifo_dma_in_prepare(__kfifo, __sgl, __nents, __len); \
+ __kfifo_dma_in_prepare_r(__kfifo, __sgl, __nents, __len, __recsize, \
+ dma) : \
+ __kfifo_dma_in_prepare(__kfifo, __sgl, __nents, __len, dma); \
})

+#define kfifo_dma_in_prepare(fifo, sgl, nents, len) \
+ kfifo_dma_in_prepare_mapped(fifo, sgl, nents, len, DMA_MAPPING_ERROR)
+
/**
* kfifo_dma_in_finish - finish a DMA IN operation
* @fifo: address of the fifo to be used
@@ -758,11 +764,12 @@ __kfifo_int_must_check_helper( \
})

/**
- * kfifo_dma_out_prepare - setup a scatterlist for DMA output
+ * kfifo_dma_out_prepare_mapped - setup a scatterlist for DMA output
* @fifo: address of the fifo to be used
* @sgl: pointer to the scatterlist array
* @nents: number of entries in the scatterlist array
* @len: number of elements to transfer
+ * @dma: mapped dma address to fill into @sgl
*
* This macro fills a scatterlist for DMA output which at most @len bytes
* to transfer.
@@ -772,7 +779,7 @@ __kfifo_int_must_check_helper( \
* Note that with only one concurrent reader and one concurrent
* writer, you don't need extra locking to use these macros.
*/
-#define kfifo_dma_out_prepare(fifo, sgl, nents, len) \
+#define kfifo_dma_out_prepare_mapped(fifo, sgl, nents, len, dma) \
({ \
typeof((fifo) + 1) __tmp = (fifo); \
struct scatterlist *__sgl = (sgl); \
@@ -781,10 +788,14 @@ __kfifo_int_must_check_helper( \
const size_t __recsize = sizeof(*__tmp->rectype); \
struct __kfifo *__kfifo = &__tmp->kfifo; \
(__recsize) ? \
- __kfifo_dma_out_prepare_r(__kfifo, __sgl, __nents, __len, __recsize) : \
- __kfifo_dma_out_prepare(__kfifo, __sgl, __nents, __len); \
+ __kfifo_dma_out_prepare_r(__kfifo, __sgl, __nents, __len, __recsize, \
+ dma) : \
+ __kfifo_dma_out_prepare(__kfifo, __sgl, __nents, __len, dma); \
})

+#define kfifo_dma_out_prepare(fifo, sgl, nents, len) \
+ kfifo_dma_out_prepare_mapped(fifo, sgl, nents, len, DMA_MAPPING_ERROR)
+
/**
* kfifo_dma_out_finish - finish a DMA OUT operation
* @fifo: address of the fifo to be used
@@ -905,10 +916,10 @@ extern int __kfifo_to_user(struct __kfifo *fifo,
void __user *to, unsigned long len, unsigned int *copied);

extern unsigned int __kfifo_dma_in_prepare(struct __kfifo *fifo,
- struct scatterlist *sgl, int nents, unsigned int len);
+ struct scatterlist *sgl, int nents, unsigned int len, dma_addr_t dma);

extern unsigned int __kfifo_dma_out_prepare(struct __kfifo *fifo,
- struct scatterlist *sgl, int nents, unsigned int len);
+ struct scatterlist *sgl, int nents, unsigned int len, dma_addr_t dma);

extern unsigned int __kfifo_out_peek(struct __kfifo *fifo,
void *buf, unsigned int len);
@@ -930,13 +941,15 @@ extern int __kfifo_to_user_r(struct __kfifo *fifo, void __user *to,
unsigned long len, unsigned int *copied, size_t recsize);

extern unsigned int __kfifo_dma_in_prepare_r(struct __kfifo *fifo,
- struct scatterlist *sgl, int nents, unsigned int len, size_t recsize);
+ struct scatterlist *sgl, int nents, unsigned int len, size_t recsize,
+ dma_addr_t dma);

extern void __kfifo_dma_in_finish_r(struct __kfifo *fifo,
unsigned int len, size_t recsize);

extern unsigned int __kfifo_dma_out_prepare_r(struct __kfifo *fifo,
- struct scatterlist *sgl, int nents, unsigned int len, size_t recsize);
+ struct scatterlist *sgl, int nents, unsigned int len, size_t recsize,
+ dma_addr_t dma);

extern unsigned int __kfifo_len_r(struct __kfifo *fifo, size_t recsize);

diff --git a/lib/kfifo.c b/lib/kfifo.c
index 3a249ce4f281..75ce9225548a 100644
--- a/lib/kfifo.c
+++ b/lib/kfifo.c
@@ -6,6 +6,7 @@
*/

#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/err.h>
@@ -307,7 +308,7 @@ EXPORT_SYMBOL(__kfifo_to_user);

static unsigned int setup_sgl_buf(struct __kfifo *fifo, struct scatterlist *sgl,
unsigned int data_offset, int nents,
- unsigned int len)
+ unsigned int len, dma_addr_t dma)
{
const void *buf = fifo->data + data_offset;

@@ -316,11 +317,16 @@ static unsigned int setup_sgl_buf(struct __kfifo *fifo, struct scatterlist *sgl,

sg_set_buf(sgl, buf, len);

+ if (dma != DMA_MAPPING_ERROR) {
+ sg_dma_address(sgl) = dma + data_offset;
+ sg_dma_len(sgl) = len;
+ }
+
return 1;
}

static unsigned int setup_sgl(struct __kfifo *fifo, struct scatterlist *sgl,
- int nents, unsigned int len, unsigned int off)
+ int nents, unsigned int len, unsigned int off, dma_addr_t dma)
{
unsigned int size = fifo->mask + 1;
unsigned int esize = fifo->esize;
@@ -335,14 +341,15 @@ static unsigned int setup_sgl(struct __kfifo *fifo, struct scatterlist *sgl,
}
len_to_end = min(len, size - off);

- n = setup_sgl_buf(fifo, sgl, off, nents, len_to_end);
- n += setup_sgl_buf(fifo, sgl + n, 0, nents - n, len - len_to_end);
+ n = setup_sgl_buf(fifo, sgl, off, nents, len_to_end, dma);
+ n += setup_sgl_buf(fifo, sgl + n, 0, nents - n, len - len_to_end, dma);

return n;
}

unsigned int __kfifo_dma_in_prepare(struct __kfifo *fifo,
- struct scatterlist *sgl, int nents, unsigned int len)
+ struct scatterlist *sgl, int nents, unsigned int len,
+ dma_addr_t dma)
{
unsigned int l;

@@ -350,12 +357,13 @@ unsigned int __kfifo_dma_in_prepare(struct __kfifo *fifo,
if (len > l)
len = l;

- return setup_sgl(fifo, sgl, nents, len, fifo->in);
+ return setup_sgl(fifo, sgl, nents, len, fifo->in, dma);
}
EXPORT_SYMBOL(__kfifo_dma_in_prepare);

unsigned int __kfifo_dma_out_prepare(struct __kfifo *fifo,
- struct scatterlist *sgl, int nents, unsigned int len)
+ struct scatterlist *sgl, int nents, unsigned int len,
+ dma_addr_t dma)
{
unsigned int l;

@@ -363,7 +371,7 @@ unsigned int __kfifo_dma_out_prepare(struct __kfifo *fifo,
if (len > l)
len = l;

- return setup_sgl(fifo, sgl, nents, len, fifo->out);
+ return setup_sgl(fifo, sgl, nents, len, fifo->out, dma);
}
EXPORT_SYMBOL(__kfifo_dma_out_prepare);

@@ -547,7 +555,8 @@ int __kfifo_to_user_r(struct __kfifo *fifo, void __user *to,
EXPORT_SYMBOL(__kfifo_to_user_r);

unsigned int __kfifo_dma_in_prepare_r(struct __kfifo *fifo,
- struct scatterlist *sgl, int nents, unsigned int len, size_t recsize)
+ struct scatterlist *sgl, int nents, unsigned int len, size_t recsize,
+ dma_addr_t dma)
{
BUG_ON(!nents);

@@ -556,7 +565,7 @@ unsigned int __kfifo_dma_in_prepare_r(struct __kfifo *fifo,
if (len + recsize > kfifo_unused(fifo))
return 0;

- return setup_sgl(fifo, sgl, nents, len, fifo->in + recsize);
+ return setup_sgl(fifo, sgl, nents, len, fifo->in + recsize, dma);
}
EXPORT_SYMBOL(__kfifo_dma_in_prepare_r);

@@ -570,7 +579,8 @@ void __kfifo_dma_in_finish_r(struct __kfifo *fifo,
EXPORT_SYMBOL(__kfifo_dma_in_finish_r);

unsigned int __kfifo_dma_out_prepare_r(struct __kfifo *fifo,
- struct scatterlist *sgl, int nents, unsigned int len, size_t recsize)
+ struct scatterlist *sgl, int nents, unsigned int len, size_t recsize,
+ dma_addr_t dma)
{
BUG_ON(!nents);

@@ -579,7 +589,7 @@ unsigned int __kfifo_dma_out_prepare_r(struct __kfifo *fifo,
if (len + recsize > fifo->in - fifo->out)
return 0;

- return setup_sgl(fifo, sgl, nents, len, fifo->out + recsize);
+ return setup_sgl(fifo, sgl, nents, len, fifo->out + recsize, dma);
}
EXPORT_SYMBOL(__kfifo_dma_out_prepare_r);

--
2.44.0


2024-04-05 06:10:28

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 08/15] kfifo: fix typos in kernel-doc

Obviously:
"This macro finish" -> "This macro finishes"
and similar.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
Cc: Stefani Seibold <[email protected]>
Cc: Andrew Morton <[email protected]>
---
include/linux/kfifo.h | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/include/linux/kfifo.h b/include/linux/kfifo.h
index c7cc25b2808b..d613748de7ff 100644
--- a/include/linux/kfifo.h
+++ b/include/linux/kfifo.h
@@ -585,7 +585,7 @@ __kfifo_uint_must_check_helper( \
* @buf: pointer to the storage buffer
* @n: max. number of elements to get
*
- * This macro get some data from the fifo and return the numbers of elements
+ * This macro gets some data from the fifo and returns the numbers of elements
* copied.
*
* Note that with only one concurrent reader and one concurrent
@@ -612,7 +612,7 @@ __kfifo_uint_must_check_helper( \
* @n: max. number of elements to get
* @lock: pointer to the spinlock to use for locking
*
- * This macro get the data from the fifo and return the numbers of elements
+ * This macro gets the data from the fifo and returns the numbers of elements
* copied.
*/
#define kfifo_out_spinlocked(fifo, buf, n, lock) \
@@ -745,7 +745,7 @@ __kfifo_int_must_check_helper( \
* @fifo: address of the fifo to be used
* @len: number of bytes to received
*
- * This macro finish a DMA IN operation. The in counter will be updated by
+ * This macro finishes a DMA IN operation. The in counter will be updated by
* the len parameter. No error checking will be done.
*
* Note that with only one concurrent reader and one concurrent
@@ -801,7 +801,7 @@ __kfifo_int_must_check_helper( \
* @fifo: address of the fifo to be used
* @len: number of bytes transferred
*
- * This macro finish a DMA OUT operation. The out counter will be updated by
+ * This macro finishes a DMA OUT operation. The out counter will be updated by
* the len parameter. No error checking will be done.
*
* Note that with only one concurrent reader and one concurrent
@@ -818,7 +818,7 @@ __kfifo_int_must_check_helper( \
* @buf: pointer to the storage buffer
* @n: max. number of elements to get
*
- * This macro get the data from the fifo and return the numbers of elements
+ * This macro gets the data from the fifo and returns the numbers of elements
* copied. The data is not removed from the fifo.
*
* Note that with only one concurrent reader and one concurrent
--
2.44.0


2024-04-05 06:11:17

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 11/15] tty: msm_serial: use dmaengine_prep_slave_sg()

This is a preparatory for the serial-to-kfifo switch. kfifo understands
only scatter-gatter approach, so switch to that.

No functional change intended, it's just dmaengine_prep_slave_single()
inline expanded.

And in this case, switch from dma_map_single() to dma_map_sg() too. This
needs struct msm_dma changes. I split the rx and tx parts into an union.
TX is now struct scatterlist, RX remains the old good phys-virt-count
triple.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
Cc: Bjorn Andersson <[email protected]>
Cc: Konrad Dybcio <[email protected]>
Cc: [email protected]
---
drivers/tty/serial/msm_serial.c | 86 +++++++++++++++++++--------------
1 file changed, 49 insertions(+), 37 deletions(-)

diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index d27c4c8c84e1..7bf30e632313 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -161,11 +161,16 @@ enum {
struct msm_dma {
struct dma_chan *chan;
enum dma_data_direction dir;
- dma_addr_t phys;
- unsigned char *virt;
+ union {
+ struct {
+ dma_addr_t phys;
+ unsigned char *virt;
+ unsigned int count;
+ } rx;
+ struct scatterlist tx_sg;
+ };
dma_cookie_t cookie;
u32 enable_bit;
- unsigned int count;
struct dma_async_tx_descriptor *desc;
};

@@ -249,8 +254,12 @@ static void msm_stop_dma(struct uart_port *port, struct msm_dma *dma)
unsigned int mapped;
u32 val;

- mapped = dma->count;
- dma->count = 0;
+ if (dma->dir == DMA_TO_DEVICE) {
+ mapped = sg_dma_len(&dma->tx_sg);
+ } else {
+ mapped = dma->rx.count;
+ dma->rx.count = 0;
+ }

dmaengine_terminate_all(dma->chan);

@@ -265,8 +274,13 @@ static void msm_stop_dma(struct uart_port *port, struct msm_dma *dma)
val &= ~dma->enable_bit;
msm_write(port, val, UARTDM_DMEN);

- if (mapped)
- dma_unmap_single(dev, dma->phys, mapped, dma->dir);
+ if (mapped) {
+ if (dma->dir == DMA_TO_DEVICE) {
+ dma_unmap_sg(dev, &dma->tx_sg, 1, dma->dir);
+ sg_init_table(&dma->tx_sg, 1);
+ } else
+ dma_unmap_single(dev, dma->rx.phys, mapped, dma->dir);
+ }
}

static void msm_release_dma(struct msm_port *msm_port)
@@ -285,7 +299,7 @@ static void msm_release_dma(struct msm_port *msm_port)
if (dma->chan) {
msm_stop_dma(&msm_port->uart, dma);
dma_release_channel(dma->chan);
- kfree(dma->virt);
+ kfree(dma->rx.virt);
}

memset(dma, 0, sizeof(*dma));
@@ -357,8 +371,8 @@ static void msm_request_rx_dma(struct msm_port *msm_port, resource_size_t base)

of_property_read_u32(dev->of_node, "qcom,rx-crci", &crci);

- dma->virt = kzalloc(UARTDM_RX_SIZE, GFP_KERNEL);
- if (!dma->virt)
+ dma->rx.virt = kzalloc(UARTDM_RX_SIZE, GFP_KERNEL);
+ if (!dma->rx.virt)
goto rel_rx;

memset(&conf, 0, sizeof(conf));
@@ -385,7 +399,7 @@ static void msm_request_rx_dma(struct msm_port *msm_port, resource_size_t base)

return;
err:
- kfree(dma->virt);
+ kfree(dma->rx.virt);
rel_rx:
dma_release_channel(dma->chan);
no_rx:
@@ -420,7 +434,7 @@ static void msm_start_tx(struct uart_port *port)
struct msm_dma *dma = &msm_port->tx_dma;

/* Already started in DMA mode */
- if (dma->count)
+ if (sg_dma_len(&dma->tx_sg))
return;

msm_port->imr |= MSM_UART_IMR_TXLEV;
@@ -448,12 +462,12 @@ static void msm_complete_tx_dma(void *args)
uart_port_lock_irqsave(port, &flags);

/* Already stopped */
- if (!dma->count)
+ if (!sg_dma_len(&dma->tx_sg))
goto done;

dmaengine_tx_status(dma->chan, dma->cookie, &state);

- dma_unmap_single(port->dev, dma->phys, dma->count, dma->dir);
+ dma_unmap_sg(port->dev, &dma->tx_sg, 1, dma->dir);

val = msm_read(port, UARTDM_DMEN);
val &= ~dma->enable_bit;
@@ -464,9 +478,9 @@ static void msm_complete_tx_dma(void *args)
msm_write(port, MSM_UART_CR_TX_ENABLE, MSM_UART_CR);
}

- count = dma->count - state.residue;
+ count = sg_dma_len(&dma->tx_sg) - state.residue;
uart_xmit_advance(port, count);
- dma->count = 0;
+ sg_init_table(&dma->tx_sg, 1);

/* Restore "Tx FIFO below watermark" interrupt */
msm_port->imr |= MSM_UART_IMR_TXLEV;
@@ -485,19 +499,18 @@ static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count)
struct circ_buf *xmit = &msm_port->uart.state->xmit;
struct uart_port *port = &msm_port->uart;
struct msm_dma *dma = &msm_port->tx_dma;
- void *cpu_addr;
int ret;
u32 val;

- cpu_addr = &xmit->buf[xmit->tail];
+ sg_init_table(&dma->tx_sg, 1);
+ sg_set_buf(&dma->tx_sg, &xmit->buf[xmit->tail], count);

- dma->phys = dma_map_single(port->dev, cpu_addr, count, dma->dir);
- ret = dma_mapping_error(port->dev, dma->phys);
+ ret = dma_map_sg(port->dev, &dma->tx_sg, 1, dma->dir);
if (ret)
return ret;

- dma->desc = dmaengine_prep_slave_single(dma->chan, dma->phys,
- count, DMA_MEM_TO_DEV,
+ dma->desc = dmaengine_prep_slave_sg(dma->chan, &dma->tx_sg, 1,
+ DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT |
DMA_PREP_FENCE);
if (!dma->desc) {
@@ -520,8 +533,6 @@ static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count)
msm_port->imr &= ~MSM_UART_IMR_TXLEV;
msm_write(port, msm_port->imr, MSM_UART_IMR);

- dma->count = count;
-
val = msm_read(port, UARTDM_DMEN);
val |= dma->enable_bit;

@@ -536,7 +547,8 @@ static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count)
dma_async_issue_pending(dma->chan);
return 0;
unmap:
- dma_unmap_single(port->dev, dma->phys, count, dma->dir);
+ dma_unmap_sg(port->dev, &dma->tx_sg, 1, dma->dir);
+ sg_init_table(&dma->tx_sg, 1);
return ret;
}

@@ -553,7 +565,7 @@ static void msm_complete_rx_dma(void *args)
uart_port_lock_irqsave(port, &flags);

/* Already stopped */
- if (!dma->count)
+ if (!dma->rx.count)
goto done;

val = msm_read(port, UARTDM_DMEN);
@@ -570,14 +582,14 @@ static void msm_complete_rx_dma(void *args)

port->icount.rx += count;

- dma->count = 0;
+ dma->rx.count = 0;

- dma_unmap_single(port->dev, dma->phys, UARTDM_RX_SIZE, dma->dir);
+ dma_unmap_single(port->dev, dma->rx.phys, UARTDM_RX_SIZE, dma->dir);

for (i = 0; i < count; i++) {
char flag = TTY_NORMAL;

- if (msm_port->break_detected && dma->virt[i] == 0) {
+ if (msm_port->break_detected && dma->rx.virt[i] == 0) {
port->icount.brk++;
flag = TTY_BREAK;
msm_port->break_detected = false;
@@ -588,9 +600,9 @@ static void msm_complete_rx_dma(void *args)
if (!(port->read_status_mask & MSM_UART_SR_RX_BREAK))
flag = TTY_NORMAL;

- sysrq = uart_prepare_sysrq_char(port, dma->virt[i]);
+ sysrq = uart_prepare_sysrq_char(port, dma->rx.virt[i]);
if (!sysrq)
- tty_insert_flip_char(tport, dma->virt[i], flag);
+ tty_insert_flip_char(tport, dma->rx.virt[i], flag);
}

msm_start_rx_dma(msm_port);
@@ -614,13 +626,13 @@ static void msm_start_rx_dma(struct msm_port *msm_port)
if (!dma->chan)
return;

- dma->phys = dma_map_single(uart->dev, dma->virt,
+ dma->rx.phys = dma_map_single(uart->dev, dma->rx.virt,
UARTDM_RX_SIZE, dma->dir);
- ret = dma_mapping_error(uart->dev, dma->phys);
+ ret = dma_mapping_error(uart->dev, dma->rx.phys);
if (ret)
goto sw_mode;

- dma->desc = dmaengine_prep_slave_single(dma->chan, dma->phys,
+ dma->desc = dmaengine_prep_slave_single(dma->chan, dma->rx.phys,
UARTDM_RX_SIZE, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT);
if (!dma->desc)
@@ -648,7 +660,7 @@ static void msm_start_rx_dma(struct msm_port *msm_port)

msm_write(uart, msm_port->imr, MSM_UART_IMR);

- dma->count = UARTDM_RX_SIZE;
+ dma->rx.count = UARTDM_RX_SIZE;

dma_async_issue_pending(dma->chan);

@@ -668,7 +680,7 @@ static void msm_start_rx_dma(struct msm_port *msm_port)

return;
unmap:
- dma_unmap_single(uart->dev, dma->phys, UARTDM_RX_SIZE, dma->dir);
+ dma_unmap_single(uart->dev, dma->rx.phys, UARTDM_RX_SIZE, dma->dir);

sw_mode:
/*
@@ -955,7 +967,7 @@ static irqreturn_t msm_uart_irq(int irq, void *dev_id)
}

if (misr & (MSM_UART_IMR_RXLEV | MSM_UART_IMR_RXSTALE)) {
- if (dma->count) {
+ if (dma->rx.count) {
val = MSM_UART_CR_CMD_STALE_EVENT_DISABLE;
msm_write(port, val, MSM_UART_CR);
val = MSM_UART_CR_CMD_RESET_STALE_INT;
--
2.44.0


2024-04-05 06:12:39

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 09/15] tty: 8250_dma: use dmaengine_prep_slave_sg()

This is a preparatory for the serial-to-kfifo switch. kfifo understands
only scatter-gatter approach, so switch to that.

No functional change intended, it's just dmaengine_prep_slave_single()
inline expanded.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/serial/8250/8250_dma.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index 8b30ca8fdd3f..8b2c3f478b17 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -89,6 +89,7 @@ int serial8250_tx_dma(struct uart_8250_port *p)
struct circ_buf *xmit = &p->port.state->xmit;
struct dma_async_tx_descriptor *desc;
struct uart_port *up = &p->port;
+ struct scatterlist sg;
int ret;

if (dma->tx_running) {
@@ -111,10 +112,13 @@ int serial8250_tx_dma(struct uart_8250_port *p)

serial8250_do_prepare_tx_dma(p);

- desc = dmaengine_prep_slave_single(dma->txchan,
- dma->tx_addr + xmit->tail,
- dma->tx_size, DMA_MEM_TO_DEV,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ sg_init_table(&sg, 1);
+ sg_dma_address(&sg) = dma->tx_addr + xmit->tail;
+ sg_dma_len(&sg) = dma->tx_size;
+
+ desc = dmaengine_prep_slave_sg(dma->txchan, &sg, 1,
+ DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
ret = -EBUSY;
goto err;
--
2.44.0


2024-04-05 06:12:47

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 15/15] tty: atmel_serial: use single DMA mapping for RX

dma_map_single() provides much easier interface for simple mappings as
used for RX in atmel_serial. So switch to that, removing all the s-g
unnecessary handling.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
Cc: Richard Genoud <[email protected]>
Cc: Nicolas Ferre <[email protected]>
Cc: Alexandre Belloni <[email protected]>
Cc: Claudiu Beznea <[email protected]>
Cc: [email protected]
---
drivers/tty/serial/atmel_serial.c | 56 ++++++++++++-------------------
1 file changed, 22 insertions(+), 34 deletions(-)

diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 5cde5077c429..0a90964d6d10 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -135,7 +135,7 @@ struct atmel_uart_port {
dma_cookie_t cookie_tx;
dma_cookie_t cookie_rx;
dma_addr_t tx_phys;
- struct scatterlist sg_rx;
+ dma_addr_t rx_phys;
struct tasklet_struct tasklet_rx;
struct tasklet_struct tasklet_tx;
atomic_t tasklet_shutdown;
@@ -1088,8 +1088,8 @@ static void atmel_release_rx_dma(struct uart_port *port)
if (chan) {
dmaengine_terminate_all(chan);
dma_release_channel(chan);
- dma_unmap_sg(port->dev, &atmel_port->sg_rx, 1,
- DMA_FROM_DEVICE);
+ dma_unmap_single(port->dev, atmel_port->rx_phys,
+ ATMEL_SERIAL_RX_SIZE, DMA_FROM_DEVICE);
}

atmel_port->desc_rx = NULL;
@@ -1122,10 +1122,8 @@ static void atmel_rx_from_dma(struct uart_port *port)
}

/* CPU claims ownership of RX DMA buffer */
- dma_sync_sg_for_cpu(port->dev,
- &atmel_port->sg_rx,
- 1,
- DMA_FROM_DEVICE);
+ dma_sync_single_for_cpu(port->dev, atmel_port->rx_phys,
+ ATMEL_SERIAL_RX_SIZE, DMA_FROM_DEVICE);

/*
* ring->head points to the end of data already written by the DMA.
@@ -1134,8 +1132,8 @@ static void atmel_rx_from_dma(struct uart_port *port)
* The current transfer size should not be larger than the dma buffer
* length.
*/
- ring->head = sg_dma_len(&atmel_port->sg_rx) - state.residue;
- BUG_ON(ring->head > sg_dma_len(&atmel_port->sg_rx));
+ ring->head = ATMEL_SERIAL_RX_SIZE - state.residue;
+ BUG_ON(ring->head > ATMEL_SERIAL_RX_SIZE);
/*
* At this point ring->head may point to the first byte right after the
* last byte of the dma buffer:
@@ -1149,7 +1147,7 @@ static void atmel_rx_from_dma(struct uart_port *port)
* tail to the end of the buffer then reset tail.
*/
if (ring->head < ring->tail) {
- count = sg_dma_len(&atmel_port->sg_rx) - ring->tail;
+ count = ATMEL_SERIAL_RX_SIZE - ring->tail;

tty_insert_flip_string(tport, ring->buf + ring->tail, count);
ring->tail = 0;
@@ -1162,17 +1160,15 @@ static void atmel_rx_from_dma(struct uart_port *port)

tty_insert_flip_string(tport, ring->buf + ring->tail, count);
/* Wrap ring->head if needed */
- if (ring->head >= sg_dma_len(&atmel_port->sg_rx))
+ if (ring->head >= ATMEL_SERIAL_RX_SIZE)
ring->head = 0;
ring->tail = ring->head;
port->icount.rx += count;
}

/* USART retreives ownership of RX DMA buffer */
- dma_sync_sg_for_device(port->dev,
- &atmel_port->sg_rx,
- 1,
- DMA_FROM_DEVICE);
+ dma_sync_single_for_device(port->dev, atmel_port->rx_phys,
+ ATMEL_SERIAL_RX_SIZE, DMA_FROM_DEVICE);

tty_flip_buffer_push(tport);

@@ -1188,7 +1184,7 @@ static int atmel_prepare_rx_dma(struct uart_port *port)
struct dma_slave_config config;
struct circ_buf *ring;
struct dma_chan *chan;
- int ret, nent;
+ int ret;

ring = &atmel_port->rx_ring;

@@ -1205,26 +1201,18 @@ static int atmel_prepare_rx_dma(struct uart_port *port)
dma_chan_name(atmel_port->chan_rx));

spin_lock_init(&atmel_port->lock_rx);
- sg_init_table(&atmel_port->sg_rx, 1);
/* UART circular rx buffer is an aligned page. */
BUG_ON(!PAGE_ALIGNED(ring->buf));
- sg_set_page(&atmel_port->sg_rx,
- virt_to_page(ring->buf),
- ATMEL_SERIAL_RX_SIZE,
- offset_in_page(ring->buf));
- nent = dma_map_sg(port->dev,
- &atmel_port->sg_rx,
- 1,
- DMA_FROM_DEVICE);
-
- if (!nent) {
+ atmel_port->rx_phys = dma_map_single(port->dev, ring->buf,
+ ATMEL_SERIAL_RX_SIZE,
+ DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(port->dev, atmel_port->rx_phys)) {
dev_dbg(port->dev, "need to release resource of dma\n");
goto chan_err;
} else {
- dev_dbg(port->dev, "%s: mapped %d@%p to %pad\n", __func__,
- sg_dma_len(&atmel_port->sg_rx),
- ring->buf,
- &sg_dma_address(&atmel_port->sg_rx));
+ dev_dbg(port->dev, "%s: mapped %zu@%p to %pad\n", __func__,
+ ATMEL_SERIAL_RX_SIZE, ring->buf, &atmel_port->rx_phys);
}

/* Configure the slave DMA */
@@ -1245,9 +1233,9 @@ static int atmel_prepare_rx_dma(struct uart_port *port)
* each one is half ring buffer size
*/
desc = dmaengine_prep_dma_cyclic(atmel_port->chan_rx,
- sg_dma_address(&atmel_port->sg_rx),
- sg_dma_len(&atmel_port->sg_rx),
- sg_dma_len(&atmel_port->sg_rx)/2,
+ atmel_port->rx_phys,
+ ATMEL_SERIAL_RX_SIZE,
+ ATMEL_SERIAL_RX_SIZE / 2,
DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT);
if (!desc) {
--
2.44.0


2024-04-05 06:13:03

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 10/15] tty: 8250_omap: use dmaengine_prep_slave_sg()

This is a preparatory for the serial-to-kfifo switch. kfifo understands
only scatter-gatter approach, so switch to that.

No functional change intended, it's just dmaengine_prep_slave_single()
inline expanded.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/serial/8250/8250_omap.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index 66901d93089a..879e77b30701 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -1140,6 +1140,7 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
struct omap8250_priv *priv = p->port.private_data;
struct circ_buf *xmit = &p->port.state->xmit;
struct dma_async_tx_descriptor *desc;
+ struct scatterlist sg;
unsigned int skip_byte = 0;
int ret;

@@ -1191,9 +1192,11 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
skip_byte = 1;
}

- desc = dmaengine_prep_slave_single(dma->txchan,
- dma->tx_addr + xmit->tail + skip_byte,
- dma->tx_size - skip_byte, DMA_MEM_TO_DEV,
+ sg_init_table(&sg, 1);
+ sg_dma_address(&sg) = dma->tx_addr + xmit->tail + skip_byte;
+ sg_dma_len(&sg) = dma->tx_size - skip_byte;
+
+ desc = dmaengine_prep_slave_sg(dma->txchan, &sg, 1, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
ret = -EBUSY;
--
2.44.0


2024-04-05 06:13:15

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 12/15] tty: serial: switch from circ_buf to kfifo

Switch from struct circ_buf to proper kfifo. kfifo provides much better
API, esp. when wrap-around of the buffer needs to be taken into account.
Look at pl011_dma_tx_refill() or cpm_uart_tx_pump() changes for example.

Kfifo API can also fill in scatter-gather DMA structures, so it easier
for that use case too. Look at lpuart_dma_tx() for example. Note that
not all drivers can be converted to that (like atmel_serial), they
handle DMA specially.

Note that usb-serial uses kfifo for TX for ages.

omap needed a bit more care as it needs to put a char into FIFO to start
the DMA transfer when OMAP_DMA_TX_KICK is set. In that case, we have to
do kfifo_dma_out_prepare twice: once to find out the tx_size (to find
out if it is worths to do DMA at all -- size >= 4), the second time for
the actual transfer.

All traces of circ_buf are removed from serial_core.h (and its struct
uart_state).

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
Cc: Al Cooper <[email protected]>
Cc: Matthias Brugger <[email protected]>
Cc: AngeloGioacchino Del Regno <[email protected]>
Cc: Kumaravel Thiagarajan <[email protected]>
Cc: Tharun Kumar P <[email protected]>
Cc: Russell King <[email protected]>
Cc: Vineet Gupta <[email protected]>
Cc: Richard Genoud <[email protected]>
Cc: Nicolas Ferre <[email protected]>
Cc: Alexandre Belloni <[email protected]>
Cc: Claudiu Beznea <[email protected]>
Cc: Alexander Shiyan <[email protected]>
Cc: Baruch Siach <[email protected]>
Cc: "Maciej W. Rozycki" <[email protected]>
Cc: Shawn Guo <[email protected]>
Cc: Sascha Hauer <[email protected]>
Cc: Fabio Estevam <[email protected]>
Cc: Neil Armstrong <[email protected]>
Cc: Kevin Hilman <[email protected]>
Cc: Jerome Brunet <[email protected]>
Cc: Martin Blumenstingl <[email protected]>
Cc: Taichi Sugaya <[email protected]>
Cc: Takao Orito <[email protected]>
Cc: Bjorn Andersson <[email protected]>
Cc: Konrad Dybcio <[email protected]>
Cc: "Pali Rohár" <[email protected]>
Cc: Michael Ellerman <[email protected]>
Cc: Nicholas Piggin <[email protected]>
Cc: Christophe Leroy <[email protected]>
Cc: "Aneesh Kumar K.V" <[email protected]>
Cc: "Naveen N. Rao" <[email protected]>
Cc: Manivannan Sadhasivam <[email protected]>
Cc: Krzysztof Kozlowski <[email protected]>
Cc: Alim Akhtar <[email protected]>
Cc: Laxman Dewangan <[email protected]>
Cc: Thierry Reding <[email protected]>
Cc: Jonathan Hunter <[email protected]>
Cc: Orson Zhai <[email protected]>
Cc: Baolin Wang <[email protected]>
Cc: Chunyan Zhang <[email protected]>
Cc: Patrice Chotard <[email protected]>
Cc: Maxime Coquelin <[email protected]>
Cc: Alexandre Torgue <[email protected]>
Cc: "David S. Miller" <[email protected]>
Cc: Hammer Hsieh <[email protected]>
Cc: Peter Korsgaard <[email protected]>
Cc: Timur Tabi <[email protected]>
Cc: Michal Simek <[email protected]>
Cc: Sumit Semwal <[email protected]>
Cc: "Christian König" <[email protected]>
---
drivers/tty/serial/8250/8250_bcm7271.c | 14 +++---
drivers/tty/serial/8250/8250_core.c | 3 +-
drivers/tty/serial/8250/8250_dma.c | 23 ++++++----
drivers/tty/serial/8250/8250_exar.c | 5 ++-
drivers/tty/serial/8250/8250_mtk.c | 2 +-
drivers/tty/serial/8250/8250_omap.c | 47 +++++++++++++-------
drivers/tty/serial/8250/8250_pci1xxxx.c | 50 ++++++++++------------
drivers/tty/serial/8250/8250_port.c | 22 ++++++----
drivers/tty/serial/amba-pl011.c | 46 +++++++++-----------
drivers/tty/serial/ar933x_uart.c | 15 +++----
drivers/tty/serial/arc_uart.c | 8 ++--
drivers/tty/serial/atmel_serial.c | 57 +++++++++++++------------
drivers/tty/serial/clps711x.c | 12 +++---
drivers/tty/serial/cpm_uart.c | 20 +++------
drivers/tty/serial/digicolor-usart.c | 12 +++---
drivers/tty/serial/dz.c | 13 +++---
drivers/tty/serial/fsl_linflexuart.c | 17 ++++----
drivers/tty/serial/fsl_lpuart.c | 39 ++++++++---------
drivers/tty/serial/icom.c | 25 +++--------
drivers/tty/serial/imx.c | 54 ++++++++++-------------
drivers/tty/serial/ip22zilog.c | 26 ++++++-----
drivers/tty/serial/jsm/jsm_cls.c | 29 ++++---------
drivers/tty/serial/jsm/jsm_neo.c | 38 ++++++-----------
drivers/tty/serial/max3100.c | 14 +++---
drivers/tty/serial/max310x.c | 35 +++++++--------
drivers/tty/serial/men_z135_uart.c | 26 +++++------
drivers/tty/serial/meson_uart.c | 11 +++--
drivers/tty/serial/milbeaut_usio.c | 15 ++++---
drivers/tty/serial/msm_serial.c | 30 ++++++-------
drivers/tty/serial/mvebu-uart.c | 8 ++--
drivers/tty/serial/mxs-auart.c | 23 +++-------
drivers/tty/serial/pch_uart.c | 21 +++++----
drivers/tty/serial/pic32_uart.c | 15 +++----
drivers/tty/serial/pmac_zilog.c | 24 +++++------
drivers/tty/serial/qcom_geni_serial.c | 36 ++++++++--------
drivers/tty/serial/rda-uart.c | 17 +++-----
drivers/tty/serial/samsung_tty.c | 54 ++++++++++++-----------
drivers/tty/serial/sb1250-duart.c | 13 +++---
drivers/tty/serial/sc16is7xx.c | 40 +++++++----------
drivers/tty/serial/sccnxp.c | 16 ++++---
drivers/tty/serial/serial-tegra.c | 43 ++++++++++---------
drivers/tty/serial/serial_core.c | 56 +++++++++---------------
drivers/tty/serial/serial_port.c | 2 +-
drivers/tty/serial/sh-sci.c | 51 +++++++++++-----------
drivers/tty/serial/sprd_serial.c | 20 ++++-----
drivers/tty/serial/st-asc.c | 4 +-
drivers/tty/serial/stm32-usart.c | 52 +++++++++-------------
drivers/tty/serial/sunhv.c | 35 ++++++++-------
drivers/tty/serial/sunplus-uart.c | 16 ++++---
drivers/tty/serial/sunsab.c | 30 +++++++------
drivers/tty/serial/sunsu.c | 15 ++++---
drivers/tty/serial/sunzilog.c | 27 ++++++------
drivers/tty/serial/tegra-tcu.c | 10 +++--
drivers/tty/serial/timbuart.c | 17 +++-----
drivers/tty/serial/uartlite.c | 13 +++---
drivers/tty/serial/ucc_uart.c | 20 +++------
drivers/tty/serial/xilinx_uartps.c | 20 ++++-----
drivers/tty/serial/zs.c | 13 +++---
include/linux/serial_core.h | 49 +++++++++++++--------
59 files changed, 688 insertions(+), 780 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_bcm7271.c b/drivers/tty/serial/8250/8250_bcm7271.c
index 5daa38d9c64e..de270863eb5e 100644
--- a/drivers/tty/serial/8250/8250_bcm7271.c
+++ b/drivers/tty/serial/8250/8250_bcm7271.c
@@ -413,20 +413,18 @@ static int stop_tx_dma(struct uart_8250_port *p)
static int brcmuart_tx_dma(struct uart_8250_port *p)
{
struct brcmuart_priv *priv = p->port.private_data;
- struct circ_buf *xmit = &p->port.state->xmit;
+ struct tty_port *tport = &p->port.state->port;
u32 tx_size;

if (uart_tx_stopped(&p->port) || priv->tx_running ||
- uart_circ_empty(xmit)) {
+ kfifo_is_empty(&tport->xmit_fifo)) {
return 0;
}
- tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);

priv->dma.tx_err = 0;
- memcpy(priv->tx_buf, &xmit->buf[xmit->tail], tx_size);
- uart_xmit_advance(&p->port, tx_size);
+ tx_size = uart_fifo_out(&p->port, priv->tx_buf, UART_XMIT_SIZE);

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&p->port);

udma_writel(priv, REGS_DMA_TX, UDMA_TX_TRANSFER_LEN, tx_size);
@@ -540,7 +538,7 @@ static void brcmuart_tx_isr(struct uart_port *up, u32 isr)
struct brcmuart_priv *priv = up->private_data;
struct device *dev = up->dev;
struct uart_8250_port *port_8250 = up_to_u8250p(up);
- struct circ_buf *xmit = &port_8250->port.state->xmit;
+ struct tty_port *tport = &port_8250->port.state->port;

if (isr & UDMA_INTR_TX_ABORT) {
if (priv->tx_running)
@@ -548,7 +546,7 @@ static void brcmuart_tx_isr(struct uart_port *up, u32 isr)
return;
}
priv->tx_running = false;
- if (!uart_circ_empty(xmit) && !uart_tx_stopped(up))
+ if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(up))
brcmuart_tx_dma(port_8250);
}

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index b62ad9006780..3a1936b7f05f 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -280,7 +280,8 @@ static void serial8250_backup_timeout(struct timer_list *t)
*/
lsr = serial_lsr_in(up);
if ((iir & UART_IIR_NO_INT) && (up->ier & UART_IER_THRI) &&
- (!uart_circ_empty(&up->port.state->xmit) || up->port.x_char) &&
+ (!kfifo_is_empty(&up->port.state->port.xmit_fifo) ||
+ up->port.x_char) &&
(lsr & UART_LSR_THRE)) {
iir &= ~(UART_IIR_ID | UART_IIR_NO_INT);
iir |= UART_IIR_THRI;
diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index 8b2c3f478b17..8a353e3cc3dd 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -15,7 +15,7 @@ static void __dma_tx_complete(void *param)
{
struct uart_8250_port *p = param;
struct uart_8250_dma *dma = p->dma;
- struct circ_buf *xmit = &p->port.state->xmit;
+ struct tty_port *tport = &p->port.state->port;
unsigned long flags;
int ret;

@@ -28,7 +28,7 @@ static void __dma_tx_complete(void *param)

uart_xmit_advance(&p->port, dma->tx_size);

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&p->port);

ret = serial8250_tx_dma(p);
@@ -86,7 +86,7 @@ static void dma_rx_complete(void *param)
int serial8250_tx_dma(struct uart_8250_port *p)
{
struct uart_8250_dma *dma = p->dma;
- struct circ_buf *xmit = &p->port.state->xmit;
+ struct tty_port *tport = &p->port.state->port;
struct dma_async_tx_descriptor *desc;
struct uart_port *up = &p->port;
struct scatterlist sg;
@@ -103,18 +103,23 @@ int serial8250_tx_dma(struct uart_8250_port *p)
uart_xchar_out(up, UART_TX);
}

- if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) {
+ if (uart_tx_stopped(&p->port) || kfifo_is_empty(&tport->xmit_fifo)) {
/* We have been called from __dma_tx_complete() */
return 0;
}

- dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
-
serial8250_do_prepare_tx_dma(p);

sg_init_table(&sg, 1);
- sg_dma_address(&sg) = dma->tx_addr + xmit->tail;
- sg_dma_len(&sg) = dma->tx_size;
+ /* kfifo can do more than one sg, we don't (quite yet) */
+ ret = kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, &sg, 1,
+ UART_XMIT_SIZE, dma->tx_addr);
+
+ /* we already checked empty fifo above, so there should be something */
+ if (WARN_ON_ONCE(ret != 1))
+ return 0;
+
+ dma->tx_size = sg_dma_len(&sg);

desc = dmaengine_prep_slave_sg(dma->txchan, &sg, 1,
DMA_MEM_TO_DEV,
@@ -257,7 +262,7 @@ int serial8250_request_dma(struct uart_8250_port *p)

/* TX buffer */
dma->tx_addr = dma_map_single(dma->txchan->device->dev,
- p->port.state->xmit.buf,
+ p->port.state->port.xmit_buf,
UART_XMIT_SIZE,
DMA_TO_DEVICE);
if (dma_mapping_error(dma->txchan->device->dev, dma->tx_addr)) {
diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c
index 0440df7de1ed..f89dabfc0f97 100644
--- a/drivers/tty/serial/8250/8250_exar.c
+++ b/drivers/tty/serial/8250/8250_exar.c
@@ -214,7 +214,7 @@ static void exar_shutdown(struct uart_port *port)
{
bool tx_complete = false;
struct uart_8250_port *up = up_to_u8250p(port);
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
int i = 0;
u16 lsr;

@@ -225,7 +225,8 @@ static void exar_shutdown(struct uart_port *port)
else
tx_complete = false;
usleep_range(1000, 1100);
- } while (!uart_circ_empty(xmit) && !tx_complete && i++ < 1000);
+ } while (!kfifo_is_empty(&tport->xmit_fifo) &&
+ !tx_complete && i++ < 1000);

serial8250_do_shutdown(port);
}
diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c
index 9ff6bbe9c086..c365a349421a 100644
--- a/drivers/tty/serial/8250/8250_mtk.c
+++ b/drivers/tty/serial/8250/8250_mtk.c
@@ -199,7 +199,7 @@ static int mtk8250_startup(struct uart_port *port)

if (up->dma) {
data->rx_status = DMA_RX_START;
- uart_circ_clear(&port->state->xmit);
+ kfifo_reset(&port->state->port.xmit_fifo);
}
#endif
memset(&port->icount, 0, sizeof(port->icount));
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index 879e77b30701..c07d924d5add 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -1094,7 +1094,7 @@ static void omap_8250_dma_tx_complete(void *param)
{
struct uart_8250_port *p = param;
struct uart_8250_dma *dma = p->dma;
- struct circ_buf *xmit = &p->port.state->xmit;
+ struct tty_port *tport = &p->port.state->port;
unsigned long flags;
bool en_thri = false;
struct omap8250_priv *priv = p->port.private_data;
@@ -1113,10 +1113,10 @@ static void omap_8250_dma_tx_complete(void *param)
omap8250_restore_regs(p);
}

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&p->port);

- if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port)) {
+ if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(&p->port)) {
int ret;

ret = omap_8250_tx_dma(p);
@@ -1138,15 +1138,15 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
{
struct uart_8250_dma *dma = p->dma;
struct omap8250_priv *priv = p->port.private_data;
- struct circ_buf *xmit = &p->port.state->xmit;
+ struct tty_port *tport = &p->port.state->port;
struct dma_async_tx_descriptor *desc;
struct scatterlist sg;
- unsigned int skip_byte = 0;
+ int skip_byte = -1;
int ret;

if (dma->tx_running)
return 0;
- if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) {
+ if (uart_tx_stopped(&p->port) || kfifo_is_empty(&tport->xmit_fifo)) {

/*
* Even if no data, we need to return an error for the two cases
@@ -1161,8 +1161,18 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
return 0;
}

- dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+ sg_init_table(&sg, 1);
+ ret = kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, &sg, 1,
+ UART_XMIT_SIZE, dma->tx_addr);
+ if (ret != 1) {
+ serial8250_clear_THRI(p);
+ return 0;
+ }
+
+ dma->tx_size = sg_dma_len(&sg);
+
if (priv->habit & OMAP_DMA_TX_KICK) {
+ unsigned char c;
u8 tx_lvl;

/*
@@ -1189,13 +1199,16 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
ret = -EINVAL;
goto err;
}
- skip_byte = 1;
+ if (!kfifo_get(&tport->xmit_fifo, &c)) {
+ ret = -EINVAL;
+ goto err;
+ }
+ skip_byte = c;
+ /* now we need to recompute due to kfifo_get */
+ kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, &sg, 1,
+ UART_XMIT_SIZE, dma->tx_addr);
}

- sg_init_table(&sg, 1);
- sg_dma_address(&sg) = dma->tx_addr + xmit->tail + skip_byte;
- sg_dma_len(&sg) = dma->tx_size - skip_byte;
-
desc = dmaengine_prep_slave_sg(dma->txchan, &sg, 1, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
@@ -1218,11 +1231,13 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
dma->tx_err = 0;

serial8250_clear_THRI(p);
- if (skip_byte)
- serial_out(p, UART_TX, xmit->buf[xmit->tail]);
- return 0;
+ ret = 0;
+ goto out_skip;
err:
dma->tx_err = 1;
+out_skip:
+ if (skip_byte >= 0)
+ serial_out(p, UART_TX, skip_byte);
return ret;
}

@@ -1311,7 +1326,7 @@ static int omap_8250_dma_handle_irq(struct uart_port *port)
serial8250_modem_status(up);
if (status & UART_LSR_THRE && up->dma->tx_err) {
if (uart_tx_stopped(&up->port) ||
- uart_circ_empty(&up->port.state->xmit)) {
+ kfifo_is_empty(&up->port.state->port.xmit_fifo)) {
up->dma->tx_err = 0;
serial8250_tx_chars(up);
} else {
diff --git a/drivers/tty/serial/8250/8250_pci1xxxx.c b/drivers/tty/serial/8250/8250_pci1xxxx.c
index 2fbb5851f788..d3930bf32fe4 100644
--- a/drivers/tty/serial/8250/8250_pci1xxxx.c
+++ b/drivers/tty/serial/8250/8250_pci1xxxx.c
@@ -382,10 +382,10 @@ static void pci1xxxx_rx_burst(struct uart_port *port, u32 uart_status)
}

static void pci1xxxx_process_write_data(struct uart_port *port,
- struct circ_buf *xmit,
int *data_empty_count,
u32 *valid_byte_count)
{
+ struct tty_port *tport = &port->state->port;
u32 valid_burst_count = *valid_byte_count / UART_BURST_SIZE;

/*
@@ -395,41 +395,36 @@ static void pci1xxxx_process_write_data(struct uart_port *port,
* one byte at a time.
*/
while (valid_burst_count) {
+ u32 c;
+
if (*data_empty_count - UART_BURST_SIZE < 0)
break;
- if (xmit->tail > (UART_XMIT_SIZE - UART_BURST_SIZE))
+ if (kfifo_len(&tport->xmit_fifo) < UART_BURST_SIZE)
+ break;
+ if (WARN_ON(kfifo_out(&tport->xmit_fifo, (u8 *)&c, sizeof(c)) !=
+ sizeof(c)))
break;
- writel(*(unsigned int *)&xmit->buf[xmit->tail],
- port->membase + UART_TX_BURST_FIFO);
+ writel(c, port->membase + UART_TX_BURST_FIFO);
*valid_byte_count -= UART_BURST_SIZE;
*data_empty_count -= UART_BURST_SIZE;
valid_burst_count -= UART_BYTE_SIZE;
-
- xmit->tail = (xmit->tail + UART_BURST_SIZE) &
- (UART_XMIT_SIZE - 1);
}

while (*valid_byte_count) {
- if (*data_empty_count - UART_BYTE_SIZE < 0)
+ u8 c;
+
+ if (!kfifo_get(&tport->xmit_fifo, &c))
break;
- writeb(xmit->buf[xmit->tail], port->membase +
- UART_TX_BYTE_FIFO);
+ writeb(c, port->membase + UART_TX_BYTE_FIFO);
*data_empty_count -= UART_BYTE_SIZE;
*valid_byte_count -= UART_BYTE_SIZE;

- /*
- * When the tail of the circular buffer is reached, the next
- * byte is transferred to the beginning of the buffer.
- */
- xmit->tail = (xmit->tail + UART_BYTE_SIZE) &
- (UART_XMIT_SIZE - 1);
-
/*
* If there are any pending burst count, data is handled by
* transmitting DWORDs at a time.
*/
- if (valid_burst_count && (xmit->tail <
- (UART_XMIT_SIZE - UART_BURST_SIZE)))
+ if (valid_burst_count &&
+ kfifo_len(&tport->xmit_fifo) >= UART_BURST_SIZE)
break;
}
}
@@ -437,11 +432,9 @@ static void pci1xxxx_process_write_data(struct uart_port *port,
static void pci1xxxx_tx_burst(struct uart_port *port, u32 uart_status)
{
struct uart_8250_port *up = up_to_u8250p(port);
+ struct tty_port *tport = &port->state->port;
u32 valid_byte_count;
int data_empty_count;
- struct circ_buf *xmit;
-
- xmit = &port->state->xmit;

if (port->x_char) {
writeb(port->x_char, port->membase + UART_TX);
@@ -450,25 +443,25 @@ static void pci1xxxx_tx_burst(struct uart_port *port, u32 uart_status)
return;
}

- if ((uart_tx_stopped(port)) || (uart_circ_empty(xmit))) {
+ if ((uart_tx_stopped(port)) || kfifo_is_empty(&tport->xmit_fifo)) {
port->ops->stop_tx(port);
} else {
data_empty_count = (pci1xxxx_read_burst_status(port) &
UART_BST_STAT_TX_COUNT_MASK) >> 8;
do {
- valid_byte_count = uart_circ_chars_pending(xmit);
+ valid_byte_count = kfifo_len(&tport->xmit_fifo);

- pci1xxxx_process_write_data(port, xmit,
+ pci1xxxx_process_write_data(port,
&data_empty_count,
&valid_byte_count);

port->icount.tx++;
- if (uart_circ_empty(xmit))
+ if (kfifo_is_empty(&tport->xmit_fifo))
break;
} while (data_empty_count && valid_byte_count);
}

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);

/*
@@ -476,7 +469,8 @@ static void pci1xxxx_tx_burst(struct uart_port *port, u32 uart_status)
* the HW can go idle. So we get here once again with empty FIFO and
* disable the interrupt and RPM in __stop_tx()
*/
- if (uart_circ_empty(xmit) && !(up->capabilities & UART_CAP_RPM))
+ if (kfifo_is_empty(&tport->xmit_fifo) &&
+ !(up->capabilities & UART_CAP_RPM))
port->ops->stop_tx(port);
}

diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index fc9dd5d45295..0c19451d0332 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -1630,7 +1630,7 @@ static void serial8250_start_tx(struct uart_port *port)
/* Port locked to synchronize UART_IER access against the console. */
lockdep_assert_held_once(&port->lock);

- if (!port->x_char && uart_circ_empty(&port->state->xmit))
+ if (!port->x_char && kfifo_is_empty(&port->state->port.xmit_fifo))
return;

serial8250_rpm_get_tx(up);
@@ -1778,7 +1778,7 @@ EXPORT_SYMBOL_GPL(serial8250_rx_chars);
void serial8250_tx_chars(struct uart_8250_port *up)
{
struct uart_port *port = &up->port;
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
int count;

if (port->x_char) {
@@ -1789,14 +1789,19 @@ void serial8250_tx_chars(struct uart_8250_port *up)
serial8250_stop_tx(port);
return;
}
- if (uart_circ_empty(xmit)) {
+ if (kfifo_is_empty(&tport->xmit_fifo)) {
__stop_tx(up);
return;
}

count = up->tx_loadsz;
do {
- serial_out(up, UART_TX, xmit->buf[xmit->tail]);
+ unsigned char c;
+
+ if (!uart_fifo_get(port, &c))
+ break;
+
+ serial_out(up, UART_TX, c);
if (up->bugs & UART_BUG_TXRACE) {
/*
* The Aspeed BMC virtual UARTs have a bug where data
@@ -1809,9 +1814,7 @@ void serial8250_tx_chars(struct uart_8250_port *up)
*/
serial_in(up, UART_SCR);
}
- uart_xmit_advance(port, 1);
- if (uart_circ_empty(xmit))
- break;
+
if ((up->capabilities & UART_CAP_HFIFO) &&
!uart_lsr_tx_empty(serial_in(up, UART_LSR)))
break;
@@ -1821,7 +1824,7 @@ void serial8250_tx_chars(struct uart_8250_port *up)
break;
} while (--count > 0);

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);

/*
@@ -1829,7 +1832,8 @@ void serial8250_tx_chars(struct uart_8250_port *up)
* HW can go idle. So we get here once again with empty FIFO and disable
* the interrupt and RPM in __stop_tx()
*/
- if (uart_circ_empty(xmit) && !(up->capabilities & UART_CAP_RPM))
+ if (kfifo_is_empty(&tport->xmit_fifo) &&
+ !(up->capabilities & UART_CAP_RPM))
__stop_tx(up);
}
EXPORT_SYMBOL_GPL(serial8250_tx_chars);
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 2fa3fb30dc6c..51b202eb8296 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -535,6 +535,7 @@ static void pl011_start_tx_pio(struct uart_amba_port *uap);
static void pl011_dma_tx_callback(void *data)
{
struct uart_amba_port *uap = data;
+ struct tty_port *tport = &uap->port.state->port;
struct pl011_dmatx_data *dmatx = &uap->dmatx;
unsigned long flags;
u16 dmacr;
@@ -558,7 +559,7 @@ static void pl011_dma_tx_callback(void *data)
* get further refills (hence we check dmacr).
*/
if (!(dmacr & UART011_TXDMAE) || uart_tx_stopped(&uap->port) ||
- uart_circ_empty(&uap->port.state->xmit)) {
+ kfifo_is_empty(&tport->xmit_fifo)) {
uap->dmatx.queued = false;
uart_port_unlock_irqrestore(&uap->port, flags);
return;
@@ -588,7 +589,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap)
struct dma_chan *chan = dmatx->chan;
struct dma_device *dma_dev = chan->device;
struct dma_async_tx_descriptor *desc;
- struct circ_buf *xmit = &uap->port.state->xmit;
+ struct tty_port *tport = &uap->port.state->port;
unsigned int count;

/*
@@ -597,7 +598,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap)
* the standard interrupt handling. This ensures that we
* issue a uart_write_wakeup() at the appropriate time.
*/
- count = uart_circ_chars_pending(xmit);
+ count = kfifo_len(&tport->xmit_fifo);
if (count < (uap->fifosize >> 1)) {
uap->dmatx.queued = false;
return 0;
@@ -613,21 +614,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap)
if (count > PL011_DMA_BUFFER_SIZE)
count = PL011_DMA_BUFFER_SIZE;

- if (xmit->tail < xmit->head) {
- memcpy(&dmatx->buf[0], &xmit->buf[xmit->tail], count);
- } else {
- size_t first = UART_XMIT_SIZE - xmit->tail;
- size_t second;
-
- if (first > count)
- first = count;
- second = count - first;
-
- memcpy(&dmatx->buf[0], &xmit->buf[xmit->tail], first);
- if (second)
- memcpy(&dmatx->buf[first], &xmit->buf[0], second);
- }
-
+ count = kfifo_out_peek(&tport->xmit_fifo, dmatx->buf, count);
dmatx->len = count;
dmatx->dma = dma_map_single(dma_dev->dev, dmatx->buf, count,
DMA_TO_DEVICE);
@@ -670,7 +657,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap)
*/
uart_xmit_advance(&uap->port, count);

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&uap->port);

return 1;
@@ -1454,7 +1441,7 @@ static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c,
/* Returns true if tx interrupts have to be (kept) enabled */
static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq)
{
- struct circ_buf *xmit = &uap->port.state->xmit;
+ struct tty_port *tport = &uap->port.state->port;
int count = uap->fifosize >> 1;

if (uap->port.x_char) {
@@ -1463,7 +1450,7 @@ static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq)
uap->port.x_char = 0;
--count;
}
- if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) {
+ if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(&uap->port)) {
pl011_stop_tx(&uap->port);
return false;
}
@@ -1472,20 +1459,25 @@ static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq)
if (pl011_dma_tx_irq(uap))
return true;

- do {
+ while (1) {
+ unsigned char c;
+
if (likely(from_irq) && count-- == 0)
break;

- if (!pl011_tx_char(uap, xmit->buf[xmit->tail], from_irq))
+ if (!kfifo_peek(&tport->xmit_fifo, &c))
+ break;
+
+ if (!pl011_tx_char(uap, c, from_irq))
break;

- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- } while (!uart_circ_empty(xmit));
+ kfifo_skip(&tport->xmit_fifo);
+ }

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&uap->port);

- if (uart_circ_empty(xmit)) {
+ if (kfifo_is_empty(&tport->xmit_fifo)) {
pl011_stop_tx(&uap->port);
return false;
}
diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c
index 7790cbc57391..f2836ecc5c19 100644
--- a/drivers/tty/serial/ar933x_uart.c
+++ b/drivers/tty/serial/ar933x_uart.c
@@ -390,7 +390,7 @@ static void ar933x_uart_rx_chars(struct ar933x_uart_port *up)

static void ar933x_uart_tx_chars(struct ar933x_uart_port *up)
{
- struct circ_buf *xmit = &up->port.state->xmit;
+ struct tty_port *tport = &up->port.state->port;
struct serial_rs485 *rs485conf = &up->port.rs485;
int count;
bool half_duplex_send = false;
@@ -399,7 +399,7 @@ static void ar933x_uart_tx_chars(struct ar933x_uart_port *up)
return;

if ((rs485conf->flags & SER_RS485_ENABLED) &&
- (up->port.x_char || !uart_circ_empty(xmit))) {
+ (up->port.x_char || !kfifo_is_empty(&tport->xmit_fifo))) {
ar933x_uart_stop_rx_interrupt(up);
gpiod_set_value(up->rts_gpiod, !!(rs485conf->flags & SER_RS485_RTS_ON_SEND));
half_duplex_send = true;
@@ -408,6 +408,7 @@ static void ar933x_uart_tx_chars(struct ar933x_uart_port *up)
count = up->port.fifosize;
do {
unsigned int rdata;
+ unsigned char c;

rdata = ar933x_uart_read(up, AR933X_UART_DATA_REG);
if ((rdata & AR933X_UART_DATA_TX_CSR) == 0)
@@ -420,18 +421,16 @@ static void ar933x_uart_tx_chars(struct ar933x_uart_port *up)
continue;
}

- if (uart_circ_empty(xmit))
+ if (!uart_fifo_get(&up->port, &c))
break;

- ar933x_uart_putc(up, xmit->buf[xmit->tail]);
-
- uart_xmit_advance(&up->port, 1);
+ ar933x_uart_putc(up, c);
} while (--count > 0);

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&up->port);

- if (!uart_circ_empty(xmit)) {
+ if (!kfifo_is_empty(&tport->xmit_fifo)) {
ar933x_uart_start_tx_interrupt(up);
} else if (half_duplex_send) {
ar933x_uart_wait_tx_complete(up);
diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c
index 1aa5b2b49c26..5c4895d154c0 100644
--- a/drivers/tty/serial/arc_uart.c
+++ b/drivers/tty/serial/arc_uart.c
@@ -155,7 +155,7 @@ static unsigned int arc_serial_tx_empty(struct uart_port *port)
*/
static void arc_serial_tx_chars(struct uart_port *port)
{
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
int sent = 0;
unsigned char ch;

@@ -164,9 +164,7 @@ static void arc_serial_tx_chars(struct uart_port *port)
port->icount.tx++;
port->x_char = 0;
sent = 1;
- } else if (!uart_circ_empty(xmit)) {
- ch = xmit->buf[xmit->tail];
- uart_xmit_advance(port, 1);
+ } else if (uart_fifo_get(port, &ch)) {
while (!(UART_GET_STATUS(port) & TXEMPTY))
cpu_relax();
UART_SET_DATA(port, ch);
@@ -177,7 +175,7 @@ static void arc_serial_tx_chars(struct uart_port *port)
* If num chars in xmit buffer are too few, ask tty layer for more.
* By Hard ISR to schedule processing in software interrupt part
*/
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);

if (sent)
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 85667f709515..5bb5e4303754 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -857,7 +857,7 @@ static void atmel_complete_tx_dma(void *arg)
{
struct atmel_uart_port *atmel_port = arg;
struct uart_port *port = &atmel_port->uart;
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
struct dma_chan *chan = atmel_port->chan_tx;
unsigned long flags;

@@ -873,15 +873,15 @@ static void atmel_complete_tx_dma(void *arg)
atmel_port->desc_tx = NULL;
spin_unlock(&atmel_port->lock_tx);

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);

/*
- * xmit is a circular buffer so, if we have just send data from
- * xmit->tail to the end of xmit->buf, now we have to transmit the
- * remaining data from the beginning of xmit->buf to xmit->head.
+ * xmit is a circular buffer so, if we have just send data from the
+ * tail to the end, now we have to transmit the remaining data from the
+ * beginning to the head.
*/
- if (!uart_circ_empty(xmit))
+ if (!kfifo_is_empty(&tport->xmit_fifo))
atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_tx);
else if (atmel_uart_is_half_duplex(port)) {
/*
@@ -919,18 +919,18 @@ static void atmel_release_tx_dma(struct uart_port *port)
static void atmel_tx_dma(struct uart_port *port)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
struct dma_chan *chan = atmel_port->chan_tx;
struct dma_async_tx_descriptor *desc;
struct scatterlist sgl[2], *sg, *sg_tx = &atmel_port->sg_tx;
- unsigned int tx_len, part1_len, part2_len, sg_len;
+ unsigned int tx_len, tail, part1_len, part2_len, sg_len;
dma_addr_t phys_addr;

/* Make sure we have an idle channel */
if (atmel_port->desc_tx != NULL)
return;

- if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) {
+ if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(port)) {
/*
* DMA is idle now.
* Port xmit buffer is already mapped,
@@ -940,9 +940,8 @@ static void atmel_tx_dma(struct uart_port *port)
* Take the port lock to get a
* consistent xmit buffer state.
*/
- tx_len = CIRC_CNT_TO_END(xmit->head,
- xmit->tail,
- UART_XMIT_SIZE);
+ tx_len = kfifo_out_linear(&tport->xmit_fifo, &tail,
+ UART_XMIT_SIZE);

if (atmel_port->fifo_size) {
/* multi data mode */
@@ -956,7 +955,7 @@ static void atmel_tx_dma(struct uart_port *port)

sg_init_table(sgl, 2);
sg_len = 0;
- phys_addr = sg_dma_address(sg_tx) + xmit->tail;
+ phys_addr = sg_dma_address(sg_tx) + tail;
if (part1_len) {
sg = &sgl[sg_len++];
sg_dma_address(sg) = phys_addr;
@@ -973,7 +972,7 @@ static void atmel_tx_dma(struct uart_port *port)

/*
* save tx_len so atmel_complete_tx_dma() will increase
- * xmit->tail correctly
+ * tail correctly
*/
atmel_port->tx_len = tx_len;

@@ -1003,13 +1002,14 @@ static void atmel_tx_dma(struct uart_port *port)
dma_async_issue_pending(chan);
}

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);
}

static int atmel_prepare_tx_dma(struct uart_port *port)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ struct tty_port *tport = &port->state->port;
struct device *mfd_dev = port->dev->parent;
dma_cap_mask_t mask;
struct dma_slave_config config;
@@ -1031,11 +1031,11 @@ static int atmel_prepare_tx_dma(struct uart_port *port)
spin_lock_init(&atmel_port->lock_tx);
sg_init_table(&atmel_port->sg_tx, 1);
/* UART circular tx buffer is an aligned page. */
- BUG_ON(!PAGE_ALIGNED(port->state->xmit.buf));
+ BUG_ON(!PAGE_ALIGNED(tport->xmit_buf));
sg_set_page(&atmel_port->sg_tx,
- virt_to_page(port->state->xmit.buf),
+ virt_to_page(tport->xmit_buf),
UART_XMIT_SIZE,
- offset_in_page(port->state->xmit.buf));
+ offset_in_page(tport->xmit_buf));
nent = dma_map_sg(port->dev,
&atmel_port->sg_tx,
1,
@@ -1047,7 +1047,7 @@ static int atmel_prepare_tx_dma(struct uart_port *port)
} else {
dev_dbg(port->dev, "%s: mapped %d@%p to %pad\n", __func__,
sg_dma_len(&atmel_port->sg_tx),
- port->state->xmit.buf,
+ tport->xmit_buf,
&sg_dma_address(&atmel_port->sg_tx));
}

@@ -1459,9 +1459,8 @@ static void atmel_release_tx_pdc(struct uart_port *port)
static void atmel_tx_pdc(struct uart_port *port)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
- int count;

/* nothing left to transmit? */
if (atmel_uart_readl(port, ATMEL_PDC_TCR))
@@ -1474,17 +1473,19 @@ static void atmel_tx_pdc(struct uart_port *port)
/* disable PDC transmit */
atmel_uart_writel(port, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS);

- if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) {
+ if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(port)) {
+ unsigned int count, tail;
+
dma_sync_single_for_device(port->dev,
pdc->dma_addr,
pdc->dma_size,
DMA_TO_DEVICE);

- count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+ count = kfifo_out_linear(&tport->xmit_fifo, &tail,
+ UART_XMIT_SIZE);
pdc->ofs = count;

- atmel_uart_writel(port, ATMEL_PDC_TPR,
- pdc->dma_addr + xmit->tail);
+ atmel_uart_writel(port, ATMEL_PDC_TPR, pdc->dma_addr + tail);
atmel_uart_writel(port, ATMEL_PDC_TCR, count);
/* re-enable PDC transmit */
atmel_uart_writel(port, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN);
@@ -1498,7 +1499,7 @@ static void atmel_tx_pdc(struct uart_port *port)
}
}

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);
}

@@ -1506,9 +1507,9 @@ static int atmel_prepare_tx_pdc(struct uart_port *port)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;

- pdc->buf = xmit->buf;
+ pdc->buf = tport->xmit_buf;
pdc->dma_addr = dma_map_single(port->dev,
pdc->buf,
UART_XMIT_SIZE,
diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c
index 7927725b8957..30425a3d19fb 100644
--- a/drivers/tty/serial/clps711x.c
+++ b/drivers/tty/serial/clps711x.c
@@ -146,7 +146,8 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id)
{
struct uart_port *port = dev_id;
struct clps711x_port *s = dev_get_drvdata(port->dev);
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
+ unsigned char c;

if (port->x_char) {
writew(port->x_char, port->membase + UARTDR_OFFSET);
@@ -155,7 +156,7 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id)
return IRQ_HANDLED;
}

- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
if (s->tx_enabled) {
disable_irq_nosync(port->irq);
s->tx_enabled = 0;
@@ -163,18 +164,17 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id)
return IRQ_HANDLED;
}

- while (!uart_circ_empty(xmit)) {
+ while (uart_fifo_get(port, &c)) {
u32 sysflg = 0;

- writew(xmit->buf[xmit->tail], port->membase + UARTDR_OFFSET);
- uart_xmit_advance(port, 1);
+ writew(c, port->membase + UARTDR_OFFSET);

regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);
if (sysflg & SYSFLG_UTXFF)
break;
}

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);

return IRQ_HANDLED;
diff --git a/drivers/tty/serial/cpm_uart.c b/drivers/tty/serial/cpm_uart.c
index df56c6c5afd0..a927478f581d 100644
--- a/drivers/tty/serial/cpm_uart.c
+++ b/drivers/tty/serial/cpm_uart.c
@@ -648,7 +648,7 @@ static int cpm_uart_tx_pump(struct uart_port *port)
int count;
struct uart_cpm_port *pinfo =
container_of(port, struct uart_cpm_port, port);
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;

/* Handle xon/xoff */
if (port->x_char) {
@@ -673,7 +673,7 @@ static int cpm_uart_tx_pump(struct uart_port *port)
return 1;
}

- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
cpm_uart_stop_tx(port);
return 0;
}
@@ -681,16 +681,10 @@ static int cpm_uart_tx_pump(struct uart_port *port)
/* Pick next descriptor and fill from buffer */
bdp = pinfo->tx_cur;

- while (!(in_be16(&bdp->cbd_sc) & BD_SC_READY) && !uart_circ_empty(xmit)) {
- count = 0;
+ while (!(in_be16(&bdp->cbd_sc) & BD_SC_READY) &&
+ !kfifo_is_empty(&tport->xmit_fifo)) {
p = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr), pinfo);
- while (count < pinfo->tx_fifosize) {
- *p++ = xmit->buf[xmit->tail];
- uart_xmit_advance(port, 1);
- count++;
- if (uart_circ_empty(xmit))
- break;
- }
+ count = uart_fifo_out(port, p, pinfo->tx_fifosize);
out_be16(&bdp->cbd_datlen, count);
setbits16(&bdp->cbd_sc, BD_SC_READY);
/* Get next BD. */
@@ -701,10 +695,10 @@ static int cpm_uart_tx_pump(struct uart_port *port)
}
pinfo->tx_cur = bdp;

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);

- if (uart_circ_empty(xmit)) {
+ if (kfifo_is_empty(&tport->xmit_fifo)) {
cpm_uart_stop_tx(port);
return 0;
}
diff --git a/drivers/tty/serial/digicolor-usart.c b/drivers/tty/serial/digicolor-usart.c
index e419c4bde8b7..2ccd13cc0a89 100644
--- a/drivers/tty/serial/digicolor-usart.c
+++ b/drivers/tty/serial/digicolor-usart.c
@@ -179,8 +179,9 @@ static void digicolor_uart_rx(struct uart_port *port)

static void digicolor_uart_tx(struct uart_port *port)
{
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
unsigned long flags;
+ unsigned char c;

if (digicolor_uart_tx_full(port))
return;
@@ -194,20 +195,19 @@ static void digicolor_uart_tx(struct uart_port *port)
goto out;
}

- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
digicolor_uart_stop_tx(port);
goto out;
}

- while (!uart_circ_empty(xmit)) {
- writeb(xmit->buf[xmit->tail], port->membase + UA_EMI_REC);
- uart_xmit_advance(port, 1);
+ while (uart_fifo_get(port, &c)) {
+ writeb(c, port->membase + UA_EMI_REC);

if (digicolor_uart_tx_full(port))
break;
}

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);

out:
diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c
index 6df7af9edc1c..eba91daedef8 100644
--- a/drivers/tty/serial/dz.c
+++ b/drivers/tty/serial/dz.c
@@ -252,13 +252,13 @@ static inline void dz_receive_chars(struct dz_mux *mux)
static inline void dz_transmit_chars(struct dz_mux *mux)
{
struct dz_port *dport = &mux->dport[0];
- struct circ_buf *xmit;
+ struct tty_port *tport;
unsigned char tmp;
u16 status;

status = dz_in(dport, DZ_CSR);
dport = &mux->dport[LINE(status)];
- xmit = &dport->port.state->xmit;
+ tport = &dport->port.state->port;

if (dport->port.x_char) { /* XON/XOFF chars */
dz_out(dport, DZ_TDR, dport->port.x_char);
@@ -267,7 +267,8 @@ static inline void dz_transmit_chars(struct dz_mux *mux)
return;
}
/* If nothing to do or stopped or hardware stopped. */
- if (uart_circ_empty(xmit) || uart_tx_stopped(&dport->port)) {
+ if (uart_tx_stopped(&dport->port) ||
+ !uart_fifo_get(&dport->port, &tmp)) {
uart_port_lock(&dport->port);
dz_stop_tx(&dport->port);
uart_port_unlock(&dport->port);
@@ -278,15 +279,13 @@ static inline void dz_transmit_chars(struct dz_mux *mux)
* If something to do... (remember the dz has no output fifo,
* so we go one char at a time) :-<
*/
- tmp = xmit->buf[xmit->tail];
dz_out(dport, DZ_TDR, tmp);
- uart_xmit_advance(&dport->port, 1);

- if (uart_circ_chars_pending(xmit) < DZ_WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < DZ_WAKEUP_CHARS)
uart_write_wakeup(&dport->port);

/* Are we are done. */
- if (uart_circ_empty(xmit)) {
+ if (kfifo_is_empty(&tport->xmit_fifo)) {
uart_port_lock(&dport->port);
dz_stop_tx(&dport->port);
uart_port_unlock(&dport->port);
diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c
index 5426322b5f0c..e972df4b188d 100644
--- a/drivers/tty/serial/fsl_linflexuart.c
+++ b/drivers/tty/serial/fsl_linflexuart.c
@@ -174,17 +174,18 @@ static void linflex_put_char(struct uart_port *sport, unsigned char c)

static inline void linflex_transmit_buffer(struct uart_port *sport)
{
- struct circ_buf *xmit = &sport->state->xmit;
+ struct tty_port *tport = &sport->state->port;
+ unsigned char c;

- while (!uart_circ_empty(xmit)) {
- linflex_put_char(sport, xmit->buf[xmit->tail]);
- uart_xmit_advance(sport, 1);
+ while (uart_fifo_get(sport, &c)) {
+ linflex_put_char(sport, c);
+ sport->icount.tx++;
}

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(sport);

- if (uart_circ_empty(xmit))
+ if (kfifo_is_empty(&tport->xmit_fifo))
linflex_stop_tx(sport);
}

@@ -200,7 +201,7 @@ static void linflex_start_tx(struct uart_port *port)
static irqreturn_t linflex_txint(int irq, void *dev_id)
{
struct uart_port *sport = dev_id;
- struct circ_buf *xmit = &sport->state->xmit;
+ struct tty_port *tport = &sport->state->port;
unsigned long flags;

uart_port_lock_irqsave(sport, &flags);
@@ -210,7 +211,7 @@ static irqreturn_t linflex_txint(int irq, void *dev_id)
goto out;
}

- if (uart_circ_empty(xmit) || uart_tx_stopped(sport)) {
+ if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(sport)) {
linflex_stop_tx(sport);
goto out;
}
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index bbcbc91482af..ae5e1ecc48fc 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -7,6 +7,7 @@

#include <linux/bitfield.h>
#include <linux/bits.h>
+#include <linux/circ_buf.h>
#include <linux/clk.h>
#include <linux/console.h>
#include <linux/delay.h>
@@ -473,7 +474,7 @@ static void lpuart32_stop_rx(struct uart_port *port)

static void lpuart_dma_tx(struct lpuart_port *sport)
{
- struct circ_buf *xmit = &sport->port.state->xmit;
+ struct tty_port *tport = &sport->port.state->port;
struct scatterlist *sgl = sport->tx_sgl;
struct device *dev = sport->port.dev;
struct dma_chan *chan = sport->dma_tx_chan;
@@ -482,18 +483,10 @@ static void lpuart_dma_tx(struct lpuart_port *sport)
if (sport->dma_tx_in_progress)
return;

- sport->dma_tx_bytes = uart_circ_chars_pending(xmit);
-
- if (xmit->tail < xmit->head || xmit->head == 0) {
- sport->dma_tx_nents = 1;
- sg_init_one(sgl, xmit->buf + xmit->tail, sport->dma_tx_bytes);
- } else {
- sport->dma_tx_nents = 2;
- sg_init_table(sgl, 2);
- sg_set_buf(sgl, xmit->buf + xmit->tail,
- UART_XMIT_SIZE - xmit->tail);
- sg_set_buf(sgl + 1, xmit->buf, xmit->head);
- }
+ sg_init_table(sgl, ARRAY_SIZE(sport->tx_sgl));
+ sport->dma_tx_bytes = kfifo_len(&tport->xmit_fifo);
+ sport->dma_tx_nents = kfifo_dma_out_prepare(&tport->xmit_fifo, sgl,
+ ARRAY_SIZE(sport->tx_sgl), sport->dma_tx_bytes);

ret = dma_map_sg(chan->device->dev, sgl, sport->dma_tx_nents,
DMA_TO_DEVICE);
@@ -521,14 +514,15 @@ static void lpuart_dma_tx(struct lpuart_port *sport)

static bool lpuart_stopped_or_empty(struct uart_port *port)
{
- return uart_circ_empty(&port->state->xmit) || uart_tx_stopped(port);
+ return kfifo_is_empty(&port->state->port.xmit_fifo) ||
+ uart_tx_stopped(port);
}

static void lpuart_dma_tx_complete(void *arg)
{
struct lpuart_port *sport = arg;
struct scatterlist *sgl = &sport->tx_sgl[0];
- struct circ_buf *xmit = &sport->port.state->xmit;
+ struct tty_port *tport = &sport->port.state->port;
struct dma_chan *chan = sport->dma_tx_chan;
unsigned long flags;

@@ -545,7 +539,7 @@ static void lpuart_dma_tx_complete(void *arg)
sport->dma_tx_in_progress = false;
uart_port_unlock_irqrestore(&sport->port, flags);

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&sport->port);

if (waitqueue_active(&sport->dma_wait)) {
@@ -756,8 +750,9 @@ static inline void lpuart_transmit_buffer(struct lpuart_port *sport)

static inline void lpuart32_transmit_buffer(struct lpuart_port *sport)
{
- struct circ_buf *xmit = &sport->port.state->xmit;
+ struct tty_port *tport = &sport->port.state->port;
unsigned long txcnt;
+ unsigned char c;

if (sport->port.x_char) {
lpuart32_write(&sport->port, sport->port.x_char, UARTDATA);
@@ -774,18 +769,18 @@ static inline void lpuart32_transmit_buffer(struct lpuart_port *sport)
txcnt = lpuart32_read(&sport->port, UARTWATER);
txcnt = txcnt >> UARTWATER_TXCNT_OFF;
txcnt &= UARTWATER_COUNT_MASK;
- while (!uart_circ_empty(xmit) && (txcnt < sport->txfifo_size)) {
- lpuart32_write(&sport->port, xmit->buf[xmit->tail], UARTDATA);
- uart_xmit_advance(&sport->port, 1);
+ while (txcnt < sport->txfifo_size &&
+ uart_fifo_get(&sport->port, &c)) {
+ lpuart32_write(&sport->port, c, UARTDATA);
txcnt = lpuart32_read(&sport->port, UARTWATER);
txcnt = txcnt >> UARTWATER_TXCNT_OFF;
txcnt &= UARTWATER_COUNT_MASK;
}

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&sport->port);

- if (uart_circ_empty(xmit))
+ if (kfifo_is_empty(&tport->xmit_fifo))
lpuart32_stop_tx(&sport->port);
}

diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c
index a75eafbcbea3..29e42831df39 100644
--- a/drivers/tty/serial/icom.c
+++ b/drivers/tty/serial/icom.c
@@ -877,10 +877,10 @@ static void shutdown(struct icom_port *icom_port)
static int icom_write(struct uart_port *port)
{
struct icom_port *icom_port = to_icom_port(port);
+ struct tty_port *tport = &port->state->port;
unsigned long data_count;
unsigned char cmdReg;
unsigned long offset;
- int temp_tail = port->state->xmit.tail;

trace(icom_port, "WRITE", 0);

@@ -890,16 +890,8 @@ static int icom_write(struct uart_port *port)
return 0;
}

- data_count = 0;
- while ((port->state->xmit.head != temp_tail) &&
- (data_count <= XMIT_BUFF_SZ)) {
-
- icom_port->xmit_buf[data_count++] =
- port->state->xmit.buf[temp_tail];
-
- temp_tail++;
- temp_tail &= (UART_XMIT_SIZE - 1);
- }
+ data_count = kfifo_out_peek(&tport->xmit_fifo, icom_port->xmit_buf,
+ XMIT_BUFF_SZ);

if (data_count) {
icom_port->statStg->xmit[0].flags =
@@ -956,7 +948,8 @@ static inline void check_modem_status(struct icom_port *icom_port)

static void xmit_interrupt(u16 port_int_reg, struct icom_port *icom_port)
{
- u16 count, i;
+ struct tty_port *tport = &icom_port->uart_port.state->port;
+ u16 count;

if (port_int_reg & (INT_XMIT_COMPLETED)) {
trace(icom_port, "XMIT_COMPLETE", 0);
@@ -968,13 +961,7 @@ static void xmit_interrupt(u16 port_int_reg, struct icom_port *icom_port)
count = le16_to_cpu(icom_port->statStg->xmit[0].leLength);
icom_port->uart_port.icount.tx += count;

- for (i=0; i<count &&
- !uart_circ_empty(&icom_port->uart_port.state->xmit); i++) {
-
- icom_port->uart_port.state->xmit.tail++;
- icom_port->uart_port.state->xmit.tail &=
- (UART_XMIT_SIZE - 1);
- }
+ kfifo_skip_count(&tport->xmit_fifo, count);

if (!icom_write(&icom_port->uart_port))
/* activate write queue */
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index e14813250616..56b76a221082 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -8,6 +8,7 @@
* Copyright (C) 2004 Pengutronix
*/

+#include <linux/circ_buf.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/init.h>
@@ -521,7 +522,8 @@ static void imx_uart_dma_tx(struct imx_port *sport);
/* called with port.lock taken and irqs off */
static inline void imx_uart_transmit_buffer(struct imx_port *sport)
{
- struct circ_buf *xmit = &sport->port.state->xmit;
+ struct tty_port *tport = &sport->port.state->port;
+ unsigned char c;

if (sport->port.x_char) {
/* Send next char */
@@ -531,7 +533,8 @@ static inline void imx_uart_transmit_buffer(struct imx_port *sport)
return;
}

- if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
+ if (kfifo_is_empty(&tport->xmit_fifo) ||
+ uart_tx_stopped(&sport->port)) {
imx_uart_stop_tx(&sport->port);
return;
}
@@ -555,26 +558,22 @@ static inline void imx_uart_transmit_buffer(struct imx_port *sport)
return;
}

- while (!uart_circ_empty(xmit) &&
- !(imx_uart_readl(sport, imx_uart_uts_reg(sport)) & UTS_TXFULL)) {
- /* send xmit->buf[xmit->tail]
- * out the port here */
- imx_uart_writel(sport, xmit->buf[xmit->tail], URTX0);
- uart_xmit_advance(&sport->port, 1);
- }
+ while (!(imx_uart_readl(sport, imx_uart_uts_reg(sport)) & UTS_TXFULL) &&
+ uart_fifo_get(&sport->port, &c))
+ imx_uart_writel(sport, c, URTX0);

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&sport->port);

- if (uart_circ_empty(xmit))
+ if (kfifo_is_empty(&tport->xmit_fifo))
imx_uart_stop_tx(&sport->port);
}

static void imx_uart_dma_tx_callback(void *data)
{
struct imx_port *sport = data;
+ struct tty_port *tport = &sport->port.state->port;
struct scatterlist *sgl = &sport->tx_sgl[0];
- struct circ_buf *xmit = &sport->port.state->xmit;
unsigned long flags;
u32 ucr1;

@@ -592,10 +591,11 @@ static void imx_uart_dma_tx_callback(void *data)

sport->dma_is_txing = 0;

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&sport->port);

- if (!uart_circ_empty(xmit) && !uart_tx_stopped(&sport->port))
+ if (!kfifo_is_empty(&tport->xmit_fifo) &&
+ !uart_tx_stopped(&sport->port))
imx_uart_dma_tx(sport);
else if (sport->port.rs485.flags & SER_RS485_ENABLED) {
u32 ucr4 = imx_uart_readl(sport, UCR4);
@@ -609,7 +609,7 @@ static void imx_uart_dma_tx_callback(void *data)
/* called with port.lock taken and irqs off */
static void imx_uart_dma_tx(struct imx_port *sport)
{
- struct circ_buf *xmit = &sport->port.state->xmit;
+ struct tty_port *tport = &sport->port.state->port;
struct scatterlist *sgl = sport->tx_sgl;
struct dma_async_tx_descriptor *desc;
struct dma_chan *chan = sport->dma_chan_tx;
@@ -624,18 +624,10 @@ static void imx_uart_dma_tx(struct imx_port *sport)
ucr4 &= ~UCR4_TCEN;
imx_uart_writel(sport, ucr4, UCR4);

- sport->tx_bytes = uart_circ_chars_pending(xmit);
-
- if (xmit->tail < xmit->head || xmit->head == 0) {
- sport->dma_tx_nents = 1;
- sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes);
- } else {
- sport->dma_tx_nents = 2;
- sg_init_table(sgl, 2);
- sg_set_buf(sgl, xmit->buf + xmit->tail,
- UART_XMIT_SIZE - xmit->tail);
- sg_set_buf(sgl + 1, xmit->buf, xmit->head);
- }
+ sg_init_table(sgl, ARRAY_SIZE(sport->tx_sgl));
+ sport->tx_bytes = kfifo_len(&tport->xmit_fifo);
+ sport->dma_tx_nents = kfifo_dma_out_prepare(&tport->xmit_fifo, sgl,
+ ARRAY_SIZE(sport->tx_sgl), sport->tx_bytes);

ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
if (ret == 0) {
@@ -653,8 +645,7 @@ static void imx_uart_dma_tx(struct imx_port *sport)
desc->callback = imx_uart_dma_tx_callback;
desc->callback_param = sport;

- dev_dbg(dev, "TX: prepare to send %lu bytes by DMA.\n",
- uart_circ_chars_pending(xmit));
+ dev_dbg(dev, "TX: prepare to send %u bytes by DMA.\n", sport->tx_bytes);

ucr1 = imx_uart_readl(sport, UCR1);
ucr1 |= UCR1_TXDMAEN;
@@ -671,9 +662,10 @@ static void imx_uart_dma_tx(struct imx_port *sport)
static void imx_uart_start_tx(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
+ struct tty_port *tport = &sport->port.state->port;
u32 ucr1;

- if (!sport->port.x_char && uart_circ_empty(&port->state->xmit))
+ if (!sport->port.x_char && kfifo_is_empty(&tport->xmit_fifo))
return;

/*
@@ -749,7 +741,7 @@ static void imx_uart_start_tx(struct uart_port *port)
return;
}

- if (!uart_circ_empty(&port->state->xmit) &&
+ if (!kfifo_is_empty(&tport->xmit_fifo) &&
!uart_tx_stopped(port))
imx_uart_dma_tx(sport);
return;
diff --git a/drivers/tty/serial/ip22zilog.c b/drivers/tty/serial/ip22zilog.c
index 320b29cd4683..c2cae50f06f3 100644
--- a/drivers/tty/serial/ip22zilog.c
+++ b/drivers/tty/serial/ip22zilog.c
@@ -355,7 +355,8 @@ static void ip22zilog_status_handle(struct uart_ip22zilog_port *up,
static void ip22zilog_transmit_chars(struct uart_ip22zilog_port *up,
struct zilog_channel *channel)
{
- struct circ_buf *xmit;
+ struct tty_port *tport;
+ unsigned char c;

if (ZS_IS_CONS(up)) {
unsigned char status = readb(&channel->control);
@@ -398,20 +399,18 @@ static void ip22zilog_transmit_chars(struct uart_ip22zilog_port *up,

if (up->port.state == NULL)
goto ack_tx_int;
- xmit = &up->port.state->xmit;
- if (uart_circ_empty(xmit))
- goto ack_tx_int;
+ tport = &up->port.state->port;
if (uart_tx_stopped(&up->port))
goto ack_tx_int;
+ if (!uart_fifo_get(&up->port, &c))
+ goto ack_tx_int;

up->flags |= IP22ZILOG_FLAG_TX_ACTIVE;
- writeb(xmit->buf[xmit->tail], &channel->data);
+ writeb(c, &channel->data);
ZSDELAY();
ZS_WSYNC(channel);

- uart_xmit_advance(&up->port, 1);
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&up->port);

return;
@@ -600,17 +599,16 @@ static void ip22zilog_start_tx(struct uart_port *port)
port->icount.tx++;
port->x_char = 0;
} else {
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
+ unsigned char c;

- if (uart_circ_empty(xmit))
+ if (!uart_fifo_get(port, &c))
return;
- writeb(xmit->buf[xmit->tail], &channel->data);
+ writeb(c, &channel->data);
ZSDELAY();
ZS_WSYNC(channel);

- uart_xmit_advance(port, 1);
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&up->port);
}
}
diff --git a/drivers/tty/serial/jsm/jsm_cls.c b/drivers/tty/serial/jsm/jsm_cls.c
index ddbd42c09637..6e40792f92cf 100644
--- a/drivers/tty/serial/jsm/jsm_cls.c
+++ b/drivers/tty/serial/jsm/jsm_cls.c
@@ -443,20 +443,14 @@ static void cls_copy_data_from_uart_to_queue(struct jsm_channel *ch)

static void cls_copy_data_from_queue_to_uart(struct jsm_channel *ch)
{
- u16 tail;
+ struct tty_port *tport;
int n;
- int qlen;
u32 len_written = 0;
- struct circ_buf *circ;

if (!ch)
return;

- circ = &ch->uart_port.state->xmit;
-
- /* No data to write to the UART */
- if (uart_circ_empty(circ))
- return;
+ tport = &ch->uart_port.state->port;

/* If port is "stopped", don't send any data to the UART */
if ((ch->ch_flags & CH_STOP) || (ch->ch_flags & CH_BREAK_SENDING))
@@ -467,29 +461,22 @@ static void cls_copy_data_from_queue_to_uart(struct jsm_channel *ch)
return;

n = 32;
+ while (n > 0) {
+ unsigned char c;

- /* cache tail of queue */
- tail = circ->tail & (UART_XMIT_SIZE - 1);
- qlen = uart_circ_chars_pending(circ);
-
- /* Find minimum of the FIFO space, versus queue length */
- n = min(n, qlen);
+ if (!kfifo_get(&tport->xmit_fifo, &c))
+ break;

- while (n > 0) {
- writeb(circ->buf[tail], &ch->ch_cls_uart->txrx);
- tail = (tail + 1) & (UART_XMIT_SIZE - 1);
+ writeb(c, &ch->ch_cls_uart->txrx);
n--;
ch->ch_txcount++;
len_written++;
}

- /* Update the final tail */
- circ->tail = tail & (UART_XMIT_SIZE - 1);
-
if (len_written > ch->ch_t_tlevel)
ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);

- if (uart_circ_empty(circ))
+ if (kfifo_is_empty(&tport->xmit_fifo))
uart_write_wakeup(&ch->uart_port);
}

diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c
index 1fa10f19368f..e8e13bf056e2 100644
--- a/drivers/tty/serial/jsm/jsm_neo.c
+++ b/drivers/tty/serial/jsm/jsm_neo.c
@@ -474,21 +474,21 @@ static void neo_copy_data_from_uart_to_queue(struct jsm_channel *ch)

static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch)
{
- u16 head;
- u16 tail;
+ struct tty_port *tport;
+ unsigned char *tail;
+ unsigned char c;
int n;
int s;
int qlen;
u32 len_written = 0;
- struct circ_buf *circ;

if (!ch)
return;

- circ = &ch->uart_port.state->xmit;
+ tport = &ch->uart_port.state->port;

/* No data to write to the UART */
- if (uart_circ_empty(circ))
+ if (kfifo_is_empty(&tport->xmit_fifo))
return;

/* If port is "stopped", don't send any data to the UART */
@@ -504,10 +504,9 @@ static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch)
if (ch->ch_cached_lsr & UART_LSR_THRE) {
ch->ch_cached_lsr &= ~(UART_LSR_THRE);

- writeb(circ->buf[circ->tail], &ch->ch_neo_uart->txrx);
- jsm_dbg(WRITE, &ch->ch_bd->pci_dev,
- "Tx data: %x\n", circ->buf[circ->tail]);
- circ->tail = (circ->tail + 1) & (UART_XMIT_SIZE - 1);
+ WARN_ON_ONCE(!kfifo_get(&tport->xmit_fifo, &c));
+ writeb(c, &ch->ch_neo_uart->txrx);
+ jsm_dbg(WRITE, &ch->ch_bd->pci_dev, "Tx data: %x\n", c);
ch->ch_txcount++;
}
return;
@@ -520,38 +519,27 @@ static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch)
return;

n = UART_17158_TX_FIFOSIZE - ch->ch_t_tlevel;
-
- /* cache head and tail of queue */
- head = circ->head & (UART_XMIT_SIZE - 1);
- tail = circ->tail & (UART_XMIT_SIZE - 1);
- qlen = uart_circ_chars_pending(circ);
+ qlen = kfifo_len(&tport->xmit_fifo);

/* Find minimum of the FIFO space, versus queue length */
n = min(n, qlen);

while (n > 0) {
-
- s = ((head >= tail) ? head : UART_XMIT_SIZE) - tail;
- s = min(s, n);
-
+ s = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, n);
if (s <= 0)
break;

- memcpy_toio(&ch->ch_neo_uart->txrxburst, circ->buf + tail, s);
- /* Add and flip queue if needed */
- tail = (tail + s) & (UART_XMIT_SIZE - 1);
+ memcpy_toio(&ch->ch_neo_uart->txrxburst, tail, s);
+ kfifo_skip_count(&tport->xmit_fifo, s);
n -= s;
ch->ch_txcount += s;
len_written += s;
}

- /* Update the final tail */
- circ->tail = tail & (UART_XMIT_SIZE - 1);
-
if (len_written >= ch->ch_t_tlevel)
ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);

- if (uart_circ_empty(circ))
+ if (kfifo_is_empty(&tport->xmit_fifo))
uart_write_wakeup(&ch->uart_port);
}

diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c
index 5efb2b593be3..b83eee37c17d 100644
--- a/drivers/tty/serial/max3100.c
+++ b/drivers/tty/serial/max3100.c
@@ -257,10 +257,11 @@ static int max3100_handlerx(struct max3100_port *s, u16 rx)
static void max3100_work(struct work_struct *w)
{
struct max3100_port *s = container_of(w, struct max3100_port, work);
+ struct tty_port *tport = &s->port.state->port;
+ unsigned char ch;
int rxchars;
u16 tx, rx;
int conf, cconf, crts;
- struct circ_buf *xmit = &s->port.state->xmit;

dev_dbg(&s->spi->dev, "%s\n", __func__);

@@ -290,10 +291,9 @@ static void max3100_work(struct work_struct *w)
tx = s->port.x_char;
s->port.icount.tx++;
s->port.x_char = 0;
- } else if (!uart_circ_empty(xmit) &&
- !uart_tx_stopped(&s->port)) {
- tx = xmit->buf[xmit->tail];
- uart_xmit_advance(&s->port, 1);
+ } else if (!uart_tx_stopped(&s->port) &&
+ uart_fifo_get(&s->port, &ch)) {
+ tx = ch;
}
if (tx != 0xffff) {
max3100_calc_parity(s, &tx);
@@ -307,13 +307,13 @@ static void max3100_work(struct work_struct *w)
tty_flip_buffer_push(&s->port.state->port);
rxchars = 0;
}
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&s->port);

} while (!s->force_end_work &&
!freezing(current) &&
((rx & MAX3100_R) ||
- (!uart_circ_empty(xmit) &&
+ (!kfifo_is_empty(&tport->xmit_fifo) &&
!uart_tx_stopped(&s->port))));

if (rxchars > 0)
diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
index 14dd9cfaa9f7..f0eb96429dae 100644
--- a/drivers/tty/serial/max310x.c
+++ b/drivers/tty/serial/max310x.c
@@ -747,8 +747,9 @@ static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen)

static void max310x_handle_tx(struct uart_port *port)
{
- struct circ_buf *xmit = &port->state->xmit;
- unsigned int txlen, to_send, until_end;
+ struct tty_port *tport = &port->state->port;
+ unsigned int txlen, to_send;
+ unsigned char *tail;

if (unlikely(port->x_char)) {
max310x_port_write(port, MAX310X_THR_REG, port->x_char);
@@ -757,32 +758,26 @@ static void max310x_handle_tx(struct uart_port *port)
return;
}

- if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+ if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port))
return;

- /* Get length of data pending in circular buffer */
- to_send = uart_circ_chars_pending(xmit);
- until_end = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
- if (likely(to_send)) {
+ /*
+ * It's a circ buffer -- wrap around.
+ * We could do that in one SPI transaction, but meh.
+ */
+ while (!kfifo_is_empty(&tport->xmit_fifo)) {
/* Limit to space available in TX FIFO */
txlen = max310x_port_read(port, MAX310X_TXFIFOLVL_REG);
txlen = port->fifosize - txlen;
- to_send = (to_send > txlen) ? txlen : to_send;
-
- if (until_end < to_send) {
- /*
- * It's a circ buffer -- wrap around.
- * We could do that in one SPI transaction, but meh.
- */
- max310x_batch_write(port, xmit->buf + xmit->tail, until_end);
- max310x_batch_write(port, xmit->buf, to_send - until_end);
- } else {
- max310x_batch_write(port, xmit->buf + xmit->tail, to_send);
- }
+ if (!txlen)
+ break;
+
+ to_send = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, txlen);
+ max310x_batch_write(port, tail, to_send);
uart_xmit_advance(port, to_send);
}

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);
}

diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c
index 8048fa542fc4..4bff422bb1bc 100644
--- a/drivers/tty/serial/men_z135_uart.c
+++ b/drivers/tty/serial/men_z135_uart.c
@@ -293,17 +293,14 @@ static void men_z135_handle_rx(struct men_z135_port *uart)
static void men_z135_handle_tx(struct men_z135_port *uart)
{
struct uart_port *port = &uart->port;
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
+ unsigned char *tail;
+ unsigned int n, txfree;
u32 txc;
u32 wptr;
int qlen;
- int n;
- int txfree;
- int head;
- int tail;
- int s;

- if (uart_circ_empty(xmit))
+ if (kfifo_is_empty(&tport->xmit_fifo))
goto out;

if (uart_tx_stopped(port))
@@ -313,7 +310,7 @@ static void men_z135_handle_tx(struct men_z135_port *uart)
goto out;

/* calculate bytes to copy */
- qlen = uart_circ_chars_pending(xmit);
+ qlen = kfifo_len(&tport->xmit_fifo);
if (qlen <= 0)
goto out;

@@ -345,21 +342,18 @@ static void men_z135_handle_tx(struct men_z135_port *uart)
if (n <= 0)
goto irq_en;

- head = xmit->head & (UART_XMIT_SIZE - 1);
- tail = xmit->tail & (UART_XMIT_SIZE - 1);
-
- s = ((head >= tail) ? head : UART_XMIT_SIZE) - tail;
- n = min(n, s);
+ n = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail,
+ min_t(unsigned int, UART_XMIT_SIZE, n));
+ memcpy_toio(port->membase + MEN_Z135_TX_RAM, tail, n);

- memcpy_toio(port->membase + MEN_Z135_TX_RAM, &xmit->buf[xmit->tail], n);
iowrite32(n & 0x3ff, port->membase + MEN_Z135_TX_CTRL);
uart_xmit_advance(port, n);

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);

irq_en:
- if (!uart_circ_empty(xmit))
+ if (!kfifo_is_empty(&tport->xmit_fifo))
men_z135_reg_set(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN);
else
men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN);
diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c
index 6feac459c0cf..4587ed4d4d5d 100644
--- a/drivers/tty/serial/meson_uart.c
+++ b/drivers/tty/serial/meson_uart.c
@@ -141,8 +141,8 @@ static void meson_uart_shutdown(struct uart_port *port)

static void meson_uart_start_tx(struct uart_port *port)
{
- struct circ_buf *xmit = &port->state->xmit;
- unsigned int ch;
+ struct tty_port *tport = &port->state->port;
+ unsigned char ch;
u32 val;

if (uart_tx_stopped(port)) {
@@ -158,21 +158,20 @@ static void meson_uart_start_tx(struct uart_port *port)
continue;
}

- if (uart_circ_empty(xmit))
+ if (!uart_fifo_get(port, &ch))
break;

- ch = xmit->buf[xmit->tail];
writel(ch, port->membase + AML_UART_WFIFO);
uart_xmit_advance(port, 1);
}

- if (!uart_circ_empty(xmit)) {
+ if (!kfifo_is_empty(&tport->xmit_fifo)) {
val = readl(port->membase + AML_UART_CONTROL);
val |= AML_UART_TX_INT_EN;
writel(val, port->membase + AML_UART_CONTROL);
}

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);
}

diff --git a/drivers/tty/serial/milbeaut_usio.c b/drivers/tty/serial/milbeaut_usio.c
index da4c6f7e2a30..fb082ee73d5b 100644
--- a/drivers/tty/serial/milbeaut_usio.c
+++ b/drivers/tty/serial/milbeaut_usio.c
@@ -72,7 +72,7 @@ static void mlb_usio_stop_tx(struct uart_port *port)

static void mlb_usio_tx_chars(struct uart_port *port)
{
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
int count;

writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FTIE,
@@ -87,7 +87,7 @@ static void mlb_usio_tx_chars(struct uart_port *port)
port->x_char = 0;
return;
}
- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
mlb_usio_stop_tx(port);
return;
}
@@ -96,12 +96,13 @@ static void mlb_usio_tx_chars(struct uart_port *port)
(readw(port->membase + MLB_USIO_REG_FBYTE) & 0xff);

do {
- writew(xmit->buf[xmit->tail], port->membase + MLB_USIO_REG_DR);
+ unsigned char ch;

- uart_xmit_advance(port, 1);
- if (uart_circ_empty(xmit))
+ if (!uart_fifo_get(port, &ch))
break;

+ writew(ch, port->membase + MLB_USIO_REG_DR);
+ port->icount.tx++;
} while (--count > 0);

writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FDRQ,
@@ -110,10 +111,10 @@ static void mlb_usio_tx_chars(struct uart_port *port)
writeb(readb(port->membase + MLB_USIO_REG_SCR) | MLB_USIO_SCR_TBIE,
port->membase + MLB_USIO_REG_SCR);

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);

- if (uart_circ_empty(xmit))
+ if (kfifo_is_empty(&tport->xmit_fifo))
mlb_usio_stop_tx(port);
}

diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index 7bf30e632313..ae7a8e3cf467 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -452,7 +452,7 @@ static void msm_complete_tx_dma(void *args)
{
struct msm_port *msm_port = args;
struct uart_port *port = &msm_port->uart;
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
struct msm_dma *dma = &msm_port->tx_dma;
struct dma_tx_state state;
unsigned long flags;
@@ -486,7 +486,7 @@ static void msm_complete_tx_dma(void *args)
msm_port->imr |= MSM_UART_IMR_TXLEV;
msm_write(port, msm_port->imr, MSM_UART_IMR);

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);

msm_handle_tx(port);
@@ -496,14 +496,14 @@ static void msm_complete_tx_dma(void *args)

static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count)
{
- struct circ_buf *xmit = &msm_port->uart.state->xmit;
struct uart_port *port = &msm_port->uart;
+ struct tty_port *tport = &port->state->port;
struct msm_dma *dma = &msm_port->tx_dma;
int ret;
u32 val;

sg_init_table(&dma->tx_sg, 1);
- sg_set_buf(&dma->tx_sg, &xmit->buf[xmit->tail], count);
+ kfifo_dma_out_prepare(&tport->xmit_fifo, &dma->tx_sg, 1, count);

ret = dma_map_sg(port->dev, &dma->tx_sg, 1, dma->dir);
if (ret)
@@ -843,8 +843,8 @@ static void msm_handle_rx(struct uart_port *port)

static void msm_handle_tx_pio(struct uart_port *port, unsigned int tx_count)
{
- struct circ_buf *xmit = &port->state->xmit;
struct msm_port *msm_port = to_msm_port(port);
+ struct tty_port *tport = &port->state->port;
unsigned int num_chars;
unsigned int tf_pointer = 0;
void __iomem *tf;
@@ -858,8 +858,7 @@ static void msm_handle_tx_pio(struct uart_port *port, unsigned int tx_count)
msm_reset_dm_count(port, tx_count);

while (tf_pointer < tx_count) {
- int i;
- char buf[4] = { 0 };
+ unsigned char buf[4] = { 0 };

if (!(msm_read(port, MSM_UART_SR) & MSM_UART_SR_TX_READY))
break;
@@ -870,26 +869,23 @@ static void msm_handle_tx_pio(struct uart_port *port, unsigned int tx_count)
else
num_chars = 1;

- for (i = 0; i < num_chars; i++)
- buf[i] = xmit->buf[xmit->tail + i];
-
+ num_chars = uart_fifo_out(port, buf, num_chars);
iowrite32_rep(tf, buf, 1);
- uart_xmit_advance(port, num_chars);
tf_pointer += num_chars;
}

/* disable tx interrupts if nothing more to send */
- if (uart_circ_empty(xmit))
+ if (kfifo_is_empty(&tport->xmit_fifo))
msm_stop_tx(port);

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);
}

static void msm_handle_tx(struct uart_port *port)
{
struct msm_port *msm_port = to_msm_port(port);
- struct circ_buf *xmit = &msm_port->uart.state->xmit;
+ struct tty_port *tport = &port->state->port;
struct msm_dma *dma = &msm_port->tx_dma;
unsigned int pio_count, dma_count, dma_min;
char buf[4] = { 0 };
@@ -913,13 +909,13 @@ static void msm_handle_tx(struct uart_port *port)
return;
}

- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
msm_stop_tx(port);
return;
}

- pio_count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
- dma_count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+ dma_count = pio_count = kfifo_out_linear(&tport->xmit_fifo, NULL,
+ UART_XMIT_SIZE);

dma_min = 1; /* Always DMA */
if (msm_port->is_uartdm > UARTDM_1P3) {
diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c
index 0255646bc175..5de57b77abdb 100644
--- a/drivers/tty/serial/mvebu-uart.c
+++ b/drivers/tty/serial/mvebu-uart.c
@@ -219,12 +219,10 @@ static void mvebu_uart_stop_tx(struct uart_port *port)
static void mvebu_uart_start_tx(struct uart_port *port)
{
unsigned int ctl;
- struct circ_buf *xmit = &port->state->xmit;
+ unsigned char c;

- if (IS_EXTENDED(port) && !uart_circ_empty(xmit)) {
- writel(xmit->buf[xmit->tail], port->membase + UART_TSH(port));
- uart_xmit_advance(port, 1);
- }
+ if (IS_EXTENDED(port) && uart_fifo_get(port, &c))
+ writel(c, port->membase + UART_TSH(port));

ctl = readl(port->membase + UART_INTR(port));
ctl |= CTRL_TX_RDY_INT(port);
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index 4749331fe618..144b35d31497 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -517,7 +517,7 @@ static void mxs_auart_tx_chars(struct mxs_auart_port *s);
static void dma_tx_callback(void *param)
{
struct mxs_auart_port *s = param;
- struct circ_buf *xmit = &s->port.state->xmit;
+ struct tty_port *tport = &s->port.state->port;

dma_unmap_sg(s->dev, &s->tx_sgl, 1, DMA_TO_DEVICE);

@@ -526,7 +526,7 @@ static void dma_tx_callback(void *param)
smp_mb__after_atomic();

/* wake up the possible processes. */
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&s->port);

mxs_auart_tx_chars(s);
@@ -568,33 +568,22 @@ static int mxs_auart_dma_tx(struct mxs_auart_port *s, int size)

static void mxs_auart_tx_chars(struct mxs_auart_port *s)
{
- struct circ_buf *xmit = &s->port.state->xmit;
+ struct tty_port *tport = &s->port.state->port;
bool pending;
u8 ch;

if (auart_dma_enabled(s)) {
u32 i = 0;
- int size;
void *buffer = s->tx_dma_buf;

if (test_and_set_bit(MXS_AUART_DMA_TX_SYNC, &s->flags))
return;

- while (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) {
- size = min_t(u32, UART_XMIT_SIZE - i,
- CIRC_CNT_TO_END(xmit->head,
- xmit->tail,
- UART_XMIT_SIZE));
- memcpy(buffer + i, xmit->buf + xmit->tail, size);
- xmit->tail = (xmit->tail + size) & (UART_XMIT_SIZE - 1);
-
- i += size;
- if (i >= UART_XMIT_SIZE)
- break;
- }
-
if (uart_tx_stopped(&s->port))
mxs_auart_stop_tx(&s->port);
+ else
+ i = kfifo_out(&tport->xmit_fifo, buffer,
+ UART_XMIT_SIZE);

if (i) {
mxs_auart_dma_tx(s, i);
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
index 89257cddf540..c7cee5fee603 100644
--- a/drivers/tty/serial/pch_uart.c
+++ b/drivers/tty/serial/pch_uart.c
@@ -808,7 +808,7 @@ static int dma_handle_rx(struct eg20t_port *priv)
static unsigned int handle_tx(struct eg20t_port *priv)
{
struct uart_port *port = &priv->port;
- struct circ_buf *xmit = &port->state->xmit;
+ unsigned char ch;
int fifo_size;
int tx_empty;

@@ -830,9 +830,9 @@ static unsigned int handle_tx(struct eg20t_port *priv)
fifo_size--;
}

- while (!uart_tx_stopped(port) && !uart_circ_empty(xmit) && fifo_size) {
- iowrite8(xmit->buf[xmit->tail], priv->membase + PCH_UART_THR);
- uart_xmit_advance(port, 1);
+ while (!uart_tx_stopped(port) && fifo_size &&
+ uart_fifo_get(port, &ch)) {
+ iowrite8(ch, priv->membase + PCH_UART_THR);
fifo_size--;
tx_empty = 0;
}
@@ -850,14 +850,14 @@ static unsigned int handle_tx(struct eg20t_port *priv)
static unsigned int dma_handle_tx(struct eg20t_port *priv)
{
struct uart_port *port = &priv->port;
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
struct scatterlist *sg;
int nent;
int fifo_size;
struct dma_async_tx_descriptor *desc;
+ unsigned int bytes, tail;
int num;
int i;
- int bytes;
int size;
int rem;

@@ -886,7 +886,7 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv)
fifo_size--;
}

- bytes = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+ bytes = kfifo_out_linear(&tport->xmit_fifo, &tail, UART_XMIT_SIZE);
if (!bytes) {
dev_dbg(priv->port.dev, "%s 0 bytes return\n", __func__);
pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT);
@@ -920,10 +920,10 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv)

for (i = 0; i < num; i++, sg++) {
if (i == (num - 1))
- sg_set_page(sg, virt_to_page(xmit->buf),
+ sg_set_page(sg, virt_to_page(tport->xmit_buf),
rem, fifo_size * i);
else
- sg_set_page(sg, virt_to_page(xmit->buf),
+ sg_set_page(sg, virt_to_page(tport->xmit_buf),
size, fifo_size * i);
}

@@ -937,8 +937,7 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv)
priv->nent = nent;

for (i = 0; i < nent; i++, sg++) {
- sg->offset = (xmit->tail & (UART_XMIT_SIZE - 1)) +
- fifo_size * i;
+ sg->offset = tail + fifo_size * i;
sg_dma_address(sg) = (sg_dma_address(sg) &
~(UART_XMIT_SIZE - 1)) + sg->offset;
if (i == (nent - 1))
diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c
index bbb46e6e98a2..f5af336a869b 100644
--- a/drivers/tty/serial/pic32_uart.c
+++ b/drivers/tty/serial/pic32_uart.c
@@ -342,7 +342,7 @@ static void pic32_uart_do_rx(struct uart_port *port)
static void pic32_uart_do_tx(struct uart_port *port)
{
struct pic32_sport *sport = to_pic32_sport(port);
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
unsigned int max_count = PIC32_UART_TX_FIFO_DEPTH;

if (port->x_char) {
@@ -357,7 +357,7 @@ static void pic32_uart_do_tx(struct uart_port *port)
return;
}

- if (uart_circ_empty(xmit))
+ if (kfifo_is_empty(&tport->xmit_fifo))
goto txq_empty;

/* keep stuffing chars into uart tx buffer
@@ -371,21 +371,20 @@ static void pic32_uart_do_tx(struct uart_port *port)
*/
while (!(PIC32_UART_STA_UTXBF &
pic32_uart_readl(sport, PIC32_UART_STA))) {
- unsigned int c = xmit->buf[xmit->tail];
+ unsigned char c;

+ if (!uart_fifo_get(port, &c))
+ break;
pic32_uart_writel(sport, PIC32_UART_TX, c);

- uart_xmit_advance(port, 1);
- if (uart_circ_empty(xmit))
- break;
if (--max_count == 0)
break;
}

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);

- if (uart_circ_empty(xmit))
+ if (kfifo_is_empty(&tport->xmit_fifo))
goto txq_empty;

return;
diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c
index 05d97e89511e..63bc726273fd 100644
--- a/drivers/tty/serial/pmac_zilog.c
+++ b/drivers/tty/serial/pmac_zilog.c
@@ -347,7 +347,8 @@ static void pmz_status_handle(struct uart_pmac_port *uap)

static void pmz_transmit_chars(struct uart_pmac_port *uap)
{
- struct circ_buf *xmit;
+ struct tty_port *tport;
+ unsigned char ch;

if (ZS_IS_CONS(uap)) {
unsigned char status = read_zsreg(uap, R0);
@@ -398,8 +399,8 @@ static void pmz_transmit_chars(struct uart_pmac_port *uap)

if (uap->port.state == NULL)
goto ack_tx_int;
- xmit = &uap->port.state->xmit;
- if (uart_circ_empty(xmit)) {
+ tport = &uap->port.state->port;
+ if (kfifo_is_empty(&tport->xmit_fifo)) {
uart_write_wakeup(&uap->port);
goto ack_tx_int;
}
@@ -407,12 +408,11 @@ static void pmz_transmit_chars(struct uart_pmac_port *uap)
goto ack_tx_int;

uap->flags |= PMACZILOG_FLAG_TX_ACTIVE;
- write_zsdata(uap, xmit->buf[xmit->tail]);
+ WARN_ON(!uart_fifo_get(&uap->port, &ch));
+ write_zsdata(uap, ch);
zssync(uap);

- uart_xmit_advance(&uap->port, 1);
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&uap->port);

return;
@@ -620,15 +620,15 @@ static void pmz_start_tx(struct uart_port *port)
port->icount.tx++;
port->x_char = 0;
} else {
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
+ unsigned char ch;

- if (uart_circ_empty(xmit))
+ if (!uart_fifo_get(&uap->port, &ch))
return;
- write_zsdata(uap, xmit->buf[xmit->tail]);
+ write_zsdata(uap, ch);
zssync(uap);
- uart_xmit_advance(port, 1);

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&uap->port);
}
}
diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
index f9f7ac1a10df..7814982f1921 100644
--- a/drivers/tty/serial/qcom_geni_serial.c
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -505,7 +505,7 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s,
*/
qcom_geni_serial_poll_tx_done(uport);

- if (!uart_circ_empty(&uport->state->xmit)) {
+ if (!kfifo_is_empty(&uport->state->port.xmit_fifo)) {
irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN);
writel(irq_en | M_TX_FIFO_WATERMARK_EN,
uport->membase + SE_GENI_M_IRQ_EN);
@@ -620,22 +620,24 @@ static void qcom_geni_serial_stop_tx_dma(struct uart_port *uport)
static void qcom_geni_serial_start_tx_dma(struct uart_port *uport)
{
struct qcom_geni_serial_port *port = to_dev_port(uport);
- struct circ_buf *xmit = &uport->state->xmit;
+ struct tty_port *tport = &uport->state->port;
unsigned int xmit_size;
+ u8 *tail;
int ret;

if (port->tx_dma_addr)
return;

- if (uart_circ_empty(xmit))
+ if (kfifo_is_empty(&tport->xmit_fifo))
return;

- xmit_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+ xmit_size = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail,
+ UART_XMIT_SIZE);

qcom_geni_serial_setup_tx(uport, xmit_size);

- ret = geni_se_tx_dma_prep(&port->se, &xmit->buf[xmit->tail],
- xmit_size, &port->tx_dma_addr);
+ ret = geni_se_tx_dma_prep(&port->se, tail, xmit_size,
+ &port->tx_dma_addr);
if (ret) {
dev_err(uport->dev, "unable to start TX SE DMA: %d\n", ret);
qcom_geni_serial_stop_tx_dma(uport);
@@ -853,18 +855,16 @@ static void qcom_geni_serial_send_chunk_fifo(struct uart_port *uport,
unsigned int chunk)
{
struct qcom_geni_serial_port *port = to_dev_port(uport);
- struct circ_buf *xmit = &uport->state->xmit;
- unsigned int tx_bytes, c, remaining = chunk;
+ struct tty_port *tport = &uport->state->port;
+ unsigned int tx_bytes, remaining = chunk;
u8 buf[BYTES_PER_FIFO_WORD];

while (remaining) {
memset(buf, 0, sizeof(buf));
tx_bytes = min(remaining, BYTES_PER_FIFO_WORD);

- for (c = 0; c < tx_bytes ; c++) {
- buf[c] = xmit->buf[xmit->tail];
- uart_xmit_advance(uport, 1);
- }
+ tx_bytes = kfifo_out(&tport->xmit_fifo, buf, tx_bytes);
+ uart_xmit_advance(uport, tx_bytes);

iowrite32_rep(uport->membase + SE_GENI_TX_FIFOn, buf, 1);

@@ -877,7 +877,7 @@ static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport,
bool done, bool active)
{
struct qcom_geni_serial_port *port = to_dev_port(uport);
- struct circ_buf *xmit = &uport->state->xmit;
+ struct tty_port *tport = &uport->state->port;
size_t avail;
size_t pending;
u32 status;
@@ -890,7 +890,7 @@ static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport,
if (active)
pending = port->tx_remaining;
else
- pending = uart_circ_chars_pending(xmit);
+ pending = kfifo_len(&tport->xmit_fifo);

/* All data has been transmitted and acknowledged as received */
if (!pending && !status && done) {
@@ -933,24 +933,24 @@ static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport,
uport->membase + SE_GENI_M_IRQ_EN);
}

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(uport);
}

static void qcom_geni_serial_handle_tx_dma(struct uart_port *uport)
{
struct qcom_geni_serial_port *port = to_dev_port(uport);
- struct circ_buf *xmit = &uport->state->xmit;
+ struct tty_port *tport = &uport->state->port;

uart_xmit_advance(uport, port->tx_remaining);
geni_se_tx_dma_unprep(&port->se, port->tx_dma_addr, port->tx_remaining);
port->tx_dma_addr = 0;
port->tx_remaining = 0;

- if (!uart_circ_empty(xmit))
+ if (!kfifo_is_empty(&tport->xmit_fifo))
qcom_geni_serial_start_tx_dma(uport);

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(uport);
}

diff --git a/drivers/tty/serial/rda-uart.c b/drivers/tty/serial/rda-uart.c
index 82def9b8632a..663e35e424bd 100644
--- a/drivers/tty/serial/rda-uart.c
+++ b/drivers/tty/serial/rda-uart.c
@@ -330,8 +330,8 @@ static void rda_uart_set_termios(struct uart_port *port,

static void rda_uart_send_chars(struct uart_port *port)
{
- struct circ_buf *xmit = &port->state->xmit;
- unsigned int ch;
+ struct tty_port *tport = &port->state->port;
+ unsigned char ch;
u32 val;

if (uart_tx_stopped(port))
@@ -347,19 +347,14 @@ static void rda_uart_send_chars(struct uart_port *port)
port->x_char = 0;
}

- while (rda_uart_read(port, RDA_UART_STATUS) & RDA_UART_TX_FIFO_MASK) {
- if (uart_circ_empty(xmit))
- break;
-
- ch = xmit->buf[xmit->tail];
+ while ((rda_uart_read(port, RDA_UART_STATUS) & RDA_UART_TX_FIFO_MASK) &&
+ uart_fifo_get(port, &ch))
rda_uart_write(port, ch, RDA_UART_RXTX_BUFFER);
- uart_xmit_advance(port, 1);
- }

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);

- if (!uart_circ_empty(xmit)) {
+ if (!kfifo_is_empty(&tport->xmit_fifo)) {
/* Re-enable Tx FIFO interrupt */
val = rda_uart_read(port, RDA_UART_IRQ_MASK);
val |= RDA_UART_TX_DATA_NEEDED;
diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c
index a2d07e05c502..dc35eb77d2ef 100644
--- a/drivers/tty/serial/samsung_tty.c
+++ b/drivers/tty/serial/samsung_tty.c
@@ -329,7 +329,7 @@ static void s3c24xx_serial_tx_dma_complete(void *args)
{
struct s3c24xx_uart_port *ourport = args;
struct uart_port *port = &ourport->port;
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
struct s3c24xx_uart_dma *dma = ourport->dma;
struct dma_tx_state state;
unsigned long flags;
@@ -348,7 +348,7 @@ static void s3c24xx_serial_tx_dma_complete(void *args)
uart_xmit_advance(port, count);
ourport->tx_in_progress = 0;

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);

s3c24xx_serial_start_next_tx(ourport);
@@ -431,17 +431,15 @@ static void s3c24xx_serial_start_tx_pio(struct s3c24xx_uart_port *ourport)
}

static int s3c24xx_serial_start_tx_dma(struct s3c24xx_uart_port *ourport,
- unsigned int count)
+ unsigned int count, unsigned int tail)
{
- struct uart_port *port = &ourport->port;
- struct circ_buf *xmit = &port->state->xmit;
struct s3c24xx_uart_dma *dma = ourport->dma;

if (ourport->tx_mode != S3C24XX_TX_DMA)
enable_tx_dma(ourport);

dma->tx_size = count & ~(dma_get_cache_alignment() - 1);
- dma->tx_transfer_addr = dma->tx_addr + xmit->tail;
+ dma->tx_transfer_addr = dma->tx_addr + tail;

dma_sync_single_for_device(dma->tx_chan->device->dev,
dma->tx_transfer_addr, dma->tx_size,
@@ -468,11 +466,11 @@ static int s3c24xx_serial_start_tx_dma(struct s3c24xx_uart_port *ourport,
static void s3c24xx_serial_start_next_tx(struct s3c24xx_uart_port *ourport)
{
struct uart_port *port = &ourport->port;
- struct circ_buf *xmit = &port->state->xmit;
- unsigned long count;
+ struct tty_port *tport = &port->state->port;
+ unsigned int count, tail;

/* Get data size up to the end of buffer */
- count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+ count = kfifo_out_linear(&tport->xmit_fifo, &tail, UART_XMIT_SIZE);

if (!count) {
s3c24xx_serial_stop_tx(port);
@@ -481,16 +479,16 @@ static void s3c24xx_serial_start_next_tx(struct s3c24xx_uart_port *ourport)

if (!ourport->dma || !ourport->dma->tx_chan ||
count < ourport->min_dma_size ||
- xmit->tail & (dma_get_cache_alignment() - 1))
+ tail & (dma_get_cache_alignment() - 1))
s3c24xx_serial_start_tx_pio(ourport);
else
- s3c24xx_serial_start_tx_dma(ourport, count);
+ s3c24xx_serial_start_tx_dma(ourport, count, tail);
}

static void s3c24xx_serial_start_tx(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;

if (!ourport->tx_enabled) {
if (port->flags & UPF_CONS_FLOW)
@@ -502,7 +500,8 @@ static void s3c24xx_serial_start_tx(struct uart_port *port)
}

if (ourport->dma && ourport->dma->tx_chan) {
- if (!uart_circ_empty(xmit) && !ourport->tx_in_progress)
+ if (!kfifo_is_empty(&tport->xmit_fifo) &&
+ !ourport->tx_in_progress)
s3c24xx_serial_start_next_tx(ourport);
}
}
@@ -868,18 +867,19 @@ static irqreturn_t s3c24xx_serial_rx_irq(int irq, void *dev_id)
static void s3c24xx_serial_tx_chars(struct s3c24xx_uart_port *ourport)
{
struct uart_port *port = &ourport->port;
- struct circ_buf *xmit = &port->state->xmit;
- int count, dma_count = 0;
+ struct tty_port *tport = &port->state->port;
+ unsigned int count, dma_count = 0, tail;

- count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+ count = kfifo_out_linear(&tport->xmit_fifo, &tail, UART_XMIT_SIZE);

if (ourport->dma && ourport->dma->tx_chan &&
count >= ourport->min_dma_size) {
int align = dma_get_cache_alignment() -
- (xmit->tail & (dma_get_cache_alignment() - 1));
+ (tail & (dma_get_cache_alignment() - 1));
if (count - align >= ourport->min_dma_size) {
dma_count = count - align;
count = align;
+ tail += align;
}
}

@@ -894,7 +894,7 @@ static void s3c24xx_serial_tx_chars(struct s3c24xx_uart_port *ourport)
* stopped, disable the uart and exit
*/

- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
s3c24xx_serial_stop_tx(port);
return;
}
@@ -906,24 +906,25 @@ static void s3c24xx_serial_tx_chars(struct s3c24xx_uart_port *ourport)
dma_count = 0;
}

- while (!uart_circ_empty(xmit) && count > 0) {
- if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
+ while (!(rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)) {
+ unsigned char ch;
+
+ if (!uart_fifo_get(port, &ch))
break;

- wr_reg(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
- uart_xmit_advance(port, 1);
+ wr_reg(port, S3C2410_UTXH, ch);
count--;
}

if (!count && dma_count) {
- s3c24xx_serial_start_tx_dma(ourport, dma_count);
+ s3c24xx_serial_start_tx_dma(ourport, dma_count, tail);
return;
}

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);

- if (uart_circ_empty(xmit))
+ if (kfifo_is_empty(&tport->xmit_fifo))
s3c24xx_serial_stop_tx(port);
}

@@ -1118,7 +1119,8 @@ static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p)

/* TX buffer */
dma->tx_addr = dma_map_single(dma->tx_chan->device->dev,
- p->port.state->xmit.buf, UART_XMIT_SIZE,
+ p->port.state->port.xmit_buf,
+ UART_XMIT_SIZE,
DMA_TO_DEVICE);
if (dma_mapping_error(dma->tx_chan->device->dev, dma->tx_addr)) {
reason = "DMA mapping error for TX buffer";
diff --git a/drivers/tty/serial/sb1250-duart.c b/drivers/tty/serial/sb1250-duart.c
index dbec29d9a6c3..b4e1b90e5960 100644
--- a/drivers/tty/serial/sb1250-duart.c
+++ b/drivers/tty/serial/sb1250-duart.c
@@ -382,7 +382,8 @@ static void sbd_receive_chars(struct sbd_port *sport)
static void sbd_transmit_chars(struct sbd_port *sport)
{
struct uart_port *uport = &sport->port;
- struct circ_buf *xmit = &sport->port.state->xmit;
+ struct tty_port *tport = &sport->port.state->port;
+ unsigned char ch;
unsigned int mask;
int stop_tx;

@@ -395,19 +396,19 @@ static void sbd_transmit_chars(struct sbd_port *sport)
}

/* If nothing to do or stopped or hardware stopped. */
- stop_tx = (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port));
+ stop_tx = uart_tx_stopped(&sport->port) ||
+ !uart_fifo_get(&sport->port, &ch);

/* Send char. */
if (!stop_tx) {
- write_sbdchn(sport, R_DUART_TX_HOLD, xmit->buf[xmit->tail]);
- uart_xmit_advance(&sport->port, 1);
+ write_sbdchn(sport, R_DUART_TX_HOLD, ch);

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&sport->port);
}

/* Are we are done? */
- if (stop_tx || uart_circ_empty(xmit)) {
+ if (stop_tx || kfifo_is_empty(&tport->xmit_fifo)) {
/* Disable tx interrupts. */
mask = read_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2));
mask &= ~M_DUART_IMR_TX;
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index 929206a9a6e1..c6983b7bd78c 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -676,9 +676,9 @@ static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen,
static void sc16is7xx_handle_tx(struct uart_port *port)
{
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
- struct circ_buf *xmit = &port->state->xmit;
- unsigned int txlen, to_send, i;
+ struct tty_port *tport = &port->state->port;
unsigned long flags;
+ unsigned int txlen;

if (unlikely(port->x_char)) {
sc16is7xx_port_write(port, SC16IS7XX_THR_REG, port->x_char);
@@ -687,40 +687,30 @@ static void sc16is7xx_handle_tx(struct uart_port *port)
return;
}

- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
uart_port_lock_irqsave(port, &flags);
sc16is7xx_stop_tx(port);
uart_port_unlock_irqrestore(port, flags);
return;
}

- /* Get length of data pending in circular buffer */
- to_send = uart_circ_chars_pending(xmit);
- if (likely(to_send)) {
- /* Limit to space available in TX FIFO */
- txlen = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG);
- if (txlen > SC16IS7XX_FIFO_SIZE) {
- dev_err_ratelimited(port->dev,
- "chip reports %d free bytes in TX fifo, but it only has %d",
- txlen, SC16IS7XX_FIFO_SIZE);
- txlen = 0;
- }
- to_send = (to_send > txlen) ? txlen : to_send;
-
- /* Convert to linear buffer */
- for (i = 0; i < to_send; ++i) {
- s->buf[i] = xmit->buf[xmit->tail];
- uart_xmit_advance(port, 1);
- }
-
- sc16is7xx_fifo_write(port, s->buf, to_send);
+ /* Limit to space available in TX FIFO */
+ txlen = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG);
+ if (txlen > SC16IS7XX_FIFO_SIZE) {
+ dev_err_ratelimited(port->dev,
+ "chip reports %d free bytes in TX fifo, but it only has %d",
+ txlen, SC16IS7XX_FIFO_SIZE);
+ txlen = 0;
}

+ txlen = uart_fifo_out(port, s->buf, txlen);
+ sc16is7xx_fifo_write(port, s->buf, txlen);
+
uart_port_lock_irqsave(port, &flags);
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);

- if (uart_circ_empty(xmit))
+ if (kfifo_is_empty(&tport->xmit_fifo))
sc16is7xx_stop_tx(port);
else
sc16is7xx_ier_set(port, SC16IS7XX_IER_THRI_BIT);
diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c
index f24217a560d7..6d1d142fd216 100644
--- a/drivers/tty/serial/sccnxp.c
+++ b/drivers/tty/serial/sccnxp.c
@@ -439,7 +439,7 @@ static void sccnxp_handle_rx(struct uart_port *port)
static void sccnxp_handle_tx(struct uart_port *port)
{
u8 sr;
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
struct sccnxp_port *s = dev_get_drvdata(port->dev);

if (unlikely(port->x_char)) {
@@ -449,7 +449,7 @@ static void sccnxp_handle_tx(struct uart_port *port)
return;
}

- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
/* Disable TX if FIFO is empty */
if (sccnxp_port_read(port, SCCNXP_SR_REG) & SR_TXEMT) {
sccnxp_disable_irq(port, IMR_TXRDY);
@@ -461,16 +461,20 @@ static void sccnxp_handle_tx(struct uart_port *port)
return;
}

- while (!uart_circ_empty(xmit)) {
+ while (1) {
+ unsigned char ch;
+
sr = sccnxp_port_read(port, SCCNXP_SR_REG);
if (!(sr & SR_TXRDY))
break;

- sccnxp_port_write(port, SCCNXP_THR_REG, xmit->buf[xmit->tail]);
- uart_xmit_advance(port, 1);
+ if (!uart_fifo_get(port, &ch))
+ break;
+
+ sccnxp_port_write(port, SCCNXP_THR_REG, ch);
}

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);
}

diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c
index 525f3a2f7bd4..1183ca54ab92 100644
--- a/drivers/tty/serial/serial-tegra.c
+++ b/drivers/tty/serial/serial-tegra.c
@@ -484,18 +484,18 @@ static void tegra_uart_release_port(struct uart_port *u)

static void tegra_uart_fill_tx_fifo(struct tegra_uart_port *tup, int max_bytes)
{
- struct circ_buf *xmit = &tup->uport.state->xmit;
+ unsigned char ch;
int i;

for (i = 0; i < max_bytes; i++) {
- BUG_ON(uart_circ_empty(xmit));
if (tup->cdata->tx_fifo_full_status) {
unsigned long lsr = tegra_uart_read(tup, UART_LSR);
if ((lsr & TEGRA_UART_LSR_TXFIFO_FULL))
break;
}
- tegra_uart_write(tup, xmit->buf[xmit->tail], UART_TX);
- uart_xmit_advance(&tup->uport, 1);
+ if (WARN_ON_ONCE(!uart_fifo_get(&tup->uport, &ch)))
+ break;
+ tegra_uart_write(tup, ch, UART_TX);
}
}

@@ -514,7 +514,7 @@ static void tegra_uart_start_pio_tx(struct tegra_uart_port *tup,
static void tegra_uart_tx_dma_complete(void *args)
{
struct tegra_uart_port *tup = args;
- struct circ_buf *xmit = &tup->uport.state->xmit;
+ struct tty_port *tport = &tup->uport.state->port;
struct dma_tx_state state;
unsigned long flags;
unsigned int count;
@@ -525,7 +525,7 @@ static void tegra_uart_tx_dma_complete(void *args)
uart_port_lock_irqsave(&tup->uport, &flags);
uart_xmit_advance(&tup->uport, count);
tup->tx_in_progress = 0;
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&tup->uport);
tegra_uart_start_next_tx(tup);
uart_port_unlock_irqrestore(&tup->uport, flags);
@@ -534,11 +534,14 @@ static void tegra_uart_tx_dma_complete(void *args)
static int tegra_uart_start_tx_dma(struct tegra_uart_port *tup,
unsigned long count)
{
- struct circ_buf *xmit = &tup->uport.state->xmit;
+ struct tty_port *tport = &tup->uport.state->port;
dma_addr_t tx_phys_addr;
+ unsigned int tail;

tup->tx_bytes = count & ~(0xF);
- tx_phys_addr = tup->tx_dma_buf_phys + xmit->tail;
+ WARN_ON_ONCE(kfifo_out_linear(&tport->xmit_fifo, &tail,
+ UART_XMIT_SIZE) < count);
+ tx_phys_addr = tup->tx_dma_buf_phys + tail;

dma_sync_single_for_device(tup->uport.dev, tx_phys_addr,
tup->tx_bytes, DMA_TO_DEVICE);
@@ -562,18 +565,21 @@ static int tegra_uart_start_tx_dma(struct tegra_uart_port *tup,

static void tegra_uart_start_next_tx(struct tegra_uart_port *tup)
{
+ struct tty_port *tport = &tup->uport.state->port;
+ unsigned char *tail_ptr;
unsigned long tail;
- unsigned long count;
- struct circ_buf *xmit = &tup->uport.state->xmit;
+ unsigned int count;

if (!tup->current_baud)
return;

- tail = (unsigned long)&xmit->buf[xmit->tail];
- count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+ count = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail_ptr,
+ UART_XMIT_SIZE);
if (!count)
return;

+ tail = (unsigned long)tail_ptr;
+
if (tup->use_tx_pio || count < TEGRA_UART_MIN_DMA)
tegra_uart_start_pio_tx(tup, count);
else if (BYTES_TO_ALIGN(tail) > 0)
@@ -586,9 +592,9 @@ static void tegra_uart_start_next_tx(struct tegra_uart_port *tup)
static void tegra_uart_start_tx(struct uart_port *u)
{
struct tegra_uart_port *tup = to_tegra_uport(u);
- struct circ_buf *xmit = &u->state->xmit;
+ struct tty_port *tport = &u->state->port;

- if (!uart_circ_empty(xmit) && !tup->tx_in_progress)
+ if (!kfifo_is_empty(&tport->xmit_fifo) && !tup->tx_in_progress)
tegra_uart_start_next_tx(tup);
}

@@ -628,11 +634,11 @@ static void tegra_uart_stop_tx(struct uart_port *u)

static void tegra_uart_handle_tx_pio(struct tegra_uart_port *tup)
{
- struct circ_buf *xmit = &tup->uport.state->xmit;
+ struct tty_port *tport = &tup->uport.state->port;

tegra_uart_fill_tx_fifo(tup, tup->tx_bytes);
tup->tx_in_progress = 0;
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&tup->uport);
tegra_uart_start_next_tx(tup);
}
@@ -1169,15 +1175,14 @@ static int tegra_uart_dma_channel_allocate(struct tegra_uart_port *tup,
tup->rx_dma_buf_virt = dma_buf;
tup->rx_dma_buf_phys = dma_phys;
} else {
+ dma_buf = tup->uport.state->port.xmit_buf;
dma_phys = dma_map_single(tup->uport.dev,
- tup->uport.state->xmit.buf, UART_XMIT_SIZE,
- DMA_TO_DEVICE);
+ dma_buf, UART_XMIT_SIZE, DMA_TO_DEVICE);
if (dma_mapping_error(tup->uport.dev, dma_phys)) {
dev_err(tup->uport.dev, "dma_map_single tx failed\n");
dma_release_channel(dma_chan);
return -ENOMEM;
}
- dma_buf = tup->uport.state->xmit.buf;
dma_sconfig.dst_addr = tup->uport.mapbase;
dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
dma_sconfig.dst_maxburst = 16;
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index ff85ebd3a007..3c0931fba1c6 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -272,9 +272,10 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
return -ENOMEM;

uart_port_lock(state, flags);
- if (!state->xmit.buf) {
- state->xmit.buf = (unsigned char *) page;
- uart_circ_clear(&state->xmit);
+ if (!state->port.xmit_buf) {
+ state->port.xmit_buf = (unsigned char *)page;
+ kfifo_init(&state->port.xmit_fifo, state->port.xmit_buf,
+ PAGE_SIZE);
uart_port_unlock(uport, flags);
} else {
uart_port_unlock(uport, flags);
@@ -387,8 +388,9 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
* can endup in printk() recursion.
*/
uart_port_lock(state, flags);
- xmit_buf = state->xmit.buf;
- state->xmit.buf = NULL;
+ xmit_buf = port->xmit_buf;
+ port->xmit_buf = NULL;
+ INIT_KFIFO(port->xmit_fifo);
uart_port_unlock(uport, flags);

free_page((unsigned long)xmit_buf);
@@ -552,22 +554,17 @@ static int uart_put_char(struct tty_struct *tty, u8 c)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port;
- struct circ_buf *circ;
unsigned long flags;
int ret = 0;

- circ = &state->xmit;
port = uart_port_lock(state, flags);
- if (!circ->buf) {
+ if (WARN_ON_ONCE(!state->port.xmit_buf)) {
uart_port_unlock(port, flags);
return 0;
}

- if (port && uart_circ_chars_free(circ) != 0) {
- circ->buf[circ->head] = c;
- circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1);
- ret = 1;
- }
+ if (port)
+ ret = kfifo_put(&state->port.xmit_fifo, c);
uart_port_unlock(port, flags);
return ret;
}
@@ -581,9 +578,8 @@ static ssize_t uart_write(struct tty_struct *tty, const u8 *buf, size_t count)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port;
- struct circ_buf *circ;
unsigned long flags;
- int c, ret = 0;
+ int ret = 0;

/*
* This means you called this function _after_ the port was
@@ -593,24 +589,13 @@ static ssize_t uart_write(struct tty_struct *tty, const u8 *buf, size_t count)
return -EL3HLT;

port = uart_port_lock(state, flags);
- circ = &state->xmit;
- if (!circ->buf) {
+ if (WARN_ON_ONCE(!state->port.xmit_buf)) {
uart_port_unlock(port, flags);
return 0;
}

- while (port) {
- c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
- if (count < c)
- c = count;
- if (c <= 0)
- break;
- memcpy(circ->buf + circ->head, buf, c);
- circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
- buf += c;
- count -= c;
- ret += c;
- }
+ if (port)
+ ret = kfifo_in(&state->port.xmit_fifo, buf, count);

__uart_start(state);
uart_port_unlock(port, flags);
@@ -625,7 +610,7 @@ static unsigned int uart_write_room(struct tty_struct *tty)
unsigned int ret;

port = uart_port_lock(state, flags);
- ret = uart_circ_chars_free(&state->xmit);
+ ret = kfifo_avail(&state->port.xmit_fifo);
uart_port_unlock(port, flags);
return ret;
}
@@ -638,7 +623,7 @@ static unsigned int uart_chars_in_buffer(struct tty_struct *tty)
unsigned int ret;

port = uart_port_lock(state, flags);
- ret = uart_circ_chars_pending(&state->xmit);
+ ret = kfifo_len(&state->port.xmit_fifo);
uart_port_unlock(port, flags);
return ret;
}
@@ -661,7 +646,7 @@ static void uart_flush_buffer(struct tty_struct *tty)
port = uart_port_lock(state, flags);
if (!port)
return;
- uart_circ_clear(&state->xmit);
+ kfifo_reset(&state->port.xmit_fifo);
if (port->ops->flush_buffer)
port->ops->flush_buffer(port);
uart_port_unlock(port, flags);
@@ -1064,7 +1049,7 @@ static int uart_get_lsr_info(struct tty_struct *tty,
* interrupt happens).
*/
if (uport->x_char ||
- ((uart_circ_chars_pending(&state->xmit) > 0) &&
+ (!kfifo_is_empty(&state->port.xmit_fifo) &&
!uart_tx_stopped(uport)))
result &= ~TIOCSER_TEMT;

@@ -1788,8 +1773,9 @@ static void uart_tty_port_shutdown(struct tty_port *port)
* Free the transmit buffer.
*/
uart_port_lock_irq(uport);
- buf = state->xmit.buf;
- state->xmit.buf = NULL;
+ buf = port->xmit_buf;
+ port->xmit_buf = NULL;
+ INIT_KFIFO(port->xmit_fifo);
uart_port_unlock_irq(uport);

free_page((unsigned long)buf);
diff --git a/drivers/tty/serial/serial_port.c b/drivers/tty/serial/serial_port.c
index 22b9eeb23e68..3408c8827561 100644
--- a/drivers/tty/serial/serial_port.c
+++ b/drivers/tty/serial/serial_port.c
@@ -23,7 +23,7 @@
static int __serial_port_busy(struct uart_port *port)
{
return !uart_tx_stopped(port) &&
- uart_circ_chars_pending(&port->state->xmit);
+ !kfifo_is_empty(&port->state->port.xmit_fifo);
}

static int serial_port_runtime_resume(struct device *dev)
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index e512eaa57ed5..97031db26ae4 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -585,7 +585,7 @@ static void sci_start_tx(struct uart_port *port)
sci_serial_out(port, SCSCR, new);
}

- if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) &&
+ if (s->chan_tx && !kfifo_is_empty(&port->state->port.xmit_fifo) &&
dma_submit_error(s->cookie_tx)) {
if (s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE)
/* Switch irq from SCIF to DMA */
@@ -817,7 +817,7 @@ static int sci_rxfill(struct uart_port *port)

static void sci_transmit_chars(struct uart_port *port)
{
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
unsigned int stopped = uart_tx_stopped(port);
unsigned short status;
unsigned short ctrl;
@@ -826,7 +826,7 @@ static void sci_transmit_chars(struct uart_port *port)
status = sci_serial_in(port, SCxSR);
if (!(status & SCxSR_TDxE(port))) {
ctrl = sci_serial_in(port, SCSCR);
- if (uart_circ_empty(xmit))
+ if (kfifo_is_empty(&tport->xmit_fifo))
ctrl &= ~SCSCR_TIE;
else
ctrl |= SCSCR_TIE;
@@ -842,15 +842,14 @@ static void sci_transmit_chars(struct uart_port *port)
if (port->x_char) {
c = port->x_char;
port->x_char = 0;
- } else if (!uart_circ_empty(xmit) && !stopped) {
- c = xmit->buf[xmit->tail];
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- } else if (port->type == PORT_SCI && uart_circ_empty(xmit)) {
- ctrl = sci_serial_in(port, SCSCR);
- ctrl &= ~SCSCR_TE;
- sci_serial_out(port, SCSCR, ctrl);
- return;
- } else {
+ } else if (stopped || !kfifo_get(&tport->xmit_fifo, &c)) {
+ if (port->type == PORT_SCI &&
+ kfifo_is_empty(&tport->xmit_fifo)) {
+ ctrl = sci_serial_in(port, SCSCR);
+ ctrl &= ~SCSCR_TE;
+ sci_serial_out(port, SCSCR, ctrl);
+ return;
+ }
break;
}

@@ -861,9 +860,9 @@ static void sci_transmit_chars(struct uart_port *port)

sci_clear_SCxSR(port, SCxSR_TDxE_CLEAR(port));

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);
- if (uart_circ_empty(xmit)) {
+ if (kfifo_is_empty(&tport->xmit_fifo)) {
if (port->type == PORT_SCI) {
ctrl = sci_serial_in(port, SCSCR);
ctrl &= ~SCSCR_TIE;
@@ -1199,7 +1198,7 @@ static void sci_dma_tx_complete(void *arg)
{
struct sci_port *s = arg;
struct uart_port *port = &s->port;
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
unsigned long flags;

dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
@@ -1208,10 +1207,10 @@ static void sci_dma_tx_complete(void *arg)

uart_xmit_advance(port, s->tx_dma_len);

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);

- if (!uart_circ_empty(xmit)) {
+ if (!kfifo_is_empty(&tport->xmit_fifo)) {
s->cookie_tx = 0;
schedule_work(&s->work_tx);
} else {
@@ -1424,10 +1423,10 @@ static void sci_dma_tx_work_fn(struct work_struct *work)
struct dma_async_tx_descriptor *desc;
struct dma_chan *chan = s->chan_tx;
struct uart_port *port = &s->port;
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
unsigned long flags;
+ unsigned int tail;
dma_addr_t buf;
- int head, tail;

/*
* DMA is idle now.
@@ -1437,10 +1436,9 @@ static void sci_dma_tx_work_fn(struct work_struct *work)
* consistent xmit buffer state.
*/
uart_port_lock_irq(port);
- head = xmit->head;
- tail = xmit->tail;
+ s->tx_dma_len = kfifo_out_linear(&tport->xmit_fifo, &tail,
+ UART_XMIT_SIZE);
buf = s->tx_dma_addr + tail;
- s->tx_dma_len = CIRC_CNT_TO_END(head, tail, UART_XMIT_SIZE);
if (!s->tx_dma_len) {
/* Transmit buffer has been flushed */
uart_port_unlock_irq(port);
@@ -1469,8 +1467,8 @@ static void sci_dma_tx_work_fn(struct work_struct *work)
}

uart_port_unlock_irq(port);
- dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n",
- __func__, xmit->buf, tail, head, s->cookie_tx);
+ dev_dbg(port->dev, "%s: %p: %u, cookie %d\n",
+ __func__, tport->xmit_buf, tail, s->cookie_tx);

dma_async_issue_pending(chan);
return;
@@ -1585,6 +1583,7 @@ static struct dma_chan *sci_request_dma_chan(struct uart_port *port,
static void sci_request_dma(struct uart_port *port)
{
struct sci_port *s = to_sci_port(port);
+ struct tty_port *tport = &port->state->port;
struct dma_chan *chan;

dev_dbg(port->dev, "%s: port %d\n", __func__, port->line);
@@ -1613,7 +1612,7 @@ static void sci_request_dma(struct uart_port *port)
if (chan) {
/* UART circular tx buffer is an aligned page. */
s->tx_dma_addr = dma_map_single(chan->device->dev,
- port->state->xmit.buf,
+ tport->xmit_buf,
UART_XMIT_SIZE,
DMA_TO_DEVICE);
if (dma_mapping_error(chan->device->dev, s->tx_dma_addr)) {
@@ -1622,7 +1621,7 @@ static void sci_request_dma(struct uart_port *port)
} else {
dev_dbg(port->dev, "%s: mapped %lu@%p to %pad\n",
__func__, UART_XMIT_SIZE,
- port->state->xmit.buf, &s->tx_dma_addr);
+ tport->xmit_buf, &s->tx_dma_addr);

INIT_WORK(&s->work_tx, sci_dma_tx_work_fn);
s->chan_tx_saved = s->chan_tx = chan;
diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c
index 15f14fa593da..3fc54cc02a1f 100644
--- a/drivers/tty/serial/sprd_serial.c
+++ b/drivers/tty/serial/sprd_serial.c
@@ -227,13 +227,13 @@ static int sprd_tx_buf_remap(struct uart_port *port)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
+ unsigned char *tail;

- sp->tx_dma.trans_len =
- CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+ sp->tx_dma.trans_len = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail,
+ UART_XMIT_SIZE);

- sp->tx_dma.phys_addr = dma_map_single(port->dev,
- (void *)&(xmit->buf[xmit->tail]),
+ sp->tx_dma.phys_addr = dma_map_single(port->dev, tail,
sp->tx_dma.trans_len,
DMA_TO_DEVICE);
return dma_mapping_error(port->dev, sp->tx_dma.phys_addr);
@@ -244,7 +244,7 @@ static void sprd_complete_tx_dma(void *data)
struct uart_port *port = (struct uart_port *)data;
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
unsigned long flags;

uart_port_lock_irqsave(port, &flags);
@@ -253,10 +253,10 @@ static void sprd_complete_tx_dma(void *data)

uart_xmit_advance(port, sp->tx_dma.trans_len);

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);

- if (uart_circ_empty(xmit) || sprd_tx_buf_remap(port) ||
+ if (kfifo_is_empty(&tport->xmit_fifo) || sprd_tx_buf_remap(port) ||
sprd_tx_dma_config(port))
sp->tx_dma.trans_len = 0;

@@ -319,7 +319,7 @@ static void sprd_start_tx_dma(struct uart_port *port)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;

if (port->x_char) {
serial_out(port, SPRD_TXD, port->x_char);
@@ -328,7 +328,7 @@ static void sprd_start_tx_dma(struct uart_port *port)
return;
}

- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
sprd_stop_tx_dma(port);
return;
}
diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c
index a23e59551848..f91753a40a69 100644
--- a/drivers/tty/serial/st-asc.c
+++ b/drivers/tty/serial/st-asc.c
@@ -387,9 +387,9 @@ static unsigned int asc_get_mctrl(struct uart_port *port)
/* There are probably characters waiting to be transmitted. */
static void asc_start_tx(struct uart_port *port)
{
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;

- if (!uart_circ_empty(xmit))
+ if (!kfifo_is_empty(&tport->xmit_fifo))
asc_enable_tx_interrupts(port);
}

diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
index 58d169e5c1db..8c66abcfe6ca 100644
--- a/drivers/tty/serial/stm32-usart.c
+++ b/drivers/tty/serial/stm32-usart.c
@@ -696,18 +696,23 @@ static void stm32_usart_transmit_chars_pio(struct uart_port *port)
{
struct stm32_port *stm32_port = to_stm32_port(port);
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
+
+ while (1) {
+ unsigned char ch;

- while (!uart_circ_empty(xmit)) {
/* Check that TDR is empty before filling FIFO */
if (!(readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE))
break;
- writel_relaxed(xmit->buf[xmit->tail], port->membase + ofs->tdr);
- uart_xmit_advance(port, 1);
+
+ if (!uart_fifo_get(port, &ch))
+ break;
+
+ writel_relaxed(ch, port->membase + ofs->tdr);
}

/* rely on TXE irq (mask or unmask) for sending remaining data */
- if (uart_circ_empty(xmit))
+ if (kfifo_is_empty(&tport->xmit_fifo))
stm32_usart_tx_interrupt_disable(port);
else
stm32_usart_tx_interrupt_enable(port);
@@ -716,7 +721,7 @@ static void stm32_usart_transmit_chars_pio(struct uart_port *port)
static void stm32_usart_transmit_chars_dma(struct uart_port *port)
{
struct stm32_port *stm32port = to_stm32_port(port);
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
struct dma_async_tx_descriptor *desc = NULL;
unsigned int count;
int ret;
@@ -728,25 +733,8 @@ static void stm32_usart_transmit_chars_dma(struct uart_port *port)
return;
}

- count = uart_circ_chars_pending(xmit);
-
- if (count > TX_BUF_L)
- count = TX_BUF_L;
-
- if (xmit->tail < xmit->head) {
- memcpy(&stm32port->tx_buf[0], &xmit->buf[xmit->tail], count);
- } else {
- size_t one = UART_XMIT_SIZE - xmit->tail;
- size_t two;
-
- if (one > count)
- one = count;
- two = count - one;
-
- memcpy(&stm32port->tx_buf[0], &xmit->buf[xmit->tail], one);
- if (two)
- memcpy(&stm32port->tx_buf[one], &xmit->buf[0], two);
- }
+ count = kfifo_out_peek(&tport->xmit_fifo, &stm32port->tx_buf[0],
+ TX_BUF_L);

desc = dmaengine_prep_slave_single(stm32port->tx_ch,
stm32port->tx_dma_buf,
@@ -792,14 +780,14 @@ static void stm32_usart_transmit_chars(struct uart_port *port)
{
struct stm32_port *stm32_port = to_stm32_port(port);
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
u32 isr;
int ret;

if (!stm32_port->hw_flow_control &&
port->rs485.flags & SER_RS485_ENABLED &&
(port->x_char ||
- !(uart_circ_empty(xmit) || uart_tx_stopped(port)))) {
+ !(kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)))) {
stm32_usart_tc_interrupt_disable(port);
stm32_usart_rs485_rts_enable(port);
}
@@ -826,7 +814,7 @@ static void stm32_usart_transmit_chars(struct uart_port *port)
return;
}

- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
stm32_usart_tx_interrupt_disable(port);
return;
}
@@ -841,10 +829,10 @@ static void stm32_usart_transmit_chars(struct uart_port *port)
else
stm32_usart_transmit_chars_pio(port);

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);

- if (uart_circ_empty(xmit)) {
+ if (kfifo_is_empty(&tport->xmit_fifo)) {
stm32_usart_tx_interrupt_disable(port);
if (!stm32_port->hw_flow_control &&
port->rs485.flags & SER_RS485_ENABLED) {
@@ -967,9 +955,9 @@ static void stm32_usart_stop_tx(struct uart_port *port)
/* There are probably characters waiting to be transmitted. */
static void stm32_usart_start_tx(struct uart_port *port)
{
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;

- if (uart_circ_empty(xmit) && !port->x_char) {
+ if (kfifo_is_empty(&tport->xmit_fifo) && !port->x_char) {
stm32_usart_rs485_rts_disable(port);
return;
}
diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c
index 8d612ab80680..7f60679fdde1 100644
--- a/drivers/tty/serial/sunhv.c
+++ b/drivers/tty/serial/sunhv.c
@@ -39,10 +39,13 @@ static char *con_read_page;

static int hung_up = 0;

-static void transmit_chars_putchar(struct uart_port *port, struct circ_buf *xmit)
+static void transmit_chars_putchar(struct uart_port *port,
+ struct tty_port *tport)
{
- while (!uart_circ_empty(xmit)) {
- long status = sun4v_con_putchar(xmit->buf[xmit->tail]);
+ unsigned char ch;
+
+ while (kfifo_peek(&tport->xmit_fifo, &ch)) {
+ long status = sun4v_con_putchar(ch);

if (status != HV_EOK)
break;
@@ -51,14 +54,16 @@ static void transmit_chars_putchar(struct uart_port *port, struct circ_buf *xmit
}
}

-static void transmit_chars_write(struct uart_port *port, struct circ_buf *xmit)
+static void transmit_chars_write(struct uart_port *port, struct tty_port *tport)
{
- while (!uart_circ_empty(xmit)) {
- unsigned long ra = __pa(xmit->buf + xmit->tail);
- unsigned long len, status, sent;
+ while (!kfifo_is_empty(&tport->xmit_fifo)) {
+ unsigned long len, ra, status, sent;
+ unsigned char *tail;
+
+ len = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail,
+ UART_XMIT_SIZE);
+ ra = __pa(tail);

- len = CIRC_CNT_TO_END(xmit->head, xmit->tail,
- UART_XMIT_SIZE);
status = sun4v_con_write(ra, len, &sent);
if (status != HV_EOK)
break;
@@ -165,7 +170,7 @@ static int receive_chars_read(struct uart_port *port)
}

struct sunhv_ops {
- void (*transmit_chars)(struct uart_port *port, struct circ_buf *xmit);
+ void (*transmit_chars)(struct uart_port *port, struct tty_port *tport);
int (*receive_chars)(struct uart_port *port);
};

@@ -196,18 +201,18 @@ static struct tty_port *receive_chars(struct uart_port *port)

static void transmit_chars(struct uart_port *port)
{
- struct circ_buf *xmit;
+ struct tty_port *tport;

if (!port->state)
return;

- xmit = &port->state->xmit;
- if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+ tport = &port->state->port;
+ if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port))
return;

- sunhv_ops->transmit_chars(port, xmit);
+ sunhv_ops->transmit_chars(port, tport);

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);
}

diff --git a/drivers/tty/serial/sunplus-uart.c b/drivers/tty/serial/sunplus-uart.c
index f5e29eb4a4ce..abf7c449308d 100644
--- a/drivers/tty/serial/sunplus-uart.c
+++ b/drivers/tty/serial/sunplus-uart.c
@@ -200,7 +200,7 @@ static void sunplus_break_ctl(struct uart_port *port, int ctl)

static void transmit_chars(struct uart_port *port)
{
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;

if (port->x_char) {
sp_uart_put_char(port, port->x_char);
@@ -209,22 +209,24 @@ static void transmit_chars(struct uart_port *port)
return;
}

- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
sunplus_stop_tx(port);
return;
}

do {
- sp_uart_put_char(port, xmit->buf[xmit->tail]);
- uart_xmit_advance(port, 1);
- if (uart_circ_empty(xmit))
+ unsigned char ch;
+
+ if (!uart_fifo_get(port, &ch))
break;
+
+ sp_uart_put_char(port, ch);
} while (sunplus_tx_buf_not_full(port));

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);

- if (uart_circ_empty(xmit))
+ if (kfifo_is_empty(&tport->xmit_fifo))
sunplus_stop_tx(port);
}

diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c
index 1ea2f33a07a7..1acbe2fba746 100644
--- a/drivers/tty/serial/sunsab.c
+++ b/drivers/tty/serial/sunsab.c
@@ -232,7 +232,7 @@ static void sunsab_tx_idle(struct uart_sunsab_port *);
static void transmit_chars(struct uart_sunsab_port *up,
union sab82532_irq_status *stat)
{
- struct circ_buf *xmit = &up->port.state->xmit;
+ struct tty_port *tport = &up->port.state->port;
int i;

if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) {
@@ -252,7 +252,7 @@ static void transmit_chars(struct uart_sunsab_port *up,
set_bit(SAB82532_XPR, &up->irqflags);
sunsab_tx_idle(up);

- if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
+ if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(&up->port)) {
up->interrupt_mask1 |= SAB82532_IMR1_XPR;
writeb(up->interrupt_mask1, &up->regs->w.imr1);
return;
@@ -265,21 +265,22 @@ static void transmit_chars(struct uart_sunsab_port *up,
/* Stuff 32 bytes into Transmit FIFO. */
clear_bit(SAB82532_XPR, &up->irqflags);
for (i = 0; i < up->port.fifosize; i++) {
- writeb(xmit->buf[xmit->tail],
- &up->regs->w.xfifo[i]);
- uart_xmit_advance(&up->port, 1);
- if (uart_circ_empty(xmit))
+ unsigned char ch;
+
+ if (!uart_fifo_get(&up->port, &ch))
break;
+
+ writeb(ch, &up->regs->w.xfifo[i]);
}

/* Issue a Transmit Frame command. */
sunsab_cec_wait(up);
writeb(SAB82532_CMDR_XF, &up->regs->w.cmdr);

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&up->port);

- if (uart_circ_empty(xmit))
+ if (kfifo_is_empty(&tport->xmit_fifo))
sunsab_stop_tx(&up->port);
}

@@ -435,10 +436,10 @@ static void sunsab_start_tx(struct uart_port *port)
{
struct uart_sunsab_port *up =
container_of(port, struct uart_sunsab_port, port);
- struct circ_buf *xmit = &up->port.state->xmit;
+ struct tty_port *tport = &up->port.state->port;
int i;

- if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+ if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port))
return;

up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS|SAB82532_IMR1_XPR);
@@ -451,11 +452,12 @@ static void sunsab_start_tx(struct uart_port *port)
clear_bit(SAB82532_XPR, &up->irqflags);

for (i = 0; i < up->port.fifosize; i++) {
- writeb(xmit->buf[xmit->tail],
- &up->regs->w.xfifo[i]);
- uart_xmit_advance(&up->port, 1);
- if (uart_circ_empty(xmit))
+ unsigned char ch;
+
+ if (!uart_fifo_get(&up->port, &ch))
break;
+
+ writeb(ch, &up->regs->w.xfifo[i]);
}

/* Issue a Transmit Frame command. */
diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c
index c8b65f4b2710..67a5fc70bb4b 100644
--- a/drivers/tty/serial/sunsu.c
+++ b/drivers/tty/serial/sunsu.c
@@ -396,7 +396,8 @@ receive_chars(struct uart_sunsu_port *up, unsigned char *status)

static void transmit_chars(struct uart_sunsu_port *up)
{
- struct circ_buf *xmit = &up->port.state->xmit;
+ struct tty_port *tport = &up->port.state->port;
+ unsigned char ch;
int count;

if (up->port.x_char) {
@@ -409,23 +410,23 @@ static void transmit_chars(struct uart_sunsu_port *up)
sunsu_stop_tx(&up->port);
return;
}
- if (uart_circ_empty(xmit)) {
+ if (kfifo_is_empty(&tport->xmit_fifo)) {
__stop_tx(up);
return;
}

count = up->port.fifosize;
do {
- serial_out(up, UART_TX, xmit->buf[xmit->tail]);
- uart_xmit_advance(&up->port, 1);
- if (uart_circ_empty(xmit))
+ if (!uart_fifo_get(&up->port, &ch))
break;
+
+ serial_out(up, UART_TX, ch);
} while (--count > 0);

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&up->port);

- if (uart_circ_empty(xmit))
+ if (kfifo_is_empty(&tport->xmit_fifo))
__stop_tx(up);
}

diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c
index c99289c6c8f8..71758ad4241c 100644
--- a/drivers/tty/serial/sunzilog.c
+++ b/drivers/tty/serial/sunzilog.c
@@ -453,7 +453,8 @@ static void sunzilog_status_handle(struct uart_sunzilog_port *up,
static void sunzilog_transmit_chars(struct uart_sunzilog_port *up,
struct zilog_channel __iomem *channel)
{
- struct circ_buf *xmit;
+ struct tty_port *tport;
+ unsigned char ch;

if (ZS_IS_CONS(up)) {
unsigned char status = readb(&channel->control);
@@ -496,21 +497,20 @@ static void sunzilog_transmit_chars(struct uart_sunzilog_port *up,

if (up->port.state == NULL)
goto ack_tx_int;
- xmit = &up->port.state->xmit;
- if (uart_circ_empty(xmit))
- goto ack_tx_int;
+ tport = &up->port.state->port;

if (uart_tx_stopped(&up->port))
goto ack_tx_int;

+ if (!uart_fifo_get(&up->port, &ch))
+ goto ack_tx_int;
+
up->flags |= SUNZILOG_FLAG_TX_ACTIVE;
- writeb(xmit->buf[xmit->tail], &channel->data);
+ writeb(ch, &channel->data);
ZSDELAY();
ZS_WSYNC(channel);

- uart_xmit_advance(&up->port, 1);
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&up->port);

return;
@@ -700,17 +700,16 @@ static void sunzilog_start_tx(struct uart_port *port)
port->icount.tx++;
port->x_char = 0;
} else {
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
+ unsigned char ch;

- if (uart_circ_empty(xmit))
+ if (!uart_fifo_get(&up->port, &ch))
return;
- writeb(xmit->buf[xmit->tail], &channel->data);
+ writeb(ch, &channel->data);
ZSDELAY();
ZS_WSYNC(channel);

- uart_xmit_advance(port, 1);
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&up->port);
}
}
diff --git a/drivers/tty/serial/tegra-tcu.c b/drivers/tty/serial/tegra-tcu.c
index d9c78320eb02..21ca5fcadf49 100644
--- a/drivers/tty/serial/tegra-tcu.c
+++ b/drivers/tty/serial/tegra-tcu.c
@@ -91,15 +91,17 @@ static void tegra_tcu_write(struct tegra_tcu *tcu, const char *s,
static void tegra_tcu_uart_start_tx(struct uart_port *port)
{
struct tegra_tcu *tcu = port->private_data;
- struct circ_buf *xmit = &port->state->xmit;
- unsigned long count;
+ struct tty_port *tport = &port->state->port;
+ unsigned char *tail;
+ unsigned int count;

for (;;) {
- count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+ count = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail,
+ UART_XMIT_SIZE);
if (!count)
break;

- tegra_tcu_write(tcu, &xmit->buf[xmit->tail], count);
+ tegra_tcu_write(tcu, tail, count);
uart_xmit_advance(port, count);
}

diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c
index 4bc89a9b380a..43fa0938b5e3 100644
--- a/drivers/tty/serial/timbuart.c
+++ b/drivers/tty/serial/timbuart.c
@@ -95,14 +95,11 @@ static void timbuart_rx_chars(struct uart_port *port)

static void timbuart_tx_chars(struct uart_port *port)
{
- struct circ_buf *xmit = &port->state->xmit;
+ unsigned char ch;

while (!(ioread32(port->membase + TIMBUART_ISR) & TXBF) &&
- !uart_circ_empty(xmit)) {
- iowrite8(xmit->buf[xmit->tail],
- port->membase + TIMBUART_TXFIFO);
- uart_xmit_advance(port, 1);
- }
+ uart_fifo_get(port, &ch))
+ iowrite8(ch, port->membase + TIMBUART_TXFIFO);

dev_dbg(port->dev,
"%s - total written %d bytes, CTL: %x, RTS: %x, baud: %x\n",
@@ -117,9 +114,9 @@ static void timbuart_handle_tx_port(struct uart_port *port, u32 isr, u32 *ier)
{
struct timbuart_port *uart =
container_of(port, struct timbuart_port, port);
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;

- if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+ if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port))
return;

if (port->x_char)
@@ -130,7 +127,7 @@ static void timbuart_handle_tx_port(struct uart_port *port, u32 isr, u32 *ier)
/* clear all TX interrupts */
iowrite32(TXFLAGS, port->membase + TIMBUART_ISR);

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);
} else
/* Re-enable any tx interrupt */
@@ -141,7 +138,7 @@ static void timbuart_handle_tx_port(struct uart_port *port, u32 isr, u32 *ier)
* we wake up the upper layer later when we got the interrupt
* to give it some time to go out...
*/
- if (!uart_circ_empty(xmit))
+ if (!kfifo_is_empty(&tport->xmit_fifo))
*ier |= TXBAE;

dev_dbg(port->dev, "%s - leaving\n", __func__);
diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c
index 10ba41b7be99..68357ac8ffe3 100644
--- a/drivers/tty/serial/uartlite.c
+++ b/drivers/tty/serial/uartlite.c
@@ -189,7 +189,8 @@ static int ulite_receive(struct uart_port *port, int stat)

static int ulite_transmit(struct uart_port *port, int stat)
{
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
+ unsigned char ch;

if (stat & ULITE_STATUS_TXFULL)
return 0;
@@ -201,14 +202,16 @@ static int ulite_transmit(struct uart_port *port, int stat)
return 1;
}

- if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+ if (uart_tx_stopped(port))
+ return 0;
+
+ if (!uart_fifo_get(port, &ch))
return 0;

- uart_out32(xmit->buf[xmit->tail], ULITE_TX, port);
- uart_xmit_advance(port, 1);
+ uart_out32(ch, ULITE_TX, port);

/* wake up */
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);

return 1;
diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c
index 397b95dff7ed..53bb8c5ef499 100644
--- a/drivers/tty/serial/ucc_uart.c
+++ b/drivers/tty/serial/ucc_uart.c
@@ -334,7 +334,7 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port)
unsigned char *p;
unsigned int count;
struct uart_port *port = &qe_port->port;
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;

/* Handle xon/xoff */
if (port->x_char) {
@@ -358,7 +358,7 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port)
return 1;
}

- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
qe_uart_stop_tx(port);
return 0;
}
@@ -366,16 +366,10 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port)
/* Pick next descriptor and fill from buffer */
bdp = qe_port->tx_cur;

- while (!(ioread16be(&bdp->status) & BD_SC_READY) && !uart_circ_empty(xmit)) {
- count = 0;
+ while (!(ioread16be(&bdp->status) & BD_SC_READY) &&
+ !kfifo_is_empty(&tport->xmit_fifo)) {
p = qe2cpu_addr(ioread32be(&bdp->buf), qe_port);
- while (count < qe_port->tx_fifosize) {
- *p++ = xmit->buf[xmit->tail];
- uart_xmit_advance(port, 1);
- count++;
- if (uart_circ_empty(xmit))
- break;
- }
+ count = uart_fifo_out(port, p, qe_port->tx_fifosize);

iowrite16be(count, &bdp->length);
qe_setbits_be16(&bdp->status, BD_SC_READY);
@@ -388,10 +382,10 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port)
}
qe_port->tx_cur = bdp;

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);

- if (uart_circ_empty(xmit)) {
+ if (kfifo_is_empty(&tport->xmit_fifo)) {
/* The kernel buffer is empty, so turn off TX interrupts. We
don't need to be told when the QE is finished transmitting
the data. */
diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index 5f48ec37cb25..de3487206bcb 100644
--- a/drivers/tty/serial/xilinx_uartps.c
+++ b/drivers/tty/serial/xilinx_uartps.c
@@ -425,32 +425,32 @@ static void cdns_uart_handle_tx(void *dev_id)
{
struct uart_port *port = (struct uart_port *)dev_id;
struct cdns_uart *cdns_uart = port->private_data;
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
unsigned int numbytes;
+ unsigned char ch;

- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
/* Disable the TX Empty interrupt */
writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IDR);
return;
}

numbytes = port->fifosize;
- while (numbytes && !uart_circ_empty(xmit) &&
- !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL)) {
-
- writel(xmit->buf[xmit->tail], port->membase + CDNS_UART_FIFO);
- uart_xmit_advance(port, 1);
+ while (numbytes &&
+ !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL) &&
+ uart_fifo_get(port, &ch)) {
+ writel(ch, port->membase + CDNS_UART_FIFO);
numbytes--;
}

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);

/* Enable the TX Empty interrupt */
writel(CDNS_UART_IXR_TXEMPTY, cdns_uart->port->membase + CDNS_UART_IER);

if (cdns_uart->port->rs485.flags & SER_RS485_ENABLED &&
- (uart_circ_empty(xmit) || uart_tx_stopped(port))) {
+ (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port))) {
cdns_uart->tx_timer.function = &cdns_rs485_rx_callback;
hrtimer_start(&cdns_uart->tx_timer,
ns_to_ktime(cdns_calc_after_tx_delay(cdns_uart)), HRTIMER_MODE_REL);
@@ -723,7 +723,7 @@ static void cdns_uart_start_tx(struct uart_port *port)
status |= CDNS_UART_CR_TX_EN;
writel(status, port->membase + CDNS_UART_CR);

- if (uart_circ_empty(&port->state->xmit))
+ if (kfifo_is_empty(&port->state->port.xmit_fifo))
return;

/* Clear the TX Empty interrupt */
diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c
index 65ca4da6e368..79ea7108a0f3 100644
--- a/drivers/tty/serial/zs.c
+++ b/drivers/tty/serial/zs.c
@@ -606,7 +606,8 @@ static void zs_receive_chars(struct zs_port *zport)

static void zs_raw_transmit_chars(struct zs_port *zport)
{
- struct circ_buf *xmit = &zport->port.state->xmit;
+ struct tty_port *tport = &zport->port.state->port;
+ unsigned char ch;

/* XON/XOFF chars. */
if (zport->port.x_char) {
@@ -617,20 +618,20 @@ static void zs_raw_transmit_chars(struct zs_port *zport)
}

/* If nothing to do or stopped or hardware stopped. */
- if (uart_circ_empty(xmit) || uart_tx_stopped(&zport->port)) {
+ if (uart_tx_stopped(&zport->port) ||
+ !uart_fifo_get(&zport->port, &ch)) {
zs_raw_stop_tx(zport);
return;
}

/* Send char. */
- write_zsdata(zport, xmit->buf[xmit->tail]);
- uart_xmit_advance(&zport->port, 1);
+ write_zsdata(zport, ch);

- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&zport->port);

/* Are we are done? */
- if (uart_circ_empty(xmit))
+ if (kfifo_is_empty(&tport->xmit_fifo))
zs_raw_stop_tx(zport);
}

diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 0a0f6e21d40e..8cb65f50e830 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -11,7 +11,6 @@
#include <linux/compiler.h>
#include <linux/console.h>
#include <linux/interrupt.h>
-#include <linux/circ_buf.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/tty.h>
@@ -699,7 +698,6 @@ struct uart_state {
struct tty_port port;

enum uart_pm_state pm_state;
- struct circ_buf xmit;

atomic_t refcount;
wait_queue_head_t remove_wait;
@@ -723,12 +721,35 @@ struct uart_state {
*/
static inline void uart_xmit_advance(struct uart_port *up, unsigned int chars)
{
- struct circ_buf *xmit = &up->state->xmit;
+ struct tty_port *tport = &up->state->port;

- xmit->tail = (xmit->tail + chars) & (UART_XMIT_SIZE - 1);
+ kfifo_skip_count(&tport->xmit_fifo, chars);
up->icount.tx += chars;
}

+static inline unsigned int uart_fifo_out(struct uart_port *up,
+ unsigned char *buf, unsigned int chars)
+{
+ struct tty_port *tport = &up->state->port;
+
+ chars = kfifo_out(&tport->xmit_fifo, buf, chars);
+ up->icount.tx += chars;
+
+ return chars;
+}
+
+static inline unsigned int uart_fifo_get(struct uart_port *up,
+ unsigned char *ch)
+{
+ struct tty_port *tport = &up->state->port;
+ unsigned int chars;
+
+ chars = kfifo_get(&tport->xmit_fifo, ch);
+ up->icount.tx += chars;
+
+ return chars;
+}
+
struct module;
struct tty_driver;

@@ -764,7 +785,7 @@ enum UART_TX_FLAGS {
for_test, for_post) \
({ \
struct uart_port *__port = (uport); \
- struct circ_buf *xmit = &__port->state->xmit; \
+ struct tty_port *__tport = &__port->state->port; \
unsigned int pending; \
\
for (; (for_test) && (tx_ready); (for_post), __port->icount.tx++) { \
@@ -775,17 +796,18 @@ enum UART_TX_FLAGS {
continue; \
} \
\
- if (uart_circ_empty(xmit) || uart_tx_stopped(__port)) \
+ if (uart_tx_stopped(__port)) \
+ break; \
+ \
+ if (!kfifo_get(&__tport->xmit_fifo, &(ch))) \
break; \
\
- (ch) = xmit->buf[xmit->tail]; \
(put_char); \
- xmit->tail = (xmit->tail + 1) % UART_XMIT_SIZE; \
} \
\
(tx_done); \
\
- pending = uart_circ_chars_pending(xmit); \
+ pending = kfifo_len(&__tport->xmit_fifo); \
if (pending < WAKEUP_CHARS) { \
uart_write_wakeup(__port); \
\
@@ -974,15 +996,6 @@ bool uart_match_port(const struct uart_port *port1,
int uart_suspend_port(struct uart_driver *reg, struct uart_port *port);
int uart_resume_port(struct uart_driver *reg, struct uart_port *port);

-#define uart_circ_empty(circ) ((circ)->head == (circ)->tail)
-#define uart_circ_clear(circ) ((circ)->head = (circ)->tail = 0)
-
-#define uart_circ_chars_pending(circ) \
- (CIRC_CNT((circ)->head, (circ)->tail, UART_XMIT_SIZE))
-
-#define uart_circ_chars_free(circ) \
- (CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE))
-
static inline int uart_tx_stopped(struct uart_port *port)
{
struct tty_struct *tty = port->state->port.tty;
--
2.44.0


2024-04-05 06:15:37

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 13/15] tty: atmel_serial: use single DMA mapping for TX

dma_map_single() provides much easier interface for simple mappings as
used for TX in atmel_serial. So switch to that, removing all the s-g
unnecessary handling.

Note that it is not easy (maybe impossible) to use kfifo_dma_* API for
atmel's serial purposes. It handles DMA very specially.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
Cc: Richard Genoud <[email protected]>
Cc: Nicolas Ferre <[email protected]>
Cc: Alexandre Belloni <[email protected]>
Cc: Claudiu Beznea <[email protected]>
Cc: [email protected]
---
drivers/tty/serial/atmel_serial.c | 35 +++++++++++++------------------
1 file changed, 14 insertions(+), 21 deletions(-)

diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 5bb5e4303754..69ec80ffc97b 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -132,7 +132,7 @@ struct atmel_uart_port {
struct dma_async_tx_descriptor *desc_rx;
dma_cookie_t cookie_tx;
dma_cookie_t cookie_rx;
- struct scatterlist sg_tx;
+ dma_addr_t tx_phys;
struct scatterlist sg_rx;
struct tasklet_struct tasklet_rx;
struct tasklet_struct tasklet_tx;
@@ -904,8 +904,8 @@ static void atmel_release_tx_dma(struct uart_port *port)
if (chan) {
dmaengine_terminate_all(chan);
dma_release_channel(chan);
- dma_unmap_sg(port->dev, &atmel_port->sg_tx, 1,
- DMA_TO_DEVICE);
+ dma_unmap_single(port->dev, atmel_port->tx_phys,
+ UART_XMIT_SIZE, DMA_TO_DEVICE);
}

atmel_port->desc_tx = NULL;
@@ -922,7 +922,7 @@ static void atmel_tx_dma(struct uart_port *port)
struct tty_port *tport = &port->state->port;
struct dma_chan *chan = atmel_port->chan_tx;
struct dma_async_tx_descriptor *desc;
- struct scatterlist sgl[2], *sg, *sg_tx = &atmel_port->sg_tx;
+ struct scatterlist sgl[2], *sg;
unsigned int tx_len, tail, part1_len, part2_len, sg_len;
dma_addr_t phys_addr;

@@ -955,7 +955,7 @@ static void atmel_tx_dma(struct uart_port *port)

sg_init_table(sgl, 2);
sg_len = 0;
- phys_addr = sg_dma_address(sg_tx) + tail;
+ phys_addr = atmel_port->tx_phys + tail;
if (part1_len) {
sg = &sgl[sg_len++];
sg_dma_address(sg) = phys_addr;
@@ -987,7 +987,8 @@ static void atmel_tx_dma(struct uart_port *port)
return;
}

- dma_sync_sg_for_device(port->dev, sg_tx, 1, DMA_TO_DEVICE);
+ dma_sync_single_for_device(port->dev, atmel_port->tx_phys,
+ UART_XMIT_SIZE, DMA_TO_DEVICE);

atmel_port->desc_tx = desc;
desc->callback = atmel_complete_tx_dma;
@@ -1014,7 +1015,7 @@ static int atmel_prepare_tx_dma(struct uart_port *port)
dma_cap_mask_t mask;
struct dma_slave_config config;
struct dma_chan *chan;
- int ret, nent;
+ int ret;

dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
@@ -1029,26 +1030,18 @@ static int atmel_prepare_tx_dma(struct uart_port *port)
dma_chan_name(atmel_port->chan_tx));

spin_lock_init(&atmel_port->lock_tx);
- sg_init_table(&atmel_port->sg_tx, 1);
/* UART circular tx buffer is an aligned page. */
BUG_ON(!PAGE_ALIGNED(tport->xmit_buf));
- sg_set_page(&atmel_port->sg_tx,
- virt_to_page(tport->xmit_buf),
- UART_XMIT_SIZE,
- offset_in_page(tport->xmit_buf));
- nent = dma_map_sg(port->dev,
- &atmel_port->sg_tx,
- 1,
- DMA_TO_DEVICE);
+ atmel_port->tx_phys = dma_map_single(port->dev, tport->xmit_buf,
+ UART_XMIT_SIZE, DMA_TO_DEVICE);

- if (!nent) {
+ if (dma_mapping_error(port->dev, atmel_port->tx_phys)) {
dev_dbg(port->dev, "need to release resource of dma\n");
goto chan_err;
} else {
- dev_dbg(port->dev, "%s: mapped %d@%p to %pad\n", __func__,
- sg_dma_len(&atmel_port->sg_tx),
- tport->xmit_buf,
- &sg_dma_address(&atmel_port->sg_tx));
+ dev_dbg(port->dev, "%s: mapped %lu@%p to %pad\n", __func__,
+ UART_XMIT_SIZE, tport->xmit_buf,
+ &atmel_port->tx_phys);
}

/* Configure the slave DMA */
--
2.44.0


2024-04-05 06:17:20

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 14/15] tty: atmel_serial: define macro for RX size

It is repeated in the code and there is also a big warning by
ATMEL_SERIAL_RINGSIZE. So define ATMEL_SERIAL_RX_SIZE and use it
appropriatelly.

The macro uses array_size() and kmalloc_array() is switched to
kmalloc().

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
Cc: Richard Genoud <[email protected]>
Cc: Nicolas Ferre <[email protected]>
Cc: Alexandre Belloni <[email protected]>
Cc: Claudiu Beznea <[email protected]>
Cc: [email protected]
---
drivers/tty/serial/atmel_serial.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 69ec80ffc97b..5cde5077c429 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -96,7 +96,9 @@ struct atmel_uart_char {
* can contain up to 1024 characters in PIO mode and up to 4096 characters in
* DMA mode.
*/
-#define ATMEL_SERIAL_RINGSIZE 1024
+#define ATMEL_SERIAL_RINGSIZE 1024
+#define ATMEL_SERIAL_RX_SIZE array_size(sizeof(struct atmel_uart_char), \
+ ATMEL_SERIAL_RINGSIZE)

/*
* at91: 6 USARTs and one DBGU port (SAM9260)
@@ -1208,7 +1210,7 @@ static int atmel_prepare_rx_dma(struct uart_port *port)
BUG_ON(!PAGE_ALIGNED(ring->buf));
sg_set_page(&atmel_port->sg_rx,
virt_to_page(ring->buf),
- sizeof(struct atmel_uart_char) * ATMEL_SERIAL_RINGSIZE,
+ ATMEL_SERIAL_RX_SIZE,
offset_in_page(ring->buf));
nent = dma_map_sg(port->dev,
&atmel_port->sg_rx,
@@ -2947,9 +2949,7 @@ static int atmel_serial_probe(struct platform_device *pdev)

if (!atmel_use_pdc_rx(&atmel_port->uart)) {
ret = -ENOMEM;
- data = kmalloc_array(ATMEL_SERIAL_RINGSIZE,
- sizeof(struct atmel_uart_char),
- GFP_KERNEL);
+ data = kmalloc(ATMEL_SERIAL_RX_SIZE, GFP_KERNEL);
if (!data)
goto err_clk_disable_unprepare;
atmel_port->rx_ring.buf = data;
--
2.44.0


2024-04-15 13:26:31

by Marek Szyprowski

[permalink] [raw]
Subject: Re: [PATCH 12/15] tty: serial: switch from circ_buf to kfifo

Dear All,

On 05.04.2024 08:08, Jiri Slaby (SUSE) wrote:
> Switch from struct circ_buf to proper kfifo. kfifo provides much better
> API, esp. when wrap-around of the buffer needs to be taken into account.
> Look at pl011_dma_tx_refill() or cpm_uart_tx_pump() changes for example.
>
> Kfifo API can also fill in scatter-gather DMA structures, so it easier
> for that use case too. Look at lpuart_dma_tx() for example. Note that
> not all drivers can be converted to that (like atmel_serial), they
> handle DMA specially.
>
> Note that usb-serial uses kfifo for TX for ages.
>
> omap needed a bit more care as it needs to put a char into FIFO to start
> the DMA transfer when OMAP_DMA_TX_KICK is set. In that case, we have to
> do kfifo_dma_out_prepare twice: once to find out the tx_size (to find
> out if it is worths to do DMA at all -- size >= 4), the second time for
> the actual transfer.
>
> All traces of circ_buf are removed from serial_core.h (and its struct
> uart_state).
>
> Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
> ...

This patch landed in linux-next as commit 1788cf6a91d9 ("tty: serial:
switch from circ_buf to kfifo"). Unfortunately it breaks UART operation
on thr Amlogic Meson based boards (drivers/tty/serial/meson_uart.c
driver) and Qualcomm RB5 board (drivers/tty/serial/qcom_geni_serial.c).
Once the init process is started, a complete garbage is printed to the
serial console. Here is an example how it looks:

[    8.763154] Run /sbin/init as init process
NT [   12.429776] platform cpufreq-dt: deferred probe pending: (reason
unknown)
[   12.434259] platform regulator-vddcpu: deferred probe pending:
pwm-regulator: Failed to get PWM
[[6if;9]Uigmkfl-tl ocretbo nrnee .[[6if;9]Uigmkfl-tl ocretbo nrnee .
[[6if;9]Uigmkfl-tl ocretbo nrnee .
[[6if;9]Uigmkfl-tl ocretbo nrnee .
[[6if;9]Uigmkfl-tl ocretbo nrnee .
[[6if;9]Uigmkfl-tl ocretbo nrnee .
[[6if;9]Uigmkfl-tl ocretbo nrnee .
[.. trightlgeet ipthr ytm-dv[[6if;9]Uigmkfl-tl ocretbo nrnee .
[.. trightlgeet ipthr ytm-dv5c[[6if;9]Uigmkfl-tl ocretbo nrnee .
[.. trightlgeet ipthr ytm-dv5c7[Gmo [94m8[2h[0.
.]Snhszn h nta opu vns(usses..[[6if;9]Uigmkfl-tl ocretbo nrnee .
[.. trightlgeet ipthr ytm-dv5c7[Gmo [94m8[2h[0.
.]Snhszn h nta opu vns(usses..[[6if;9]Uigmkfl-tl ocretbo nrnee .
[.. trightlgeet ipthr ytm-dv5c7[Gmo [94m8[2h[0.
.]Snhszn h nta opu vns(usses..[[6if;9]Uigmkfl-tl ocretbo nrnee .
[.. trightlgeet ipthr ytm-dv5c7[Gmo [94m8[2h[0.
.]Snhszn h nta opu vns(usses..[[6if;9]Uigmkfl-tl ocretbo nrnee .
[.. trightlgeet ipthr ytm-dv5c7[Gmo [94m8[2h[0.
.]Snhszn h nta opu vns(usses..[[6if;9]Uigmkfl-tl ocretbo nrnee .
[.. trightlgeet ipthr ytm-dv5c7[Gmo [94m8[2h[0.
.]Snhszn h nta opu vns(usses..[[6if;9]Uigmkfl-tl ocretbo nrnee .
[.. trightlgeet ipthr ytm-dv5c7[Gmo [94m8[2h[0.
.]Snhszn h nta opu vns(usses..[[6if;9]Uigmkfl-tl ocretbo nrnee .
[.. trightlgeet ipthr ytm-dv5c7[Gmo [94m8[2h[0.
.]Snhszn h nta opu vns(usses..[[6if;9]Uigmkfl-tl ocretbo nrnee .
[.. trightlgeet ipthr ytm-dv5c7[Gmo [94m8[2h[0.
.]Snhszn h nta opu vns(usses..[2l[1[[6if;9]Uigmkfl-tl ocretbo nrnee .
[.. trightlgeet ipthr ytm-dv5c7[Gmo [94m8[2h[0.
[.. yteiigteiiilhtlgeet dvcs..[[6if;9]Uigmkfl-tl ocretbo nrnee .
[.. trightlgeet ipthr ytm-dv5c7[Gmo [94m8[2h[0.
[.. yteiigteiiilhtlgeet dvcs..[[6if;9]Uigmkfl-tl ocretbo nrnee .
[.. trightlgeet ipthr ytm-dv5c7[Gmo [94m8[2h[0.
[.. yteiigteiiilhtlgeet dvcs..[[6if;9]Uigmkfl-tl ocretbo nrnee .
[.. trightlgeet ipthr ytm-dv5c7[Gmo [94m8[2h[0.
[.. yteiigteiiilhtlgeet dvcs..[[6if;9]Uigmkfl-tl ocretbo nrnee .
[.. trightlgeet ipthr ytm-dv5c7[Gmo [94m8[2h[0.
[.. yteiigteiiilhtlgeet dvcs..[  105.613420] debugfs: Directory
'ff800280.cec' with parent 'regmap' already present!
[[6if;9]Uigmkfl-tl ocretbo nrnee .
[.. trightlgeet ipthr ytm-dv5c7[Gmo [94m8[2h[0.
.]Snhszn h nta opu vns(usses..[2l[11[[2 k;9?5[  105.638809] mc: Linux
media interface: v0.10
[.. atn o dvt eflyppltd.[  105.707390] meson-vrtc ff8000a8.rtc:
registered as rtc0


I found this patch by bisecting today's linux-next. I've checked the
changes in the related UART drivers and I don't see any obvious issues
though. Let me know if I can help debugging this issue somehow.

I've trimmed recipient list due to my smtp server limitation.


Best regards

--
Marek Szyprowski, PhD
Samsung R&D Institute Poland


2024-04-15 13:55:08

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCH 12/15] tty: serial: switch from circ_buf to kfifo

On 15. 04. 24, 14:58, Marek Szyprowski wrote:
> Dear All,
>
> On 05.04.2024 08:08, Jiri Slaby (SUSE) wrote:
>> Switch from struct circ_buf to proper kfifo. kfifo provides much better
>> API, esp. when wrap-around of the buffer needs to be taken into account.
>> Look at pl011_dma_tx_refill() or cpm_uart_tx_pump() changes for example.
>>
>> Kfifo API can also fill in scatter-gather DMA structures, so it easier
>> for that use case too. Look at lpuart_dma_tx() for example. Note that
>> not all drivers can be converted to that (like atmel_serial), they
>> handle DMA specially.
>>
>> Note that usb-serial uses kfifo for TX for ages.
>>
>> omap needed a bit more care as it needs to put a char into FIFO to start
>> the DMA transfer when OMAP_DMA_TX_KICK is set. In that case, we have to
>> do kfifo_dma_out_prepare twice: once to find out the tx_size (to find
>> out if it is worths to do DMA at all -- size >= 4), the second time for
>> the actual transfer.
>>
>> All traces of circ_buf are removed from serial_core.h (and its struct
>> uart_state).
>>
>> Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
>> ...
>
> This patch landed in linux-next as commit 1788cf6a91d9 ("tty: serial:
> switch from circ_buf to kfifo"). Unfortunately it breaks UART operation
> on thr Amlogic Meson based boards (drivers/tty/serial/meson_uart.c
> driver) and Qualcomm RB5 board (drivers/tty/serial/qcom_geni_serial.c).
> Once the init process is started, a complete garbage is printed to the
> serial console. Here is an example how it looks:

Oh my!

Both drivers move the tail using both kfifo and uart_xmit_advance()
interfaces. Bah. Does it help to remove that uart_xmit_advance() for
both of them? (TX stats will be broken.)

Users of uart_port_tx() are not affected.

This is my fault when merging uart_xmit_advance() with this series.

thanks,
--
js
suse labs


2024-04-15 14:18:00

by Marek Szyprowski

[permalink] [raw]
Subject: Re: [PATCH 12/15] tty: serial: switch from circ_buf to kfifo

On 15.04.2024 15:28, Jiri Slaby wrote:
> On 15. 04. 24, 14:58, Marek Szyprowski wrote:
>> On 05.04.2024 08:08, Jiri Slaby (SUSE) wrote:
>>> Switch from struct circ_buf to proper kfifo. kfifo provides much better
>>> API, esp. when wrap-around of the buffer needs to be taken into
>>> account.
>>> Look at pl011_dma_tx_refill() or cpm_uart_tx_pump() changes for
>>> example.
>>>
>>> Kfifo API can also fill in scatter-gather DMA structures, so it easier
>>> for that use case too. Look at lpuart_dma_tx() for example. Note that
>>> not all drivers can be converted to that (like atmel_serial), they
>>> handle DMA specially.
>>>
>>> Note that usb-serial uses kfifo for TX for ages.
>>>
>>> omap needed a bit more care as it needs to put a char into FIFO to
>>> start
>>> the DMA transfer when OMAP_DMA_TX_KICK is set. In that case, we have to
>>> do kfifo_dma_out_prepare twice: once to find out the tx_size (to find
>>> out if it is worths to do DMA at all -- size >= 4), the second time for
>>> the actual transfer.
>>>
>>> All traces of circ_buf are removed from serial_core.h (and its struct
>>> uart_state).
>>>
>>> Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
>>> ...
>>
>> This patch landed in linux-next as commit 1788cf6a91d9 ("tty: serial:
>> switch from circ_buf to kfifo"). Unfortunately it breaks UART operation
>> on thr Amlogic Meson based boards (drivers/tty/serial/meson_uart.c
>> driver) and Qualcomm RB5 board (drivers/tty/serial/qcom_geni_serial.c).
>> Once the init process is started, a complete garbage is printed to the
>> serial console. Here is an example how it looks:
>
> Oh my!
>
> Both drivers move the tail using both kfifo and uart_xmit_advance()
> interfaces. Bah. Does it help to remove that uart_xmit_advance() for
> both of them? (TX stats will be broken.)
>
> Users of uart_port_tx() are not affected.
>
> This is my fault when merging uart_xmit_advance() with this series.

Yes, removing uart_xmit_advance() from both drivers seems to be fixing
the console output.


Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland


2024-04-15 21:19:34

by Marek Szyprowski

[permalink] [raw]
Subject: Re: [PATCH 11/15] tty: msm_serial: use dmaengine_prep_slave_sg()

On 05.04.2024 08:08, Jiri Slaby (SUSE) wrote:
> This is a preparatory for the serial-to-kfifo switch. kfifo understands
> only scatter-gatter approach, so switch to that.
>
> No functional change intended, it's just dmaengine_prep_slave_single()
> inline expanded.
>
> And in this case, switch from dma_map_single() to dma_map_sg() too. This
> needs struct msm_dma changes. I split the rx and tx parts into an union.
> TX is now struct scatterlist, RX remains the old good phys-virt-count
> triple.
>
> Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
> Cc: Bjorn Andersson <[email protected]>
> Cc: Konrad Dybcio <[email protected]>
> Cc: [email protected]

I've just found that this patch broke UART operation on DragonBoard
410c. I briefly checked and didn't notice anything obviously wrong here,
but the board stops transmitting any data from its serial port after the
first message. I will try to analyze this issue a bit more tomorrow.

> ---
> drivers/tty/serial/msm_serial.c | 86 +++++++++++++++++++--------------
> 1 file changed, 49 insertions(+), 37 deletions(-)
>
> diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
> index d27c4c8c84e1..7bf30e632313 100644
> --- a/drivers/tty/serial/msm_serial.c
> +++ b/drivers/tty/serial/msm_serial.c
> @@ -161,11 +161,16 @@ enum {
> struct msm_dma {
> struct dma_chan *chan;
> enum dma_data_direction dir;
> - dma_addr_t phys;
> - unsigned char *virt;
> + union {
> + struct {
> + dma_addr_t phys;
> + unsigned char *virt;
> + unsigned int count;
> + } rx;
> + struct scatterlist tx_sg;
> + };
> dma_cookie_t cookie;
> u32 enable_bit;
> - unsigned int count;
> struct dma_async_tx_descriptor *desc;
> };
>
> @@ -249,8 +254,12 @@ static void msm_stop_dma(struct uart_port *port, struct msm_dma *dma)
> unsigned int mapped;
> u32 val;
>
> - mapped = dma->count;
> - dma->count = 0;
> + if (dma->dir == DMA_TO_DEVICE) {
> + mapped = sg_dma_len(&dma->tx_sg);
> + } else {
> + mapped = dma->rx.count;
> + dma->rx.count = 0;
> + }
>
> dmaengine_terminate_all(dma->chan);
>
> @@ -265,8 +274,13 @@ static void msm_stop_dma(struct uart_port *port, struct msm_dma *dma)
> val &= ~dma->enable_bit;
> msm_write(port, val, UARTDM_DMEN);
>
> - if (mapped)
> - dma_unmap_single(dev, dma->phys, mapped, dma->dir);
> + if (mapped) {
> + if (dma->dir == DMA_TO_DEVICE) {
> + dma_unmap_sg(dev, &dma->tx_sg, 1, dma->dir);
> + sg_init_table(&dma->tx_sg, 1);
> + } else
> + dma_unmap_single(dev, dma->rx.phys, mapped, dma->dir);
> + }
> }
>
> static void msm_release_dma(struct msm_port *msm_port)
> @@ -285,7 +299,7 @@ static void msm_release_dma(struct msm_port *msm_port)
> if (dma->chan) {
> msm_stop_dma(&msm_port->uart, dma);
> dma_release_channel(dma->chan);
> - kfree(dma->virt);
> + kfree(dma->rx.virt);
> }
>
> memset(dma, 0, sizeof(*dma));
> @@ -357,8 +371,8 @@ static void msm_request_rx_dma(struct msm_port *msm_port, resource_size_t base)
>
> of_property_read_u32(dev->of_node, "qcom,rx-crci", &crci);
>
> - dma->virt = kzalloc(UARTDM_RX_SIZE, GFP_KERNEL);
> - if (!dma->virt)
> + dma->rx.virt = kzalloc(UARTDM_RX_SIZE, GFP_KERNEL);
> + if (!dma->rx.virt)
> goto rel_rx;
>
> memset(&conf, 0, sizeof(conf));
> @@ -385,7 +399,7 @@ static void msm_request_rx_dma(struct msm_port *msm_port, resource_size_t base)
>
> return;
> err:
> - kfree(dma->virt);
> + kfree(dma->rx.virt);
> rel_rx:
> dma_release_channel(dma->chan);
> no_rx:
> @@ -420,7 +434,7 @@ static void msm_start_tx(struct uart_port *port)
> struct msm_dma *dma = &msm_port->tx_dma;
>
> /* Already started in DMA mode */
> - if (dma->count)
> + if (sg_dma_len(&dma->tx_sg))
> return;
>
> msm_port->imr |= MSM_UART_IMR_TXLEV;
> @@ -448,12 +462,12 @@ static void msm_complete_tx_dma(void *args)
> uart_port_lock_irqsave(port, &flags);
>
> /* Already stopped */
> - if (!dma->count)
> + if (!sg_dma_len(&dma->tx_sg))
> goto done;
>
> dmaengine_tx_status(dma->chan, dma->cookie, &state);
>
> - dma_unmap_single(port->dev, dma->phys, dma->count, dma->dir);
> + dma_unmap_sg(port->dev, &dma->tx_sg, 1, dma->dir);
>
> val = msm_read(port, UARTDM_DMEN);
> val &= ~dma->enable_bit;
> @@ -464,9 +478,9 @@ static void msm_complete_tx_dma(void *args)
> msm_write(port, MSM_UART_CR_TX_ENABLE, MSM_UART_CR);
> }
>
> - count = dma->count - state.residue;
> + count = sg_dma_len(&dma->tx_sg) - state.residue;
> uart_xmit_advance(port, count);
> - dma->count = 0;
> + sg_init_table(&dma->tx_sg, 1);
>
> /* Restore "Tx FIFO below watermark" interrupt */
> msm_port->imr |= MSM_UART_IMR_TXLEV;
> @@ -485,19 +499,18 @@ static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count)
> struct circ_buf *xmit = &msm_port->uart.state->xmit;
> struct uart_port *port = &msm_port->uart;
> struct msm_dma *dma = &msm_port->tx_dma;
> - void *cpu_addr;
> int ret;
> u32 val;
>
> - cpu_addr = &xmit->buf[xmit->tail];
> + sg_init_table(&dma->tx_sg, 1);
> + sg_set_buf(&dma->tx_sg, &xmit->buf[xmit->tail], count);
>
> - dma->phys = dma_map_single(port->dev, cpu_addr, count, dma->dir);
> - ret = dma_mapping_error(port->dev, dma->phys);
> + ret = dma_map_sg(port->dev, &dma->tx_sg, 1, dma->dir);
> if (ret)
> return ret;
>
> - dma->desc = dmaengine_prep_slave_single(dma->chan, dma->phys,
> - count, DMA_MEM_TO_DEV,
> + dma->desc = dmaengine_prep_slave_sg(dma->chan, &dma->tx_sg, 1,
> + DMA_MEM_TO_DEV,
> DMA_PREP_INTERRUPT |
> DMA_PREP_FENCE);
> if (!dma->desc) {
> @@ -520,8 +533,6 @@ static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count)
> msm_port->imr &= ~MSM_UART_IMR_TXLEV;
> msm_write(port, msm_port->imr, MSM_UART_IMR);
>
> - dma->count = count;
> -
> val = msm_read(port, UARTDM_DMEN);
> val |= dma->enable_bit;
>
> @@ -536,7 +547,8 @@ static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count)
> dma_async_issue_pending(dma->chan);
> return 0;
> unmap:
> - dma_unmap_single(port->dev, dma->phys, count, dma->dir);
> + dma_unmap_sg(port->dev, &dma->tx_sg, 1, dma->dir);
> + sg_init_table(&dma->tx_sg, 1);
> return ret;
> }
>
> @@ -553,7 +565,7 @@ static void msm_complete_rx_dma(void *args)
> uart_port_lock_irqsave(port, &flags);
>
> /* Already stopped */
> - if (!dma->count)
> + if (!dma->rx.count)
> goto done;
>
> val = msm_read(port, UARTDM_DMEN);
> @@ -570,14 +582,14 @@ static void msm_complete_rx_dma(void *args)
>
> port->icount.rx += count;
>
> - dma->count = 0;
> + dma->rx.count = 0;
>
> - dma_unmap_single(port->dev, dma->phys, UARTDM_RX_SIZE, dma->dir);
> + dma_unmap_single(port->dev, dma->rx.phys, UARTDM_RX_SIZE, dma->dir);
>
> for (i = 0; i < count; i++) {
> char flag = TTY_NORMAL;
>
> - if (msm_port->break_detected && dma->virt[i] == 0) {
> + if (msm_port->break_detected && dma->rx.virt[i] == 0) {
> port->icount.brk++;
> flag = TTY_BREAK;
> msm_port->break_detected = false;
> @@ -588,9 +600,9 @@ static void msm_complete_rx_dma(void *args)
> if (!(port->read_status_mask & MSM_UART_SR_RX_BREAK))
> flag = TTY_NORMAL;
>
> - sysrq = uart_prepare_sysrq_char(port, dma->virt[i]);
> + sysrq = uart_prepare_sysrq_char(port, dma->rx.virt[i]);
> if (!sysrq)
> - tty_insert_flip_char(tport, dma->virt[i], flag);
> + tty_insert_flip_char(tport, dma->rx.virt[i], flag);
> }
>
> msm_start_rx_dma(msm_port);
> @@ -614,13 +626,13 @@ static void msm_start_rx_dma(struct msm_port *msm_port)
> if (!dma->chan)
> return;
>
> - dma->phys = dma_map_single(uart->dev, dma->virt,
> + dma->rx.phys = dma_map_single(uart->dev, dma->rx.virt,
> UARTDM_RX_SIZE, dma->dir);
> - ret = dma_mapping_error(uart->dev, dma->phys);
> + ret = dma_mapping_error(uart->dev, dma->rx.phys);
> if (ret)
> goto sw_mode;
>
> - dma->desc = dmaengine_prep_slave_single(dma->chan, dma->phys,
> + dma->desc = dmaengine_prep_slave_single(dma->chan, dma->rx.phys,
> UARTDM_RX_SIZE, DMA_DEV_TO_MEM,
> DMA_PREP_INTERRUPT);
> if (!dma->desc)
> @@ -648,7 +660,7 @@ static void msm_start_rx_dma(struct msm_port *msm_port)
>
> msm_write(uart, msm_port->imr, MSM_UART_IMR);
>
> - dma->count = UARTDM_RX_SIZE;
> + dma->rx.count = UARTDM_RX_SIZE;
>
> dma_async_issue_pending(dma->chan);
>
> @@ -668,7 +680,7 @@ static void msm_start_rx_dma(struct msm_port *msm_port)
>
> return;
> unmap:
> - dma_unmap_single(uart->dev, dma->phys, UARTDM_RX_SIZE, dma->dir);
> + dma_unmap_single(uart->dev, dma->rx.phys, UARTDM_RX_SIZE, dma->dir);
>
> sw_mode:
> /*
> @@ -955,7 +967,7 @@ static irqreturn_t msm_uart_irq(int irq, void *dev_id)
> }
>
> if (misr & (MSM_UART_IMR_RXLEV | MSM_UART_IMR_RXSTALE)) {
> - if (dma->count) {
> + if (dma->rx.count) {
> val = MSM_UART_CR_CMD_STALE_EVENT_DISABLE;
> msm_write(port, val, MSM_UART_CR);
> val = MSM_UART_CR_CMD_RESET_STALE_INT;

Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland


2024-04-16 03:29:40

by Pengfei Xu

[permalink] [raw]
Subject: Re: [PATCH 12/15] tty: serial: switch from circ_buf to kfifo

Hi Jiri Slaby,

On 2024-04-05 at 08:08:23 +0200, Jiri Slaby (SUSE) wrote:
> Switch from struct circ_buf to proper kfifo. kfifo provides much better
> API, esp. when wrap-around of the buffer needs to be taken into account.
> Look at pl011_dma_tx_refill() or cpm_uart_tx_pump() changes for example.
>
> Kfifo API can also fill in scatter-gather DMA structures, so it easier
> for that use case too. Look at lpuart_dma_tx() for example. Note that
> not all drivers can be converted to that (like atmel_serial), they
> handle DMA specially.
>
> Note that usb-serial uses kfifo for TX for ages.
>
> omap needed a bit more care as it needs to put a char into FIFO to start
> the DMA transfer when OMAP_DMA_TX_KICK is set. In that case, we have to
> do kfifo_dma_out_prepare twice: once to find out the tx_size (to find
> out if it is worths to do DMA at all -- size >= 4), the second time for
> the actual transfer.
>
> All traces of circ_buf are removed from serial_core.h (and its struct
> uart_state).
>
> Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
> Cc: Al Cooper <[email protected]>
> Cc: Matthias Brugger <[email protected]>
> Cc: AngeloGioacchino Del Regno <[email protected]>
> Cc: Kumaravel Thiagarajan <[email protected]>
> Cc: Tharun Kumar P <[email protected]>
> Cc: Russell King <[email protected]>
> Cc: Vineet Gupta <[email protected]>
> Cc: Richard Genoud <[email protected]>
> Cc: Nicolas Ferre <[email protected]>
> Cc: Alexandre Belloni <[email protected]>
> Cc: Claudiu Beznea <[email protected]>
> Cc: Alexander Shiyan <[email protected]>
> Cc: Baruch Siach <[email protected]>
> Cc: "Maciej W. Rozycki" <[email protected]>
> Cc: Shawn Guo <[email protected]>
> Cc: Sascha Hauer <[email protected]>
> Cc: Fabio Estevam <[email protected]>
> Cc: Neil Armstrong <[email protected]>
> Cc: Kevin Hilman <[email protected]>
> Cc: Jerome Brunet <[email protected]>
> Cc: Martin Blumenstingl <[email protected]>
> Cc: Taichi Sugaya <[email protected]>
> Cc: Takao Orito <[email protected]>
> Cc: Bjorn Andersson <[email protected]>
> Cc: Konrad Dybcio <[email protected]>
> Cc: "Pali Roh?r" <[email protected]>
> Cc: Michael Ellerman <[email protected]>
> Cc: Nicholas Piggin <[email protected]>
> Cc: Christophe Leroy <[email protected]>
> Cc: "Aneesh Kumar K.V" <[email protected]>
> Cc: "Naveen N. Rao" <[email protected]>
> Cc: Manivannan Sadhasivam <[email protected]>
> Cc: Krzysztof Kozlowski <[email protected]>
> Cc: Alim Akhtar <[email protected]>
> Cc: Laxman Dewangan <[email protected]>
> Cc: Thierry Reding <[email protected]>
> Cc: Jonathan Hunter <[email protected]>
> Cc: Orson Zhai <[email protected]>
> Cc: Baolin Wang <[email protected]>
> Cc: Chunyan Zhang <[email protected]>
> Cc: Patrice Chotard <[email protected]>
> Cc: Maxime Coquelin <[email protected]>
> Cc: Alexandre Torgue <[email protected]>
> Cc: "David S. Miller" <[email protected]>
> Cc: Hammer Hsieh <[email protected]>
> Cc: Peter Korsgaard <[email protected]>
> Cc: Timur Tabi <[email protected]>
> Cc: Michal Simek <[email protected]>
> Cc: Sumit Semwal <[email protected]>
> Cc: "Christian K?nig" <[email protected]>
> ---
> drivers/tty/serial/8250/8250_bcm7271.c | 14 +++---
> drivers/tty/serial/8250/8250_core.c | 3 +-
> drivers/tty/serial/8250/8250_dma.c | 23 ++++++----
> drivers/tty/serial/8250/8250_exar.c | 5 ++-
> drivers/tty/serial/8250/8250_mtk.c | 2 +-
> drivers/tty/serial/8250/8250_omap.c | 47 +++++++++++++-------
> drivers/tty/serial/8250/8250_pci1xxxx.c | 50 ++++++++++------------
> drivers/tty/serial/8250/8250_port.c | 22 ++++++----
> drivers/tty/serial/amba-pl011.c | 46 +++++++++-----------
> drivers/tty/serial/ar933x_uart.c | 15 +++----
> drivers/tty/serial/arc_uart.c | 8 ++--
> drivers/tty/serial/atmel_serial.c | 57 +++++++++++++------------
> drivers/tty/serial/clps711x.c | 12 +++---
> drivers/tty/serial/cpm_uart.c | 20 +++------
> drivers/tty/serial/digicolor-usart.c | 12 +++---
> drivers/tty/serial/dz.c | 13 +++---
> drivers/tty/serial/fsl_linflexuart.c | 17 ++++----
> drivers/tty/serial/fsl_lpuart.c | 39 ++++++++---------
> drivers/tty/serial/icom.c | 25 +++--------
> drivers/tty/serial/imx.c | 54 ++++++++++-------------
> drivers/tty/serial/ip22zilog.c | 26 ++++++-----
> drivers/tty/serial/jsm/jsm_cls.c | 29 ++++---------
> drivers/tty/serial/jsm/jsm_neo.c | 38 ++++++-----------
> drivers/tty/serial/max3100.c | 14 +++---
> drivers/tty/serial/max310x.c | 35 +++++++--------
> drivers/tty/serial/men_z135_uart.c | 26 +++++------
> drivers/tty/serial/meson_uart.c | 11 +++--
> drivers/tty/serial/milbeaut_usio.c | 15 ++++---
> drivers/tty/serial/msm_serial.c | 30 ++++++-------
> drivers/tty/serial/mvebu-uart.c | 8 ++--
> drivers/tty/serial/mxs-auart.c | 23 +++-------
> drivers/tty/serial/pch_uart.c | 21 +++++----
> drivers/tty/serial/pic32_uart.c | 15 +++----
> drivers/tty/serial/pmac_zilog.c | 24 +++++------
> drivers/tty/serial/qcom_geni_serial.c | 36 ++++++++--------
> drivers/tty/serial/rda-uart.c | 17 +++-----
> drivers/tty/serial/samsung_tty.c | 54 ++++++++++++-----------
> drivers/tty/serial/sb1250-duart.c | 13 +++---
> drivers/tty/serial/sc16is7xx.c | 40 +++++++----------
> drivers/tty/serial/sccnxp.c | 16 ++++---
> drivers/tty/serial/serial-tegra.c | 43 ++++++++++---------
> drivers/tty/serial/serial_core.c | 56 +++++++++---------------
> drivers/tty/serial/serial_port.c | 2 +-
> drivers/tty/serial/sh-sci.c | 51 +++++++++++-----------
> drivers/tty/serial/sprd_serial.c | 20 ++++-----
> drivers/tty/serial/st-asc.c | 4 +-
> drivers/tty/serial/stm32-usart.c | 52 +++++++++-------------
> drivers/tty/serial/sunhv.c | 35 ++++++++-------
> drivers/tty/serial/sunplus-uart.c | 16 ++++---
> drivers/tty/serial/sunsab.c | 30 +++++++------
> drivers/tty/serial/sunsu.c | 15 ++++---
> drivers/tty/serial/sunzilog.c | 27 ++++++------
> drivers/tty/serial/tegra-tcu.c | 10 +++--
> drivers/tty/serial/timbuart.c | 17 +++-----
> drivers/tty/serial/uartlite.c | 13 +++---
> drivers/tty/serial/ucc_uart.c | 20 +++------
> drivers/tty/serial/xilinx_uartps.c | 20 ++++-----
> drivers/tty/serial/zs.c | 13 +++---
> include/linux/serial_core.h | 49 +++++++++++++--------
> 59 files changed, 688 insertions(+), 780 deletions(-)
>

Greeting!
I used syzkaller and Internal v6.9-rc4 kernel which include Linux next kernel,
and found deadlock in console_flush_all issue, bisected the commit is:
"
1788cf6a91d9 tty: serial: switch from circ_buf to kfifo
"

All info: https://github.com/xupengfe/syzkaller_logs/tree/main/240415_195447_console_flush_all
Syzkaller reproduced code: https://github.com/xupengfe/syzkaller_logs/blob/main/240415_195447_console_flush_all/repro.c
Syzkaller syscall repro steps: https://github.com/xupengfe/syzkaller_logs/blob/main/240415_195447_console_flush_all/repro.prog
Syzkaller report: https://github.com/xupengfe/syzkaller_logs/blob/main/240415_195447_console_flush_all/repro.report
Bisect info: https://github.com/xupengfe/syzkaller_logs/blob/main/240415_195447_console_flush_all/bisect_info.log
Kconfig(make olddefconfig): https://github.com/xupengfe/syzkaller_logs/blob/main/240415_195447_console_flush_all/kconfig_origin
Issue dmesg: https://github.com/xupengfe/syzkaller_logs/blob/main/240415_195447_console_flush_all/df0b4fecf9eb384062e52e3b572ee8c431afc63b_dmesg.log
Issue bzImage: https://github.com/xupengfe/syzkaller_logs/raw/main/240415_195447_console_flush_all/bzImage_df0b4fecf_v6.9-rc4_next.tar.gz

[ 17.841572] ------------[ cut here ]------------
[ 17.841624]
[ 17.841628] ======================================================
[ 17.841631] WARNING: possible circular locking dependency detected
[ 17.841632] 6.9.0-rc4-df0b4fecf9eb+ #1 Not tainted
[ 17.841636] ------------------------------------------------------
[ 17.841638] repro/726 is trying to acquire lock:
[ 17.841641] ffffffff86d6da80 (console_owner){....}-{0:0}, at: console_flush_all+0x51a/0xc40
[ 17.841665]
[ 17.841665] but task is already holding lock:
[ 17.841667] ffffffff89aa52b8 (&port_lock_key){-...}-{2:2}, at: uart_put_char+0x118/0x510
[ 17.841684]
[ 17.841684] which lock already depends on the new lock.
[ 17.841684]
[ 17.841686]
[ 17.841686] the existing dependency chain (in reverse order) is:
[ 17.841687]
[ 17.841687] -> #1 (&port_lock_key){-...}-{2:2}:
[ 17.841695] _raw_spin_lock_irqsave+0x52/0x80
[ 17.841707] serial8250_console_write+0xad4/0x10a0
[ 17.841715] univ8250_console_write+0x6f/0x90
[ 17.841723] console_flush_all+0x55c/0xc40
[ 17.841730] console_unlock+0xb6/0x240
[ 17.841738] vprintk_emit+0x2d0/0x680
[ 17.841745] vprintk_default+0x2f/0x40
[ 17.841752] vprintk+0xd0/0x170
[ 17.841758] _printk+0xc4/0x100
[ 17.841764] register_console+0xcb4/0x13e0
[ 17.841771] univ8250_console_init+0x41/0x60
[ 17.841783] console_init+0xae/0x5a0
[ 17.841793] start_kernel+0x275/0x510
[ 17.841799] x86_64_start_reservations+0x1c/0x30
[ 17.841807] x86_64_start_kernel+0xa0/0xb0
[ 17.841815] common_startup_64+0x13e/0x141
[ 17.841826]
[ 17.841826] -> #0 (console_owner){....}-{0:0}:
[ 17.841833] __lock_acquire+0x2fc1/0x5ca0
[ 17.841843] lock_acquire+0x1c8/0x550
[ 17.841850] console_flush_all+0x52f/0xc40
[ 17.841857] console_unlock+0xb6/0x240
[ 17.841864] vprintk_emit+0x2d0/0x680
[ 17.841870] vprintk_default+0x2f/0x40
[ 17.841877] vprintk+0xd0/0x170
[ 17.841884] _printk+0xc4/0x100
[ 17.841889] report_bug+0x26a/0x4b0
[ 17.841897] handle_bug+0xa2/0x130
[ 17.841905] exc_invalid_op+0x3c/0x80
[ 17.841912] asm_exc_invalid_op+0x1f/0x30
[ 17.841923] uart_put_char+0x3ac/0x510
[ 17.841929] tty_put_char+0xce/0x170
[ 17.841937] do_output_char+0x164/0x890
[ 17.841947] __process_echoes+0x225/0xb30
[ 17.841954] n_tty_receive_buf_common+0x4cb/0x16c0
[ 17.841960] n_tty_receive_buf+0x3a/0x50
[ 17.841966] tty_ioctl+0x529/0x1740
[ 17.841973] __x64_sys_ioctl+0x1b9/0x230
[ 17.841983] x64_sys_call+0x120d/0x20c0
[ 17.841990] do_syscall_64+0x6f/0x150
[ 17.841996] entry_SYSCALL_64_after_hwframe+0x76/0x7e
[ 17.842004]
[ 17.842004] other info that might help us debug this:
[ 17.842004]
[ 17.842005] Possible unsafe locking scenario:
[ 17.842005]
[ 17.842007] CPU0 CPU1
[ 17.842008] ---- ----
[ 17.842009] lock(&port_lock_key);
[ 17.842013] lock(console_owner);
[ 17.842016] lock(&port_lock_key);
[ 17.842020] lock(console_owner);
[ 17.842023]
[ 17.842023] *** DEADLOCK ***
[ 17.842023]
[ 17.842024] 7 locks held by repro/726:
[ 17.842028] #0: ffff888018b120a0 (&tty->ldisc_sem){++++}-{0:0}, at: ldsem_down_read+0x3f/0x50
[ 17.842042] #1: ffff88800c6d0ca0 (&buf->lock){+.+.}-{3:3}, at: tty_buffer_lock_exclusive+0x39/0x50
[ 17.842059] #2: ffff888018b122e8 (&tty->termios_rwsem){++++}-{3:3}, at: n_tty_receive_buf_common+0x97/0x16c0
[ 17.842074] #3: ffffc90000bd2380 (&ldata->output_lock){+.+.}-{3:3}, at: n_tty_receive_buf_common+0x495/0x16c0
[ 17.842088] #4: ffffffff89aa52b8 (&port_lock_key){-...}-{2:2}, at: uart_put_char+0x118/0x510
[ 17.842103] #5: ffffffff86e4e0e0 (console_lock){+.+.}-{0:0}, at: vprintk_default+0x2f/0x40
[ 17.842118] #6: ffffffff86e4e150 (console_srcu){....}-{0:0}, at: console_flush_all+0x14d/0xc40
[ 17.842133]
[ 17.842133] stack backtrace:
[ 17.842136] CPU: 0 PID: 726 Comm: repro Not tainted 6.9.0-rc4-df0b4fecf9eb+ #1
[ 17.842143] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org 04/01/2014
[ 17.842146] Call Trace:
[ 17.842150] <TASK>
[ 17.842153] dump_stack_lvl+0xea/0x150
[ 17.842162] dump_stack+0x19/0x20
[ 17.842168] print_circular_bug+0x477/0x740
[ 17.842179] check_noncircular+0x2f4/0x3e0
[ 17.842189] ? __pfx_check_noncircular+0x10/0x10
[ 17.842200] ? lockdep_lock+0xd0/0x1d0
[ 17.842207] ? __pfx_lockdep_lock+0x10/0x10
[ 17.842217] __lock_acquire+0x2fc1/0x5ca0
[ 17.842232] ? __pfx___lock_acquire+0x10/0x10
[ 17.842243] lock_acquire+0x1c8/0x550
[ 17.842252] ? console_flush_all+0x51a/0xc40
[ 17.842261] ? __pfx_lock_acquire+0x10/0x10
[ 17.842269] ? __pfx_lock_release+0x10/0x10
[ 17.842278] ? __pfx_do_raw_spin_lock+0x10/0x10
[ 17.842289] console_flush_all+0x52f/0xc40
[ 17.842296] ? console_flush_all+0x51a/0xc40
[ 17.842307] ? __pfx_console_flush_all+0x10/0x10
[ 17.842316] ? lock_acquire+0x1d8/0x550
[ 17.842326] console_unlock+0xb6/0x240
[ 17.842334] ? __pfx_console_unlock+0x10/0x10
[ 17.842342] ? do_raw_spin_unlock+0x15c/0x210
[ 17.842349] ? _raw_spin_unlock_irqrestore+0x45/0x70
[ 17.842357] ? vprintk_default+0x2f/0x40
[ 17.842366] vprintk_emit+0x2d0/0x680
[ 17.842377] vprintk_default+0x2f/0x40
[ 17.842385] vprintk+0xd0/0x170
[ 17.842394] _printk+0xc4/0x100
[ 17.842400] ? __pfx__printk+0x10/0x10
[ 17.842408] ? __pfx___lock_acquire+0x10/0x10
[ 17.842417] ? lockdep_unlock+0xf5/0x250
[ 17.842424] ? report_bug+0x25e/0x4b0
[ 17.842430] ? report_bug+0x253/0x4b0
[ 17.842437] ? uart_put_char+0x3ac/0x510
[ 17.842444] report_bug+0x26a/0x4b0
[ 17.842451] ? uart_put_char+0x3ac/0x510
[ 17.842461] handle_bug+0xa2/0x130
[ 17.842469] exc_invalid_op+0x3c/0x80
[ 17.842479] asm_exc_invalid_op+0x1f/0x30
[ 17.842487] RIP: 0010:uart_put_char+0x3ac/0x510
[ 17.842495] Code: 49 8b bc 24 08 01 00 00 31 c9 ba 01 00 00 00 be 03 00 00 00 48 81 c7 a0 03 00 00 e8 ce 78 e4 fd e9 63 ff ff ff e8 44 b8 0b fe <0f> 0b e8 3d b8 0b fe 4d 8d ac 24 08 01 00 00 4c 89 f6 4c 89 e7 e8
[ 17.842502] RSP: 0018:ffff888010ec7a40 EFLAGS: 00010093
[ 17.842509] RAX: 0000000000000000 RBX: ffff88800c6d0be8 RCX: ffffffff81420511
[ 17.842514] RDX: ffff88801cd8ca00 RSI: ffffffff83590dec RDI: ffff88800c6d0f48
[ 17.842518] RBP: ffff888010ec7a80 R08: 0000000000000001 R09: ffffed10021d8f34
[ 17.842522] R10: 0000000000000003 R11: 0000000000000001 R12: ffffffff89aa52a0
[ 17.842527] R13: ffff88800c6d0f84 R14: 0000000000000293 R15: ffffffff83590a40
[ 17.842532] ? __pfx_uart_put_char+0x10/0x10
[ 17.842542] ? do_raw_spin_lock+0x141/0x280
[ 17.842549] ? uart_put_char+0x3ac/0x510
[ 17.842560] ? __pfx_uart_put_char+0x10/0x10
[ 17.842567] tty_put_char+0xce/0x170
[ 17.842574] ? __pfx_tty_put_char+0x10/0x10
[ 17.842581] ? _raw_spin_unlock_irqrestore+0x35/0x70
[ 17.842589] ? lockdep_hardirqs_on+0x89/0x110
[ 17.842599] do_output_char+0x164/0x890
[ 17.842609] __process_echoes+0x225/0xb30
[ 17.842622] n_tty_receive_buf_common+0x4cb/0x16c0
[ 17.842629] ? __pfx___mutex_lock+0x10/0x10
[ 17.842643] ? __pfx_n_tty_receive_buf+0x10/0x10
[ 17.842650] n_tty_receive_buf+0x3a/0x50
[ 17.842658] tty_ioctl+0x529/0x1740
[ 17.842667] ? __pfx_tty_ioctl+0x10/0x10
[ 17.842674] ? lock_release+0x415/0x800
[ 17.842683] ? __pfx_lock_release+0x10/0x10
[ 17.842694] ? __this_cpu_preempt_check+0x21/0x30
[ 17.842703] ? seqcount_lockdep_reader_access.constprop.0+0xb4/0xd0
[ 17.842715] ? lockdep_hardirqs_on+0x89/0x110
[ 17.842723] ? trace_hardirqs_on+0x51/0x60
[ 17.842732] ? seqcount_lockdep_reader_access.constprop.0+0xc0/0xd0
[ 17.842741] ? __sanitizer_cov_trace_cmp4+0x1a/0x20
[ 17.842751] ? __sanitizer_cov_trace_const_cmp4+0x1a/0x20
[ 17.842759] ? security_file_ioctl+0x9d/0xd0
[ 17.842771] ? __pfx_tty_ioctl+0x10/0x10
[ 17.842781] __x64_sys_ioctl+0x1b9/0x230
[ 17.842793] x64_sys_call+0x120d/0x20c0
[ 17.842799] do_syscall_64+0x6f/0x150
[ 17.842807] entry_SYSCALL_64_after_hwframe+0x76/0x7e
[ 17.842817] RIP: 0033:0x7f53ca43ee5d
[ 17.842823] Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 93 af 1b 00 f7 d8 64 89 01 48
[ 17.842829] RSP: 002b:00007ffce9c89108 EFLAGS: 00000213 ORIG_RAX: 0000000000000010
[ 17.842835] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f53ca43ee5d
[ 17.842839] RDX: 0000000020000000 RSI: 0000000000005412 RDI: 0000000000000003
[ 17.842843] RBP: 00007ffce9c89120 R08: 0000000000000800 R09: 0000000000000800
[ 17.842849] R10: 0000000000000000 R11: 0000000000000213 R12: 00007ffce9c89238
[ 17.842853] R13: 0000000000401136 R14: 0000000000403e08 R15: 00007f53ca71e000
[ 17.842863] </TASK>
[ 17.879899] WARNING: CPU: 0 PID: 726 at drivers/tty/serial/serial_core.c:561 uart_put_char+0x3ac/0x510
[ 17.880328] Modules linked in:
[ 17.880477] CPU: 0 PID: 726 Comm: repro Not tainted 6.9.0-rc4-df0b4fecf9eb+ #1
[ 17.880808] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org 04/01/2014
[ 17.881320] RIP: 0010:uart_put_char+0x3ac/0x510
[ 17.881538] Code: 49 8b bc 24 08 01 00 00 31 c9 ba 01 00 00 00 be 03 00 00 00 48 81 c7 a0 03 00 00 e8 ce 78 e4 fd e9 63 ff ff ff e8 44 b8 0b fe <0f> 0b e8 3d b8 0b fe 4d 8d ac 24 08 01 00 00 4c 89 f6 4c 89 e7 e8
[ 17.882367] RSP: 0018:ffff888010ec7a40 EFLAGS: 00010093
[ 17.882610] RAX: 0000000000000000 RBX: ffff88800c6d0be8 RCX: ffffffff81420511
[ 17.882935] RDX: ffff88801cd8ca00 RSI: ffffffff83590dec RDI: ffff88800c6d0f48
[ 17.883260] RBP: ffff888010ec7a80 R08: 0000000000000001 R09: ffffed10021d8f34
[ 17.883588] R10: 0000000000000003 R11: 0000000000000001 R12: ffffffff89aa52a0
[ 17.883913] R13: ffff88800c6d0f84 R14: 0000000000000293 R15: ffffffff83590a40
[ 17.884239] FS: 00007f53ca6d7600(0000) GS:ffff88806ca00000(0000) knlGS:0000000000000000
[ 17.884606] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 17.884872] CR2: 0000000020000040 CR3: 00000000118de000 CR4: 0000000000750ef0
[ 17.885198] PKRU: 55555554
[ 17.885329] Call Trace:
[ 17.885448] <TASK>
[ 17.885554] ? show_regs+0xa8/0xc0
[ 17.885724] ? __warn+0xee/0x380
[ 17.885888] ? report_bug+0x25e/0x4b0
[ 17.886064] ? uart_put_char+0x3ac/0x510
[ 17.886254] ? report_bug+0x2cb/0x4b0
[ 17.886431] ? uart_put_char+0x3ac/0x510
[ 17.886622] ? handle_bug+0xa2/0x130
[ 17.886798] ? exc_invalid_op+0x3c/0x80
[ 17.886985] ? asm_exc_invalid_op+0x1f/0x30
[ 17.887186] ? __pfx_uart_put_char+0x10/0x10
[ 17.887397] ? do_raw_spin_lock+0x141/0x280
[ 17.887604] ? uart_put_char+0x3ac/0x510
[ 17.887795] ? uart_put_char+0x3ac/0x510
[ 17.887987] ? __pfx_uart_put_char+0x10/0x10
[ 17.888193] tty_put_char+0xce/0x170
[ 17.888367] ? __pfx_tty_put_char+0x10/0x10
[ 17.888566] ? _raw_spin_unlock_irqrestore+0x35/0x70
[ 17.888801] ? lockdep_hardirqs_on+0x89/0x110
[ 17.889014] do_output_char+0x164/0x890
[ 17.889203] __process_echoes+0x225/0xb30
[ 17.889403] n_tty_receive_buf_common+0x4cb/0x16c0
[ 17.889629] ? __pfx___mutex_lock+0x10/0x10
[ 17.889836] ? __pfx_n_tty_receive_buf+0x10/0x10
[ 17.890055] n_tty_receive_buf+0x3a/0x50
[ 17.890244] tty_ioctl+0x529/0x1740
[ 17.890417] ? __pfx_tty_ioctl+0x10/0x10
[ 17.890608] ? lock_release+0x415/0x800
[ 17.890797] ? __pfx_lock_release+0x10/0x10
[ 17.891002] ? __this_cpu_preempt_check+0x21/0x30
[ 17.891227] ? seqcount_lockdep_reader_access.constprop.0+0xb4/0xd0
[ 17.891526] ? lockdep_hardirqs_on+0x89/0x110
[ 17.891737] ? trace_hardirqs_on+0x51/0x60
[ 17.891933] ? seqcount_lockdep_reader_access.constprop.0+0xc0/0xd0
[ 17.892227] ? __sanitizer_cov_trace_cmp4+0x1a/0x20
[ 17.892460] ? __sanitizer_cov_trace_const_cmp4+0x1a/0x20
[ 17.892715] ? security_file_ioctl+0x9d/0xd0
[ 17.892921] ? __pfx_tty_ioctl+0x10/0x10
[ 17.893111] __x64_sys_ioctl+0x1b9/0x230
[ 17.893303] x64_sys_call+0x120d/0x20c0
[ 17.893487] do_syscall_64+0x6f/0x150
[ 17.893664] entry_SYSCALL_64_after_hwframe+0x76/0x7e
[ 17.893904] RIP: 0033:0x7f53ca43ee5d
[ 17.894075] Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 93 af 1b 00 f7 d8 64 89 01 48
[ 17.894904] RSP: 002b:00007ffce9c89108 EFLAGS: 00000213 ORIG_RAX: 0000000000000010
[ 17.895249] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f53ca43ee5d
[ 17.895578] RDX: 0000000020000000 RSI: 0000000000005412 RDI: 0000000000000003
[ 17.895903] RBP: 00007ffce9c89120 R08: 0000000000000800 R09: 0000000000000800
[ 17.896229] R10: 0000000000000000 R11: 0000000000000213 R12: 00007ffce9c89238
[ 17.896553] R13: 0000000000401136 R14: 0000000000403e08 R15: 00007f53ca71e000
[ 17.896884] </TASK>
[ 17.896993] irq event stamp: 3868
[ 17.897152] hardirqs last enabled at (3867): [<ffffffff856c5e05>] _raw_spin_unlock_irqrestore+0x35/0x70
[ 17.897582] hardirqs last disabled at (3868): [<ffffffff856c5ab0>] _raw_spin_lock_irqsave+0x70/0x80
[ 17.897994] softirqs last enabled at (1736): [<ffffffff811172dd>] fpu_flush_thread+0x2fd/0x3e0
[ 17.898396] softirqs last disabled at (1734): [<ffffffff8111721e>] fpu_flush_thread+0x23e/0x3e0
[ 17.898797] ---[ end trace 0000000000000000 ]---

Hope reproduced code and bisection is helpful.


---

If you don't need the following environment to reproduce the problem or if you
already have one reproduced environment, please ignore the following information.

How to reproduce:
git clone https://gitlab.com/xupengfe/repro_vm_env.git
cd repro_vm_env
tar -xvf repro_vm_env.tar.gz
cd repro_vm_env; ./start3.sh // it needs qemu-system-x86_64 and I used v7.1.0
// start3.sh will load bzImage_2241ab53cbb5cdb08a6b2d4688feb13971058f65 v6.2-rc5 kernel
// You could change the bzImage_xxx as you want
// Maybe you need to remove line "-drive if=pflash,format=raw,readonly=on,file=./OVMF_CODE.fd \" for different qemu version
You could use below command to log in, there is no password for root.
ssh -p 10023 root@localhost

After login vm(virtual machine) successfully, you could transfer reproduced
binary to the vm by below way, and reproduce the problem in vm:
gcc -pthread -o repro repro.c
scp -P 10023 repro root@localhost:/root/

Get the bzImage for target kernel:
Please use target kconfig and copy it to kernel_src/.config
make olddefconfig
make -jx bzImage //x should equal or less than cpu num your pc has

Fill the bzImage file into above start3.sh to load the target kernel in vm.


Tips:
If you already have qemu-system-x86_64, please ignore below info.
If you want to install qemu v7.1.0 version:
git clone https://github.com/qemu/qemu.git
cd qemu
git checkout -f v7.1.0
mkdir build
cd build
yum install -y ninja-build.x86_64
yum -y install libslirp-devel.x86_64
./configure --target-list=x86_64-softmmu --enable-kvm --enable-vnc --enable-gtk --enable-sdl --enable-usb-redir --enable-slirp
make
make install

Best Regards,
Thanks!


> diff --git a/drivers/tty/serial/8250/8250_bcm7271.c b/drivers/tty/serial/8250/8250_bcm7271.c
> index 5daa38d9c64e..de270863eb5e 100644
> --- a/drivers/tty/serial/8250/8250_bcm7271.c
> +++ b/drivers/tty/serial/8250/8250_bcm7271.c
> @@ -413,20 +413,18 @@ static int stop_tx_dma(struct uart_8250_port *p)
> static int brcmuart_tx_dma(struct uart_8250_port *p)
> {
> struct brcmuart_priv *priv = p->port.private_data;
> - struct circ_buf *xmit = &p->port.state->xmit;
> + struct tty_port *tport = &p->port.state->port;
> u32 tx_size;
>
> if (uart_tx_stopped(&p->port) || priv->tx_running ||
> - uart_circ_empty(xmit)) {
> + kfifo_is_empty(&tport->xmit_fifo)) {
> return 0;
> }
> - tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
>
> priv->dma.tx_err = 0;
> - memcpy(priv->tx_buf, &xmit->buf[xmit->tail], tx_size);
> - uart_xmit_advance(&p->port, tx_size);
> + tx_size = uart_fifo_out(&p->port, priv->tx_buf, UART_XMIT_SIZE);
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&p->port);
>
> udma_writel(priv, REGS_DMA_TX, UDMA_TX_TRANSFER_LEN, tx_size);
> @@ -540,7 +538,7 @@ static void brcmuart_tx_isr(struct uart_port *up, u32 isr)
> struct brcmuart_priv *priv = up->private_data;
> struct device *dev = up->dev;
> struct uart_8250_port *port_8250 = up_to_u8250p(up);
> - struct circ_buf *xmit = &port_8250->port.state->xmit;
> + struct tty_port *tport = &port_8250->port.state->port;
>
> if (isr & UDMA_INTR_TX_ABORT) {
> if (priv->tx_running)
> @@ -548,7 +546,7 @@ static void brcmuart_tx_isr(struct uart_port *up, u32 isr)
> return;
> }
> priv->tx_running = false;
> - if (!uart_circ_empty(xmit) && !uart_tx_stopped(up))
> + if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(up))
> brcmuart_tx_dma(port_8250);
> }
>
> diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
> index b62ad9006780..3a1936b7f05f 100644
> --- a/drivers/tty/serial/8250/8250_core.c
> +++ b/drivers/tty/serial/8250/8250_core.c
> @@ -280,7 +280,8 @@ static void serial8250_backup_timeout(struct timer_list *t)
> */
> lsr = serial_lsr_in(up);
> if ((iir & UART_IIR_NO_INT) && (up->ier & UART_IER_THRI) &&
> - (!uart_circ_empty(&up->port.state->xmit) || up->port.x_char) &&
> + (!kfifo_is_empty(&up->port.state->port.xmit_fifo) ||
> + up->port.x_char) &&
> (lsr & UART_LSR_THRE)) {
> iir &= ~(UART_IIR_ID | UART_IIR_NO_INT);
> iir |= UART_IIR_THRI;
> diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
> index 8b2c3f478b17..8a353e3cc3dd 100644
> --- a/drivers/tty/serial/8250/8250_dma.c
> +++ b/drivers/tty/serial/8250/8250_dma.c
> @@ -15,7 +15,7 @@ static void __dma_tx_complete(void *param)
> {
> struct uart_8250_port *p = param;
> struct uart_8250_dma *dma = p->dma;
> - struct circ_buf *xmit = &p->port.state->xmit;
> + struct tty_port *tport = &p->port.state->port;
> unsigned long flags;
> int ret;
>
> @@ -28,7 +28,7 @@ static void __dma_tx_complete(void *param)
>
> uart_xmit_advance(&p->port, dma->tx_size);
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&p->port);
>
> ret = serial8250_tx_dma(p);
> @@ -86,7 +86,7 @@ static void dma_rx_complete(void *param)
> int serial8250_tx_dma(struct uart_8250_port *p)
> {
> struct uart_8250_dma *dma = p->dma;
> - struct circ_buf *xmit = &p->port.state->xmit;
> + struct tty_port *tport = &p->port.state->port;
> struct dma_async_tx_descriptor *desc;
> struct uart_port *up = &p->port;
> struct scatterlist sg;
> @@ -103,18 +103,23 @@ int serial8250_tx_dma(struct uart_8250_port *p)
> uart_xchar_out(up, UART_TX);
> }
>
> - if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) {
> + if (uart_tx_stopped(&p->port) || kfifo_is_empty(&tport->xmit_fifo)) {
> /* We have been called from __dma_tx_complete() */
> return 0;
> }
>
> - dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
> -
> serial8250_do_prepare_tx_dma(p);
>
> sg_init_table(&sg, 1);
> - sg_dma_address(&sg) = dma->tx_addr + xmit->tail;
> - sg_dma_len(&sg) = dma->tx_size;
> + /* kfifo can do more than one sg, we don't (quite yet) */
> + ret = kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, &sg, 1,
> + UART_XMIT_SIZE, dma->tx_addr);
> +
> + /* we already checked empty fifo above, so there should be something */
> + if (WARN_ON_ONCE(ret != 1))
> + return 0;
> +
> + dma->tx_size = sg_dma_len(&sg);
>
> desc = dmaengine_prep_slave_sg(dma->txchan, &sg, 1,
> DMA_MEM_TO_DEV,
> @@ -257,7 +262,7 @@ int serial8250_request_dma(struct uart_8250_port *p)
>
> /* TX buffer */
> dma->tx_addr = dma_map_single(dma->txchan->device->dev,
> - p->port.state->xmit.buf,
> + p->port.state->port.xmit_buf,
> UART_XMIT_SIZE,
> DMA_TO_DEVICE);
> if (dma_mapping_error(dma->txchan->device->dev, dma->tx_addr)) {
> diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c
> index 0440df7de1ed..f89dabfc0f97 100644
> --- a/drivers/tty/serial/8250/8250_exar.c
> +++ b/drivers/tty/serial/8250/8250_exar.c
> @@ -214,7 +214,7 @@ static void exar_shutdown(struct uart_port *port)
> {
> bool tx_complete = false;
> struct uart_8250_port *up = up_to_u8250p(port);
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> int i = 0;
> u16 lsr;
>
> @@ -225,7 +225,8 @@ static void exar_shutdown(struct uart_port *port)
> else
> tx_complete = false;
> usleep_range(1000, 1100);
> - } while (!uart_circ_empty(xmit) && !tx_complete && i++ < 1000);
> + } while (!kfifo_is_empty(&tport->xmit_fifo) &&
> + !tx_complete && i++ < 1000);
>
> serial8250_do_shutdown(port);
> }
> diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c
> index 9ff6bbe9c086..c365a349421a 100644
> --- a/drivers/tty/serial/8250/8250_mtk.c
> +++ b/drivers/tty/serial/8250/8250_mtk.c
> @@ -199,7 +199,7 @@ static int mtk8250_startup(struct uart_port *port)
>
> if (up->dma) {
> data->rx_status = DMA_RX_START;
> - uart_circ_clear(&port->state->xmit);
> + kfifo_reset(&port->state->port.xmit_fifo);
> }
> #endif
> memset(&port->icount, 0, sizeof(port->icount));
> diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
> index 879e77b30701..c07d924d5add 100644
> --- a/drivers/tty/serial/8250/8250_omap.c
> +++ b/drivers/tty/serial/8250/8250_omap.c
> @@ -1094,7 +1094,7 @@ static void omap_8250_dma_tx_complete(void *param)
> {
> struct uart_8250_port *p = param;
> struct uart_8250_dma *dma = p->dma;
> - struct circ_buf *xmit = &p->port.state->xmit;
> + struct tty_port *tport = &p->port.state->port;
> unsigned long flags;
> bool en_thri = false;
> struct omap8250_priv *priv = p->port.private_data;
> @@ -1113,10 +1113,10 @@ static void omap_8250_dma_tx_complete(void *param)
> omap8250_restore_regs(p);
> }
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&p->port);
>
> - if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port)) {
> + if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(&p->port)) {
> int ret;
>
> ret = omap_8250_tx_dma(p);
> @@ -1138,15 +1138,15 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
> {
> struct uart_8250_dma *dma = p->dma;
> struct omap8250_priv *priv = p->port.private_data;
> - struct circ_buf *xmit = &p->port.state->xmit;
> + struct tty_port *tport = &p->port.state->port;
> struct dma_async_tx_descriptor *desc;
> struct scatterlist sg;
> - unsigned int skip_byte = 0;
> + int skip_byte = -1;
> int ret;
>
> if (dma->tx_running)
> return 0;
> - if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) {
> + if (uart_tx_stopped(&p->port) || kfifo_is_empty(&tport->xmit_fifo)) {
>
> /*
> * Even if no data, we need to return an error for the two cases
> @@ -1161,8 +1161,18 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
> return 0;
> }
>
> - dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
> + sg_init_table(&sg, 1);
> + ret = kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, &sg, 1,
> + UART_XMIT_SIZE, dma->tx_addr);
> + if (ret != 1) {
> + serial8250_clear_THRI(p);
> + return 0;
> + }
> +
> + dma->tx_size = sg_dma_len(&sg);
> +
> if (priv->habit & OMAP_DMA_TX_KICK) {
> + unsigned char c;
> u8 tx_lvl;
>
> /*
> @@ -1189,13 +1199,16 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
> ret = -EINVAL;
> goto err;
> }
> - skip_byte = 1;
> + if (!kfifo_get(&tport->xmit_fifo, &c)) {
> + ret = -EINVAL;
> + goto err;
> + }
> + skip_byte = c;
> + /* now we need to recompute due to kfifo_get */
> + kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, &sg, 1,
> + UART_XMIT_SIZE, dma->tx_addr);
> }
>
> - sg_init_table(&sg, 1);
> - sg_dma_address(&sg) = dma->tx_addr + xmit->tail + skip_byte;
> - sg_dma_len(&sg) = dma->tx_size - skip_byte;
> -
> desc = dmaengine_prep_slave_sg(dma->txchan, &sg, 1, DMA_MEM_TO_DEV,
> DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
> if (!desc) {
> @@ -1218,11 +1231,13 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
> dma->tx_err = 0;
>
> serial8250_clear_THRI(p);
> - if (skip_byte)
> - serial_out(p, UART_TX, xmit->buf[xmit->tail]);
> - return 0;
> + ret = 0;
> + goto out_skip;
> err:
> dma->tx_err = 1;
> +out_skip:
> + if (skip_byte >= 0)
> + serial_out(p, UART_TX, skip_byte);
> return ret;
> }
>
> @@ -1311,7 +1326,7 @@ static int omap_8250_dma_handle_irq(struct uart_port *port)
> serial8250_modem_status(up);
> if (status & UART_LSR_THRE && up->dma->tx_err) {
> if (uart_tx_stopped(&up->port) ||
> - uart_circ_empty(&up->port.state->xmit)) {
> + kfifo_is_empty(&up->port.state->port.xmit_fifo)) {
> up->dma->tx_err = 0;
> serial8250_tx_chars(up);
> } else {
> diff --git a/drivers/tty/serial/8250/8250_pci1xxxx.c b/drivers/tty/serial/8250/8250_pci1xxxx.c
> index 2fbb5851f788..d3930bf32fe4 100644
> --- a/drivers/tty/serial/8250/8250_pci1xxxx.c
> +++ b/drivers/tty/serial/8250/8250_pci1xxxx.c
> @@ -382,10 +382,10 @@ static void pci1xxxx_rx_burst(struct uart_port *port, u32 uart_status)
> }
>
> static void pci1xxxx_process_write_data(struct uart_port *port,
> - struct circ_buf *xmit,
> int *data_empty_count,
> u32 *valid_byte_count)
> {
> + struct tty_port *tport = &port->state->port;
> u32 valid_burst_count = *valid_byte_count / UART_BURST_SIZE;
>
> /*
> @@ -395,41 +395,36 @@ static void pci1xxxx_process_write_data(struct uart_port *port,
> * one byte at a time.
> */
> while (valid_burst_count) {
> + u32 c;
> +
> if (*data_empty_count - UART_BURST_SIZE < 0)
> break;
> - if (xmit->tail > (UART_XMIT_SIZE - UART_BURST_SIZE))
> + if (kfifo_len(&tport->xmit_fifo) < UART_BURST_SIZE)
> + break;
> + if (WARN_ON(kfifo_out(&tport->xmit_fifo, (u8 *)&c, sizeof(c)) !=
> + sizeof(c)))
> break;
> - writel(*(unsigned int *)&xmit->buf[xmit->tail],
> - port->membase + UART_TX_BURST_FIFO);
> + writel(c, port->membase + UART_TX_BURST_FIFO);
> *valid_byte_count -= UART_BURST_SIZE;
> *data_empty_count -= UART_BURST_SIZE;
> valid_burst_count -= UART_BYTE_SIZE;
> -
> - xmit->tail = (xmit->tail + UART_BURST_SIZE) &
> - (UART_XMIT_SIZE - 1);
> }
>
> while (*valid_byte_count) {
> - if (*data_empty_count - UART_BYTE_SIZE < 0)
> + u8 c;
> +
> + if (!kfifo_get(&tport->xmit_fifo, &c))
> break;
> - writeb(xmit->buf[xmit->tail], port->membase +
> - UART_TX_BYTE_FIFO);
> + writeb(c, port->membase + UART_TX_BYTE_FIFO);
> *data_empty_count -= UART_BYTE_SIZE;
> *valid_byte_count -= UART_BYTE_SIZE;
>
> - /*
> - * When the tail of the circular buffer is reached, the next
> - * byte is transferred to the beginning of the buffer.
> - */
> - xmit->tail = (xmit->tail + UART_BYTE_SIZE) &
> - (UART_XMIT_SIZE - 1);
> -
> /*
> * If there are any pending burst count, data is handled by
> * transmitting DWORDs at a time.
> */
> - if (valid_burst_count && (xmit->tail <
> - (UART_XMIT_SIZE - UART_BURST_SIZE)))
> + if (valid_burst_count &&
> + kfifo_len(&tport->xmit_fifo) >= UART_BURST_SIZE)
> break;
> }
> }
> @@ -437,11 +432,9 @@ static void pci1xxxx_process_write_data(struct uart_port *port,
> static void pci1xxxx_tx_burst(struct uart_port *port, u32 uart_status)
> {
> struct uart_8250_port *up = up_to_u8250p(port);
> + struct tty_port *tport = &port->state->port;
> u32 valid_byte_count;
> int data_empty_count;
> - struct circ_buf *xmit;
> -
> - xmit = &port->state->xmit;
>
> if (port->x_char) {
> writeb(port->x_char, port->membase + UART_TX);
> @@ -450,25 +443,25 @@ static void pci1xxxx_tx_burst(struct uart_port *port, u32 uart_status)
> return;
> }
>
> - if ((uart_tx_stopped(port)) || (uart_circ_empty(xmit))) {
> + if ((uart_tx_stopped(port)) || kfifo_is_empty(&tport->xmit_fifo)) {
> port->ops->stop_tx(port);
> } else {
> data_empty_count = (pci1xxxx_read_burst_status(port) &
> UART_BST_STAT_TX_COUNT_MASK) >> 8;
> do {
> - valid_byte_count = uart_circ_chars_pending(xmit);
> + valid_byte_count = kfifo_len(&tport->xmit_fifo);
>
> - pci1xxxx_process_write_data(port, xmit,
> + pci1xxxx_process_write_data(port,
> &data_empty_count,
> &valid_byte_count);
>
> port->icount.tx++;
> - if (uart_circ_empty(xmit))
> + if (kfifo_is_empty(&tport->xmit_fifo))
> break;
> } while (data_empty_count && valid_byte_count);
> }
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
>
> /*
> @@ -476,7 +469,8 @@ static void pci1xxxx_tx_burst(struct uart_port *port, u32 uart_status)
> * the HW can go idle. So we get here once again with empty FIFO and
> * disable the interrupt and RPM in __stop_tx()
> */
> - if (uart_circ_empty(xmit) && !(up->capabilities & UART_CAP_RPM))
> + if (kfifo_is_empty(&tport->xmit_fifo) &&
> + !(up->capabilities & UART_CAP_RPM))
> port->ops->stop_tx(port);
> }
>
> diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
> index fc9dd5d45295..0c19451d0332 100644
> --- a/drivers/tty/serial/8250/8250_port.c
> +++ b/drivers/tty/serial/8250/8250_port.c
> @@ -1630,7 +1630,7 @@ static void serial8250_start_tx(struct uart_port *port)
> /* Port locked to synchronize UART_IER access against the console. */
> lockdep_assert_held_once(&port->lock);
>
> - if (!port->x_char && uart_circ_empty(&port->state->xmit))
> + if (!port->x_char && kfifo_is_empty(&port->state->port.xmit_fifo))
> return;
>
> serial8250_rpm_get_tx(up);
> @@ -1778,7 +1778,7 @@ EXPORT_SYMBOL_GPL(serial8250_rx_chars);
> void serial8250_tx_chars(struct uart_8250_port *up)
> {
> struct uart_port *port = &up->port;
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> int count;
>
> if (port->x_char) {
> @@ -1789,14 +1789,19 @@ void serial8250_tx_chars(struct uart_8250_port *up)
> serial8250_stop_tx(port);
> return;
> }
> - if (uart_circ_empty(xmit)) {
> + if (kfifo_is_empty(&tport->xmit_fifo)) {
> __stop_tx(up);
> return;
> }
>
> count = up->tx_loadsz;
> do {
> - serial_out(up, UART_TX, xmit->buf[xmit->tail]);
> + unsigned char c;
> +
> + if (!uart_fifo_get(port, &c))
> + break;
> +
> + serial_out(up, UART_TX, c);
> if (up->bugs & UART_BUG_TXRACE) {
> /*
> * The Aspeed BMC virtual UARTs have a bug where data
> @@ -1809,9 +1814,7 @@ void serial8250_tx_chars(struct uart_8250_port *up)
> */
> serial_in(up, UART_SCR);
> }
> - uart_xmit_advance(port, 1);
> - if (uart_circ_empty(xmit))
> - break;
> +
> if ((up->capabilities & UART_CAP_HFIFO) &&
> !uart_lsr_tx_empty(serial_in(up, UART_LSR)))
> break;
> @@ -1821,7 +1824,7 @@ void serial8250_tx_chars(struct uart_8250_port *up)
> break;
> } while (--count > 0);
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
>
> /*
> @@ -1829,7 +1832,8 @@ void serial8250_tx_chars(struct uart_8250_port *up)
> * HW can go idle. So we get here once again with empty FIFO and disable
> * the interrupt and RPM in __stop_tx()
> */
> - if (uart_circ_empty(xmit) && !(up->capabilities & UART_CAP_RPM))
> + if (kfifo_is_empty(&tport->xmit_fifo) &&
> + !(up->capabilities & UART_CAP_RPM))
> __stop_tx(up);
> }
> EXPORT_SYMBOL_GPL(serial8250_tx_chars);
> diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
> index 2fa3fb30dc6c..51b202eb8296 100644
> --- a/drivers/tty/serial/amba-pl011.c
> +++ b/drivers/tty/serial/amba-pl011.c
> @@ -535,6 +535,7 @@ static void pl011_start_tx_pio(struct uart_amba_port *uap);
> static void pl011_dma_tx_callback(void *data)
> {
> struct uart_amba_port *uap = data;
> + struct tty_port *tport = &uap->port.state->port;
> struct pl011_dmatx_data *dmatx = &uap->dmatx;
> unsigned long flags;
> u16 dmacr;
> @@ -558,7 +559,7 @@ static void pl011_dma_tx_callback(void *data)
> * get further refills (hence we check dmacr).
> */
> if (!(dmacr & UART011_TXDMAE) || uart_tx_stopped(&uap->port) ||
> - uart_circ_empty(&uap->port.state->xmit)) {
> + kfifo_is_empty(&tport->xmit_fifo)) {
> uap->dmatx.queued = false;
> uart_port_unlock_irqrestore(&uap->port, flags);
> return;
> @@ -588,7 +589,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap)
> struct dma_chan *chan = dmatx->chan;
> struct dma_device *dma_dev = chan->device;
> struct dma_async_tx_descriptor *desc;
> - struct circ_buf *xmit = &uap->port.state->xmit;
> + struct tty_port *tport = &uap->port.state->port;
> unsigned int count;
>
> /*
> @@ -597,7 +598,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap)
> * the standard interrupt handling. This ensures that we
> * issue a uart_write_wakeup() at the appropriate time.
> */
> - count = uart_circ_chars_pending(xmit);
> + count = kfifo_len(&tport->xmit_fifo);
> if (count < (uap->fifosize >> 1)) {
> uap->dmatx.queued = false;
> return 0;
> @@ -613,21 +614,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap)
> if (count > PL011_DMA_BUFFER_SIZE)
> count = PL011_DMA_BUFFER_SIZE;
>
> - if (xmit->tail < xmit->head) {
> - memcpy(&dmatx->buf[0], &xmit->buf[xmit->tail], count);
> - } else {
> - size_t first = UART_XMIT_SIZE - xmit->tail;
> - size_t second;
> -
> - if (first > count)
> - first = count;
> - second = count - first;
> -
> - memcpy(&dmatx->buf[0], &xmit->buf[xmit->tail], first);
> - if (second)
> - memcpy(&dmatx->buf[first], &xmit->buf[0], second);
> - }
> -
> + count = kfifo_out_peek(&tport->xmit_fifo, dmatx->buf, count);
> dmatx->len = count;
> dmatx->dma = dma_map_single(dma_dev->dev, dmatx->buf, count,
> DMA_TO_DEVICE);
> @@ -670,7 +657,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap)
> */
> uart_xmit_advance(&uap->port, count);
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&uap->port);
>
> return 1;
> @@ -1454,7 +1441,7 @@ static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c,
> /* Returns true if tx interrupts have to be (kept) enabled */
> static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq)
> {
> - struct circ_buf *xmit = &uap->port.state->xmit;
> + struct tty_port *tport = &uap->port.state->port;
> int count = uap->fifosize >> 1;
>
> if (uap->port.x_char) {
> @@ -1463,7 +1450,7 @@ static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq)
> uap->port.x_char = 0;
> --count;
> }
> - if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) {
> + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(&uap->port)) {
> pl011_stop_tx(&uap->port);
> return false;
> }
> @@ -1472,20 +1459,25 @@ static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq)
> if (pl011_dma_tx_irq(uap))
> return true;
>
> - do {
> + while (1) {
> + unsigned char c;
> +
> if (likely(from_irq) && count-- == 0)
> break;
>
> - if (!pl011_tx_char(uap, xmit->buf[xmit->tail], from_irq))
> + if (!kfifo_peek(&tport->xmit_fifo, &c))
> + break;
> +
> + if (!pl011_tx_char(uap, c, from_irq))
> break;
>
> - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
> - } while (!uart_circ_empty(xmit));
> + kfifo_skip(&tport->xmit_fifo);
> + }
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&uap->port);
>
> - if (uart_circ_empty(xmit)) {
> + if (kfifo_is_empty(&tport->xmit_fifo)) {
> pl011_stop_tx(&uap->port);
> return false;
> }
> diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c
> index 7790cbc57391..f2836ecc5c19 100644
> --- a/drivers/tty/serial/ar933x_uart.c
> +++ b/drivers/tty/serial/ar933x_uart.c
> @@ -390,7 +390,7 @@ static void ar933x_uart_rx_chars(struct ar933x_uart_port *up)
>
> static void ar933x_uart_tx_chars(struct ar933x_uart_port *up)
> {
> - struct circ_buf *xmit = &up->port.state->xmit;
> + struct tty_port *tport = &up->port.state->port;
> struct serial_rs485 *rs485conf = &up->port.rs485;
> int count;
> bool half_duplex_send = false;
> @@ -399,7 +399,7 @@ static void ar933x_uart_tx_chars(struct ar933x_uart_port *up)
> return;
>
> if ((rs485conf->flags & SER_RS485_ENABLED) &&
> - (up->port.x_char || !uart_circ_empty(xmit))) {
> + (up->port.x_char || !kfifo_is_empty(&tport->xmit_fifo))) {
> ar933x_uart_stop_rx_interrupt(up);
> gpiod_set_value(up->rts_gpiod, !!(rs485conf->flags & SER_RS485_RTS_ON_SEND));
> half_duplex_send = true;
> @@ -408,6 +408,7 @@ static void ar933x_uart_tx_chars(struct ar933x_uart_port *up)
> count = up->port.fifosize;
> do {
> unsigned int rdata;
> + unsigned char c;
>
> rdata = ar933x_uart_read(up, AR933X_UART_DATA_REG);
> if ((rdata & AR933X_UART_DATA_TX_CSR) == 0)
> @@ -420,18 +421,16 @@ static void ar933x_uart_tx_chars(struct ar933x_uart_port *up)
> continue;
> }
>
> - if (uart_circ_empty(xmit))
> + if (!uart_fifo_get(&up->port, &c))
> break;
>
> - ar933x_uart_putc(up, xmit->buf[xmit->tail]);
> -
> - uart_xmit_advance(&up->port, 1);
> + ar933x_uart_putc(up, c);
> } while (--count > 0);
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&up->port);
>
> - if (!uart_circ_empty(xmit)) {
> + if (!kfifo_is_empty(&tport->xmit_fifo)) {
> ar933x_uart_start_tx_interrupt(up);
> } else if (half_duplex_send) {
> ar933x_uart_wait_tx_complete(up);
> diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c
> index 1aa5b2b49c26..5c4895d154c0 100644
> --- a/drivers/tty/serial/arc_uart.c
> +++ b/drivers/tty/serial/arc_uart.c
> @@ -155,7 +155,7 @@ static unsigned int arc_serial_tx_empty(struct uart_port *port)
> */
> static void arc_serial_tx_chars(struct uart_port *port)
> {
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> int sent = 0;
> unsigned char ch;
>
> @@ -164,9 +164,7 @@ static void arc_serial_tx_chars(struct uart_port *port)
> port->icount.tx++;
> port->x_char = 0;
> sent = 1;
> - } else if (!uart_circ_empty(xmit)) {
> - ch = xmit->buf[xmit->tail];
> - uart_xmit_advance(port, 1);
> + } else if (uart_fifo_get(port, &ch)) {
> while (!(UART_GET_STATUS(port) & TXEMPTY))
> cpu_relax();
> UART_SET_DATA(port, ch);
> @@ -177,7 +175,7 @@ static void arc_serial_tx_chars(struct uart_port *port)
> * If num chars in xmit buffer are too few, ask tty layer for more.
> * By Hard ISR to schedule processing in software interrupt part
> */
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
>
> if (sent)
> diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
> index 85667f709515..5bb5e4303754 100644
> --- a/drivers/tty/serial/atmel_serial.c
> +++ b/drivers/tty/serial/atmel_serial.c
> @@ -857,7 +857,7 @@ static void atmel_complete_tx_dma(void *arg)
> {
> struct atmel_uart_port *atmel_port = arg;
> struct uart_port *port = &atmel_port->uart;
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> struct dma_chan *chan = atmel_port->chan_tx;
> unsigned long flags;
>
> @@ -873,15 +873,15 @@ static void atmel_complete_tx_dma(void *arg)
> atmel_port->desc_tx = NULL;
> spin_unlock(&atmel_port->lock_tx);
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
>
> /*
> - * xmit is a circular buffer so, if we have just send data from
> - * xmit->tail to the end of xmit->buf, now we have to transmit the
> - * remaining data from the beginning of xmit->buf to xmit->head.
> + * xmit is a circular buffer so, if we have just send data from the
> + * tail to the end, now we have to transmit the remaining data from the
> + * beginning to the head.
> */
> - if (!uart_circ_empty(xmit))
> + if (!kfifo_is_empty(&tport->xmit_fifo))
> atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_tx);
> else if (atmel_uart_is_half_duplex(port)) {
> /*
> @@ -919,18 +919,18 @@ static void atmel_release_tx_dma(struct uart_port *port)
> static void atmel_tx_dma(struct uart_port *port)
> {
> struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> struct dma_chan *chan = atmel_port->chan_tx;
> struct dma_async_tx_descriptor *desc;
> struct scatterlist sgl[2], *sg, *sg_tx = &atmel_port->sg_tx;
> - unsigned int tx_len, part1_len, part2_len, sg_len;
> + unsigned int tx_len, tail, part1_len, part2_len, sg_len;
> dma_addr_t phys_addr;
>
> /* Make sure we have an idle channel */
> if (atmel_port->desc_tx != NULL)
> return;
>
> - if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) {
> + if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(port)) {
> /*
> * DMA is idle now.
> * Port xmit buffer is already mapped,
> @@ -940,9 +940,8 @@ static void atmel_tx_dma(struct uart_port *port)
> * Take the port lock to get a
> * consistent xmit buffer state.
> */
> - tx_len = CIRC_CNT_TO_END(xmit->head,
> - xmit->tail,
> - UART_XMIT_SIZE);
> + tx_len = kfifo_out_linear(&tport->xmit_fifo, &tail,
> + UART_XMIT_SIZE);
>
> if (atmel_port->fifo_size) {
> /* multi data mode */
> @@ -956,7 +955,7 @@ static void atmel_tx_dma(struct uart_port *port)
>
> sg_init_table(sgl, 2);
> sg_len = 0;
> - phys_addr = sg_dma_address(sg_tx) + xmit->tail;
> + phys_addr = sg_dma_address(sg_tx) + tail;
> if (part1_len) {
> sg = &sgl[sg_len++];
> sg_dma_address(sg) = phys_addr;
> @@ -973,7 +972,7 @@ static void atmel_tx_dma(struct uart_port *port)
>
> /*
> * save tx_len so atmel_complete_tx_dma() will increase
> - * xmit->tail correctly
> + * tail correctly
> */
> atmel_port->tx_len = tx_len;
>
> @@ -1003,13 +1002,14 @@ static void atmel_tx_dma(struct uart_port *port)
> dma_async_issue_pending(chan);
> }
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
> }
>
> static int atmel_prepare_tx_dma(struct uart_port *port)
> {
> struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
> + struct tty_port *tport = &port->state->port;
> struct device *mfd_dev = port->dev->parent;
> dma_cap_mask_t mask;
> struct dma_slave_config config;
> @@ -1031,11 +1031,11 @@ static int atmel_prepare_tx_dma(struct uart_port *port)
> spin_lock_init(&atmel_port->lock_tx);
> sg_init_table(&atmel_port->sg_tx, 1);
> /* UART circular tx buffer is an aligned page. */
> - BUG_ON(!PAGE_ALIGNED(port->state->xmit.buf));
> + BUG_ON(!PAGE_ALIGNED(tport->xmit_buf));
> sg_set_page(&atmel_port->sg_tx,
> - virt_to_page(port->state->xmit.buf),
> + virt_to_page(tport->xmit_buf),
> UART_XMIT_SIZE,
> - offset_in_page(port->state->xmit.buf));
> + offset_in_page(tport->xmit_buf));
> nent = dma_map_sg(port->dev,
> &atmel_port->sg_tx,
> 1,
> @@ -1047,7 +1047,7 @@ static int atmel_prepare_tx_dma(struct uart_port *port)
> } else {
> dev_dbg(port->dev, "%s: mapped %d@%p to %pad\n", __func__,
> sg_dma_len(&atmel_port->sg_tx),
> - port->state->xmit.buf,
> + tport->xmit_buf,
> &sg_dma_address(&atmel_port->sg_tx));
> }
>
> @@ -1459,9 +1459,8 @@ static void atmel_release_tx_pdc(struct uart_port *port)
> static void atmel_tx_pdc(struct uart_port *port)
> {
> struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
> - int count;
>
> /* nothing left to transmit? */
> if (atmel_uart_readl(port, ATMEL_PDC_TCR))
> @@ -1474,17 +1473,19 @@ static void atmel_tx_pdc(struct uart_port *port)
> /* disable PDC transmit */
> atmel_uart_writel(port, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS);
>
> - if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) {
> + if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(port)) {
> + unsigned int count, tail;
> +
> dma_sync_single_for_device(port->dev,
> pdc->dma_addr,
> pdc->dma_size,
> DMA_TO_DEVICE);
>
> - count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
> + count = kfifo_out_linear(&tport->xmit_fifo, &tail,
> + UART_XMIT_SIZE);
> pdc->ofs = count;
>
> - atmel_uart_writel(port, ATMEL_PDC_TPR,
> - pdc->dma_addr + xmit->tail);
> + atmel_uart_writel(port, ATMEL_PDC_TPR, pdc->dma_addr + tail);
> atmel_uart_writel(port, ATMEL_PDC_TCR, count);
> /* re-enable PDC transmit */
> atmel_uart_writel(port, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN);
> @@ -1498,7 +1499,7 @@ static void atmel_tx_pdc(struct uart_port *port)
> }
> }
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
> }
>
> @@ -1506,9 +1507,9 @@ static int atmel_prepare_tx_pdc(struct uart_port *port)
> {
> struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
> struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
>
> - pdc->buf = xmit->buf;
> + pdc->buf = tport->xmit_buf;
> pdc->dma_addr = dma_map_single(port->dev,
> pdc->buf,
> UART_XMIT_SIZE,
> diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c
> index 7927725b8957..30425a3d19fb 100644
> --- a/drivers/tty/serial/clps711x.c
> +++ b/drivers/tty/serial/clps711x.c
> @@ -146,7 +146,8 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id)
> {
> struct uart_port *port = dev_id;
> struct clps711x_port *s = dev_get_drvdata(port->dev);
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> + unsigned char c;
>
> if (port->x_char) {
> writew(port->x_char, port->membase + UARTDR_OFFSET);
> @@ -155,7 +156,7 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id)
> return IRQ_HANDLED;
> }
>
> - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
> + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
> if (s->tx_enabled) {
> disable_irq_nosync(port->irq);
> s->tx_enabled = 0;
> @@ -163,18 +164,17 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id)
> return IRQ_HANDLED;
> }
>
> - while (!uart_circ_empty(xmit)) {
> + while (uart_fifo_get(port, &c)) {
> u32 sysflg = 0;
>
> - writew(xmit->buf[xmit->tail], port->membase + UARTDR_OFFSET);
> - uart_xmit_advance(port, 1);
> + writew(c, port->membase + UARTDR_OFFSET);
>
> regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);
> if (sysflg & SYSFLG_UTXFF)
> break;
> }
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
>
> return IRQ_HANDLED;
> diff --git a/drivers/tty/serial/cpm_uart.c b/drivers/tty/serial/cpm_uart.c
> index df56c6c5afd0..a927478f581d 100644
> --- a/drivers/tty/serial/cpm_uart.c
> +++ b/drivers/tty/serial/cpm_uart.c
> @@ -648,7 +648,7 @@ static int cpm_uart_tx_pump(struct uart_port *port)
> int count;
> struct uart_cpm_port *pinfo =
> container_of(port, struct uart_cpm_port, port);
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
>
> /* Handle xon/xoff */
> if (port->x_char) {
> @@ -673,7 +673,7 @@ static int cpm_uart_tx_pump(struct uart_port *port)
> return 1;
> }
>
> - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
> + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
> cpm_uart_stop_tx(port);
> return 0;
> }
> @@ -681,16 +681,10 @@ static int cpm_uart_tx_pump(struct uart_port *port)
> /* Pick next descriptor and fill from buffer */
> bdp = pinfo->tx_cur;
>
> - while (!(in_be16(&bdp->cbd_sc) & BD_SC_READY) && !uart_circ_empty(xmit)) {
> - count = 0;
> + while (!(in_be16(&bdp->cbd_sc) & BD_SC_READY) &&
> + !kfifo_is_empty(&tport->xmit_fifo)) {
> p = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr), pinfo);
> - while (count < pinfo->tx_fifosize) {
> - *p++ = xmit->buf[xmit->tail];
> - uart_xmit_advance(port, 1);
> - count++;
> - if (uart_circ_empty(xmit))
> - break;
> - }
> + count = uart_fifo_out(port, p, pinfo->tx_fifosize);
> out_be16(&bdp->cbd_datlen, count);
> setbits16(&bdp->cbd_sc, BD_SC_READY);
> /* Get next BD. */
> @@ -701,10 +695,10 @@ static int cpm_uart_tx_pump(struct uart_port *port)
> }
> pinfo->tx_cur = bdp;
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
>
> - if (uart_circ_empty(xmit)) {
> + if (kfifo_is_empty(&tport->xmit_fifo)) {
> cpm_uart_stop_tx(port);
> return 0;
> }
> diff --git a/drivers/tty/serial/digicolor-usart.c b/drivers/tty/serial/digicolor-usart.c
> index e419c4bde8b7..2ccd13cc0a89 100644
> --- a/drivers/tty/serial/digicolor-usart.c
> +++ b/drivers/tty/serial/digicolor-usart.c
> @@ -179,8 +179,9 @@ static void digicolor_uart_rx(struct uart_port *port)
>
> static void digicolor_uart_tx(struct uart_port *port)
> {
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> unsigned long flags;
> + unsigned char c;
>
> if (digicolor_uart_tx_full(port))
> return;
> @@ -194,20 +195,19 @@ static void digicolor_uart_tx(struct uart_port *port)
> goto out;
> }
>
> - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
> + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
> digicolor_uart_stop_tx(port);
> goto out;
> }
>
> - while (!uart_circ_empty(xmit)) {
> - writeb(xmit->buf[xmit->tail], port->membase + UA_EMI_REC);
> - uart_xmit_advance(port, 1);
> + while (uart_fifo_get(port, &c)) {
> + writeb(c, port->membase + UA_EMI_REC);
>
> if (digicolor_uart_tx_full(port))
> break;
> }
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
>
> out:
> diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c
> index 6df7af9edc1c..eba91daedef8 100644
> --- a/drivers/tty/serial/dz.c
> +++ b/drivers/tty/serial/dz.c
> @@ -252,13 +252,13 @@ static inline void dz_receive_chars(struct dz_mux *mux)
> static inline void dz_transmit_chars(struct dz_mux *mux)
> {
> struct dz_port *dport = &mux->dport[0];
> - struct circ_buf *xmit;
> + struct tty_port *tport;
> unsigned char tmp;
> u16 status;
>
> status = dz_in(dport, DZ_CSR);
> dport = &mux->dport[LINE(status)];
> - xmit = &dport->port.state->xmit;
> + tport = &dport->port.state->port;
>
> if (dport->port.x_char) { /* XON/XOFF chars */
> dz_out(dport, DZ_TDR, dport->port.x_char);
> @@ -267,7 +267,8 @@ static inline void dz_transmit_chars(struct dz_mux *mux)
> return;
> }
> /* If nothing to do or stopped or hardware stopped. */
> - if (uart_circ_empty(xmit) || uart_tx_stopped(&dport->port)) {
> + if (uart_tx_stopped(&dport->port) ||
> + !uart_fifo_get(&dport->port, &tmp)) {
> uart_port_lock(&dport->port);
> dz_stop_tx(&dport->port);
> uart_port_unlock(&dport->port);
> @@ -278,15 +279,13 @@ static inline void dz_transmit_chars(struct dz_mux *mux)
> * If something to do... (remember the dz has no output fifo,
> * so we go one char at a time) :-<
> */
> - tmp = xmit->buf[xmit->tail];
> dz_out(dport, DZ_TDR, tmp);
> - uart_xmit_advance(&dport->port, 1);
>
> - if (uart_circ_chars_pending(xmit) < DZ_WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < DZ_WAKEUP_CHARS)
> uart_write_wakeup(&dport->port);
>
> /* Are we are done. */
> - if (uart_circ_empty(xmit)) {
> + if (kfifo_is_empty(&tport->xmit_fifo)) {
> uart_port_lock(&dport->port);
> dz_stop_tx(&dport->port);
> uart_port_unlock(&dport->port);
> diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c
> index 5426322b5f0c..e972df4b188d 100644
> --- a/drivers/tty/serial/fsl_linflexuart.c
> +++ b/drivers/tty/serial/fsl_linflexuart.c
> @@ -174,17 +174,18 @@ static void linflex_put_char(struct uart_port *sport, unsigned char c)
>
> static inline void linflex_transmit_buffer(struct uart_port *sport)
> {
> - struct circ_buf *xmit = &sport->state->xmit;
> + struct tty_port *tport = &sport->state->port;
> + unsigned char c;
>
> - while (!uart_circ_empty(xmit)) {
> - linflex_put_char(sport, xmit->buf[xmit->tail]);
> - uart_xmit_advance(sport, 1);
> + while (uart_fifo_get(sport, &c)) {
> + linflex_put_char(sport, c);
> + sport->icount.tx++;
> }
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(sport);
>
> - if (uart_circ_empty(xmit))
> + if (kfifo_is_empty(&tport->xmit_fifo))
> linflex_stop_tx(sport);
> }
>
> @@ -200,7 +201,7 @@ static void linflex_start_tx(struct uart_port *port)
> static irqreturn_t linflex_txint(int irq, void *dev_id)
> {
> struct uart_port *sport = dev_id;
> - struct circ_buf *xmit = &sport->state->xmit;
> + struct tty_port *tport = &sport->state->port;
> unsigned long flags;
>
> uart_port_lock_irqsave(sport, &flags);
> @@ -210,7 +211,7 @@ static irqreturn_t linflex_txint(int irq, void *dev_id)
> goto out;
> }
>
> - if (uart_circ_empty(xmit) || uart_tx_stopped(sport)) {
> + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(sport)) {
> linflex_stop_tx(sport);
> goto out;
> }
> diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
> index bbcbc91482af..ae5e1ecc48fc 100644
> --- a/drivers/tty/serial/fsl_lpuart.c
> +++ b/drivers/tty/serial/fsl_lpuart.c
> @@ -7,6 +7,7 @@
>
> #include <linux/bitfield.h>
> #include <linux/bits.h>
> +#include <linux/circ_buf.h>
> #include <linux/clk.h>
> #include <linux/console.h>
> #include <linux/delay.h>
> @@ -473,7 +474,7 @@ static void lpuart32_stop_rx(struct uart_port *port)
>
> static void lpuart_dma_tx(struct lpuart_port *sport)
> {
> - struct circ_buf *xmit = &sport->port.state->xmit;
> + struct tty_port *tport = &sport->port.state->port;
> struct scatterlist *sgl = sport->tx_sgl;
> struct device *dev = sport->port.dev;
> struct dma_chan *chan = sport->dma_tx_chan;
> @@ -482,18 +483,10 @@ static void lpuart_dma_tx(struct lpuart_port *sport)
> if (sport->dma_tx_in_progress)
> return;
>
> - sport->dma_tx_bytes = uart_circ_chars_pending(xmit);
> -
> - if (xmit->tail < xmit->head || xmit->head == 0) {
> - sport->dma_tx_nents = 1;
> - sg_init_one(sgl, xmit->buf + xmit->tail, sport->dma_tx_bytes);
> - } else {
> - sport->dma_tx_nents = 2;
> - sg_init_table(sgl, 2);
> - sg_set_buf(sgl, xmit->buf + xmit->tail,
> - UART_XMIT_SIZE - xmit->tail);
> - sg_set_buf(sgl + 1, xmit->buf, xmit->head);
> - }
> + sg_init_table(sgl, ARRAY_SIZE(sport->tx_sgl));
> + sport->dma_tx_bytes = kfifo_len(&tport->xmit_fifo);
> + sport->dma_tx_nents = kfifo_dma_out_prepare(&tport->xmit_fifo, sgl,
> + ARRAY_SIZE(sport->tx_sgl), sport->dma_tx_bytes);
>
> ret = dma_map_sg(chan->device->dev, sgl, sport->dma_tx_nents,
> DMA_TO_DEVICE);
> @@ -521,14 +514,15 @@ static void lpuart_dma_tx(struct lpuart_port *sport)
>
> static bool lpuart_stopped_or_empty(struct uart_port *port)
> {
> - return uart_circ_empty(&port->state->xmit) || uart_tx_stopped(port);
> + return kfifo_is_empty(&port->state->port.xmit_fifo) ||
> + uart_tx_stopped(port);
> }
>
> static void lpuart_dma_tx_complete(void *arg)
> {
> struct lpuart_port *sport = arg;
> struct scatterlist *sgl = &sport->tx_sgl[0];
> - struct circ_buf *xmit = &sport->port.state->xmit;
> + struct tty_port *tport = &sport->port.state->port;
> struct dma_chan *chan = sport->dma_tx_chan;
> unsigned long flags;
>
> @@ -545,7 +539,7 @@ static void lpuart_dma_tx_complete(void *arg)
> sport->dma_tx_in_progress = false;
> uart_port_unlock_irqrestore(&sport->port, flags);
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&sport->port);
>
> if (waitqueue_active(&sport->dma_wait)) {
> @@ -756,8 +750,9 @@ static inline void lpuart_transmit_buffer(struct lpuart_port *sport)
>
> static inline void lpuart32_transmit_buffer(struct lpuart_port *sport)
> {
> - struct circ_buf *xmit = &sport->port.state->xmit;
> + struct tty_port *tport = &sport->port.state->port;
> unsigned long txcnt;
> + unsigned char c;
>
> if (sport->port.x_char) {
> lpuart32_write(&sport->port, sport->port.x_char, UARTDATA);
> @@ -774,18 +769,18 @@ static inline void lpuart32_transmit_buffer(struct lpuart_port *sport)
> txcnt = lpuart32_read(&sport->port, UARTWATER);
> txcnt = txcnt >> UARTWATER_TXCNT_OFF;
> txcnt &= UARTWATER_COUNT_MASK;
> - while (!uart_circ_empty(xmit) && (txcnt < sport->txfifo_size)) {
> - lpuart32_write(&sport->port, xmit->buf[xmit->tail], UARTDATA);
> - uart_xmit_advance(&sport->port, 1);
> + while (txcnt < sport->txfifo_size &&
> + uart_fifo_get(&sport->port, &c)) {
> + lpuart32_write(&sport->port, c, UARTDATA);
> txcnt = lpuart32_read(&sport->port, UARTWATER);
> txcnt = txcnt >> UARTWATER_TXCNT_OFF;
> txcnt &= UARTWATER_COUNT_MASK;
> }
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&sport->port);
>
> - if (uart_circ_empty(xmit))
> + if (kfifo_is_empty(&tport->xmit_fifo))
> lpuart32_stop_tx(&sport->port);
> }
>
> diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c
> index a75eafbcbea3..29e42831df39 100644
> --- a/drivers/tty/serial/icom.c
> +++ b/drivers/tty/serial/icom.c
> @@ -877,10 +877,10 @@ static void shutdown(struct icom_port *icom_port)
> static int icom_write(struct uart_port *port)
> {
> struct icom_port *icom_port = to_icom_port(port);
> + struct tty_port *tport = &port->state->port;
> unsigned long data_count;
> unsigned char cmdReg;
> unsigned long offset;
> - int temp_tail = port->state->xmit.tail;
>
> trace(icom_port, "WRITE", 0);
>
> @@ -890,16 +890,8 @@ static int icom_write(struct uart_port *port)
> return 0;
> }
>
> - data_count = 0;
> - while ((port->state->xmit.head != temp_tail) &&
> - (data_count <= XMIT_BUFF_SZ)) {
> -
> - icom_port->xmit_buf[data_count++] =
> - port->state->xmit.buf[temp_tail];
> -
> - temp_tail++;
> - temp_tail &= (UART_XMIT_SIZE - 1);
> - }
> + data_count = kfifo_out_peek(&tport->xmit_fifo, icom_port->xmit_buf,
> + XMIT_BUFF_SZ);
>
> if (data_count) {
> icom_port->statStg->xmit[0].flags =
> @@ -956,7 +948,8 @@ static inline void check_modem_status(struct icom_port *icom_port)
>
> static void xmit_interrupt(u16 port_int_reg, struct icom_port *icom_port)
> {
> - u16 count, i;
> + struct tty_port *tport = &icom_port->uart_port.state->port;
> + u16 count;
>
> if (port_int_reg & (INT_XMIT_COMPLETED)) {
> trace(icom_port, "XMIT_COMPLETE", 0);
> @@ -968,13 +961,7 @@ static void xmit_interrupt(u16 port_int_reg, struct icom_port *icom_port)
> count = le16_to_cpu(icom_port->statStg->xmit[0].leLength);
> icom_port->uart_port.icount.tx += count;
>
> - for (i=0; i<count &&
> - !uart_circ_empty(&icom_port->uart_port.state->xmit); i++) {
> -
> - icom_port->uart_port.state->xmit.tail++;
> - icom_port->uart_port.state->xmit.tail &=
> - (UART_XMIT_SIZE - 1);
> - }
> + kfifo_skip_count(&tport->xmit_fifo, count);
>
> if (!icom_write(&icom_port->uart_port))
> /* activate write queue */
> diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
> index e14813250616..56b76a221082 100644
> --- a/drivers/tty/serial/imx.c
> +++ b/drivers/tty/serial/imx.c
> @@ -8,6 +8,7 @@
> * Copyright (C) 2004 Pengutronix
> */
>
> +#include <linux/circ_buf.h>
> #include <linux/module.h>
> #include <linux/ioport.h>
> #include <linux/init.h>
> @@ -521,7 +522,8 @@ static void imx_uart_dma_tx(struct imx_port *sport);
> /* called with port.lock taken and irqs off */
> static inline void imx_uart_transmit_buffer(struct imx_port *sport)
> {
> - struct circ_buf *xmit = &sport->port.state->xmit;
> + struct tty_port *tport = &sport->port.state->port;
> + unsigned char c;
>
> if (sport->port.x_char) {
> /* Send next char */
> @@ -531,7 +533,8 @@ static inline void imx_uart_transmit_buffer(struct imx_port *sport)
> return;
> }
>
> - if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
> + if (kfifo_is_empty(&tport->xmit_fifo) ||
> + uart_tx_stopped(&sport->port)) {
> imx_uart_stop_tx(&sport->port);
> return;
> }
> @@ -555,26 +558,22 @@ static inline void imx_uart_transmit_buffer(struct imx_port *sport)
> return;
> }
>
> - while (!uart_circ_empty(xmit) &&
> - !(imx_uart_readl(sport, imx_uart_uts_reg(sport)) & UTS_TXFULL)) {
> - /* send xmit->buf[xmit->tail]
> - * out the port here */
> - imx_uart_writel(sport, xmit->buf[xmit->tail], URTX0);
> - uart_xmit_advance(&sport->port, 1);
> - }
> + while (!(imx_uart_readl(sport, imx_uart_uts_reg(sport)) & UTS_TXFULL) &&
> + uart_fifo_get(&sport->port, &c))
> + imx_uart_writel(sport, c, URTX0);
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&sport->port);
>
> - if (uart_circ_empty(xmit))
> + if (kfifo_is_empty(&tport->xmit_fifo))
> imx_uart_stop_tx(&sport->port);
> }
>
> static void imx_uart_dma_tx_callback(void *data)
> {
> struct imx_port *sport = data;
> + struct tty_port *tport = &sport->port.state->port;
> struct scatterlist *sgl = &sport->tx_sgl[0];
> - struct circ_buf *xmit = &sport->port.state->xmit;
> unsigned long flags;
> u32 ucr1;
>
> @@ -592,10 +591,11 @@ static void imx_uart_dma_tx_callback(void *data)
>
> sport->dma_is_txing = 0;
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&sport->port);
>
> - if (!uart_circ_empty(xmit) && !uart_tx_stopped(&sport->port))
> + if (!kfifo_is_empty(&tport->xmit_fifo) &&
> + !uart_tx_stopped(&sport->port))
> imx_uart_dma_tx(sport);
> else if (sport->port.rs485.flags & SER_RS485_ENABLED) {
> u32 ucr4 = imx_uart_readl(sport, UCR4);
> @@ -609,7 +609,7 @@ static void imx_uart_dma_tx_callback(void *data)
> /* called with port.lock taken and irqs off */
> static void imx_uart_dma_tx(struct imx_port *sport)
> {
> - struct circ_buf *xmit = &sport->port.state->xmit;
> + struct tty_port *tport = &sport->port.state->port;
> struct scatterlist *sgl = sport->tx_sgl;
> struct dma_async_tx_descriptor *desc;
> struct dma_chan *chan = sport->dma_chan_tx;
> @@ -624,18 +624,10 @@ static void imx_uart_dma_tx(struct imx_port *sport)
> ucr4 &= ~UCR4_TCEN;
> imx_uart_writel(sport, ucr4, UCR4);
>
> - sport->tx_bytes = uart_circ_chars_pending(xmit);
> -
> - if (xmit->tail < xmit->head || xmit->head == 0) {
> - sport->dma_tx_nents = 1;
> - sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes);
> - } else {
> - sport->dma_tx_nents = 2;
> - sg_init_table(sgl, 2);
> - sg_set_buf(sgl, xmit->buf + xmit->tail,
> - UART_XMIT_SIZE - xmit->tail);
> - sg_set_buf(sgl + 1, xmit->buf, xmit->head);
> - }
> + sg_init_table(sgl, ARRAY_SIZE(sport->tx_sgl));
> + sport->tx_bytes = kfifo_len(&tport->xmit_fifo);
> + sport->dma_tx_nents = kfifo_dma_out_prepare(&tport->xmit_fifo, sgl,
> + ARRAY_SIZE(sport->tx_sgl), sport->tx_bytes);
>
> ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
> if (ret == 0) {
> @@ -653,8 +645,7 @@ static void imx_uart_dma_tx(struct imx_port *sport)
> desc->callback = imx_uart_dma_tx_callback;
> desc->callback_param = sport;
>
> - dev_dbg(dev, "TX: prepare to send %lu bytes by DMA.\n",
> - uart_circ_chars_pending(xmit));
> + dev_dbg(dev, "TX: prepare to send %u bytes by DMA.\n", sport->tx_bytes);
>
> ucr1 = imx_uart_readl(sport, UCR1);
> ucr1 |= UCR1_TXDMAEN;
> @@ -671,9 +662,10 @@ static void imx_uart_dma_tx(struct imx_port *sport)
> static void imx_uart_start_tx(struct uart_port *port)
> {
> struct imx_port *sport = (struct imx_port *)port;
> + struct tty_port *tport = &sport->port.state->port;
> u32 ucr1;
>
> - if (!sport->port.x_char && uart_circ_empty(&port->state->xmit))
> + if (!sport->port.x_char && kfifo_is_empty(&tport->xmit_fifo))
> return;
>
> /*
> @@ -749,7 +741,7 @@ static void imx_uart_start_tx(struct uart_port *port)
> return;
> }
>
> - if (!uart_circ_empty(&port->state->xmit) &&
> + if (!kfifo_is_empty(&tport->xmit_fifo) &&
> !uart_tx_stopped(port))
> imx_uart_dma_tx(sport);
> return;
> diff --git a/drivers/tty/serial/ip22zilog.c b/drivers/tty/serial/ip22zilog.c
> index 320b29cd4683..c2cae50f06f3 100644
> --- a/drivers/tty/serial/ip22zilog.c
> +++ b/drivers/tty/serial/ip22zilog.c
> @@ -355,7 +355,8 @@ static void ip22zilog_status_handle(struct uart_ip22zilog_port *up,
> static void ip22zilog_transmit_chars(struct uart_ip22zilog_port *up,
> struct zilog_channel *channel)
> {
> - struct circ_buf *xmit;
> + struct tty_port *tport;
> + unsigned char c;
>
> if (ZS_IS_CONS(up)) {
> unsigned char status = readb(&channel->control);
> @@ -398,20 +399,18 @@ static void ip22zilog_transmit_chars(struct uart_ip22zilog_port *up,
>
> if (up->port.state == NULL)
> goto ack_tx_int;
> - xmit = &up->port.state->xmit;
> - if (uart_circ_empty(xmit))
> - goto ack_tx_int;
> + tport = &up->port.state->port;
> if (uart_tx_stopped(&up->port))
> goto ack_tx_int;
> + if (!uart_fifo_get(&up->port, &c))
> + goto ack_tx_int;
>
> up->flags |= IP22ZILOG_FLAG_TX_ACTIVE;
> - writeb(xmit->buf[xmit->tail], &channel->data);
> + writeb(c, &channel->data);
> ZSDELAY();
> ZS_WSYNC(channel);
>
> - uart_xmit_advance(&up->port, 1);
> -
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&up->port);
>
> return;
> @@ -600,17 +599,16 @@ static void ip22zilog_start_tx(struct uart_port *port)
> port->icount.tx++;
> port->x_char = 0;
> } else {
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> + unsigned char c;
>
> - if (uart_circ_empty(xmit))
> + if (!uart_fifo_get(port, &c))
> return;
> - writeb(xmit->buf[xmit->tail], &channel->data);
> + writeb(c, &channel->data);
> ZSDELAY();
> ZS_WSYNC(channel);
>
> - uart_xmit_advance(port, 1);
> -
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&up->port);
> }
> }
> diff --git a/drivers/tty/serial/jsm/jsm_cls.c b/drivers/tty/serial/jsm/jsm_cls.c
> index ddbd42c09637..6e40792f92cf 100644
> --- a/drivers/tty/serial/jsm/jsm_cls.c
> +++ b/drivers/tty/serial/jsm/jsm_cls.c
> @@ -443,20 +443,14 @@ static void cls_copy_data_from_uart_to_queue(struct jsm_channel *ch)
>
> static void cls_copy_data_from_queue_to_uart(struct jsm_channel *ch)
> {
> - u16 tail;
> + struct tty_port *tport;
> int n;
> - int qlen;
> u32 len_written = 0;
> - struct circ_buf *circ;
>
> if (!ch)
> return;
>
> - circ = &ch->uart_port.state->xmit;
> -
> - /* No data to write to the UART */
> - if (uart_circ_empty(circ))
> - return;
> + tport = &ch->uart_port.state->port;
>
> /* If port is "stopped", don't send any data to the UART */
> if ((ch->ch_flags & CH_STOP) || (ch->ch_flags & CH_BREAK_SENDING))
> @@ -467,29 +461,22 @@ static void cls_copy_data_from_queue_to_uart(struct jsm_channel *ch)
> return;
>
> n = 32;
> + while (n > 0) {
> + unsigned char c;
>
> - /* cache tail of queue */
> - tail = circ->tail & (UART_XMIT_SIZE - 1);
> - qlen = uart_circ_chars_pending(circ);
> -
> - /* Find minimum of the FIFO space, versus queue length */
> - n = min(n, qlen);
> + if (!kfifo_get(&tport->xmit_fifo, &c))
> + break;
>
> - while (n > 0) {
> - writeb(circ->buf[tail], &ch->ch_cls_uart->txrx);
> - tail = (tail + 1) & (UART_XMIT_SIZE - 1);
> + writeb(c, &ch->ch_cls_uart->txrx);
> n--;
> ch->ch_txcount++;
> len_written++;
> }
>
> - /* Update the final tail */
> - circ->tail = tail & (UART_XMIT_SIZE - 1);
> -
> if (len_written > ch->ch_t_tlevel)
> ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
>
> - if (uart_circ_empty(circ))
> + if (kfifo_is_empty(&tport->xmit_fifo))
> uart_write_wakeup(&ch->uart_port);
> }
>
> diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c
> index 1fa10f19368f..e8e13bf056e2 100644
> --- a/drivers/tty/serial/jsm/jsm_neo.c
> +++ b/drivers/tty/serial/jsm/jsm_neo.c
> @@ -474,21 +474,21 @@ static void neo_copy_data_from_uart_to_queue(struct jsm_channel *ch)
>
> static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch)
> {
> - u16 head;
> - u16 tail;
> + struct tty_port *tport;
> + unsigned char *tail;
> + unsigned char c;
> int n;
> int s;
> int qlen;
> u32 len_written = 0;
> - struct circ_buf *circ;
>
> if (!ch)
> return;
>
> - circ = &ch->uart_port.state->xmit;
> + tport = &ch->uart_port.state->port;
>
> /* No data to write to the UART */
> - if (uart_circ_empty(circ))
> + if (kfifo_is_empty(&tport->xmit_fifo))
> return;
>
> /* If port is "stopped", don't send any data to the UART */
> @@ -504,10 +504,9 @@ static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch)
> if (ch->ch_cached_lsr & UART_LSR_THRE) {
> ch->ch_cached_lsr &= ~(UART_LSR_THRE);
>
> - writeb(circ->buf[circ->tail], &ch->ch_neo_uart->txrx);
> - jsm_dbg(WRITE, &ch->ch_bd->pci_dev,
> - "Tx data: %x\n", circ->buf[circ->tail]);
> - circ->tail = (circ->tail + 1) & (UART_XMIT_SIZE - 1);
> + WARN_ON_ONCE(!kfifo_get(&tport->xmit_fifo, &c));
> + writeb(c, &ch->ch_neo_uart->txrx);
> + jsm_dbg(WRITE, &ch->ch_bd->pci_dev, "Tx data: %x\n", c);
> ch->ch_txcount++;
> }
> return;
> @@ -520,38 +519,27 @@ static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch)
> return;
>
> n = UART_17158_TX_FIFOSIZE - ch->ch_t_tlevel;
> -
> - /* cache head and tail of queue */
> - head = circ->head & (UART_XMIT_SIZE - 1);
> - tail = circ->tail & (UART_XMIT_SIZE - 1);
> - qlen = uart_circ_chars_pending(circ);
> + qlen = kfifo_len(&tport->xmit_fifo);
>
> /* Find minimum of the FIFO space, versus queue length */
> n = min(n, qlen);
>
> while (n > 0) {
> -
> - s = ((head >= tail) ? head : UART_XMIT_SIZE) - tail;
> - s = min(s, n);
> -
> + s = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, n);
> if (s <= 0)
> break;
>
> - memcpy_toio(&ch->ch_neo_uart->txrxburst, circ->buf + tail, s);
> - /* Add and flip queue if needed */
> - tail = (tail + s) & (UART_XMIT_SIZE - 1);
> + memcpy_toio(&ch->ch_neo_uart->txrxburst, tail, s);
> + kfifo_skip_count(&tport->xmit_fifo, s);
> n -= s;
> ch->ch_txcount += s;
> len_written += s;
> }
>
> - /* Update the final tail */
> - circ->tail = tail & (UART_XMIT_SIZE - 1);
> -
> if (len_written >= ch->ch_t_tlevel)
> ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
>
> - if (uart_circ_empty(circ))
> + if (kfifo_is_empty(&tport->xmit_fifo))
> uart_write_wakeup(&ch->uart_port);
> }
>
> diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c
> index 5efb2b593be3..b83eee37c17d 100644
> --- a/drivers/tty/serial/max3100.c
> +++ b/drivers/tty/serial/max3100.c
> @@ -257,10 +257,11 @@ static int max3100_handlerx(struct max3100_port *s, u16 rx)
> static void max3100_work(struct work_struct *w)
> {
> struct max3100_port *s = container_of(w, struct max3100_port, work);
> + struct tty_port *tport = &s->port.state->port;
> + unsigned char ch;
> int rxchars;
> u16 tx, rx;
> int conf, cconf, crts;
> - struct circ_buf *xmit = &s->port.state->xmit;
>
> dev_dbg(&s->spi->dev, "%s\n", __func__);
>
> @@ -290,10 +291,9 @@ static void max3100_work(struct work_struct *w)
> tx = s->port.x_char;
> s->port.icount.tx++;
> s->port.x_char = 0;
> - } else if (!uart_circ_empty(xmit) &&
> - !uart_tx_stopped(&s->port)) {
> - tx = xmit->buf[xmit->tail];
> - uart_xmit_advance(&s->port, 1);
> + } else if (!uart_tx_stopped(&s->port) &&
> + uart_fifo_get(&s->port, &ch)) {
> + tx = ch;
> }
> if (tx != 0xffff) {
> max3100_calc_parity(s, &tx);
> @@ -307,13 +307,13 @@ static void max3100_work(struct work_struct *w)
> tty_flip_buffer_push(&s->port.state->port);
> rxchars = 0;
> }
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&s->port);
>
> } while (!s->force_end_work &&
> !freezing(current) &&
> ((rx & MAX3100_R) ||
> - (!uart_circ_empty(xmit) &&
> + (!kfifo_is_empty(&tport->xmit_fifo) &&
> !uart_tx_stopped(&s->port))));
>
> if (rxchars > 0)
> diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
> index 14dd9cfaa9f7..f0eb96429dae 100644
> --- a/drivers/tty/serial/max310x.c
> +++ b/drivers/tty/serial/max310x.c
> @@ -747,8 +747,9 @@ static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen)
>
> static void max310x_handle_tx(struct uart_port *port)
> {
> - struct circ_buf *xmit = &port->state->xmit;
> - unsigned int txlen, to_send, until_end;
> + struct tty_port *tport = &port->state->port;
> + unsigned int txlen, to_send;
> + unsigned char *tail;
>
> if (unlikely(port->x_char)) {
> max310x_port_write(port, MAX310X_THR_REG, port->x_char);
> @@ -757,32 +758,26 @@ static void max310x_handle_tx(struct uart_port *port)
> return;
> }
>
> - if (uart_circ_empty(xmit) || uart_tx_stopped(port))
> + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port))
> return;
>
> - /* Get length of data pending in circular buffer */
> - to_send = uart_circ_chars_pending(xmit);
> - until_end = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
> - if (likely(to_send)) {
> + /*
> + * It's a circ buffer -- wrap around.
> + * We could do that in one SPI transaction, but meh.
> + */
> + while (!kfifo_is_empty(&tport->xmit_fifo)) {
> /* Limit to space available in TX FIFO */
> txlen = max310x_port_read(port, MAX310X_TXFIFOLVL_REG);
> txlen = port->fifosize - txlen;
> - to_send = (to_send > txlen) ? txlen : to_send;
> -
> - if (until_end < to_send) {
> - /*
> - * It's a circ buffer -- wrap around.
> - * We could do that in one SPI transaction, but meh.
> - */
> - max310x_batch_write(port, xmit->buf + xmit->tail, until_end);
> - max310x_batch_write(port, xmit->buf, to_send - until_end);
> - } else {
> - max310x_batch_write(port, xmit->buf + xmit->tail, to_send);
> - }
> + if (!txlen)
> + break;
> +
> + to_send = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, txlen);
> + max310x_batch_write(port, tail, to_send);
> uart_xmit_advance(port, to_send);
> }
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
> }
>
> diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c
> index 8048fa542fc4..4bff422bb1bc 100644
> --- a/drivers/tty/serial/men_z135_uart.c
> +++ b/drivers/tty/serial/men_z135_uart.c
> @@ -293,17 +293,14 @@ static void men_z135_handle_rx(struct men_z135_port *uart)
> static void men_z135_handle_tx(struct men_z135_port *uart)
> {
> struct uart_port *port = &uart->port;
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> + unsigned char *tail;
> + unsigned int n, txfree;
> u32 txc;
> u32 wptr;
> int qlen;
> - int n;
> - int txfree;
> - int head;
> - int tail;
> - int s;
>
> - if (uart_circ_empty(xmit))
> + if (kfifo_is_empty(&tport->xmit_fifo))
> goto out;
>
> if (uart_tx_stopped(port))
> @@ -313,7 +310,7 @@ static void men_z135_handle_tx(struct men_z135_port *uart)
> goto out;
>
> /* calculate bytes to copy */
> - qlen = uart_circ_chars_pending(xmit);
> + qlen = kfifo_len(&tport->xmit_fifo);
> if (qlen <= 0)
> goto out;
>
> @@ -345,21 +342,18 @@ static void men_z135_handle_tx(struct men_z135_port *uart)
> if (n <= 0)
> goto irq_en;
>
> - head = xmit->head & (UART_XMIT_SIZE - 1);
> - tail = xmit->tail & (UART_XMIT_SIZE - 1);
> -
> - s = ((head >= tail) ? head : UART_XMIT_SIZE) - tail;
> - n = min(n, s);
> + n = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail,
> + min_t(unsigned int, UART_XMIT_SIZE, n));
> + memcpy_toio(port->membase + MEN_Z135_TX_RAM, tail, n);
>
> - memcpy_toio(port->membase + MEN_Z135_TX_RAM, &xmit->buf[xmit->tail], n);
> iowrite32(n & 0x3ff, port->membase + MEN_Z135_TX_CTRL);
> uart_xmit_advance(port, n);
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
>
> irq_en:
> - if (!uart_circ_empty(xmit))
> + if (!kfifo_is_empty(&tport->xmit_fifo))
> men_z135_reg_set(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN);
> else
> men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN);
> diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c
> index 6feac459c0cf..4587ed4d4d5d 100644
> --- a/drivers/tty/serial/meson_uart.c
> +++ b/drivers/tty/serial/meson_uart.c
> @@ -141,8 +141,8 @@ static void meson_uart_shutdown(struct uart_port *port)
>
> static void meson_uart_start_tx(struct uart_port *port)
> {
> - struct circ_buf *xmit = &port->state->xmit;
> - unsigned int ch;
> + struct tty_port *tport = &port->state->port;
> + unsigned char ch;
> u32 val;
>
> if (uart_tx_stopped(port)) {
> @@ -158,21 +158,20 @@ static void meson_uart_start_tx(struct uart_port *port)
> continue;
> }
>
> - if (uart_circ_empty(xmit))
> + if (!uart_fifo_get(port, &ch))
> break;
>
> - ch = xmit->buf[xmit->tail];
> writel(ch, port->membase + AML_UART_WFIFO);
> uart_xmit_advance(port, 1);
> }
>
> - if (!uart_circ_empty(xmit)) {
> + if (!kfifo_is_empty(&tport->xmit_fifo)) {
> val = readl(port->membase + AML_UART_CONTROL);
> val |= AML_UART_TX_INT_EN;
> writel(val, port->membase + AML_UART_CONTROL);
> }
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
> }
>
> diff --git a/drivers/tty/serial/milbeaut_usio.c b/drivers/tty/serial/milbeaut_usio.c
> index da4c6f7e2a30..fb082ee73d5b 100644
> --- a/drivers/tty/serial/milbeaut_usio.c
> +++ b/drivers/tty/serial/milbeaut_usio.c
> @@ -72,7 +72,7 @@ static void mlb_usio_stop_tx(struct uart_port *port)
>
> static void mlb_usio_tx_chars(struct uart_port *port)
> {
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> int count;
>
> writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FTIE,
> @@ -87,7 +87,7 @@ static void mlb_usio_tx_chars(struct uart_port *port)
> port->x_char = 0;
> return;
> }
> - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
> + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
> mlb_usio_stop_tx(port);
> return;
> }
> @@ -96,12 +96,13 @@ static void mlb_usio_tx_chars(struct uart_port *port)
> (readw(port->membase + MLB_USIO_REG_FBYTE) & 0xff);
>
> do {
> - writew(xmit->buf[xmit->tail], port->membase + MLB_USIO_REG_DR);
> + unsigned char ch;
>
> - uart_xmit_advance(port, 1);
> - if (uart_circ_empty(xmit))
> + if (!uart_fifo_get(port, &ch))
> break;
>
> + writew(ch, port->membase + MLB_USIO_REG_DR);
> + port->icount.tx++;
> } while (--count > 0);
>
> writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FDRQ,
> @@ -110,10 +111,10 @@ static void mlb_usio_tx_chars(struct uart_port *port)
> writeb(readb(port->membase + MLB_USIO_REG_SCR) | MLB_USIO_SCR_TBIE,
> port->membase + MLB_USIO_REG_SCR);
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
>
> - if (uart_circ_empty(xmit))
> + if (kfifo_is_empty(&tport->xmit_fifo))
> mlb_usio_stop_tx(port);
> }
>
> diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
> index 7bf30e632313..ae7a8e3cf467 100644
> --- a/drivers/tty/serial/msm_serial.c
> +++ b/drivers/tty/serial/msm_serial.c
> @@ -452,7 +452,7 @@ static void msm_complete_tx_dma(void *args)
> {
> struct msm_port *msm_port = args;
> struct uart_port *port = &msm_port->uart;
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> struct msm_dma *dma = &msm_port->tx_dma;
> struct dma_tx_state state;
> unsigned long flags;
> @@ -486,7 +486,7 @@ static void msm_complete_tx_dma(void *args)
> msm_port->imr |= MSM_UART_IMR_TXLEV;
> msm_write(port, msm_port->imr, MSM_UART_IMR);
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
>
> msm_handle_tx(port);
> @@ -496,14 +496,14 @@ static void msm_complete_tx_dma(void *args)
>
> static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count)
> {
> - struct circ_buf *xmit = &msm_port->uart.state->xmit;
> struct uart_port *port = &msm_port->uart;
> + struct tty_port *tport = &port->state->port;
> struct msm_dma *dma = &msm_port->tx_dma;
> int ret;
> u32 val;
>
> sg_init_table(&dma->tx_sg, 1);
> - sg_set_buf(&dma->tx_sg, &xmit->buf[xmit->tail], count);
> + kfifo_dma_out_prepare(&tport->xmit_fifo, &dma->tx_sg, 1, count);
>
> ret = dma_map_sg(port->dev, &dma->tx_sg, 1, dma->dir);
> if (ret)
> @@ -843,8 +843,8 @@ static void msm_handle_rx(struct uart_port *port)
>
> static void msm_handle_tx_pio(struct uart_port *port, unsigned int tx_count)
> {
> - struct circ_buf *xmit = &port->state->xmit;
> struct msm_port *msm_port = to_msm_port(port);
> + struct tty_port *tport = &port->state->port;
> unsigned int num_chars;
> unsigned int tf_pointer = 0;
> void __iomem *tf;
> @@ -858,8 +858,7 @@ static void msm_handle_tx_pio(struct uart_port *port, unsigned int tx_count)
> msm_reset_dm_count(port, tx_count);
>
> while (tf_pointer < tx_count) {
> - int i;
> - char buf[4] = { 0 };
> + unsigned char buf[4] = { 0 };
>
> if (!(msm_read(port, MSM_UART_SR) & MSM_UART_SR_TX_READY))
> break;
> @@ -870,26 +869,23 @@ static void msm_handle_tx_pio(struct uart_port *port, unsigned int tx_count)
> else
> num_chars = 1;
>
> - for (i = 0; i < num_chars; i++)
> - buf[i] = xmit->buf[xmit->tail + i];
> -
> + num_chars = uart_fifo_out(port, buf, num_chars);
> iowrite32_rep(tf, buf, 1);
> - uart_xmit_advance(port, num_chars);
> tf_pointer += num_chars;
> }
>
> /* disable tx interrupts if nothing more to send */
> - if (uart_circ_empty(xmit))
> + if (kfifo_is_empty(&tport->xmit_fifo))
> msm_stop_tx(port);
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
> }
>
> static void msm_handle_tx(struct uart_port *port)
> {
> struct msm_port *msm_port = to_msm_port(port);
> - struct circ_buf *xmit = &msm_port->uart.state->xmit;
> + struct tty_port *tport = &port->state->port;
> struct msm_dma *dma = &msm_port->tx_dma;
> unsigned int pio_count, dma_count, dma_min;
> char buf[4] = { 0 };
> @@ -913,13 +909,13 @@ static void msm_handle_tx(struct uart_port *port)
> return;
> }
>
> - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
> + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
> msm_stop_tx(port);
> return;
> }
>
> - pio_count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
> - dma_count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
> + dma_count = pio_count = kfifo_out_linear(&tport->xmit_fifo, NULL,
> + UART_XMIT_SIZE);
>
> dma_min = 1; /* Always DMA */
> if (msm_port->is_uartdm > UARTDM_1P3) {
> diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c
> index 0255646bc175..5de57b77abdb 100644
> --- a/drivers/tty/serial/mvebu-uart.c
> +++ b/drivers/tty/serial/mvebu-uart.c
> @@ -219,12 +219,10 @@ static void mvebu_uart_stop_tx(struct uart_port *port)
> static void mvebu_uart_start_tx(struct uart_port *port)
> {
> unsigned int ctl;
> - struct circ_buf *xmit = &port->state->xmit;
> + unsigned char c;
>
> - if (IS_EXTENDED(port) && !uart_circ_empty(xmit)) {
> - writel(xmit->buf[xmit->tail], port->membase + UART_TSH(port));
> - uart_xmit_advance(port, 1);
> - }
> + if (IS_EXTENDED(port) && uart_fifo_get(port, &c))
> + writel(c, port->membase + UART_TSH(port));
>
> ctl = readl(port->membase + UART_INTR(port));
> ctl |= CTRL_TX_RDY_INT(port);
> diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
> index 4749331fe618..144b35d31497 100644
> --- a/drivers/tty/serial/mxs-auart.c
> +++ b/drivers/tty/serial/mxs-auart.c
> @@ -517,7 +517,7 @@ static void mxs_auart_tx_chars(struct mxs_auart_port *s);
> static void dma_tx_callback(void *param)
> {
> struct mxs_auart_port *s = param;
> - struct circ_buf *xmit = &s->port.state->xmit;
> + struct tty_port *tport = &s->port.state->port;
>
> dma_unmap_sg(s->dev, &s->tx_sgl, 1, DMA_TO_DEVICE);
>
> @@ -526,7 +526,7 @@ static void dma_tx_callback(void *param)
> smp_mb__after_atomic();
>
> /* wake up the possible processes. */
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&s->port);
>
> mxs_auart_tx_chars(s);
> @@ -568,33 +568,22 @@ static int mxs_auart_dma_tx(struct mxs_auart_port *s, int size)
>
> static void mxs_auart_tx_chars(struct mxs_auart_port *s)
> {
> - struct circ_buf *xmit = &s->port.state->xmit;
> + struct tty_port *tport = &s->port.state->port;
> bool pending;
> u8 ch;
>
> if (auart_dma_enabled(s)) {
> u32 i = 0;
> - int size;
> void *buffer = s->tx_dma_buf;
>
> if (test_and_set_bit(MXS_AUART_DMA_TX_SYNC, &s->flags))
> return;
>
> - while (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) {
> - size = min_t(u32, UART_XMIT_SIZE - i,
> - CIRC_CNT_TO_END(xmit->head,
> - xmit->tail,
> - UART_XMIT_SIZE));
> - memcpy(buffer + i, xmit->buf + xmit->tail, size);
> - xmit->tail = (xmit->tail + size) & (UART_XMIT_SIZE - 1);
> -
> - i += size;
> - if (i >= UART_XMIT_SIZE)
> - break;
> - }
> -
> if (uart_tx_stopped(&s->port))
> mxs_auart_stop_tx(&s->port);
> + else
> + i = kfifo_out(&tport->xmit_fifo, buffer,
> + UART_XMIT_SIZE);
>
> if (i) {
> mxs_auart_dma_tx(s, i);
> diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
> index 89257cddf540..c7cee5fee603 100644
> --- a/drivers/tty/serial/pch_uart.c
> +++ b/drivers/tty/serial/pch_uart.c
> @@ -808,7 +808,7 @@ static int dma_handle_rx(struct eg20t_port *priv)
> static unsigned int handle_tx(struct eg20t_port *priv)
> {
> struct uart_port *port = &priv->port;
> - struct circ_buf *xmit = &port->state->xmit;
> + unsigned char ch;
> int fifo_size;
> int tx_empty;
>
> @@ -830,9 +830,9 @@ static unsigned int handle_tx(struct eg20t_port *priv)
> fifo_size--;
> }
>
> - while (!uart_tx_stopped(port) && !uart_circ_empty(xmit) && fifo_size) {
> - iowrite8(xmit->buf[xmit->tail], priv->membase + PCH_UART_THR);
> - uart_xmit_advance(port, 1);
> + while (!uart_tx_stopped(port) && fifo_size &&
> + uart_fifo_get(port, &ch)) {
> + iowrite8(ch, priv->membase + PCH_UART_THR);
> fifo_size--;
> tx_empty = 0;
> }
> @@ -850,14 +850,14 @@ static unsigned int handle_tx(struct eg20t_port *priv)
> static unsigned int dma_handle_tx(struct eg20t_port *priv)
> {
> struct uart_port *port = &priv->port;
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> struct scatterlist *sg;
> int nent;
> int fifo_size;
> struct dma_async_tx_descriptor *desc;
> + unsigned int bytes, tail;
> int num;
> int i;
> - int bytes;
> int size;
> int rem;
>
> @@ -886,7 +886,7 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv)
> fifo_size--;
> }
>
> - bytes = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
> + bytes = kfifo_out_linear(&tport->xmit_fifo, &tail, UART_XMIT_SIZE);
> if (!bytes) {
> dev_dbg(priv->port.dev, "%s 0 bytes return\n", __func__);
> pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT);
> @@ -920,10 +920,10 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv)
>
> for (i = 0; i < num; i++, sg++) {
> if (i == (num - 1))
> - sg_set_page(sg, virt_to_page(xmit->buf),
> + sg_set_page(sg, virt_to_page(tport->xmit_buf),
> rem, fifo_size * i);
> else
> - sg_set_page(sg, virt_to_page(xmit->buf),
> + sg_set_page(sg, virt_to_page(tport->xmit_buf),
> size, fifo_size * i);
> }
>
> @@ -937,8 +937,7 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv)
> priv->nent = nent;
>
> for (i = 0; i < nent; i++, sg++) {
> - sg->offset = (xmit->tail & (UART_XMIT_SIZE - 1)) +
> - fifo_size * i;
> + sg->offset = tail + fifo_size * i;
> sg_dma_address(sg) = (sg_dma_address(sg) &
> ~(UART_XMIT_SIZE - 1)) + sg->offset;
> if (i == (nent - 1))
> diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c
> index bbb46e6e98a2..f5af336a869b 100644
> --- a/drivers/tty/serial/pic32_uart.c
> +++ b/drivers/tty/serial/pic32_uart.c
> @@ -342,7 +342,7 @@ static void pic32_uart_do_rx(struct uart_port *port)
> static void pic32_uart_do_tx(struct uart_port *port)
> {
> struct pic32_sport *sport = to_pic32_sport(port);
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> unsigned int max_count = PIC32_UART_TX_FIFO_DEPTH;
>
> if (port->x_char) {
> @@ -357,7 +357,7 @@ static void pic32_uart_do_tx(struct uart_port *port)
> return;
> }
>
> - if (uart_circ_empty(xmit))
> + if (kfifo_is_empty(&tport->xmit_fifo))
> goto txq_empty;
>
> /* keep stuffing chars into uart tx buffer
> @@ -371,21 +371,20 @@ static void pic32_uart_do_tx(struct uart_port *port)
> */
> while (!(PIC32_UART_STA_UTXBF &
> pic32_uart_readl(sport, PIC32_UART_STA))) {
> - unsigned int c = xmit->buf[xmit->tail];
> + unsigned char c;
>
> + if (!uart_fifo_get(port, &c))
> + break;
> pic32_uart_writel(sport, PIC32_UART_TX, c);
>
> - uart_xmit_advance(port, 1);
> - if (uart_circ_empty(xmit))
> - break;
> if (--max_count == 0)
> break;
> }
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
>
> - if (uart_circ_empty(xmit))
> + if (kfifo_is_empty(&tport->xmit_fifo))
> goto txq_empty;
>
> return;
> diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c
> index 05d97e89511e..63bc726273fd 100644
> --- a/drivers/tty/serial/pmac_zilog.c
> +++ b/drivers/tty/serial/pmac_zilog.c
> @@ -347,7 +347,8 @@ static void pmz_status_handle(struct uart_pmac_port *uap)
>
> static void pmz_transmit_chars(struct uart_pmac_port *uap)
> {
> - struct circ_buf *xmit;
> + struct tty_port *tport;
> + unsigned char ch;
>
> if (ZS_IS_CONS(uap)) {
> unsigned char status = read_zsreg(uap, R0);
> @@ -398,8 +399,8 @@ static void pmz_transmit_chars(struct uart_pmac_port *uap)
>
> if (uap->port.state == NULL)
> goto ack_tx_int;
> - xmit = &uap->port.state->xmit;
> - if (uart_circ_empty(xmit)) {
> + tport = &uap->port.state->port;
> + if (kfifo_is_empty(&tport->xmit_fifo)) {
> uart_write_wakeup(&uap->port);
> goto ack_tx_int;
> }
> @@ -407,12 +408,11 @@ static void pmz_transmit_chars(struct uart_pmac_port *uap)
> goto ack_tx_int;
>
> uap->flags |= PMACZILOG_FLAG_TX_ACTIVE;
> - write_zsdata(uap, xmit->buf[xmit->tail]);
> + WARN_ON(!uart_fifo_get(&uap->port, &ch));
> + write_zsdata(uap, ch);
> zssync(uap);
>
> - uart_xmit_advance(&uap->port, 1);
> -
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&uap->port);
>
> return;
> @@ -620,15 +620,15 @@ static void pmz_start_tx(struct uart_port *port)
> port->icount.tx++;
> port->x_char = 0;
> } else {
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> + unsigned char ch;
>
> - if (uart_circ_empty(xmit))
> + if (!uart_fifo_get(&uap->port, &ch))
> return;
> - write_zsdata(uap, xmit->buf[xmit->tail]);
> + write_zsdata(uap, ch);
> zssync(uap);
> - uart_xmit_advance(port, 1);
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&uap->port);
> }
> }
> diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
> index f9f7ac1a10df..7814982f1921 100644
> --- a/drivers/tty/serial/qcom_geni_serial.c
> +++ b/drivers/tty/serial/qcom_geni_serial.c
> @@ -505,7 +505,7 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s,
> */
> qcom_geni_serial_poll_tx_done(uport);
>
> - if (!uart_circ_empty(&uport->state->xmit)) {
> + if (!kfifo_is_empty(&uport->state->port.xmit_fifo)) {
> irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN);
> writel(irq_en | M_TX_FIFO_WATERMARK_EN,
> uport->membase + SE_GENI_M_IRQ_EN);
> @@ -620,22 +620,24 @@ static void qcom_geni_serial_stop_tx_dma(struct uart_port *uport)
> static void qcom_geni_serial_start_tx_dma(struct uart_port *uport)
> {
> struct qcom_geni_serial_port *port = to_dev_port(uport);
> - struct circ_buf *xmit = &uport->state->xmit;
> + struct tty_port *tport = &uport->state->port;
> unsigned int xmit_size;
> + u8 *tail;
> int ret;
>
> if (port->tx_dma_addr)
> return;
>
> - if (uart_circ_empty(xmit))
> + if (kfifo_is_empty(&tport->xmit_fifo))
> return;
>
> - xmit_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
> + xmit_size = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail,
> + UART_XMIT_SIZE);
>
> qcom_geni_serial_setup_tx(uport, xmit_size);
>
> - ret = geni_se_tx_dma_prep(&port->se, &xmit->buf[xmit->tail],
> - xmit_size, &port->tx_dma_addr);
> + ret = geni_se_tx_dma_prep(&port->se, tail, xmit_size,
> + &port->tx_dma_addr);
> if (ret) {
> dev_err(uport->dev, "unable to start TX SE DMA: %d\n", ret);
> qcom_geni_serial_stop_tx_dma(uport);
> @@ -853,18 +855,16 @@ static void qcom_geni_serial_send_chunk_fifo(struct uart_port *uport,
> unsigned int chunk)
> {
> struct qcom_geni_serial_port *port = to_dev_port(uport);
> - struct circ_buf *xmit = &uport->state->xmit;
> - unsigned int tx_bytes, c, remaining = chunk;
> + struct tty_port *tport = &uport->state->port;
> + unsigned int tx_bytes, remaining = chunk;
> u8 buf[BYTES_PER_FIFO_WORD];
>
> while (remaining) {
> memset(buf, 0, sizeof(buf));
> tx_bytes = min(remaining, BYTES_PER_FIFO_WORD);
>
> - for (c = 0; c < tx_bytes ; c++) {
> - buf[c] = xmit->buf[xmit->tail];
> - uart_xmit_advance(uport, 1);
> - }
> + tx_bytes = kfifo_out(&tport->xmit_fifo, buf, tx_bytes);
> + uart_xmit_advance(uport, tx_bytes);
>
> iowrite32_rep(uport->membase + SE_GENI_TX_FIFOn, buf, 1);
>
> @@ -877,7 +877,7 @@ static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport,
> bool done, bool active)
> {
> struct qcom_geni_serial_port *port = to_dev_port(uport);
> - struct circ_buf *xmit = &uport->state->xmit;
> + struct tty_port *tport = &uport->state->port;
> size_t avail;
> size_t pending;
> u32 status;
> @@ -890,7 +890,7 @@ static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport,
> if (active)
> pending = port->tx_remaining;
> else
> - pending = uart_circ_chars_pending(xmit);
> + pending = kfifo_len(&tport->xmit_fifo);
>
> /* All data has been transmitted and acknowledged as received */
> if (!pending && !status && done) {
> @@ -933,24 +933,24 @@ static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport,
> uport->membase + SE_GENI_M_IRQ_EN);
> }
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(uport);
> }
>
> static void qcom_geni_serial_handle_tx_dma(struct uart_port *uport)
> {
> struct qcom_geni_serial_port *port = to_dev_port(uport);
> - struct circ_buf *xmit = &uport->state->xmit;
> + struct tty_port *tport = &uport->state->port;
>
> uart_xmit_advance(uport, port->tx_remaining);
> geni_se_tx_dma_unprep(&port->se, port->tx_dma_addr, port->tx_remaining);
> port->tx_dma_addr = 0;
> port->tx_remaining = 0;
>
> - if (!uart_circ_empty(xmit))
> + if (!kfifo_is_empty(&tport->xmit_fifo))
> qcom_geni_serial_start_tx_dma(uport);
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(uport);
> }
>
> diff --git a/drivers/tty/serial/rda-uart.c b/drivers/tty/serial/rda-uart.c
> index 82def9b8632a..663e35e424bd 100644
> --- a/drivers/tty/serial/rda-uart.c
> +++ b/drivers/tty/serial/rda-uart.c
> @@ -330,8 +330,8 @@ static void rda_uart_set_termios(struct uart_port *port,
>
> static void rda_uart_send_chars(struct uart_port *port)
> {
> - struct circ_buf *xmit = &port->state->xmit;
> - unsigned int ch;
> + struct tty_port *tport = &port->state->port;
> + unsigned char ch;
> u32 val;
>
> if (uart_tx_stopped(port))
> @@ -347,19 +347,14 @@ static void rda_uart_send_chars(struct uart_port *port)
> port->x_char = 0;
> }
>
> - while (rda_uart_read(port, RDA_UART_STATUS) & RDA_UART_TX_FIFO_MASK) {
> - if (uart_circ_empty(xmit))
> - break;
> -
> - ch = xmit->buf[xmit->tail];
> + while ((rda_uart_read(port, RDA_UART_STATUS) & RDA_UART_TX_FIFO_MASK) &&
> + uart_fifo_get(port, &ch))
> rda_uart_write(port, ch, RDA_UART_RXTX_BUFFER);
> - uart_xmit_advance(port, 1);
> - }
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
>
> - if (!uart_circ_empty(xmit)) {
> + if (!kfifo_is_empty(&tport->xmit_fifo)) {
> /* Re-enable Tx FIFO interrupt */
> val = rda_uart_read(port, RDA_UART_IRQ_MASK);
> val |= RDA_UART_TX_DATA_NEEDED;
> diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c
> index a2d07e05c502..dc35eb77d2ef 100644
> --- a/drivers/tty/serial/samsung_tty.c
> +++ b/drivers/tty/serial/samsung_tty.c
> @@ -329,7 +329,7 @@ static void s3c24xx_serial_tx_dma_complete(void *args)
> {
> struct s3c24xx_uart_port *ourport = args;
> struct uart_port *port = &ourport->port;
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> struct s3c24xx_uart_dma *dma = ourport->dma;
> struct dma_tx_state state;
> unsigned long flags;
> @@ -348,7 +348,7 @@ static void s3c24xx_serial_tx_dma_complete(void *args)
> uart_xmit_advance(port, count);
> ourport->tx_in_progress = 0;
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
>
> s3c24xx_serial_start_next_tx(ourport);
> @@ -431,17 +431,15 @@ static void s3c24xx_serial_start_tx_pio(struct s3c24xx_uart_port *ourport)
> }
>
> static int s3c24xx_serial_start_tx_dma(struct s3c24xx_uart_port *ourport,
> - unsigned int count)
> + unsigned int count, unsigned int tail)
> {
> - struct uart_port *port = &ourport->port;
> - struct circ_buf *xmit = &port->state->xmit;
> struct s3c24xx_uart_dma *dma = ourport->dma;
>
> if (ourport->tx_mode != S3C24XX_TX_DMA)
> enable_tx_dma(ourport);
>
> dma->tx_size = count & ~(dma_get_cache_alignment() - 1);
> - dma->tx_transfer_addr = dma->tx_addr + xmit->tail;
> + dma->tx_transfer_addr = dma->tx_addr + tail;
>
> dma_sync_single_for_device(dma->tx_chan->device->dev,
> dma->tx_transfer_addr, dma->tx_size,
> @@ -468,11 +466,11 @@ static int s3c24xx_serial_start_tx_dma(struct s3c24xx_uart_port *ourport,
> static void s3c24xx_serial_start_next_tx(struct s3c24xx_uart_port *ourport)
> {
> struct uart_port *port = &ourport->port;
> - struct circ_buf *xmit = &port->state->xmit;
> - unsigned long count;
> + struct tty_port *tport = &port->state->port;
> + unsigned int count, tail;
>
> /* Get data size up to the end of buffer */
> - count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
> + count = kfifo_out_linear(&tport->xmit_fifo, &tail, UART_XMIT_SIZE);
>
> if (!count) {
> s3c24xx_serial_stop_tx(port);
> @@ -481,16 +479,16 @@ static void s3c24xx_serial_start_next_tx(struct s3c24xx_uart_port *ourport)
>
> if (!ourport->dma || !ourport->dma->tx_chan ||
> count < ourport->min_dma_size ||
> - xmit->tail & (dma_get_cache_alignment() - 1))
> + tail & (dma_get_cache_alignment() - 1))
> s3c24xx_serial_start_tx_pio(ourport);
> else
> - s3c24xx_serial_start_tx_dma(ourport, count);
> + s3c24xx_serial_start_tx_dma(ourport, count, tail);
> }
>
> static void s3c24xx_serial_start_tx(struct uart_port *port)
> {
> struct s3c24xx_uart_port *ourport = to_ourport(port);
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
>
> if (!ourport->tx_enabled) {
> if (port->flags & UPF_CONS_FLOW)
> @@ -502,7 +500,8 @@ static void s3c24xx_serial_start_tx(struct uart_port *port)
> }
>
> if (ourport->dma && ourport->dma->tx_chan) {
> - if (!uart_circ_empty(xmit) && !ourport->tx_in_progress)
> + if (!kfifo_is_empty(&tport->xmit_fifo) &&
> + !ourport->tx_in_progress)
> s3c24xx_serial_start_next_tx(ourport);
> }
> }
> @@ -868,18 +867,19 @@ static irqreturn_t s3c24xx_serial_rx_irq(int irq, void *dev_id)
> static void s3c24xx_serial_tx_chars(struct s3c24xx_uart_port *ourport)
> {
> struct uart_port *port = &ourport->port;
> - struct circ_buf *xmit = &port->state->xmit;
> - int count, dma_count = 0;
> + struct tty_port *tport = &port->state->port;
> + unsigned int count, dma_count = 0, tail;
>
> - count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
> + count = kfifo_out_linear(&tport->xmit_fifo, &tail, UART_XMIT_SIZE);
>
> if (ourport->dma && ourport->dma->tx_chan &&
> count >= ourport->min_dma_size) {
> int align = dma_get_cache_alignment() -
> - (xmit->tail & (dma_get_cache_alignment() - 1));
> + (tail & (dma_get_cache_alignment() - 1));
> if (count - align >= ourport->min_dma_size) {
> dma_count = count - align;
> count = align;
> + tail += align;
> }
> }
>
> @@ -894,7 +894,7 @@ static void s3c24xx_serial_tx_chars(struct s3c24xx_uart_port *ourport)
> * stopped, disable the uart and exit
> */
>
> - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
> + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
> s3c24xx_serial_stop_tx(port);
> return;
> }
> @@ -906,24 +906,25 @@ static void s3c24xx_serial_tx_chars(struct s3c24xx_uart_port *ourport)
> dma_count = 0;
> }
>
> - while (!uart_circ_empty(xmit) && count > 0) {
> - if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
> + while (!(rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)) {
> + unsigned char ch;
> +
> + if (!uart_fifo_get(port, &ch))
> break;
>
> - wr_reg(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
> - uart_xmit_advance(port, 1);
> + wr_reg(port, S3C2410_UTXH, ch);
> count--;
> }
>
> if (!count && dma_count) {
> - s3c24xx_serial_start_tx_dma(ourport, dma_count);
> + s3c24xx_serial_start_tx_dma(ourport, dma_count, tail);
> return;
> }
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
>
> - if (uart_circ_empty(xmit))
> + if (kfifo_is_empty(&tport->xmit_fifo))
> s3c24xx_serial_stop_tx(port);
> }
>
> @@ -1118,7 +1119,8 @@ static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p)
>
> /* TX buffer */
> dma->tx_addr = dma_map_single(dma->tx_chan->device->dev,
> - p->port.state->xmit.buf, UART_XMIT_SIZE,
> + p->port.state->port.xmit_buf,
> + UART_XMIT_SIZE,
> DMA_TO_DEVICE);
> if (dma_mapping_error(dma->tx_chan->device->dev, dma->tx_addr)) {
> reason = "DMA mapping error for TX buffer";
> diff --git a/drivers/tty/serial/sb1250-duart.c b/drivers/tty/serial/sb1250-duart.c
> index dbec29d9a6c3..b4e1b90e5960 100644
> --- a/drivers/tty/serial/sb1250-duart.c
> +++ b/drivers/tty/serial/sb1250-duart.c
> @@ -382,7 +382,8 @@ static void sbd_receive_chars(struct sbd_port *sport)
> static void sbd_transmit_chars(struct sbd_port *sport)
> {
> struct uart_port *uport = &sport->port;
> - struct circ_buf *xmit = &sport->port.state->xmit;
> + struct tty_port *tport = &sport->port.state->port;
> + unsigned char ch;
> unsigned int mask;
> int stop_tx;
>
> @@ -395,19 +396,19 @@ static void sbd_transmit_chars(struct sbd_port *sport)
> }
>
> /* If nothing to do or stopped or hardware stopped. */
> - stop_tx = (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port));
> + stop_tx = uart_tx_stopped(&sport->port) ||
> + !uart_fifo_get(&sport->port, &ch);
>
> /* Send char. */
> if (!stop_tx) {
> - write_sbdchn(sport, R_DUART_TX_HOLD, xmit->buf[xmit->tail]);
> - uart_xmit_advance(&sport->port, 1);
> + write_sbdchn(sport, R_DUART_TX_HOLD, ch);
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&sport->port);
> }
>
> /* Are we are done? */
> - if (stop_tx || uart_circ_empty(xmit)) {
> + if (stop_tx || kfifo_is_empty(&tport->xmit_fifo)) {
> /* Disable tx interrupts. */
> mask = read_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2));
> mask &= ~M_DUART_IMR_TX;
> diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
> index 929206a9a6e1..c6983b7bd78c 100644
> --- a/drivers/tty/serial/sc16is7xx.c
> +++ b/drivers/tty/serial/sc16is7xx.c
> @@ -676,9 +676,9 @@ static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen,
> static void sc16is7xx_handle_tx(struct uart_port *port)
> {
> struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
> - struct circ_buf *xmit = &port->state->xmit;
> - unsigned int txlen, to_send, i;
> + struct tty_port *tport = &port->state->port;
> unsigned long flags;
> + unsigned int txlen;
>
> if (unlikely(port->x_char)) {
> sc16is7xx_port_write(port, SC16IS7XX_THR_REG, port->x_char);
> @@ -687,40 +687,30 @@ static void sc16is7xx_handle_tx(struct uart_port *port)
> return;
> }
>
> - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
> + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
> uart_port_lock_irqsave(port, &flags);
> sc16is7xx_stop_tx(port);
> uart_port_unlock_irqrestore(port, flags);
> return;
> }
>
> - /* Get length of data pending in circular buffer */
> - to_send = uart_circ_chars_pending(xmit);
> - if (likely(to_send)) {
> - /* Limit to space available in TX FIFO */
> - txlen = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG);
> - if (txlen > SC16IS7XX_FIFO_SIZE) {
> - dev_err_ratelimited(port->dev,
> - "chip reports %d free bytes in TX fifo, but it only has %d",
> - txlen, SC16IS7XX_FIFO_SIZE);
> - txlen = 0;
> - }
> - to_send = (to_send > txlen) ? txlen : to_send;
> -
> - /* Convert to linear buffer */
> - for (i = 0; i < to_send; ++i) {
> - s->buf[i] = xmit->buf[xmit->tail];
> - uart_xmit_advance(port, 1);
> - }
> -
> - sc16is7xx_fifo_write(port, s->buf, to_send);
> + /* Limit to space available in TX FIFO */
> + txlen = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG);
> + if (txlen > SC16IS7XX_FIFO_SIZE) {
> + dev_err_ratelimited(port->dev,
> + "chip reports %d free bytes in TX fifo, but it only has %d",
> + txlen, SC16IS7XX_FIFO_SIZE);
> + txlen = 0;
> }
>
> + txlen = uart_fifo_out(port, s->buf, txlen);
> + sc16is7xx_fifo_write(port, s->buf, txlen);
> +
> uart_port_lock_irqsave(port, &flags);
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
>
> - if (uart_circ_empty(xmit))
> + if (kfifo_is_empty(&tport->xmit_fifo))
> sc16is7xx_stop_tx(port);
> else
> sc16is7xx_ier_set(port, SC16IS7XX_IER_THRI_BIT);
> diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c
> index f24217a560d7..6d1d142fd216 100644
> --- a/drivers/tty/serial/sccnxp.c
> +++ b/drivers/tty/serial/sccnxp.c
> @@ -439,7 +439,7 @@ static void sccnxp_handle_rx(struct uart_port *port)
> static void sccnxp_handle_tx(struct uart_port *port)
> {
> u8 sr;
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> struct sccnxp_port *s = dev_get_drvdata(port->dev);
>
> if (unlikely(port->x_char)) {
> @@ -449,7 +449,7 @@ static void sccnxp_handle_tx(struct uart_port *port)
> return;
> }
>
> - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
> + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
> /* Disable TX if FIFO is empty */
> if (sccnxp_port_read(port, SCCNXP_SR_REG) & SR_TXEMT) {
> sccnxp_disable_irq(port, IMR_TXRDY);
> @@ -461,16 +461,20 @@ static void sccnxp_handle_tx(struct uart_port *port)
> return;
> }
>
> - while (!uart_circ_empty(xmit)) {
> + while (1) {
> + unsigned char ch;
> +
> sr = sccnxp_port_read(port, SCCNXP_SR_REG);
> if (!(sr & SR_TXRDY))
> break;
>
> - sccnxp_port_write(port, SCCNXP_THR_REG, xmit->buf[xmit->tail]);
> - uart_xmit_advance(port, 1);
> + if (!uart_fifo_get(port, &ch))
> + break;
> +
> + sccnxp_port_write(port, SCCNXP_THR_REG, ch);
> }
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
> }
>
> diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c
> index 525f3a2f7bd4..1183ca54ab92 100644
> --- a/drivers/tty/serial/serial-tegra.c
> +++ b/drivers/tty/serial/serial-tegra.c
> @@ -484,18 +484,18 @@ static void tegra_uart_release_port(struct uart_port *u)
>
> static void tegra_uart_fill_tx_fifo(struct tegra_uart_port *tup, int max_bytes)
> {
> - struct circ_buf *xmit = &tup->uport.state->xmit;
> + unsigned char ch;
> int i;
>
> for (i = 0; i < max_bytes; i++) {
> - BUG_ON(uart_circ_empty(xmit));
> if (tup->cdata->tx_fifo_full_status) {
> unsigned long lsr = tegra_uart_read(tup, UART_LSR);
> if ((lsr & TEGRA_UART_LSR_TXFIFO_FULL))
> break;
> }
> - tegra_uart_write(tup, xmit->buf[xmit->tail], UART_TX);
> - uart_xmit_advance(&tup->uport, 1);
> + if (WARN_ON_ONCE(!uart_fifo_get(&tup->uport, &ch)))
> + break;
> + tegra_uart_write(tup, ch, UART_TX);
> }
> }
>
> @@ -514,7 +514,7 @@ static void tegra_uart_start_pio_tx(struct tegra_uart_port *tup,
> static void tegra_uart_tx_dma_complete(void *args)
> {
> struct tegra_uart_port *tup = args;
> - struct circ_buf *xmit = &tup->uport.state->xmit;
> + struct tty_port *tport = &tup->uport.state->port;
> struct dma_tx_state state;
> unsigned long flags;
> unsigned int count;
> @@ -525,7 +525,7 @@ static void tegra_uart_tx_dma_complete(void *args)
> uart_port_lock_irqsave(&tup->uport, &flags);
> uart_xmit_advance(&tup->uport, count);
> tup->tx_in_progress = 0;
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&tup->uport);
> tegra_uart_start_next_tx(tup);
> uart_port_unlock_irqrestore(&tup->uport, flags);
> @@ -534,11 +534,14 @@ static void tegra_uart_tx_dma_complete(void *args)
> static int tegra_uart_start_tx_dma(struct tegra_uart_port *tup,
> unsigned long count)
> {
> - struct circ_buf *xmit = &tup->uport.state->xmit;
> + struct tty_port *tport = &tup->uport.state->port;
> dma_addr_t tx_phys_addr;
> + unsigned int tail;
>
> tup->tx_bytes = count & ~(0xF);
> - tx_phys_addr = tup->tx_dma_buf_phys + xmit->tail;
> + WARN_ON_ONCE(kfifo_out_linear(&tport->xmit_fifo, &tail,
> + UART_XMIT_SIZE) < count);
> + tx_phys_addr = tup->tx_dma_buf_phys + tail;
>
> dma_sync_single_for_device(tup->uport.dev, tx_phys_addr,
> tup->tx_bytes, DMA_TO_DEVICE);
> @@ -562,18 +565,21 @@ static int tegra_uart_start_tx_dma(struct tegra_uart_port *tup,
>
> static void tegra_uart_start_next_tx(struct tegra_uart_port *tup)
> {
> + struct tty_port *tport = &tup->uport.state->port;
> + unsigned char *tail_ptr;
> unsigned long tail;
> - unsigned long count;
> - struct circ_buf *xmit = &tup->uport.state->xmit;
> + unsigned int count;
>
> if (!tup->current_baud)
> return;
>
> - tail = (unsigned long)&xmit->buf[xmit->tail];
> - count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
> + count = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail_ptr,
> + UART_XMIT_SIZE);
> if (!count)
> return;
>
> + tail = (unsigned long)tail_ptr;
> +
> if (tup->use_tx_pio || count < TEGRA_UART_MIN_DMA)
> tegra_uart_start_pio_tx(tup, count);
> else if (BYTES_TO_ALIGN(tail) > 0)
> @@ -586,9 +592,9 @@ static void tegra_uart_start_next_tx(struct tegra_uart_port *tup)
> static void tegra_uart_start_tx(struct uart_port *u)
> {
> struct tegra_uart_port *tup = to_tegra_uport(u);
> - struct circ_buf *xmit = &u->state->xmit;
> + struct tty_port *tport = &u->state->port;
>
> - if (!uart_circ_empty(xmit) && !tup->tx_in_progress)
> + if (!kfifo_is_empty(&tport->xmit_fifo) && !tup->tx_in_progress)
> tegra_uart_start_next_tx(tup);
> }
>
> @@ -628,11 +634,11 @@ static void tegra_uart_stop_tx(struct uart_port *u)
>
> static void tegra_uart_handle_tx_pio(struct tegra_uart_port *tup)
> {
> - struct circ_buf *xmit = &tup->uport.state->xmit;
> + struct tty_port *tport = &tup->uport.state->port;
>
> tegra_uart_fill_tx_fifo(tup, tup->tx_bytes);
> tup->tx_in_progress = 0;
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&tup->uport);
> tegra_uart_start_next_tx(tup);
> }
> @@ -1169,15 +1175,14 @@ static int tegra_uart_dma_channel_allocate(struct tegra_uart_port *tup,
> tup->rx_dma_buf_virt = dma_buf;
> tup->rx_dma_buf_phys = dma_phys;
> } else {
> + dma_buf = tup->uport.state->port.xmit_buf;
> dma_phys = dma_map_single(tup->uport.dev,
> - tup->uport.state->xmit.buf, UART_XMIT_SIZE,
> - DMA_TO_DEVICE);
> + dma_buf, UART_XMIT_SIZE, DMA_TO_DEVICE);
> if (dma_mapping_error(tup->uport.dev, dma_phys)) {
> dev_err(tup->uport.dev, "dma_map_single tx failed\n");
> dma_release_channel(dma_chan);
> return -ENOMEM;
> }
> - dma_buf = tup->uport.state->xmit.buf;
> dma_sconfig.dst_addr = tup->uport.mapbase;
> dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
> dma_sconfig.dst_maxburst = 16;
> diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
> index ff85ebd3a007..3c0931fba1c6 100644
> --- a/drivers/tty/serial/serial_core.c
> +++ b/drivers/tty/serial/serial_core.c
> @@ -272,9 +272,10 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
> return -ENOMEM;
>
> uart_port_lock(state, flags);
> - if (!state->xmit.buf) {
> - state->xmit.buf = (unsigned char *) page;
> - uart_circ_clear(&state->xmit);
> + if (!state->port.xmit_buf) {
> + state->port.xmit_buf = (unsigned char *)page;
> + kfifo_init(&state->port.xmit_fifo, state->port.xmit_buf,
> + PAGE_SIZE);
> uart_port_unlock(uport, flags);
> } else {
> uart_port_unlock(uport, flags);
> @@ -387,8 +388,9 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
> * can endup in printk() recursion.
> */
> uart_port_lock(state, flags);
> - xmit_buf = state->xmit.buf;
> - state->xmit.buf = NULL;
> + xmit_buf = port->xmit_buf;
> + port->xmit_buf = NULL;
> + INIT_KFIFO(port->xmit_fifo);
> uart_port_unlock(uport, flags);
>
> free_page((unsigned long)xmit_buf);
> @@ -552,22 +554,17 @@ static int uart_put_char(struct tty_struct *tty, u8 c)
> {
> struct uart_state *state = tty->driver_data;
> struct uart_port *port;
> - struct circ_buf *circ;
> unsigned long flags;
> int ret = 0;
>
> - circ = &state->xmit;
> port = uart_port_lock(state, flags);
> - if (!circ->buf) {
> + if (WARN_ON_ONCE(!state->port.xmit_buf)) {
> uart_port_unlock(port, flags);
> return 0;
> }
>
> - if (port && uart_circ_chars_free(circ) != 0) {
> - circ->buf[circ->head] = c;
> - circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1);
> - ret = 1;
> - }
> + if (port)
> + ret = kfifo_put(&state->port.xmit_fifo, c);
> uart_port_unlock(port, flags);
> return ret;
> }
> @@ -581,9 +578,8 @@ static ssize_t uart_write(struct tty_struct *tty, const u8 *buf, size_t count)
> {
> struct uart_state *state = tty->driver_data;
> struct uart_port *port;
> - struct circ_buf *circ;
> unsigned long flags;
> - int c, ret = 0;
> + int ret = 0;
>
> /*
> * This means you called this function _after_ the port was
> @@ -593,24 +589,13 @@ static ssize_t uart_write(struct tty_struct *tty, const u8 *buf, size_t count)
> return -EL3HLT;
>
> port = uart_port_lock(state, flags);
> - circ = &state->xmit;
> - if (!circ->buf) {
> + if (WARN_ON_ONCE(!state->port.xmit_buf)) {
> uart_port_unlock(port, flags);
> return 0;
> }
>
> - while (port) {
> - c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
> - if (count < c)
> - c = count;
> - if (c <= 0)
> - break;
> - memcpy(circ->buf + circ->head, buf, c);
> - circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
> - buf += c;
> - count -= c;
> - ret += c;
> - }
> + if (port)
> + ret = kfifo_in(&state->port.xmit_fifo, buf, count);
>
> __uart_start(state);
> uart_port_unlock(port, flags);
> @@ -625,7 +610,7 @@ static unsigned int uart_write_room(struct tty_struct *tty)
> unsigned int ret;
>
> port = uart_port_lock(state, flags);
> - ret = uart_circ_chars_free(&state->xmit);
> + ret = kfifo_avail(&state->port.xmit_fifo);
> uart_port_unlock(port, flags);
> return ret;
> }
> @@ -638,7 +623,7 @@ static unsigned int uart_chars_in_buffer(struct tty_struct *tty)
> unsigned int ret;
>
> port = uart_port_lock(state, flags);
> - ret = uart_circ_chars_pending(&state->xmit);
> + ret = kfifo_len(&state->port.xmit_fifo);
> uart_port_unlock(port, flags);
> return ret;
> }
> @@ -661,7 +646,7 @@ static void uart_flush_buffer(struct tty_struct *tty)
> port = uart_port_lock(state, flags);
> if (!port)
> return;
> - uart_circ_clear(&state->xmit);
> + kfifo_reset(&state->port.xmit_fifo);
> if (port->ops->flush_buffer)
> port->ops->flush_buffer(port);
> uart_port_unlock(port, flags);
> @@ -1064,7 +1049,7 @@ static int uart_get_lsr_info(struct tty_struct *tty,
> * interrupt happens).
> */
> if (uport->x_char ||
> - ((uart_circ_chars_pending(&state->xmit) > 0) &&
> + (!kfifo_is_empty(&state->port.xmit_fifo) &&
> !uart_tx_stopped(uport)))
> result &= ~TIOCSER_TEMT;
>
> @@ -1788,8 +1773,9 @@ static void uart_tty_port_shutdown(struct tty_port *port)
> * Free the transmit buffer.
> */
> uart_port_lock_irq(uport);
> - buf = state->xmit.buf;
> - state->xmit.buf = NULL;
> + buf = port->xmit_buf;
> + port->xmit_buf = NULL;
> + INIT_KFIFO(port->xmit_fifo);
> uart_port_unlock_irq(uport);
>
> free_page((unsigned long)buf);
> diff --git a/drivers/tty/serial/serial_port.c b/drivers/tty/serial/serial_port.c
> index 22b9eeb23e68..3408c8827561 100644
> --- a/drivers/tty/serial/serial_port.c
> +++ b/drivers/tty/serial/serial_port.c
> @@ -23,7 +23,7 @@
> static int __serial_port_busy(struct uart_port *port)
> {
> return !uart_tx_stopped(port) &&
> - uart_circ_chars_pending(&port->state->xmit);
> + !kfifo_is_empty(&port->state->port.xmit_fifo);
> }
>
> static int serial_port_runtime_resume(struct device *dev)
> diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
> index e512eaa57ed5..97031db26ae4 100644
> --- a/drivers/tty/serial/sh-sci.c
> +++ b/drivers/tty/serial/sh-sci.c
> @@ -585,7 +585,7 @@ static void sci_start_tx(struct uart_port *port)
> sci_serial_out(port, SCSCR, new);
> }
>
> - if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) &&
> + if (s->chan_tx && !kfifo_is_empty(&port->state->port.xmit_fifo) &&
> dma_submit_error(s->cookie_tx)) {
> if (s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE)
> /* Switch irq from SCIF to DMA */
> @@ -817,7 +817,7 @@ static int sci_rxfill(struct uart_port *port)
>
> static void sci_transmit_chars(struct uart_port *port)
> {
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> unsigned int stopped = uart_tx_stopped(port);
> unsigned short status;
> unsigned short ctrl;
> @@ -826,7 +826,7 @@ static void sci_transmit_chars(struct uart_port *port)
> status = sci_serial_in(port, SCxSR);
> if (!(status & SCxSR_TDxE(port))) {
> ctrl = sci_serial_in(port, SCSCR);
> - if (uart_circ_empty(xmit))
> + if (kfifo_is_empty(&tport->xmit_fifo))
> ctrl &= ~SCSCR_TIE;
> else
> ctrl |= SCSCR_TIE;
> @@ -842,15 +842,14 @@ static void sci_transmit_chars(struct uart_port *port)
> if (port->x_char) {
> c = port->x_char;
> port->x_char = 0;
> - } else if (!uart_circ_empty(xmit) && !stopped) {
> - c = xmit->buf[xmit->tail];
> - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
> - } else if (port->type == PORT_SCI && uart_circ_empty(xmit)) {
> - ctrl = sci_serial_in(port, SCSCR);
> - ctrl &= ~SCSCR_TE;
> - sci_serial_out(port, SCSCR, ctrl);
> - return;
> - } else {
> + } else if (stopped || !kfifo_get(&tport->xmit_fifo, &c)) {
> + if (port->type == PORT_SCI &&
> + kfifo_is_empty(&tport->xmit_fifo)) {
> + ctrl = sci_serial_in(port, SCSCR);
> + ctrl &= ~SCSCR_TE;
> + sci_serial_out(port, SCSCR, ctrl);
> + return;
> + }
> break;
> }
>
> @@ -861,9 +860,9 @@ static void sci_transmit_chars(struct uart_port *port)
>
> sci_clear_SCxSR(port, SCxSR_TDxE_CLEAR(port));
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
> - if (uart_circ_empty(xmit)) {
> + if (kfifo_is_empty(&tport->xmit_fifo)) {
> if (port->type == PORT_SCI) {
> ctrl = sci_serial_in(port, SCSCR);
> ctrl &= ~SCSCR_TIE;
> @@ -1199,7 +1198,7 @@ static void sci_dma_tx_complete(void *arg)
> {
> struct sci_port *s = arg;
> struct uart_port *port = &s->port;
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> unsigned long flags;
>
> dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
> @@ -1208,10 +1207,10 @@ static void sci_dma_tx_complete(void *arg)
>
> uart_xmit_advance(port, s->tx_dma_len);
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
>
> - if (!uart_circ_empty(xmit)) {
> + if (!kfifo_is_empty(&tport->xmit_fifo)) {
> s->cookie_tx = 0;
> schedule_work(&s->work_tx);
> } else {
> @@ -1424,10 +1423,10 @@ static void sci_dma_tx_work_fn(struct work_struct *work)
> struct dma_async_tx_descriptor *desc;
> struct dma_chan *chan = s->chan_tx;
> struct uart_port *port = &s->port;
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> unsigned long flags;
> + unsigned int tail;
> dma_addr_t buf;
> - int head, tail;
>
> /*
> * DMA is idle now.
> @@ -1437,10 +1436,9 @@ static void sci_dma_tx_work_fn(struct work_struct *work)
> * consistent xmit buffer state.
> */
> uart_port_lock_irq(port);
> - head = xmit->head;
> - tail = xmit->tail;
> + s->tx_dma_len = kfifo_out_linear(&tport->xmit_fifo, &tail,
> + UART_XMIT_SIZE);
> buf = s->tx_dma_addr + tail;
> - s->tx_dma_len = CIRC_CNT_TO_END(head, tail, UART_XMIT_SIZE);
> if (!s->tx_dma_len) {
> /* Transmit buffer has been flushed */
> uart_port_unlock_irq(port);
> @@ -1469,8 +1467,8 @@ static void sci_dma_tx_work_fn(struct work_struct *work)
> }
>
> uart_port_unlock_irq(port);
> - dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n",
> - __func__, xmit->buf, tail, head, s->cookie_tx);
> + dev_dbg(port->dev, "%s: %p: %u, cookie %d\n",
> + __func__, tport->xmit_buf, tail, s->cookie_tx);
>
> dma_async_issue_pending(chan);
> return;
> @@ -1585,6 +1583,7 @@ static struct dma_chan *sci_request_dma_chan(struct uart_port *port,
> static void sci_request_dma(struct uart_port *port)
> {
> struct sci_port *s = to_sci_port(port);
> + struct tty_port *tport = &port->state->port;
> struct dma_chan *chan;
>
> dev_dbg(port->dev, "%s: port %d\n", __func__, port->line);
> @@ -1613,7 +1612,7 @@ static void sci_request_dma(struct uart_port *port)
> if (chan) {
> /* UART circular tx buffer is an aligned page. */
> s->tx_dma_addr = dma_map_single(chan->device->dev,
> - port->state->xmit.buf,
> + tport->xmit_buf,
> UART_XMIT_SIZE,
> DMA_TO_DEVICE);
> if (dma_mapping_error(chan->device->dev, s->tx_dma_addr)) {
> @@ -1622,7 +1621,7 @@ static void sci_request_dma(struct uart_port *port)
> } else {
> dev_dbg(port->dev, "%s: mapped %lu@%p to %pad\n",
> __func__, UART_XMIT_SIZE,
> - port->state->xmit.buf, &s->tx_dma_addr);
> + tport->xmit_buf, &s->tx_dma_addr);
>
> INIT_WORK(&s->work_tx, sci_dma_tx_work_fn);
> s->chan_tx_saved = s->chan_tx = chan;
> diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c
> index 15f14fa593da..3fc54cc02a1f 100644
> --- a/drivers/tty/serial/sprd_serial.c
> +++ b/drivers/tty/serial/sprd_serial.c
> @@ -227,13 +227,13 @@ static int sprd_tx_buf_remap(struct uart_port *port)
> {
> struct sprd_uart_port *sp =
> container_of(port, struct sprd_uart_port, port);
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> + unsigned char *tail;
>
> - sp->tx_dma.trans_len =
> - CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
> + sp->tx_dma.trans_len = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail,
> + UART_XMIT_SIZE);
>
> - sp->tx_dma.phys_addr = dma_map_single(port->dev,
> - (void *)&(xmit->buf[xmit->tail]),
> + sp->tx_dma.phys_addr = dma_map_single(port->dev, tail,
> sp->tx_dma.trans_len,
> DMA_TO_DEVICE);
> return dma_mapping_error(port->dev, sp->tx_dma.phys_addr);
> @@ -244,7 +244,7 @@ static void sprd_complete_tx_dma(void *data)
> struct uart_port *port = (struct uart_port *)data;
> struct sprd_uart_port *sp =
> container_of(port, struct sprd_uart_port, port);
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> unsigned long flags;
>
> uart_port_lock_irqsave(port, &flags);
> @@ -253,10 +253,10 @@ static void sprd_complete_tx_dma(void *data)
>
> uart_xmit_advance(port, sp->tx_dma.trans_len);
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
>
> - if (uart_circ_empty(xmit) || sprd_tx_buf_remap(port) ||
> + if (kfifo_is_empty(&tport->xmit_fifo) || sprd_tx_buf_remap(port) ||
> sprd_tx_dma_config(port))
> sp->tx_dma.trans_len = 0;
>
> @@ -319,7 +319,7 @@ static void sprd_start_tx_dma(struct uart_port *port)
> {
> struct sprd_uart_port *sp =
> container_of(port, struct sprd_uart_port, port);
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
>
> if (port->x_char) {
> serial_out(port, SPRD_TXD, port->x_char);
> @@ -328,7 +328,7 @@ static void sprd_start_tx_dma(struct uart_port *port)
> return;
> }
>
> - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
> + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
> sprd_stop_tx_dma(port);
> return;
> }
> diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c
> index a23e59551848..f91753a40a69 100644
> --- a/drivers/tty/serial/st-asc.c
> +++ b/drivers/tty/serial/st-asc.c
> @@ -387,9 +387,9 @@ static unsigned int asc_get_mctrl(struct uart_port *port)
> /* There are probably characters waiting to be transmitted. */
> static void asc_start_tx(struct uart_port *port)
> {
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
>
> - if (!uart_circ_empty(xmit))
> + if (!kfifo_is_empty(&tport->xmit_fifo))
> asc_enable_tx_interrupts(port);
> }
>
> diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
> index 58d169e5c1db..8c66abcfe6ca 100644
> --- a/drivers/tty/serial/stm32-usart.c
> +++ b/drivers/tty/serial/stm32-usart.c
> @@ -696,18 +696,23 @@ static void stm32_usart_transmit_chars_pio(struct uart_port *port)
> {
> struct stm32_port *stm32_port = to_stm32_port(port);
> const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> +
> + while (1) {
> + unsigned char ch;
>
> - while (!uart_circ_empty(xmit)) {
> /* Check that TDR is empty before filling FIFO */
> if (!(readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE))
> break;
> - writel_relaxed(xmit->buf[xmit->tail], port->membase + ofs->tdr);
> - uart_xmit_advance(port, 1);
> +
> + if (!uart_fifo_get(port, &ch))
> + break;
> +
> + writel_relaxed(ch, port->membase + ofs->tdr);
> }
>
> /* rely on TXE irq (mask or unmask) for sending remaining data */
> - if (uart_circ_empty(xmit))
> + if (kfifo_is_empty(&tport->xmit_fifo))
> stm32_usart_tx_interrupt_disable(port);
> else
> stm32_usart_tx_interrupt_enable(port);
> @@ -716,7 +721,7 @@ static void stm32_usart_transmit_chars_pio(struct uart_port *port)
> static void stm32_usart_transmit_chars_dma(struct uart_port *port)
> {
> struct stm32_port *stm32port = to_stm32_port(port);
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> struct dma_async_tx_descriptor *desc = NULL;
> unsigned int count;
> int ret;
> @@ -728,25 +733,8 @@ static void stm32_usart_transmit_chars_dma(struct uart_port *port)
> return;
> }
>
> - count = uart_circ_chars_pending(xmit);
> -
> - if (count > TX_BUF_L)
> - count = TX_BUF_L;
> -
> - if (xmit->tail < xmit->head) {
> - memcpy(&stm32port->tx_buf[0], &xmit->buf[xmit->tail], count);
> - } else {
> - size_t one = UART_XMIT_SIZE - xmit->tail;
> - size_t two;
> -
> - if (one > count)
> - one = count;
> - two = count - one;
> -
> - memcpy(&stm32port->tx_buf[0], &xmit->buf[xmit->tail], one);
> - if (two)
> - memcpy(&stm32port->tx_buf[one], &xmit->buf[0], two);
> - }
> + count = kfifo_out_peek(&tport->xmit_fifo, &stm32port->tx_buf[0],
> + TX_BUF_L);
>
> desc = dmaengine_prep_slave_single(stm32port->tx_ch,
> stm32port->tx_dma_buf,
> @@ -792,14 +780,14 @@ static void stm32_usart_transmit_chars(struct uart_port *port)
> {
> struct stm32_port *stm32_port = to_stm32_port(port);
> const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> u32 isr;
> int ret;
>
> if (!stm32_port->hw_flow_control &&
> port->rs485.flags & SER_RS485_ENABLED &&
> (port->x_char ||
> - !(uart_circ_empty(xmit) || uart_tx_stopped(port)))) {
> + !(kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)))) {
> stm32_usart_tc_interrupt_disable(port);
> stm32_usart_rs485_rts_enable(port);
> }
> @@ -826,7 +814,7 @@ static void stm32_usart_transmit_chars(struct uart_port *port)
> return;
> }
>
> - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
> + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
> stm32_usart_tx_interrupt_disable(port);
> return;
> }
> @@ -841,10 +829,10 @@ static void stm32_usart_transmit_chars(struct uart_port *port)
> else
> stm32_usart_transmit_chars_pio(port);
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
>
> - if (uart_circ_empty(xmit)) {
> + if (kfifo_is_empty(&tport->xmit_fifo)) {
> stm32_usart_tx_interrupt_disable(port);
> if (!stm32_port->hw_flow_control &&
> port->rs485.flags & SER_RS485_ENABLED) {
> @@ -967,9 +955,9 @@ static void stm32_usart_stop_tx(struct uart_port *port)
> /* There are probably characters waiting to be transmitted. */
> static void stm32_usart_start_tx(struct uart_port *port)
> {
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
>
> - if (uart_circ_empty(xmit) && !port->x_char) {
> + if (kfifo_is_empty(&tport->xmit_fifo) && !port->x_char) {
> stm32_usart_rs485_rts_disable(port);
> return;
> }
> diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c
> index 8d612ab80680..7f60679fdde1 100644
> --- a/drivers/tty/serial/sunhv.c
> +++ b/drivers/tty/serial/sunhv.c
> @@ -39,10 +39,13 @@ static char *con_read_page;
>
> static int hung_up = 0;
>
> -static void transmit_chars_putchar(struct uart_port *port, struct circ_buf *xmit)
> +static void transmit_chars_putchar(struct uart_port *port,
> + struct tty_port *tport)
> {
> - while (!uart_circ_empty(xmit)) {
> - long status = sun4v_con_putchar(xmit->buf[xmit->tail]);
> + unsigned char ch;
> +
> + while (kfifo_peek(&tport->xmit_fifo, &ch)) {
> + long status = sun4v_con_putchar(ch);
>
> if (status != HV_EOK)
> break;
> @@ -51,14 +54,16 @@ static void transmit_chars_putchar(struct uart_port *port, struct circ_buf *xmit
> }
> }
>
> -static void transmit_chars_write(struct uart_port *port, struct circ_buf *xmit)
> +static void transmit_chars_write(struct uart_port *port, struct tty_port *tport)
> {
> - while (!uart_circ_empty(xmit)) {
> - unsigned long ra = __pa(xmit->buf + xmit->tail);
> - unsigned long len, status, sent;
> + while (!kfifo_is_empty(&tport->xmit_fifo)) {
> + unsigned long len, ra, status, sent;
> + unsigned char *tail;
> +
> + len = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail,
> + UART_XMIT_SIZE);
> + ra = __pa(tail);
>
> - len = CIRC_CNT_TO_END(xmit->head, xmit->tail,
> - UART_XMIT_SIZE);
> status = sun4v_con_write(ra, len, &sent);
> if (status != HV_EOK)
> break;
> @@ -165,7 +170,7 @@ static int receive_chars_read(struct uart_port *port)
> }
>
> struct sunhv_ops {
> - void (*transmit_chars)(struct uart_port *port, struct circ_buf *xmit);
> + void (*transmit_chars)(struct uart_port *port, struct tty_port *tport);
> int (*receive_chars)(struct uart_port *port);
> };
>
> @@ -196,18 +201,18 @@ static struct tty_port *receive_chars(struct uart_port *port)
>
> static void transmit_chars(struct uart_port *port)
> {
> - struct circ_buf *xmit;
> + struct tty_port *tport;
>
> if (!port->state)
> return;
>
> - xmit = &port->state->xmit;
> - if (uart_circ_empty(xmit) || uart_tx_stopped(port))
> + tport = &port->state->port;
> + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port))
> return;
>
> - sunhv_ops->transmit_chars(port, xmit);
> + sunhv_ops->transmit_chars(port, tport);
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
> }
>
> diff --git a/drivers/tty/serial/sunplus-uart.c b/drivers/tty/serial/sunplus-uart.c
> index f5e29eb4a4ce..abf7c449308d 100644
> --- a/drivers/tty/serial/sunplus-uart.c
> +++ b/drivers/tty/serial/sunplus-uart.c
> @@ -200,7 +200,7 @@ static void sunplus_break_ctl(struct uart_port *port, int ctl)
>
> static void transmit_chars(struct uart_port *port)
> {
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
>
> if (port->x_char) {
> sp_uart_put_char(port, port->x_char);
> @@ -209,22 +209,24 @@ static void transmit_chars(struct uart_port *port)
> return;
> }
>
> - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
> + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
> sunplus_stop_tx(port);
> return;
> }
>
> do {
> - sp_uart_put_char(port, xmit->buf[xmit->tail]);
> - uart_xmit_advance(port, 1);
> - if (uart_circ_empty(xmit))
> + unsigned char ch;
> +
> + if (!uart_fifo_get(port, &ch))
> break;
> +
> + sp_uart_put_char(port, ch);
> } while (sunplus_tx_buf_not_full(port));
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
>
> - if (uart_circ_empty(xmit))
> + if (kfifo_is_empty(&tport->xmit_fifo))
> sunplus_stop_tx(port);
> }
>
> diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c
> index 1ea2f33a07a7..1acbe2fba746 100644
> --- a/drivers/tty/serial/sunsab.c
> +++ b/drivers/tty/serial/sunsab.c
> @@ -232,7 +232,7 @@ static void sunsab_tx_idle(struct uart_sunsab_port *);
> static void transmit_chars(struct uart_sunsab_port *up,
> union sab82532_irq_status *stat)
> {
> - struct circ_buf *xmit = &up->port.state->xmit;
> + struct tty_port *tport = &up->port.state->port;
> int i;
>
> if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) {
> @@ -252,7 +252,7 @@ static void transmit_chars(struct uart_sunsab_port *up,
> set_bit(SAB82532_XPR, &up->irqflags);
> sunsab_tx_idle(up);
>
> - if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
> + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(&up->port)) {
> up->interrupt_mask1 |= SAB82532_IMR1_XPR;
> writeb(up->interrupt_mask1, &up->regs->w.imr1);
> return;
> @@ -265,21 +265,22 @@ static void transmit_chars(struct uart_sunsab_port *up,
> /* Stuff 32 bytes into Transmit FIFO. */
> clear_bit(SAB82532_XPR, &up->irqflags);
> for (i = 0; i < up->port.fifosize; i++) {
> - writeb(xmit->buf[xmit->tail],
> - &up->regs->w.xfifo[i]);
> - uart_xmit_advance(&up->port, 1);
> - if (uart_circ_empty(xmit))
> + unsigned char ch;
> +
> + if (!uart_fifo_get(&up->port, &ch))
> break;
> +
> + writeb(ch, &up->regs->w.xfifo[i]);
> }
>
> /* Issue a Transmit Frame command. */
> sunsab_cec_wait(up);
> writeb(SAB82532_CMDR_XF, &up->regs->w.cmdr);
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&up->port);
>
> - if (uart_circ_empty(xmit))
> + if (kfifo_is_empty(&tport->xmit_fifo))
> sunsab_stop_tx(&up->port);
> }
>
> @@ -435,10 +436,10 @@ static void sunsab_start_tx(struct uart_port *port)
> {
> struct uart_sunsab_port *up =
> container_of(port, struct uart_sunsab_port, port);
> - struct circ_buf *xmit = &up->port.state->xmit;
> + struct tty_port *tport = &up->port.state->port;
> int i;
>
> - if (uart_circ_empty(xmit) || uart_tx_stopped(port))
> + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port))
> return;
>
> up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS|SAB82532_IMR1_XPR);
> @@ -451,11 +452,12 @@ static void sunsab_start_tx(struct uart_port *port)
> clear_bit(SAB82532_XPR, &up->irqflags);
>
> for (i = 0; i < up->port.fifosize; i++) {
> - writeb(xmit->buf[xmit->tail],
> - &up->regs->w.xfifo[i]);
> - uart_xmit_advance(&up->port, 1);
> - if (uart_circ_empty(xmit))
> + unsigned char ch;
> +
> + if (!uart_fifo_get(&up->port, &ch))
> break;
> +
> + writeb(ch, &up->regs->w.xfifo[i]);
> }
>
> /* Issue a Transmit Frame command. */
> diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c
> index c8b65f4b2710..67a5fc70bb4b 100644
> --- a/drivers/tty/serial/sunsu.c
> +++ b/drivers/tty/serial/sunsu.c
> @@ -396,7 +396,8 @@ receive_chars(struct uart_sunsu_port *up, unsigned char *status)
>
> static void transmit_chars(struct uart_sunsu_port *up)
> {
> - struct circ_buf *xmit = &up->port.state->xmit;
> + struct tty_port *tport = &up->port.state->port;
> + unsigned char ch;
> int count;
>
> if (up->port.x_char) {
> @@ -409,23 +410,23 @@ static void transmit_chars(struct uart_sunsu_port *up)
> sunsu_stop_tx(&up->port);
> return;
> }
> - if (uart_circ_empty(xmit)) {
> + if (kfifo_is_empty(&tport->xmit_fifo)) {
> __stop_tx(up);
> return;
> }
>
> count = up->port.fifosize;
> do {
> - serial_out(up, UART_TX, xmit->buf[xmit->tail]);
> - uart_xmit_advance(&up->port, 1);
> - if (uart_circ_empty(xmit))
> + if (!uart_fifo_get(&up->port, &ch))
> break;
> +
> + serial_out(up, UART_TX, ch);
> } while (--count > 0);
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&up->port);
>
> - if (uart_circ_empty(xmit))
> + if (kfifo_is_empty(&tport->xmit_fifo))
> __stop_tx(up);
> }
>
> diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c
> index c99289c6c8f8..71758ad4241c 100644
> --- a/drivers/tty/serial/sunzilog.c
> +++ b/drivers/tty/serial/sunzilog.c
> @@ -453,7 +453,8 @@ static void sunzilog_status_handle(struct uart_sunzilog_port *up,
> static void sunzilog_transmit_chars(struct uart_sunzilog_port *up,
> struct zilog_channel __iomem *channel)
> {
> - struct circ_buf *xmit;
> + struct tty_port *tport;
> + unsigned char ch;
>
> if (ZS_IS_CONS(up)) {
> unsigned char status = readb(&channel->control);
> @@ -496,21 +497,20 @@ static void sunzilog_transmit_chars(struct uart_sunzilog_port *up,
>
> if (up->port.state == NULL)
> goto ack_tx_int;
> - xmit = &up->port.state->xmit;
> - if (uart_circ_empty(xmit))
> - goto ack_tx_int;
> + tport = &up->port.state->port;
>
> if (uart_tx_stopped(&up->port))
> goto ack_tx_int;
>
> + if (!uart_fifo_get(&up->port, &ch))
> + goto ack_tx_int;
> +
> up->flags |= SUNZILOG_FLAG_TX_ACTIVE;
> - writeb(xmit->buf[xmit->tail], &channel->data);
> + writeb(ch, &channel->data);
> ZSDELAY();
> ZS_WSYNC(channel);
>
> - uart_xmit_advance(&up->port, 1);
> -
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&up->port);
>
> return;
> @@ -700,17 +700,16 @@ static void sunzilog_start_tx(struct uart_port *port)
> port->icount.tx++;
> port->x_char = 0;
> } else {
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> + unsigned char ch;
>
> - if (uart_circ_empty(xmit))
> + if (!uart_fifo_get(&up->port, &ch))
> return;
> - writeb(xmit->buf[xmit->tail], &channel->data);
> + writeb(ch, &channel->data);
> ZSDELAY();
> ZS_WSYNC(channel);
>
> - uart_xmit_advance(port, 1);
> -
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&up->port);
> }
> }
> diff --git a/drivers/tty/serial/tegra-tcu.c b/drivers/tty/serial/tegra-tcu.c
> index d9c78320eb02..21ca5fcadf49 100644
> --- a/drivers/tty/serial/tegra-tcu.c
> +++ b/drivers/tty/serial/tegra-tcu.c
> @@ -91,15 +91,17 @@ static void tegra_tcu_write(struct tegra_tcu *tcu, const char *s,
> static void tegra_tcu_uart_start_tx(struct uart_port *port)
> {
> struct tegra_tcu *tcu = port->private_data;
> - struct circ_buf *xmit = &port->state->xmit;
> - unsigned long count;
> + struct tty_port *tport = &port->state->port;
> + unsigned char *tail;
> + unsigned int count;
>
> for (;;) {
> - count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
> + count = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail,
> + UART_XMIT_SIZE);
> if (!count)
> break;
>
> - tegra_tcu_write(tcu, &xmit->buf[xmit->tail], count);
> + tegra_tcu_write(tcu, tail, count);
> uart_xmit_advance(port, count);
> }
>
> diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c
> index 4bc89a9b380a..43fa0938b5e3 100644
> --- a/drivers/tty/serial/timbuart.c
> +++ b/drivers/tty/serial/timbuart.c
> @@ -95,14 +95,11 @@ static void timbuart_rx_chars(struct uart_port *port)
>
> static void timbuart_tx_chars(struct uart_port *port)
> {
> - struct circ_buf *xmit = &port->state->xmit;
> + unsigned char ch;
>
> while (!(ioread32(port->membase + TIMBUART_ISR) & TXBF) &&
> - !uart_circ_empty(xmit)) {
> - iowrite8(xmit->buf[xmit->tail],
> - port->membase + TIMBUART_TXFIFO);
> - uart_xmit_advance(port, 1);
> - }
> + uart_fifo_get(port, &ch))
> + iowrite8(ch, port->membase + TIMBUART_TXFIFO);
>
> dev_dbg(port->dev,
> "%s - total written %d bytes, CTL: %x, RTS: %x, baud: %x\n",
> @@ -117,9 +114,9 @@ static void timbuart_handle_tx_port(struct uart_port *port, u32 isr, u32 *ier)
> {
> struct timbuart_port *uart =
> container_of(port, struct timbuart_port, port);
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
>
> - if (uart_circ_empty(xmit) || uart_tx_stopped(port))
> + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port))
> return;
>
> if (port->x_char)
> @@ -130,7 +127,7 @@ static void timbuart_handle_tx_port(struct uart_port *port, u32 isr, u32 *ier)
> /* clear all TX interrupts */
> iowrite32(TXFLAGS, port->membase + TIMBUART_ISR);
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
> } else
> /* Re-enable any tx interrupt */
> @@ -141,7 +138,7 @@ static void timbuart_handle_tx_port(struct uart_port *port, u32 isr, u32 *ier)
> * we wake up the upper layer later when we got the interrupt
> * to give it some time to go out...
> */
> - if (!uart_circ_empty(xmit))
> + if (!kfifo_is_empty(&tport->xmit_fifo))
> *ier |= TXBAE;
>
> dev_dbg(port->dev, "%s - leaving\n", __func__);
> diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c
> index 10ba41b7be99..68357ac8ffe3 100644
> --- a/drivers/tty/serial/uartlite.c
> +++ b/drivers/tty/serial/uartlite.c
> @@ -189,7 +189,8 @@ static int ulite_receive(struct uart_port *port, int stat)
>
> static int ulite_transmit(struct uart_port *port, int stat)
> {
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> + unsigned char ch;
>
> if (stat & ULITE_STATUS_TXFULL)
> return 0;
> @@ -201,14 +202,16 @@ static int ulite_transmit(struct uart_port *port, int stat)
> return 1;
> }
>
> - if (uart_circ_empty(xmit) || uart_tx_stopped(port))
> + if (uart_tx_stopped(port))
> + return 0;
> +
> + if (!uart_fifo_get(port, &ch))
> return 0;
>
> - uart_out32(xmit->buf[xmit->tail], ULITE_TX, port);
> - uart_xmit_advance(port, 1);
> + uart_out32(ch, ULITE_TX, port);
>
> /* wake up */
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
>
> return 1;
> diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c
> index 397b95dff7ed..53bb8c5ef499 100644
> --- a/drivers/tty/serial/ucc_uart.c
> +++ b/drivers/tty/serial/ucc_uart.c
> @@ -334,7 +334,7 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port)
> unsigned char *p;
> unsigned int count;
> struct uart_port *port = &qe_port->port;
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
>
> /* Handle xon/xoff */
> if (port->x_char) {
> @@ -358,7 +358,7 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port)
> return 1;
> }
>
> - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
> + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
> qe_uart_stop_tx(port);
> return 0;
> }
> @@ -366,16 +366,10 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port)
> /* Pick next descriptor and fill from buffer */
> bdp = qe_port->tx_cur;
>
> - while (!(ioread16be(&bdp->status) & BD_SC_READY) && !uart_circ_empty(xmit)) {
> - count = 0;
> + while (!(ioread16be(&bdp->status) & BD_SC_READY) &&
> + !kfifo_is_empty(&tport->xmit_fifo)) {
> p = qe2cpu_addr(ioread32be(&bdp->buf), qe_port);
> - while (count < qe_port->tx_fifosize) {
> - *p++ = xmit->buf[xmit->tail];
> - uart_xmit_advance(port, 1);
> - count++;
> - if (uart_circ_empty(xmit))
> - break;
> - }
> + count = uart_fifo_out(port, p, qe_port->tx_fifosize);
>
> iowrite16be(count, &bdp->length);
> qe_setbits_be16(&bdp->status, BD_SC_READY);
> @@ -388,10 +382,10 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port)
> }
> qe_port->tx_cur = bdp;
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
>
> - if (uart_circ_empty(xmit)) {
> + if (kfifo_is_empty(&tport->xmit_fifo)) {
> /* The kernel buffer is empty, so turn off TX interrupts. We
> don't need to be told when the QE is finished transmitting
> the data. */
> diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
> index 5f48ec37cb25..de3487206bcb 100644
> --- a/drivers/tty/serial/xilinx_uartps.c
> +++ b/drivers/tty/serial/xilinx_uartps.c
> @@ -425,32 +425,32 @@ static void cdns_uart_handle_tx(void *dev_id)
> {
> struct uart_port *port = (struct uart_port *)dev_id;
> struct cdns_uart *cdns_uart = port->private_data;
> - struct circ_buf *xmit = &port->state->xmit;
> + struct tty_port *tport = &port->state->port;
> unsigned int numbytes;
> + unsigned char ch;
>
> - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
> + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
> /* Disable the TX Empty interrupt */
> writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IDR);
> return;
> }
>
> numbytes = port->fifosize;
> - while (numbytes && !uart_circ_empty(xmit) &&
> - !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL)) {
> -
> - writel(xmit->buf[xmit->tail], port->membase + CDNS_UART_FIFO);
> - uart_xmit_advance(port, 1);
> + while (numbytes &&
> + !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL) &&
> + uart_fifo_get(port, &ch)) {
> + writel(ch, port->membase + CDNS_UART_FIFO);
> numbytes--;
> }
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(port);
>
> /* Enable the TX Empty interrupt */
> writel(CDNS_UART_IXR_TXEMPTY, cdns_uart->port->membase + CDNS_UART_IER);
>
> if (cdns_uart->port->rs485.flags & SER_RS485_ENABLED &&
> - (uart_circ_empty(xmit) || uart_tx_stopped(port))) {
> + (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port))) {
> cdns_uart->tx_timer.function = &cdns_rs485_rx_callback;
> hrtimer_start(&cdns_uart->tx_timer,
> ns_to_ktime(cdns_calc_after_tx_delay(cdns_uart)), HRTIMER_MODE_REL);
> @@ -723,7 +723,7 @@ static void cdns_uart_start_tx(struct uart_port *port)
> status |= CDNS_UART_CR_TX_EN;
> writel(status, port->membase + CDNS_UART_CR);
>
> - if (uart_circ_empty(&port->state->xmit))
> + if (kfifo_is_empty(&port->state->port.xmit_fifo))
> return;
>
> /* Clear the TX Empty interrupt */
> diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c
> index 65ca4da6e368..79ea7108a0f3 100644
> --- a/drivers/tty/serial/zs.c
> +++ b/drivers/tty/serial/zs.c
> @@ -606,7 +606,8 @@ static void zs_receive_chars(struct zs_port *zport)
>
> static void zs_raw_transmit_chars(struct zs_port *zport)
> {
> - struct circ_buf *xmit = &zport->port.state->xmit;
> + struct tty_port *tport = &zport->port.state->port;
> + unsigned char ch;
>
> /* XON/XOFF chars. */
> if (zport->port.x_char) {
> @@ -617,20 +618,20 @@ static void zs_raw_transmit_chars(struct zs_port *zport)
> }
>
> /* If nothing to do or stopped or hardware stopped. */
> - if (uart_circ_empty(xmit) || uart_tx_stopped(&zport->port)) {
> + if (uart_tx_stopped(&zport->port) ||
> + !uart_fifo_get(&zport->port, &ch)) {
> zs_raw_stop_tx(zport);
> return;
> }
>
> /* Send char. */
> - write_zsdata(zport, xmit->buf[xmit->tail]);
> - uart_xmit_advance(&zport->port, 1);
> + write_zsdata(zport, ch);
>
> - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
> uart_write_wakeup(&zport->port);
>
> /* Are we are done? */
> - if (uart_circ_empty(xmit))
> + if (kfifo_is_empty(&tport->xmit_fifo))
> zs_raw_stop_tx(zport);
> }
>
> diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
> index 0a0f6e21d40e..8cb65f50e830 100644
> --- a/include/linux/serial_core.h
> +++ b/include/linux/serial_core.h
> @@ -11,7 +11,6 @@
> #include <linux/compiler.h>
> #include <linux/console.h>
> #include <linux/interrupt.h>
> -#include <linux/circ_buf.h>
> #include <linux/spinlock.h>
> #include <linux/sched.h>
> #include <linux/tty.h>
> @@ -699,7 +698,6 @@ struct uart_state {
> struct tty_port port;
>
> enum uart_pm_state pm_state;
> - struct circ_buf xmit;
>
> atomic_t refcount;
> wait_queue_head_t remove_wait;
> @@ -723,12 +721,35 @@ struct uart_state {
> */
> static inline void uart_xmit_advance(struct uart_port *up, unsigned int chars)
> {
> - struct circ_buf *xmit = &up->state->xmit;
> + struct tty_port *tport = &up->state->port;
>
> - xmit->tail = (xmit->tail + chars) & (UART_XMIT_SIZE - 1);
> + kfifo_skip_count(&tport->xmit_fifo, chars);
> up->icount.tx += chars;
> }
>
> +static inline unsigned int uart_fifo_out(struct uart_port *up,
> + unsigned char *buf, unsigned int chars)
> +{
> + struct tty_port *tport = &up->state->port;
> +
> + chars = kfifo_out(&tport->xmit_fifo, buf, chars);
> + up->icount.tx += chars;
> +
> + return chars;
> +}
> +
> +static inline unsigned int uart_fifo_get(struct uart_port *up,
> + unsigned char *ch)
> +{
> + struct tty_port *tport = &up->state->port;
> + unsigned int chars;
> +
> + chars = kfifo_get(&tport->xmit_fifo, ch);
> + up->icount.tx += chars;
> +
> + return chars;
> +}
> +
> struct module;
> struct tty_driver;
>
> @@ -764,7 +785,7 @@ enum UART_TX_FLAGS {
> for_test, for_post) \
> ({ \
> struct uart_port *__port = (uport); \
> - struct circ_buf *xmit = &__port->state->xmit; \
> + struct tty_port *__tport = &__port->state->port; \
> unsigned int pending; \
> \
> for (; (for_test) && (tx_ready); (for_post), __port->icount.tx++) { \
> @@ -775,17 +796,18 @@ enum UART_TX_FLAGS {
> continue; \
> } \
> \
> - if (uart_circ_empty(xmit) || uart_tx_stopped(__port)) \
> + if (uart_tx_stopped(__port)) \
> + break; \
> + \
> + if (!kfifo_get(&__tport->xmit_fifo, &(ch))) \
> break; \
> \
> - (ch) = xmit->buf[xmit->tail]; \
> (put_char); \
> - xmit->tail = (xmit->tail + 1) % UART_XMIT_SIZE; \
> } \
> \
> (tx_done); \
> \
> - pending = uart_circ_chars_pending(xmit); \
> + pending = kfifo_len(&__tport->xmit_fifo); \
> if (pending < WAKEUP_CHARS) { \
> uart_write_wakeup(__port); \
> \
> @@ -974,15 +996,6 @@ bool uart_match_port(const struct uart_port *port1,
> int uart_suspend_port(struct uart_driver *reg, struct uart_port *port);
> int uart_resume_port(struct uart_driver *reg, struct uart_port *port);
>
> -#define uart_circ_empty(circ) ((circ)->head == (circ)->tail)
> -#define uart_circ_clear(circ) ((circ)->head = (circ)->tail = 0)
> -
> -#define uart_circ_chars_pending(circ) \
> - (CIRC_CNT((circ)->head, (circ)->tail, UART_XMIT_SIZE))
> -
> -#define uart_circ_chars_free(circ) \
> - (CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE))
> -
> static inline int uart_tx_stopped(struct uart_port *port)
> {
> struct tty_struct *tty = port->state->port.tty;
> --
> 2.44.0
>

2024-04-16 05:48:40

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH] serial: meson+qcom: don't advance the kfifo twice

Marek reports, that the -next commit 1788cf6a91d9 (tty: serial: switch
from circ_buf to kfifo) broke meson_uart and qcom_geni_serial. The
commit mistakenly advanced the kfifo twice: once by
uart_fifo_get()/kfifo_out() and second time by uart_xmit_advance().

To advance the fifo only once, drop the superfluous uart_xmit_advance()
from both.

To count the TX statistics properly, use uart_fifo_out() in
qcom_geni_serial (meson_uart_start_tx() already uses that).

I checked all other uses of uart_xmit_advance() and they appear correct:
either they are finishing DMA transfers or are after peek/linear_ptr
(i.e. they do not advance fifo).

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
Reported-by: Marek Szyprowski <[email protected]>
Cc: Neil Armstrong <[email protected]>
Cc: Kevin Hilman <[email protected]>
Cc: Jerome Brunet <[email protected]>
Cc: Martin Blumenstingl <[email protected]>
---
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
---
drivers/tty/serial/meson_uart.c | 1 -
drivers/tty/serial/qcom_geni_serial.c | 4 +---
2 files changed, 1 insertion(+), 4 deletions(-)

diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c
index 4587ed4d4d5d..8eb586ac3b0d 100644
--- a/drivers/tty/serial/meson_uart.c
+++ b/drivers/tty/serial/meson_uart.c
@@ -162,7 +162,6 @@ static void meson_uart_start_tx(struct uart_port *port)
break;

writel(ch, port->membase + AML_UART_WFIFO);
- uart_xmit_advance(port, 1);
}

if (!kfifo_is_empty(&tport->xmit_fifo)) {
diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
index 7814982f1921..2bd25afe0d92 100644
--- a/drivers/tty/serial/qcom_geni_serial.c
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -855,7 +855,6 @@ static void qcom_geni_serial_send_chunk_fifo(struct uart_port *uport,
unsigned int chunk)
{
struct qcom_geni_serial_port *port = to_dev_port(uport);
- struct tty_port *tport = &uport->state->port;
unsigned int tx_bytes, remaining = chunk;
u8 buf[BYTES_PER_FIFO_WORD];

@@ -863,8 +862,7 @@ static void qcom_geni_serial_send_chunk_fifo(struct uart_port *uport,
memset(buf, 0, sizeof(buf));
tx_bytes = min(remaining, BYTES_PER_FIFO_WORD);

- tx_bytes = kfifo_out(&tport->xmit_fifo, buf, tx_bytes);
- uart_xmit_advance(uport, tx_bytes);
+ tx_bytes = uart_fifo_out(uport, buf, tx_bytes);

iowrite32_rep(uport->membase + SE_GENI_TX_FIFOn, buf, 1);

--
2.44.0


2024-04-16 07:05:15

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCH 12/15] tty: serial: switch from circ_buf to kfifo

Hi,

On 16. 04. 24, 5:24, Pengfei Xu wrote:
> On 2024-04-05 at 08:08:23 +0200, Jiri Slaby (SUSE) wrote:
>> Switch from struct circ_buf to proper kfifo. kfifo provides much better
>> API, esp. when wrap-around of the buffer needs to be taken into account.
>> Look at pl011_dma_tx_refill() or cpm_uart_tx_pump() changes for example.
..
> Greeting!
> I used syzkaller and Internal v6.9-rc4 kernel which include Linux next kernel,
> and found deadlock in console_flush_all issue, bisected the commit is:
> "
> 1788cf6a91d9 tty: serial: switch from circ_buf to kfifo
> "
..
> [ 17.841628] ======================================================
> [ 17.841631] WARNING: possible circular locking dependency detected
> [ 17.841632] 6.9.0-rc4-df0b4fecf9eb+ #1 Not tainted
> [ 17.841636] ------------------------------------------------------
> [ 17.841638] repro/726 is trying to acquire lock:
> [ 17.841641] ffffffff86d6da80 (console_owner){....}-{0:0}, at: console_flush_all+0x51a/0xc40
> [ 17.841665]
> [ 17.841665] but task is already holding lock:
> [ 17.841667] ffffffff89aa52b8 (&port_lock_key){-...}-{2:2}, at: uart_put_char+0x118/0x510
> [ 17.841684]
> [ 17.841684] which lock already depends on the new lock.
..
> [ 17.842133] stack backtrace:
..
> [ 17.842461] handle_bug+0xa2/0x130
> [ 17.842469] exc_invalid_op+0x3c/0x80
> [ 17.842479] asm_exc_invalid_op+0x1f/0x30
> [ 17.842487] RIP: 0010:uart_put_char+0x3ac/0x510

So you hit a "WARNING" (see below). And printing pretty much anything
(like the WARNING report) from this point inside uart_put_char() will
lead to this deadlock.

..
> [ 17.879899] WARNING: CPU: 0 PID: 726 at drivers/tty/serial/serial_core.c:561 uart_put_char+0x3ac/0x510

This is the real thing/root cause of the above.

WARN_ON_ONCE(!state->port.xmit_buf)) was hit in uart_put_char().

Let me see how kfifo changed xmit_buf handling.

> Hope reproduced code and bisection is helpful.

Thanks, let me investigate.

--
js
suse labs


2024-04-16 07:20:38

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH] serial: drop debugging WARN_ON_ONCE() from uart_put_char()

Pengfei Xu reports, that the -next commit 1788cf6a91d9 (tty: serial:
switch from circ_buf to kfifo) tries to emit a WARNING and that leads to
lockdep errors. Obviously we cannot print anything from uart_put_char()!

This WARN_ON_ONCE() was/is a debug aid to check if the condition in
uart_put_char() can happen at all. Pengfei Xu confirmed it can. Unlike
me and kbuild bot in my queue.

Second, I completely forgot about it, so I did not remove it in the
final version, nor mentioned it in the commit log.

Drop it now as we are all good. And we even have stack traces (and a
reproducer)!

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
Reported-by: Pengfei Xu <[email protected]>
---
drivers/tty/serial/serial_core.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 558f159ada15..bd4a72f8e821 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -573,7 +573,7 @@ static int uart_put_char(struct tty_struct *tty, u8 c)
int ret = 0;

port = uart_port_lock(state, flags);
- if (WARN_ON_ONCE(!state->port.xmit_buf)) {
+ if (!state->port.xmit_buf) {
uart_port_unlock(port, flags);
return 0;
}
--
2.44.0


2024-04-16 10:23:34

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCH 11/15] tty: msm_serial: use dmaengine_prep_slave_sg()

On 15. 04. 24, 23:17, Marek Szyprowski wrote:
> On 05.04.2024 08:08, Jiri Slaby (SUSE) wrote:
>> This is a preparatory for the serial-to-kfifo switch. kfifo understands
>> only scatter-gatter approach, so switch to that.
>>
>> No functional change intended, it's just dmaengine_prep_slave_single()
>> inline expanded.
>>
>> And in this case, switch from dma_map_single() to dma_map_sg() too. This
>> needs struct msm_dma changes. I split the rx and tx parts into an union.
>> TX is now struct scatterlist, RX remains the old good phys-virt-count
>> triple.
>>
>> Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
>> Cc: Bjorn Andersson <[email protected]>
>> Cc: Konrad Dybcio <[email protected]>
>> Cc: [email protected]
>
> I've just found that this patch broke UART operation on DragonBoard
> 410c. I briefly checked and didn't notice anything obviously wrong here,
> but the board stops transmitting any data from its serial port after the
> first message. I will try to analyze this issue a bit more tomorrow.

I double checked, but I see no immediate issues in the patch too. So
please, if you can analyze this more…

thanks,
--
js
suse labs


2024-04-17 10:08:17

by Anders Roxell

[permalink] [raw]
Subject: Re: [PATCH 12/15] tty: serial: switch from circ_buf to kfifo

On 2024-04-15 15:28, Jiri Slaby wrote:
> On 15. 04. 24, 14:58, Marek Szyprowski wrote:
> > Dear All,
> >
> > On 05.04.2024 08:08, Jiri Slaby (SUSE) wrote:
> > > Switch from struct circ_buf to proper kfifo. kfifo provides much better
> > > API, esp. when wrap-around of the buffer needs to be taken into account.
> > > Look at pl011_dma_tx_refill() or cpm_uart_tx_pump() changes for example.
> > >
> > > Kfifo API can also fill in scatter-gather DMA structures, so it easier
> > > for that use case too. Look at lpuart_dma_tx() for example. Note that
> > > not all drivers can be converted to that (like atmel_serial), they
> > > handle DMA specially.
> > >
> > > Note that usb-serial uses kfifo for TX for ages.
> > >
> > > omap needed a bit more care as it needs to put a char into FIFO to start
> > > the DMA transfer when OMAP_DMA_TX_KICK is set. In that case, we have to
> > > do kfifo_dma_out_prepare twice: once to find out the tx_size (to find
> > > out if it is worths to do DMA at all -- size >= 4), the second time for
> > > the actual transfer.
> > >
> > > All traces of circ_buf are removed from serial_core.h (and its struct
> > > uart_state).
> > >
> > > Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
> > > ...
> >
> > This patch landed in linux-next as commit 1788cf6a91d9 ("tty: serial:
> > switch from circ_buf to kfifo"). Unfortunately it breaks UART operation
> > on thr Amlogic Meson based boards (drivers/tty/serial/meson_uart.c
> > driver) and Qualcomm RB5 board (drivers/tty/serial/qcom_geni_serial.c).
> > Once the init process is started, a complete garbage is printed to the
> > serial console. Here is an example how it looks:
>
> Oh my!
>
> Both drivers move the tail using both kfifo and uart_xmit_advance()
> interfaces. Bah. Does it help to remove that uart_xmit_advance() for both of
> them? (TX stats will be broken.)
>
> Users of uart_port_tx() are not affected.
>
> This is my fault when merging uart_xmit_advance() with this series.
>

I'm trying to run on two dragonboard devices db410c and db845c and both
fails to boot see the boot failure from db845c [1], linux-next tag: next-20240415.
I tried to apply the patch [2] (that you proposed in this thread) ontop of next-20240415. However, that didn't
help bootlog on db845c [3].

Cheers,
Anders
[1] https://tuxapi.tuxsuite.com/v1/groups/linaro/projects/lkft/tests/2f7sLxYtIQXQzsnTzE1Dye2xweg/logs?format=html
[2] https://lore.kernel.org/lkml/[email protected]/raw
[3] https://tuxapi.tuxsuite.com/v1/groups/linaro/projects/anders/tests/2fDgvWnyEmFm9mqMCxOaruBOfTe/logs?format=html

2024-04-17 10:15:41

by Marek Szyprowski

[permalink] [raw]
Subject: Re: [PATCH 11/15] tty: msm_serial: use dmaengine_prep_slave_sg()

Hi Jiri,

On 16.04.2024 12:23, Jiri Slaby wrote:
> On 15. 04. 24, 23:17, Marek Szyprowski wrote:
>> On 05.04.2024 08:08, Jiri Slaby (SUSE) wrote:
>>> This is a preparatory for the serial-to-kfifo switch. kfifo understands
>>> only scatter-gatter approach, so switch to that.
>>>
>>> No functional change intended, it's just dmaengine_prep_slave_single()
>>> inline expanded.
>>>
>>> And in this case, switch from dma_map_single() to dma_map_sg() too.
>>> This
>>> needs struct msm_dma changes. I split the rx and tx parts into an
>>> union.
>>> TX is now struct scatterlist, RX remains the old good phys-virt-count
>>> triple.
>>>
>>> Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
>>> Cc: Bjorn Andersson <[email protected]>
>>> Cc: Konrad Dybcio <[email protected]>
>>> Cc: [email protected]
>>
>> I've just found that this patch broke UART operation on DragonBoard
>> 410c. I briefly checked and didn't notice anything obviously wrong here,
>> but the board stops transmitting any data from its serial port after the
>> first message. I will try to analyze this issue a bit more tomorrow.
>
> I double checked, but I see no immediate issues in the patch too. So
> please, if you can analyze this more…

I've spent some time digging into this issue and frankly speaking I
still have no idea WHY it doesn't work (or I seriously mixed something
in the scatterlist principles). However I found a workaround to make it
working. Maybe it will help a bit guessing what happens there.

Here is a workaround:

diff --git a/drivers/tty/serial/msm_serial.c
b/drivers/tty/serial/msm_serial.c
index ae7a8e3cf467..fd3f3bf03f33 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -169,6 +169,7 @@ struct msm_dma {
                } rx;
                struct scatterlist tx_sg;
        };
+       int                     mapped;
        dma_cookie_t            cookie;
        u32                     enable_bit;
        struct dma_async_tx_descriptor  *desc;
@@ -278,6 +279,7 @@ static void msm_stop_dma(struct uart_port *port,
struct msm_dma *dma)
                if (dma->dir == DMA_TO_DEVICE) {
                        dma_unmap_sg(dev, &dma->tx_sg, 1, dma->dir);
                        sg_init_table(&dma->tx_sg, 1);
+                       dma->mapped = 0;
                } else
                        dma_unmap_single(dev, dma->rx.phys, mapped,
dma->dir);
        }
@@ -434,7 +436,7 @@ static void msm_start_tx(struct uart_port *port)
        struct msm_dma *dma = &msm_port->tx_dma;

        /* Already started in DMA mode */
-       if (sg_dma_len(&dma->tx_sg))
+       if (dma->mapped)
                return;

        msm_port->imr |= MSM_UART_IMR_TXLEV;
@@ -462,7 +464,7 @@ static void msm_complete_tx_dma(void *args)
        uart_port_lock_irqsave(port, &flags);

        /* Already stopped */
-       if (!sg_dma_len(&dma->tx_sg))
+       if (!dma->mapped)
                goto done;

        dmaengine_tx_status(dma->chan, dma->cookie, &state);
@@ -481,6 +483,7 @@ static void msm_complete_tx_dma(void *args)
        count = sg_dma_len(&dma->tx_sg) - state.residue;
        uart_xmit_advance(port, count);
        sg_init_table(&dma->tx_sg, 1);
+       dma->mapped = 0;

        /* Restore "Tx FIFO below watermark" interrupt */
        msm_port->imr |= MSM_UART_IMR_TXLEV;
@@ -522,6 +525,7 @@ static int msm_handle_tx_dma(struct msm_port
*msm_port, unsigned int count)
        dma->desc->callback_param = msm_port;

        dma->cookie = dmaengine_submit(dma->desc);
+       dma->mapped = 1;
        ret = dma_submit_error(dma->cookie);
        if (ret)
                goto unmap;
@@ -549,6 +553,7 @@ static int msm_handle_tx_dma(struct msm_port
*msm_port, unsigned int count)
 unmap:
        dma_unmap_sg(port->dev, &dma->tx_sg, 1, dma->dir);
        sg_init_table(&dma->tx_sg, 1);
+       dma->mapped = 0;
        return ret;
 }


Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland


2024-04-17 10:23:03

by Marek Szyprowski

[permalink] [raw]
Subject: Re: [PATCH 12/15] tty: serial: switch from circ_buf to kfifo

On 17.04.2024 12:08, Anders Roxell wrote:
> On 2024-04-15 15:28, Jiri Slaby wrote:
>> On 15. 04. 24, 14:58, Marek Szyprowski wrote:
>>> On 05.04.2024 08:08, Jiri Slaby (SUSE) wrote:
>>>> Switch from struct circ_buf to proper kfifo. kfifo provides much better
>>>> API, esp. when wrap-around of the buffer needs to be taken into account.
>>>> Look at pl011_dma_tx_refill() or cpm_uart_tx_pump() changes for example.
>>>>
>>>> Kfifo API can also fill in scatter-gather DMA structures, so it easier
>>>> for that use case too. Look at lpuart_dma_tx() for example. Note that
>>>> not all drivers can be converted to that (like atmel_serial), they
>>>> handle DMA specially.
>>>>
>>>> Note that usb-serial uses kfifo for TX for ages.
>>>>
>>>> omap needed a bit more care as it needs to put a char into FIFO to start
>>>> the DMA transfer when OMAP_DMA_TX_KICK is set. In that case, we have to
>>>> do kfifo_dma_out_prepare twice: once to find out the tx_size (to find
>>>> out if it is worths to do DMA at all -- size >= 4), the second time for
>>>> the actual transfer.
>>>>
>>>> All traces of circ_buf are removed from serial_core.h (and its struct
>>>> uart_state).
>>>>
>>>> Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
>>>> ...
>>> This patch landed in linux-next as commit 1788cf6a91d9 ("tty: serial:
>>> switch from circ_buf to kfifo"). Unfortunately it breaks UART operation
>>> on thr Amlogic Meson based boards (drivers/tty/serial/meson_uart.c
>>> driver) and Qualcomm RB5 board (drivers/tty/serial/qcom_geni_serial.c).
>>> Once the init process is started, a complete garbage is printed to the
>>> serial console. Here is an example how it looks:
>> Oh my!
>>
>> Both drivers move the tail using both kfifo and uart_xmit_advance()
>> interfaces. Bah. Does it help to remove that uart_xmit_advance() for both of
>> them? (TX stats will be broken.)
>>
>> Users of uart_port_tx() are not affected.
>>
>> This is my fault when merging uart_xmit_advance() with this series.
>>
> I'm trying to run on two dragonboard devices db410c and db845c and both
> fails to boot see the boot failure from db845c [1], linux-next tag: next-20240415.
> I tried to apply the patch [2] (that you proposed in this thread) ontop of next-20240415. However, that didn't
> help bootlog on db845c [3].

This is a different issue, which I've reported 2 days ago. See the
following thread:

https://lore.kernel.org/all/[email protected]/


Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland


2024-04-17 10:51:04

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCH 11/15] tty: msm_serial: use dmaengine_prep_slave_sg()

On 17. 04. 24, 12:15, Marek Szyprowski wrote:
> Hi Jiri,
>
> On 16.04.2024 12:23, Jiri Slaby wrote:
>> On 15. 04. 24, 23:17, Marek Szyprowski wrote:
>>> On 05.04.2024 08:08, Jiri Slaby (SUSE) wrote:
>>>> This is a preparatory for the serial-to-kfifo switch. kfifo understands
>>>> only scatter-gatter approach, so switch to that.
>>>>
>>>> No functional change intended, it's just dmaengine_prep_slave_single()
>>>> inline expanded.
>>>>
>>>> And in this case, switch from dma_map_single() to dma_map_sg() too.
>>>> This
>>>> needs struct msm_dma changes. I split the rx and tx parts into an
>>>> union.
>>>> TX is now struct scatterlist, RX remains the old good phys-virt-count
>>>> triple.
>>>>
>>>> Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
>>>> Cc: Bjorn Andersson <[email protected]>
>>>> Cc: Konrad Dybcio <[email protected]>
>>>> Cc: [email protected]
>>>
>>> I've just found that this patch broke UART operation on DragonBoard
>>> 410c. I briefly checked and didn't notice anything obviously wrong here,
>>> but the board stops transmitting any data from its serial port after the
>>> first message. I will try to analyze this issue a bit more tomorrow.
>>
>> I double checked, but I see no immediate issues in the patch too. So
>> please, if you can analyze this more…
>
> I've spent some time digging into this issue and frankly speaking I
> still have no idea WHY it doesn't work (or I seriously mixed something
> in the scatterlist principles). However I found a workaround to make it
> working. Maybe it will help a bit guessing what happens there.
..
> @@ -434,7 +436,7 @@ static void msm_start_tx(struct uart_port *port)
>         struct msm_dma *dma = &msm_port->tx_dma;
>
>         /* Already started in DMA mode */
> -       if (sg_dma_len(&dma->tx_sg))
> +       if (dma->mapped)

Thanks for looking into this.

I was hesitant if I should use a flag. I should have, apparently.

Quick question:
What's value of CONFIG_NEED_SG_DMA_LENGTH in your .config?

thanks,
--
js
suse labs


2024-04-17 11:19:38

by Anders Roxell

[permalink] [raw]
Subject: Re: [PATCH 12/15] tty: serial: switch from circ_buf to kfifo

On Wed, 17 Apr 2024 at 12:20, Marek Szyprowski <[email protected]> wrote:
>
> On 17.04.2024 12:08, Anders Roxell wrote:
> > On 2024-04-15 15:28, Jiri Slaby wrote:
> >> On 15. 04. 24, 14:58, Marek Szyprowski wrote:
> >>> On 05.04.2024 08:08, Jiri Slaby (SUSE) wrote:
> >>>> Switch from struct circ_buf to proper kfifo. kfifo provides much better
> >>>> API, esp. when wrap-around of the buffer needs to be taken into account.
> >>>> Look at pl011_dma_tx_refill() or cpm_uart_tx_pump() changes for example.
> >>>>
> >>>> Kfifo API can also fill in scatter-gather DMA structures, so it easier
> >>>> for that use case too. Look at lpuart_dma_tx() for example. Note that
> >>>> not all drivers can be converted to that (like atmel_serial), they
> >>>> handle DMA specially.
> >>>>
> >>>> Note that usb-serial uses kfifo for TX for ages.
> >>>>
> >>>> omap needed a bit more care as it needs to put a char into FIFO to start
> >>>> the DMA transfer when OMAP_DMA_TX_KICK is set. In that case, we have to
> >>>> do kfifo_dma_out_prepare twice: once to find out the tx_size (to find
> >>>> out if it is worths to do DMA at all -- size >= 4), the second time for
> >>>> the actual transfer.
> >>>>
> >>>> All traces of circ_buf are removed from serial_core.h (and its struct
> >>>> uart_state).
> >>>>
> >>>> Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
> >>>> ...
> >>> This patch landed in linux-next as commit 1788cf6a91d9 ("tty: serial:
> >>> switch from circ_buf to kfifo"). Unfortunately it breaks UART operation
> >>> on thr Amlogic Meson based boards (drivers/tty/serial/meson_uart.c
> >>> driver) and Qualcomm RB5 board (drivers/tty/serial/qcom_geni_serial.c).
> >>> Once the init process is started, a complete garbage is printed to the
> >>> serial console. Here is an example how it looks:
> >> Oh my!
> >>
> >> Both drivers move the tail using both kfifo and uart_xmit_advance()
> >> interfaces. Bah. Does it help to remove that uart_xmit_advance() for both of
> >> them? (TX stats will be broken.)
> >>
> >> Users of uart_port_tx() are not affected.
> >>
> >> This is my fault when merging uart_xmit_advance() with this series.
> >>
> > I'm trying to run on two dragonboard devices db410c and db845c and both
> > fails to boot see the boot failure from db845c [1], linux-next tag: next-20240415.
> > I tried to apply the patch [2] (that you proposed in this thread) ontop of next-20240415. However, that didn't
> > help bootlog on db845c [3].
>
> This is a different issue, which I've reported 2 days ago. See the
> following thread:
>
> https://lore.kernel.org/all/[email protected]/

Oh ok, I did the bisection on db845v, and that led me to this
patch 1788cf6a91d9 ("tty: serial: switch from circ_buf to kfifo")

Cheers,
Anders

2024-04-17 12:46:11

by Marek Szyprowski

[permalink] [raw]
Subject: Re: [PATCH 11/15] tty: msm_serial: use dmaengine_prep_slave_sg()

On 17.04.2024 12:50, Jiri Slaby wrote:
> On 17. 04. 24, 12:15, Marek Szyprowski wrote:
>> On 16.04.2024 12:23, Jiri Slaby wrote:
>>> On 15. 04. 24, 23:17, Marek Szyprowski wrote:
>>>> On 05.04.2024 08:08, Jiri Slaby (SUSE) wrote:
>>>>> This is a preparatory for the serial-to-kfifo switch. kfifo
>>>>> understands
>>>>> only scatter-gatter approach, so switch to that.
>>>>>
>>>>> No functional change intended, it's just
>>>>> dmaengine_prep_slave_single()
>>>>> inline expanded.
>>>>>
>>>>> And in this case, switch from dma_map_single() to dma_map_sg() too.
>>>>> This
>>>>> needs struct msm_dma changes. I split the rx and tx parts into an
>>>>> union.
>>>>> TX is now struct scatterlist, RX remains the old good phys-virt-count
>>>>> triple.
>>>>>
>>>>> Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
>>>>> Cc: Bjorn Andersson <[email protected]>
>>>>> Cc: Konrad Dybcio <[email protected]>
>>>>> Cc: [email protected]
>>>>
>>>> I've just found that this patch broke UART operation on DragonBoard
>>>> 410c. I briefly checked and didn't notice anything obviously wrong
>>>> here,
>>>> but the board stops transmitting any data from its serial port
>>>> after the
>>>> first message. I will try to analyze this issue a bit more tomorrow.
>>>
>>> I double checked, but I see no immediate issues in the patch too. So
>>> please, if you can analyze this more…
>>
>> I've spent some time digging into this issue and frankly speaking I
>> still have no idea WHY it doesn't work (or I seriously mixed something
>> in the scatterlist principles). However I found a workaround to make it
>> working. Maybe it will help a bit guessing what happens there.
> ...
>> @@ -434,7 +436,7 @@ static void msm_start_tx(struct uart_port *port)
>>           struct msm_dma *dma = &msm_port->tx_dma;
>>
>>           /* Already started in DMA mode */
>> -       if (sg_dma_len(&dma->tx_sg))
>> +       if (dma->mapped)
>
> Thanks for looking into this.
>
> I was hesitant if I should use a flag. I should have, apparently.
>
> Quick question:
> What's value of CONFIG_NEED_SG_DMA_LENGTH in your .config?


CONFIG_NEED_SG_DMA_LENGTH=y


I alse tried to change the "if (dma->mapped)" check in msm_start_tx() to:

1. if (dma->tx_sg.length)

2. if (dma->tx_sg.page_link & ~SG_PAGE_LINK_MASK)

but none of the above worked what is really strange and incomprehensible
for me.


Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland


2024-04-19 07:17:40

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCH 11/15] tty: msm_serial: use dmaengine_prep_slave_sg()

On 17. 04. 24, 14:45, Marek Szyprowski wrote:
> On 17.04.2024 12:50, Jiri Slaby wrote:
>> On 17. 04. 24, 12:15, Marek Szyprowski wrote:
>>> On 16.04.2024 12:23, Jiri Slaby wrote:
>>>> On 15. 04. 24, 23:17, Marek Szyprowski wrote:
>>>>> On 05.04.2024 08:08, Jiri Slaby (SUSE) wrote:
>>>>>> This is a preparatory for the serial-to-kfifo switch. kfifo
>>>>>> understands
>>>>>> only scatter-gatter approach, so switch to that.
>>>>>>
>>>>>> No functional change intended, it's just
>>>>>> dmaengine_prep_slave_single()
>>>>>> inline expanded.
>>>>>>
>>>>>> And in this case, switch from dma_map_single() to dma_map_sg() too.
>>>>>> This
>>>>>> needs struct msm_dma changes. I split the rx and tx parts into an
>>>>>> union.
>>>>>> TX is now struct scatterlist, RX remains the old good phys-virt-count
>>>>>> triple.
>>>>>>
>>>>>> Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
>>>>>> Cc: Bjorn Andersson <[email protected]>
>>>>>> Cc: Konrad Dybcio <[email protected]>
>>>>>> Cc: [email protected]
>>>>>
>>>>> I've just found that this patch broke UART operation on DragonBoard
>>>>> 410c. I briefly checked and didn't notice anything obviously wrong
>>>>> here,
>>>>> but the board stops transmitting any data from its serial port
>>>>> after the
>>>>> first message. I will try to analyze this issue a bit more tomorrow.
>>>>
>>>> I double checked, but I see no immediate issues in the patch too. So
>>>> please, if you can analyze this more…
>>>
>>> I've spent some time digging into this issue and frankly speaking I
>>> still have no idea WHY it doesn't work (or I seriously mixed something
>>> in the scatterlist principles). However I found a workaround to make it
>>> working. Maybe it will help a bit guessing what happens there.
>> ...
>>> @@ -434,7 +436,7 @@ static void msm_start_tx(struct uart_port *port)
>>>           struct msm_dma *dma = &msm_port->tx_dma;
>>>
>>>           /* Already started in DMA mode */
>>> -       if (sg_dma_len(&dma->tx_sg))
>>> +       if (dma->mapped)
>>
>> Thanks for looking into this.
>>
>> I was hesitant if I should use a flag. I should have, apparently.
>>
>> Quick question:
>> What's value of CONFIG_NEED_SG_DMA_LENGTH in your .config?
>
>
> CONFIG_NEED_SG_DMA_LENGTH=y
>
>
> I alse tried to change the "if (dma->mapped)" check in msm_start_tx() to:
>
> 1. if (dma->tx_sg.length)
>
> 2. if (dma->tx_sg.page_link & ~SG_PAGE_LINK_MASK)
>
> but none of the above worked what is really strange and incomprehensible
> for me.

Thanks. Neither for me. Could you add:
{
static DEFINE_RATELIMIT_STATE(rs,
DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST);
if (dma->mapped != !!sg_dma_len(&dma->tx_sg) &&
__ratelimit(&rs))
printk_deferred(KERN_DEBUG "%s (%d): mapped=%u
dma_len=%u\n",
__func__, __LINE__,
dma->mapped, sg_dma_len(&dma->tx_sg));
}

before each of your 'if (dma->mapped)' to see where sg_dma_len() is
wrong and what is its value in the bad case. I hope I did the logic right.

thanks,
--
js
suse labs


2024-04-19 07:43:59

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCH 11/15] tty: msm_serial: use dmaengine_prep_slave_sg()

On 17. 04. 24, 14:45, Marek Szyprowski wrote:
> I alse tried to change the "if (dma->mapped)" check in msm_start_tx() to:
>
> 1. if (dma->tx_sg.length)
>
> 2. if (dma->tx_sg.page_link & ~SG_PAGE_LINK_MASK)
>
> but none of the above worked what is really strange and incomprehensible
> for me.
>

Aaaah, nevermind, what about this?

Two bugs:
1) dma_map_sg() returns the number of mapped entries. Not zero!
2) And I forgot to zero tx_sg in case of error.

--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -506,8 +506,8 @@ static int msm_handle_tx_dma(struct msm_port
*msm_port, unsigned int count)
kfifo_dma_out_prepare(&tport->xmit_fifo, &dma->tx_sg, 1, count);

ret = dma_map_sg(port->dev, &dma->tx_sg, 1, dma->dir);
- if (ret)
- return ret;
+ if (!ret)
+ goto zero_out;

dma->desc = dmaengine_prep_slave_sg(dma->chan, &dma->tx_sg, 1,
DMA_MEM_TO_DEV,
@@ -548,6 +548,7 @@ static int msm_handle_tx_dma(struct msm_port
*msm_port, unsigned int count)
return 0;
unmap:
dma_unmap_sg(port->dev, &dma->tx_sg, 1, dma->dir);
+zero_out:
sg_init_table(&dma->tx_sg, 1);
return ret;
}


thanks,
--
js
suse labs


2024-04-19 07:53:20

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCH 11/15] tty: msm_serial: use dmaengine_prep_slave_sg()

On 19. 04. 24, 9:43, Jiri Slaby wrote:
> On 17. 04. 24, 14:45, Marek Szyprowski wrote:
>> I alse tried to change the "if (dma->mapped)" check in msm_start_tx() to:
>>
>> 1. if (dma->tx_sg.length)
>>
>> 2. if (dma->tx_sg.page_link & ~SG_PAGE_LINK_MASK)
>>
>> but none of the above worked what is really strange and incomprehensible
>> for me.
>>
>
> Aaaah, nevermind, what about this?
>
> Two bugs:
> 1) dma_map_sg() returns the number of mapped entries. Not zero!
> 2) And I forgot to zero tx_sg in case of error.
>
> --- a/drivers/tty/serial/msm_serial.c
> +++ b/drivers/tty/serial/msm_serial.c
> @@ -506,8 +506,8 @@ static int msm_handle_tx_dma(struct msm_port
> *msm_port, unsigned int count)
>         kfifo_dma_out_prepare(&tport->xmit_fifo, &dma->tx_sg, 1, count);
>
>         ret = dma_map_sg(port->dev, &dma->tx_sg, 1, dma->dir);
> -       if (ret)
> -               return ret;
> +       if (!ret)

Still wrong, ret = -EIO missing in here.

> +               goto zero_out;
>
>         dma->desc = dmaengine_prep_slave_sg(dma->chan, &dma->tx_sg, 1,
>                                                 DMA_MEM_TO_DEV,
> @@ -548,6 +548,7 @@ static int msm_handle_tx_dma(struct msm_port
> *msm_port, unsigned int count)
>         return 0;
>  unmap:
>         dma_unmap_sg(port->dev, &dma->tx_sg, 1, dma->dir);
> +zero_out:
>         sg_init_table(&dma->tx_sg, 1);
>         return ret;
>  }
>
>
> thanks,

--
js
suse labs


2024-04-19 08:02:39

by Marek Szyprowski

[permalink] [raw]
Subject: Re: [PATCH 11/15] tty: msm_serial: use dmaengine_prep_slave_sg()

On 19.04.2024 09:53, Jiri Slaby wrote:
> On 19. 04. 24, 9:43, Jiri Slaby wrote:
>> On 17. 04. 24, 14:45, Marek Szyprowski wrote:
>>> I alse tried to change the "if (dma->mapped)" check in
>>> msm_start_tx() to:
>>>
>>> 1. if (dma->tx_sg.length)
>>>
>>> 2. if (dma->tx_sg.page_link & ~SG_PAGE_LINK_MASK)
>>>
>>> but none of the above worked what is really strange and
>>> incomprehensible
>>> for me.
>>>
>>
>> Aaaah, nevermind, what about this?
>>
>> Two bugs:
>> 1) dma_map_sg() returns the number of mapped entries. Not zero!
>> 2) And I forgot to zero tx_sg in case of error.
>>
>> --- a/drivers/tty/serial/msm_serial.c
>> +++ b/drivers/tty/serial/msm_serial.c
>> @@ -506,8 +506,8 @@ static int msm_handle_tx_dma(struct msm_port
>> *msm_port, unsigned int count)
>>          kfifo_dma_out_prepare(&tport->xmit_fifo, &dma->tx_sg, 1,
>> count);
>>
>>          ret = dma_map_sg(port->dev, &dma->tx_sg, 1, dma->dir);
>> -       if (ret)
>> -               return ret;
>> +       if (!ret)
>
> Still wrong, ret = -EIO missing in here.

"if (ret <= 0)" seems to be better here.

Indeed this fixed the issue. I checked the code many times, but I missed
this dma_map_sg() return value issue.

Tested-by: Marek Szyprowski <[email protected]>

>
>> +               goto zero_out;
>>
>>          dma->desc = dmaengine_prep_slave_sg(dma->chan, &dma->tx_sg, 1,
>>                                                  DMA_MEM_TO_DEV,
>> @@ -548,6 +548,7 @@ static int msm_handle_tx_dma(struct msm_port
>> *msm_port, unsigned int count)
>>          return 0;
>>   unmap:
>>          dma_unmap_sg(port->dev, &dma->tx_sg, 1, dma->dir);
>> +zero_out:
>>          sg_init_table(&dma->tx_sg, 1);
>>          return ret;
>>   }
>>
>>
>> thanks,
>
Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland


2024-04-19 08:09:32

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCH 11/15] tty: msm_serial: use dmaengine_prep_slave_sg()

On 19. 04. 24, 10:00, Marek Szyprowski wrote:
> On 19.04.2024 09:53, Jiri Slaby wrote:
>> On 19. 04. 24, 9:43, Jiri Slaby wrote:
>>> On 17. 04. 24, 14:45, Marek Szyprowski wrote:
>>>> I alse tried to change the "if (dma->mapped)" check in
>>>> msm_start_tx() to:
>>>>
>>>> 1. if (dma->tx_sg.length)
>>>>
>>>> 2. if (dma->tx_sg.page_link & ~SG_PAGE_LINK_MASK)
>>>>
>>>> but none of the above worked what is really strange and
>>>> incomprehensible
>>>> for me.
>>>>
>>>
>>> Aaaah, nevermind, what about this?
>>>
>>> Two bugs:
>>> 1) dma_map_sg() returns the number of mapped entries. Not zero!
>>> 2) And I forgot to zero tx_sg in case of error.
>>>
>>> --- a/drivers/tty/serial/msm_serial.c
>>> +++ b/drivers/tty/serial/msm_serial.c
>>> @@ -506,8 +506,8 @@ static int msm_handle_tx_dma(struct msm_port
>>> *msm_port, unsigned int count)
>>>          kfifo_dma_out_prepare(&tport->xmit_fifo, &dma->tx_sg, 1,
>>> count);
>>>
>>>          ret = dma_map_sg(port->dev, &dma->tx_sg, 1, dma->dir);
>>> -       if (ret)
>>> -               return ret;
>>> +       if (!ret)
>>
>> Still wrong, ret = -EIO missing in here.
>
> "if (ret <= 0)" seems to be better here.

It returns unsigned, so I have a better patch. Will send in a second.

> Indeed this fixed the issue. I checked the code many times, but I missed
> this dma_map_sg() return value issue.

Perfect!

> Tested-by: Marek Szyprowski <[email protected]>

Could you test that one too :)?

thanks,
--
js
suse labs


2024-04-19 08:09:41

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH] serial: msm: check dma_map_sg() return value properly

The -next commit f8fef2fa419f (tty: msm_serial: use
dmaengine_prep_slave_sg()), switched to using dma_map_sg(). But the
return value of dma_map_sg() is special: it returns number of elements
mapped. And not a standard error value.

The commit also forgot to reset dma->tx_sg in case of this failure.

Fix both these mistakes.

Thanks to Marek who helped debugging this.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
Reported-by: Marek Szyprowski <[email protected]>
---
drivers/tty/serial/msm_serial.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index ae7a8e3cf467..0a9c5219df88 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -499,15 +499,18 @@ static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count)
struct uart_port *port = &msm_port->uart;
struct tty_port *tport = &port->state->port;
struct msm_dma *dma = &msm_port->tx_dma;
+ unsigned int mapped;
int ret;
u32 val;

sg_init_table(&dma->tx_sg, 1);
kfifo_dma_out_prepare(&tport->xmit_fifo, &dma->tx_sg, 1, count);

- ret = dma_map_sg(port->dev, &dma->tx_sg, 1, dma->dir);
- if (ret)
- return ret;
+ mapped = dma_map_sg(port->dev, &dma->tx_sg, 1, dma->dir);
+ if (!mapped) {
+ ret = -EIO;
+ goto zero_sg;
+ }

dma->desc = dmaengine_prep_slave_sg(dma->chan, &dma->tx_sg, 1,
DMA_MEM_TO_DEV,
@@ -548,6 +551,7 @@ static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count)
return 0;
unmap:
dma_unmap_sg(port->dev, &dma->tx_sg, 1, dma->dir);
+zero_sg:
sg_init_table(&dma->tx_sg, 1);
return ret;
}
--
2.44.0


2024-04-19 09:03:56

by Marek Szyprowski

[permalink] [raw]
Subject: Re: [PATCH] serial: msm: check dma_map_sg() return value properly

On 19.04.2024 10:09, Jiri Slaby (SUSE) wrote:
> The -next commit f8fef2fa419f (tty: msm_serial: use
> dmaengine_prep_slave_sg()), switched to using dma_map_sg(). But the
> return value of dma_map_sg() is special: it returns number of elements
> mapped. And not a standard error value.
>
> The commit also forgot to reset dma->tx_sg in case of this failure.
>
> Fix both these mistakes.
>
> Thanks to Marek who helped debugging this.
>
> Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
> Reported-by: Marek Szyprowski <[email protected]>

Tested-by: Marek Szyprowski <[email protected]>

> ---
> drivers/tty/serial/msm_serial.c | 10 +++++++---
> 1 file changed, 7 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
> index ae7a8e3cf467..0a9c5219df88 100644
> --- a/drivers/tty/serial/msm_serial.c
> +++ b/drivers/tty/serial/msm_serial.c
> @@ -499,15 +499,18 @@ static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count)
> struct uart_port *port = &msm_port->uart;
> struct tty_port *tport = &port->state->port;
> struct msm_dma *dma = &msm_port->tx_dma;
> + unsigned int mapped;
> int ret;
> u32 val;
>
> sg_init_table(&dma->tx_sg, 1);
> kfifo_dma_out_prepare(&tport->xmit_fifo, &dma->tx_sg, 1, count);
>
> - ret = dma_map_sg(port->dev, &dma->tx_sg, 1, dma->dir);
> - if (ret)
> - return ret;
> + mapped = dma_map_sg(port->dev, &dma->tx_sg, 1, dma->dir);
> + if (!mapped) {
> + ret = -EIO;
> + goto zero_sg;
> + }
>
> dma->desc = dmaengine_prep_slave_sg(dma->chan, &dma->tx_sg, 1,
> DMA_MEM_TO_DEV,
> @@ -548,6 +551,7 @@ static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count)
> return 0;
> unmap:
> dma_unmap_sg(port->dev, &dma->tx_sg, 1, dma->dir);
> +zero_sg:
> sg_init_table(&dma->tx_sg, 1);
> return ret;
> }

Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland


2024-04-19 15:13:22

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH 00/15] tty: serial: switch from circ_buf to kfifo

Hi Jiri,

On 05/04/2024 08:08, Jiri Slaby (SUSE) wrote:
> This series switches tty serial layer to use kfifo instead of circ_buf.
>
> The reasoning can be found in the switching patch in this series:
> """
> Switch from struct circ_buf to proper kfifo. kfifo provides much better
> API, esp. when wrap-around of the buffer needs to be taken into account.
> Look at pl011_dma_tx_refill() or cpm_uart_tx_pump() changes for example.
>
> Kfifo API can also fill in scatter-gather DMA structures, so it easier
> for that use case too. Look at lpuart_dma_tx() for example. Note that
> not all drivers can be converted to that (like atmel_serial), they
> handle DMA specially.
>
> Note that usb-serial uses kfifo for TX for ages.
> """
>
> Cc: Al Cooper <[email protected]>
> Cc: Alexander Shiyan <[email protected]>
> Cc: Alexandre Belloni <[email protected]>
> Cc: Alexandre Torgue <[email protected]>
> Cc: Alim Akhtar <[email protected]>
> Cc: Andrew Morton <[email protected]>
> Cc: "Aneesh Kumar K.V" <[email protected]>
> Cc: AngeloGioacchino Del Regno <[email protected]>
> Cc: Baolin Wang <[email protected]>
> Cc: Baruch Siach <[email protected]>
> Cc: Bjorn Andersson <[email protected]>
> Cc: Claudiu Beznea <[email protected]>
> Cc: "David S. Miller" <[email protected]>
> Cc: Fabio Estevam <[email protected]>
> Cc: Hammer Hsieh <[email protected]>
> Cc: "Christian König" <[email protected]>
> Cc: Christophe Leroy <[email protected]>
> Cc: Chunyan Zhang <[email protected]>
> Cc: Jerome Brunet <[email protected]>
> Cc: Jonathan Hunter <[email protected]>
> Cc: Kevin Hilman <[email protected]>
> Cc: Konrad Dybcio <[email protected]>
> Cc: Krzysztof Kozlowski <[email protected]>
> Cc: Kumaravel Thiagarajan <[email protected]>
> Cc: Laxman Dewangan <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: "Maciej W. Rozycki" <[email protected]>
> Cc: Manivannan Sadhasivam <[email protected]>
> Cc: Martin Blumenstingl <[email protected]>
> Cc: Matthias Brugger <[email protected]>
> Cc: Maxime Coquelin <[email protected]>
> Cc: Michael Ellerman <[email protected]>
> Cc: Michal Simek <[email protected]>
> Cc: "Naveen N. Rao" <[email protected]>
> Cc: Neil Armstrong <[email protected]>
> Cc: Nicolas Ferre <[email protected]>
> Cc: Nicholas Piggin <[email protected]>
> Cc: Orson Zhai <[email protected]>
> Cc: "Pali Rohár" <[email protected]>
> Cc: Patrice Chotard <[email protected]>
> Cc: Peter Korsgaard <[email protected]>
> Cc: Richard Genoud <[email protected]>
> Cc: Russell King <[email protected]>
> Cc: Sascha Hauer <[email protected]>
> Cc: Shawn Guo <[email protected]>
> Cc: Stefani Seibold <[email protected]>
> Cc: Sumit Semwal <[email protected]>
> Cc: Taichi Sugaya <[email protected]>
> Cc: Takao Orito <[email protected]>
> Cc: Tharun Kumar P <[email protected]>
> Cc: Thierry Reding <[email protected]>
> Cc: Timur Tabi <[email protected]>
> Cc: Vineet Gupta <[email protected]>
>
> Jiri Slaby (SUSE) (15):
> kfifo: drop __kfifo_dma_out_finish_r()
> kfifo: introduce and use kfifo_skip_count()
> kfifo: add kfifo_out_linear{,_ptr}()
> kfifo: remove support for physically non-contiguous memory
> kfifo: rename l to len_to_end in setup_sgl()
> kfifo: pass offset to setup_sgl_buf() instead of a pointer
> kfifo: add kfifo_dma_out_prepare_mapped()
> kfifo: fix typos in kernel-doc
> tty: 8250_dma: use dmaengine_prep_slave_sg()
> tty: 8250_omap: use dmaengine_prep_slave_sg()
> tty: msm_serial: use dmaengine_prep_slave_sg()
> tty: serial: switch from circ_buf to kfifo
> tty: atmel_serial: use single DMA mapping for TX
> tty: atmel_serial: define macro for RX size
> tty: atmel_serial: use single DMA mapping for RX
>
> drivers/tty/serial/8250/8250_bcm7271.c | 14 +--
> drivers/tty/serial/8250/8250_core.c | 3 +-
> drivers/tty/serial/8250/8250_dma.c | 31 +++--
> drivers/tty/serial/8250/8250_exar.c | 5 +-
> drivers/tty/serial/8250/8250_mtk.c | 2 +-
> drivers/tty/serial/8250/8250_omap.c | 48 +++++---
> drivers/tty/serial/8250/8250_pci1xxxx.c | 50 ++++----
> drivers/tty/serial/8250/8250_port.c | 22 ++--
> drivers/tty/serial/amba-pl011.c | 46 +++-----
> drivers/tty/serial/ar933x_uart.c | 15 ++-
> drivers/tty/serial/arc_uart.c | 8 +-
> drivers/tty/serial/atmel_serial.c | 150 +++++++++++-------------
> drivers/tty/serial/clps711x.c | 12 +-
> drivers/tty/serial/cpm_uart.c | 20 ++--
> drivers/tty/serial/digicolor-usart.c | 12 +-
> drivers/tty/serial/dz.c | 13 +-
> drivers/tty/serial/fsl_linflexuart.c | 17 +--
> drivers/tty/serial/fsl_lpuart.c | 39 +++---
> drivers/tty/serial/icom.c | 25 +---
> drivers/tty/serial/imx.c | 54 ++++-----
> drivers/tty/serial/ip22zilog.c | 26 ++--
> drivers/tty/serial/jsm/jsm_cls.c | 29 ++---
> drivers/tty/serial/jsm/jsm_neo.c | 38 ++----
> drivers/tty/serial/max3100.c | 14 +--
> drivers/tty/serial/max310x.c | 35 +++---
> drivers/tty/serial/men_z135_uart.c | 26 ++--
> drivers/tty/serial/meson_uart.c | 11 +-
> drivers/tty/serial/milbeaut_usio.c | 15 +--
> drivers/tty/serial/msm_serial.c | 114 +++++++++---------
> drivers/tty/serial/mvebu-uart.c | 8 +-
> drivers/tty/serial/mxs-auart.c | 23 +---
> drivers/tty/serial/pch_uart.c | 21 ++--
> drivers/tty/serial/pic32_uart.c | 15 ++-
> drivers/tty/serial/pmac_zilog.c | 24 ++--
> drivers/tty/serial/qcom_geni_serial.c | 36 +++---
> drivers/tty/serial/rda-uart.c | 17 +--
> drivers/tty/serial/samsung_tty.c | 54 +++++----
> drivers/tty/serial/sb1250-duart.c | 13 +-
> drivers/tty/serial/sc16is7xx.c | 40 +++----
> drivers/tty/serial/sccnxp.c | 16 ++-
> drivers/tty/serial/serial-tegra.c | 43 ++++---
> drivers/tty/serial/serial_core.c | 56 ++++-----
> drivers/tty/serial/serial_port.c | 2 +-
> drivers/tty/serial/sh-sci.c | 51 ++++----
> drivers/tty/serial/sprd_serial.c | 20 ++--
> drivers/tty/serial/st-asc.c | 4 +-
> drivers/tty/serial/stm32-usart.c | 52 ++++----
> drivers/tty/serial/sunhv.c | 35 +++---
> drivers/tty/serial/sunplus-uart.c | 16 +--
> drivers/tty/serial/sunsab.c | 30 ++---
> drivers/tty/serial/sunsu.c | 15 +--
> drivers/tty/serial/sunzilog.c | 27 ++---
> drivers/tty/serial/tegra-tcu.c | 10 +-
> drivers/tty/serial/timbuart.c | 17 ++-
> drivers/tty/serial/uartlite.c | 13 +-
> drivers/tty/serial/ucc_uart.c | 20 ++--
> drivers/tty/serial/xilinx_uartps.c | 20 ++--
> drivers/tty/serial/zs.c | 13 +-
> include/linux/kfifo.h | 143 ++++++++++++++++------
> include/linux/serial_core.h | 49 +++++---
> lib/kfifo.c | 107 +++++++++--------
> 61 files changed, 944 insertions(+), 960 deletions(-)
>

This patchset has at least broken all Amlogic and Qualcomm boards so far, only part of them were fixed in next- but this serie has been merged in v1 with no serious testing and should've been dropped immediately when the first regressions were reported.

Thanks,
Neil

2024-04-20 05:42:45

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH 00/15] tty: serial: switch from circ_buf to kfifo

On Fri, Apr 19, 2024 at 05:12:28PM +0200, Neil Armstrong wrote:
> This patchset has at least broken all Amlogic and Qualcomm boards so
> far, only part of them were fixed in next- but this serie has been
> merged in v1 with no serious testing and should've been dropped
> immediately when the first regressions were reported.

What is not yet fixed with the recent patch that was just sent to the
list?

Doing core changes like this is hard, I have seen no lack of willingness
to fix reported problems or major breakages that would deserve a revert.

greg k-h

2024-04-22 05:58:12

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCH 00/15] tty: serial: switch from circ_buf to kfifo

Hi,

On 19. 04. 24, 17:12, Neil Armstrong wrote:
> On 05/04/2024 08:08, Jiri Slaby (SUSE) wrote:
>> This series switches tty serial layer to use kfifo instead of circ_buf.
>>
>> The reasoning can be found in the switching patch in this series:
>> """
>> Switch from struct circ_buf to proper kfifo. kfifo provides much better
>> API, esp. when wrap-around of the buffer needs to be taken into account.
>> Look at pl011_dma_tx_refill() or cpm_uart_tx_pump() changes for example.
>>
>> Kfifo API can also fill in scatter-gather DMA structures, so it easier
>> for that use case too. Look at lpuart_dma_tx() for example. Note that
>> not all drivers can be converted to that (like atmel_serial), they
>> handle DMA specially.
>>
>> Note that usb-serial uses kfifo for TX for ages.
>> """
..
> This patchset has at least broken all Amlogic and Qualcomm boards so
> far, only part of them were fixed in next-

So are there still not fixed problems yet?

> but this serie has been
> merged in v1

Ugh, are you saying that v1 patches are not worth taking? That doesn't
fit with my experience.

> with no serious testing

Sadly, everyone had a chance to test the series:
https://lore.kernel.org/all/[email protected]/
for more than two weeks before I sent this version for inclusion. And
then it took another 5 days till this series appeared in -next. But
noone with this HW apparently cared enough back then. I'd wish they
(you) didn't. Maybe next time, people will listen more carefully:
===
This is Request for Testing as I cannot test all the changes
(obviously). So please test your HW's serial properly.
===

> and should've been dropped
> immediately when the first regressions were reported.

Provided the RFT was mostly ignored (anyone who tested that here, or I
only wasted my time?), how exactly would dropping help me finding
potential issues in the series? In the end, noone is running -next in
production, so glitches are sort of expected, right? And I believe I
smashed them quickly enough (despite I was sidetracked to handle the
n_gsm issue). But I might be wrong, as usual.

So no, dropping is not helping moving forward, actions taken by e.g.
Marek Szyprowski <[email protected]> do, IMNSHO.

thanks,
--
js
suse labs


2024-04-22 06:46:04

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCH 12/15] tty: serial: switch from circ_buf to kfifo

On 17. 04. 24, 13:19, Anders Roxell wrote:
>> I'm trying to run on two dragonboard devices db410c and db845c and both
>>> fails to boot see the boot failure from db845c [1], linux-next tag: next-20240415.
>>> I tried to apply the patch [2] (that you proposed in this thread) ontop of next-20240415. However, that didn't
>>> help bootlog on db845c [3].
>>
>> This is a different issue, which I've reported 2 days ago. See the
>> following thread:
>>
>> https://lore.kernel.org/all/[email protected]/
>
> Oh ok, I did the bisection on db845v, and that led me to this
> patch 1788cf6a91d9 ("tty: serial: switch from circ_buf to kfifo")

Could you re-test with the today's -next?

In particular, with this commit:
commit f70f95b485d78838ad28dbec804b986d11ad7bb0
Author: Jiri Slaby (SUSE) <[email protected]>
Date: Fri Apr 19 10:09:31 2024 +0200

serial: msm: check dma_map_sg() return value properly


thanks,
--
js
suse labs


2024-04-22 07:45:40

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH 00/15] tty: serial: switch from circ_buf to kfifo

Hi Jiri,

On 22/04/2024 07:51, Jiri Slaby wrote:
> Hi,
>
> On 19. 04. 24, 17:12, Neil Armstrong wrote:
>> On 05/04/2024 08:08, Jiri Slaby (SUSE) wrote:
>>> This series switches tty serial layer to use kfifo instead of circ_buf.
>>>
>>> The reasoning can be found in the switching patch in this series:
>>> """
>>> Switch from struct circ_buf to proper kfifo. kfifo provides much better
>>> API, esp. when wrap-around of the buffer needs to be taken into account.
>>> Look at pl011_dma_tx_refill() or cpm_uart_tx_pump() changes for example.
>>>
>>> Kfifo API can also fill in scatter-gather DMA structures, so it easier
>>> for that use case too. Look at lpuart_dma_tx() for example. Note that
>>> not all drivers can be converted to that (like atmel_serial), they
>>> handle DMA specially.
>>>
>>> Note that usb-serial uses kfifo for TX for ages.
>>> """
> ...
>> This patchset has at least broken all Amlogic and Qualcomm boards so far, only part of them were fixed in next-
>
> So are there still not fixed problems yet?

My last ci run on next-20240419 was still failing on db410c.

>
>> but this serie has been merged in v1
>
> Ugh, are you saying that v1 patches are not worth taking? That doesn't fit with my experience.

In my experience, most of my patches are taken in v2, it's not an uncommon thing to have more versions, especially when touching core subsystems.

>
>> with no serious testing
>
> Sadly, everyone had a chance to test the series:
>   https://lore.kernel.org/all/[email protected]/
> for more than two weeks before I sent this version for inclusion. And then it took another 5 days till this series appeared in -next. But noone with this HW apparently cared enough back then. I'd wish they (you) didn't. Maybe next time, people will listen more carefully:
> ===
> This is Request for Testing as I cannot test all the changes
> (obviously). So please test your HW's serial properly.
> ===

This RFT was sent during the merge window, only a few people looks at the list between those 2 weeks.

>
>> and should've been dropped immediately when the first regressions were reported.
>
> Provided the RFT was mostly ignored (anyone who tested that here, or I only wasted my time?), how exactly would dropping help me finding potential issues in the series? In the end, noone is running -next in production, so glitches are sort of expected, right? And I believe I smashed them quickly enough (despite I was sidetracked to handle the n_gsm issue). But I might be wrong, as usual.

So since it was ignored, it's ok to apply it as-is ??????

>
> So no, dropping is not helping moving forward, actions taken by e.g. Marek Szyprowski <[email protected]> do, IMNSHO.

well thanks to Marek, but most of Qualcomm maintainers and myself were in EOSS in Seattle for the week and came back home in Saturday, and we were busy. Hopefully Marek was available.

>
> thanks,

Neil



2024-04-22 07:51:13

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH 00/15] tty: serial: switch from circ_buf to kfifo

Hi Greg,

On 20/04/2024 07:42, Greg KH wrote:
> On Fri, Apr 19, 2024 at 05:12:28PM +0200, Neil Armstrong wrote:
>> This patchset has at least broken all Amlogic and Qualcomm boards so
>> far, only part of them were fixed in next- but this serie has been
>> merged in v1 with no serious testing and should've been dropped
>> immediately when the first regressions were reported.
>
> What is not yet fixed with the recent patch that was just sent to the
> list?
>
> Doing core changes like this is hard, I have seen no lack of willingness
> to fix reported problems or major breakages that would deserve a revert.

It broken all Amlogic and Qualcomm boards, are we sure it didn't break other systems that are not CI tested on -next ?

This serie clearly deserved a v2, patch 11 wasn't seriously reviewed, and it deserved a ping on the RFT before sending a v1.

I don't understand why speeding up this changeset and applying it without any reviews nor tests was so important.

Thanks,
Neil
>
> greg k-h


2024-04-22 10:12:10

by Anders Roxell

[permalink] [raw]
Subject: Re: [PATCH 12/15] tty: serial: switch from circ_buf to kfifo

On Mon, 22 Apr 2024 at 08:45, Jiri Slaby <[email protected]> wrote:
>
> On 17. 04. 24, 13:19, Anders Roxell wrote:
> >> I'm trying to run on two dragonboard devices db410c and db845c and both
> >>> fails to boot see the boot failure from db845c [1], linux-next tag: next-20240415.
> >>> I tried to apply the patch [2] (that you proposed in this thread) ontop of next-20240415. However, that didn't
> >>> help bootlog on db845c [3].
> >>
> >> This is a different issue, which I've reported 2 days ago. See the
> >> following thread:
> >>
> >> https://lore.kernel.org/all/[email protected]/
> >
> > Oh ok, I did the bisection on db845v, and that led me to this
> > patch 1788cf6a91d9 ("tty: serial: switch from circ_buf to kfifo")
>
> Could you re-test with the today's -next?

Tested todays next and it boots fine.

Tested-by: Anders Roxell <[email protected]>

Cheers,
Anders

>
> In particular, with this commit:
> commit f70f95b485d78838ad28dbec804b986d11ad7bb0
> Author: Jiri Slaby (SUSE) <[email protected]>
> Date: Fri Apr 19 10:09:31 2024 +0200
>
> serial: msm: check dma_map_sg() return value properly
>
>
> thanks,
> --
> js
> suse labs
>