Unverified Commit d9d4eb85 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!12545 Fix TX fifo corruption in patch [3

Merge Pull Request from: @ci-robot 
 
PR sync from: Zhao Yipeng <zhaoyipeng5@huawei.com>
https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/WWMYQY77ETFVI4U3UE677MMJ53EIWZHT/ 
Patch [1-2] are pre patches.
Patch [3] fix TX fifo corruption.

Hugo Villeneuve (2):
  [Backport] serial: sc16is7xx: refactor FIFO access functions to
    increase commonality
  [Backport] serial: sc16is7xx: fix TX fifo corruption

Jiri Slaby (SUSE) (1):
  [Backport] kfifo: add kfifo_out_linear{,_ptr}()


-- 
2.34.1
 
https://gitee.com/src-openeuler/kernel/issues/IAOXYG 
 
Link:https://gitee.com/openeuler/kernel/pulls/12545

 

Reviewed-by: default avatarZhang Peng <zhangpeng362@huawei.com>
Signed-off-by: default avatarZhang Peng <zhangpeng362@huawei.com>
parents 602a68e1 658575b0
Loading
Loading
Loading
Loading
+28 −39
Original line number Diff line number Diff line
@@ -329,6 +329,7 @@ struct sc16is7xx_one {
	struct kthread_work		reg_work;
	struct kthread_delayed_work	ms_work;
	struct sc16is7xx_one_config	config;
	unsigned char           buf[SC16IS7XX_FIFO_SIZE]; /* Rx buffer. */
	bool				irda_mode;
	unsigned int			old_mctrl;
};
@@ -341,7 +342,6 @@ struct sc16is7xx_port {
	unsigned long			gpio_valid_mask;
#endif
	u8				mctrl_mask;
	unsigned char			buf[SC16IS7XX_FIFO_SIZE];
	struct kthread_worker		kworker;
	struct task_struct		*kworker_task;
	struct sc16is7xx_one		p[];
@@ -378,17 +378,15 @@ static void sc16is7xx_port_write(struct uart_port *port, u8 reg, u8 val)
	regmap_write(one->regmap, reg, val);
}

static void sc16is7xx_fifo_read(struct uart_port *port, unsigned int rxlen)
static void sc16is7xx_fifo_read(struct uart_port *port, u8 *rxbuf, unsigned int rxlen)
{
	struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
	struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);

	regmap_noinc_read(one->regmap, SC16IS7XX_RHR_REG, s->buf, rxlen);
	regmap_noinc_read(one->regmap, SC16IS7XX_RHR_REG, rxbuf, rxlen);
}

static void sc16is7xx_fifo_write(struct uart_port *port, u8 to_send)
static void sc16is7xx_fifo_write(struct uart_port *port, u8 *txbuf, u8 to_send)
{
	struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
	struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);

	/*
@@ -398,7 +396,7 @@ static void sc16is7xx_fifo_write(struct uart_port *port, u8 to_send)
	if (unlikely(!to_send))
		return;

	regmap_noinc_write(one->regmap, SC16IS7XX_THR_REG, s->buf, to_send);
	regmap_noinc_write(one->regmap, SC16IS7XX_THR_REG, txbuf, to_send);
}

static void sc16is7xx_port_update(struct uart_port *port, u8 reg,
@@ -568,18 +566,18 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud)
static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen,
				unsigned int iir)
{
	struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
	struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
	unsigned int lsr = 0, bytes_read, i;
	bool read_lsr = (iir == SC16IS7XX_IIR_RLSE_SRC) ? true : false;
	u8 ch, flag;

	if (unlikely(rxlen >= sizeof(s->buf))) {
	if (unlikely(rxlen >= sizeof(one->buf))) {
		dev_warn_ratelimited(port->dev,
				     "ttySC%i: Possible RX FIFO overrun: %d\n",
				     port->line, rxlen);
		port->icount.buf_overrun++;
		/* Ensure sanity of RX level */
		rxlen = sizeof(s->buf);
		rxlen = sizeof(one->buf);
	}

	while (rxlen) {
@@ -592,10 +590,10 @@ static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen,
			lsr = 0;

		if (read_lsr) {
			s->buf[0] = sc16is7xx_port_read(port, SC16IS7XX_RHR_REG);
			one->buf[0] = sc16is7xx_port_read(port, SC16IS7XX_RHR_REG);
			bytes_read = 1;
		} else {
			sc16is7xx_fifo_read(port, rxlen);
			sc16is7xx_fifo_read(port, one->buf, rxlen);
			bytes_read = rxlen;
		}

@@ -628,7 +626,7 @@ static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen,
		}

		for (i = 0; i < bytes_read; ++i) {
			ch = s->buf[i];
			ch = one->buf[i];
			if (uart_handle_sysrq_char(port, ch))
				continue;

@@ -646,10 +644,10 @@ 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;
	unsigned char *tail;

	if (unlikely(port->x_char)) {
		sc16is7xx_port_write(port, SC16IS7XX_THR_REG, port->x_char);
@@ -658,17 +656,14 @@ 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 size of TX FIFO */
	/* 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,
@@ -676,22 +671,16 @@ static void sc16is7xx_handle_tx(struct uart_port *port)
			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, to_send);
	}
	txlen = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, txlen);
	sc16is7xx_fifo_write(port, tail, txlen);
	uart_xmit_advance(port, 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);
+63 −0
Original line number Diff line number Diff line
@@ -828,6 +828,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);

@@ -857,6 +914,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);

@@ -888,6 +948,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
+26 −0
Original line number Diff line number Diff line
@@ -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)
{