uart 驱动之用户发送与中断发送的互斥

linux 内核中  发送环在判空等操作时,如何确保 原子性。用户在调用写接口写数据时,head的值会变动,而此时在中断里面判空,如何保证准确性。

1) 例如 驱动代码,中断发送的代码  serial-tegra.c    drivers\tty\serial    

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

	for (i = 0; i < max_bytes; i++) {
		BUG_ON(uart_circ_empty(xmit));    //这里判空,也没有保护,如果head此时增加了?
		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);
		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);  //驱动取数据,从tail获取
		tup->uport.icount.tx++;
	}
}

2) uart写缓存的代码, (serial_core.c    drivers\tty\serial   )  ,这里对整个写入过程都加了锁。

static int uart_write(struct tty_struct *tty,
					const unsigned char *buf, int count)
{
	struct uart_state *state = tty->driver_data;
	struct uart_port *port;
	struct circ_buf *circ;
	unsigned long flags;
	int c, ret = 0;

	/*
	 * This means you called this function _after_ the port was
	 * closed.  No cookie for you.
	 */
	if (!state) {
		WARN_ON(1);
		return -EL3HLT;
	}

	port = uart_port_lock(state, flags);  //这里加锁了
	circ = &state->xmit;
	if (!circ->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;
	}

	__uart_start(tty);
	uart_port_unlock(port, flags); //到这里才释放锁  
	return ret;
}
#define uart_port_lock(state, flags)					\
	({								\
		struct uart_port *__uport = uart_port_ref(state);	\   //此处为锁的代码也是uart_port中的lock
		if (__uport)						\
			spin_lock_irqsave(&__uport->lock, flags);	\
		__uport;						\
	}) 

3) 回到中断的话题,在中断处理的入口代码

static irqreturn_t tegra_uart_isr(int irq, void *data)
{
	struct tegra_uart_port *tup = data;
	struct uart_port *u = &tup->uport;
	unsigned long iir;
	unsigned long ier;
	bool is_rx_start = false;
	bool is_rx_int = false;
	unsigned long flags;

	spin_lock_irqsave(&u->lock, flags);  //这里加锁了,采用的uart_port的lock
	while (1) {
		iir = tegra_uart_read(tup, UART_IIR);
		if (iir & UART_IIR_NO_INT) {
			if (!tup->use_rx_pio && is_rx_int) {
				tegra_uart_handle_rx_dma(tup);
				if (tup->rx_in_progress) {
					ier = tup->ier_shadow;
					ier |= (UART_IER_RLSI | UART_IER_RTOIE |
						TEGRA_UART_IER_EORD | UART_IER_RDI);
					tup->ier_shadow = ier;
					tegra_uart_write(tup, ier, UART_IER);
				}
			} else if (is_rx_start) {
				tegra_uart_start_rx_dma(tup);
			}
			spin_unlock_irqrestore(&u->lock, flags);  这里释放锁了,采用的uart_port的lock
			return IRQ_HANDLED;
		}

case 1: /* Transmit interrupt only triggered when using PIO */
			tup->ier_shadow &= ~UART_IER_THRI;
			tegra_uart_write(tup, tup->ier_shadow, UART_IER);
			tegra_uart_handle_tx_pio(tup);   //这里受此锁保护,此处为第一段代码的入口
			break;

综上所述,在用户层发送接口 和中断驱动的发送接口中,都是通过 uart_port结构体中lock 锁代码进行互斥访问的。获取锁的位置:1) 中断处理的入口  2) 写函数的入口。

第二个问题:

在有些操作系统中,即使采用spin_lock_irq,号称禁止中断,但是tx 发送中断在用户进行数据发送时依然会产生,这就导致:

    用户进程A在通过串口写数据时,已经获得spin_lock_irq 自旋锁,并进行数据发送。由于默认发送中断打开,导致数据发送后,进入中断发送的流程中,而中断发送抢占了用户进程A,且中断处理也想获得spin_lock_irq自旋锁,此时就死锁了。

     

     在串口发送的过程中,在通用的接口start_tx中,也即uart_core层的代码:

static void __uart_start(struct tty_struct *tty)
{
	struct uart_state *state = tty->driver_data;
	struct uart_port *port = state->uart_port;

	if (port && !uart_tx_stopped(port))
		port->ops->start_tx(port);
}

通常在底层串口驱动层会使能发送中断,例如:

static void ar933x_uart_start_tx(struct uart_port *port)
{
	struct ar933x_uart_port *up =
		container_of(port, struct ar933x_uart_port, port);

	ar933x_uart_start_tx_interrupt(up);
}

那么这里有个问题,即stop_tx的调用时机是在哪里?

理论上讲,在发送数据前调用,禁止发送中断,以保证用户发送。具体要看tty相关代码了

static const struct tty_operations uart_ops = {
	.install	= uart_install,
	.open		= uart_open,
	.close		= uart_close,
	.write		= uart_write,
	.put_char	= uart_put_char,
	.flush_chars	= uart_flush_chars,
	.stop		= uart_stop,

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

proware

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值