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,