最近在加速 uart 的傳輸 , 發現少量 資料的時候會比較慢 , 並且慢的有點離譜.
由訊號看來 , UART 的 Rx 傳輸已經結束 ,
但是我的 read(tty)卻 大約慢10 ~ 1 mS 後才會收到.
想說是否 kernel 內部在 context switch 的時候花比較久.
於是 ''超頻" 兩倍看看是否有改善, 結果差不多一樣. !?
詭異了,竟然沒有改善, 我也使用 select () 方式看看是否加快 (之前使用 read() block type),
結果也沒有..... !?
沒法了,只好進 kernel 的 tty driver 來看看了.
TTY 的詳細結構可以參考下列 pdf file , 這個 file 應該是 linux driver (脫韁野馬) 的第 18 章節.
就不在詳細說明 TTY 了.
https://www.google.com.tw/url?sa=t&rct=j&q=&esrc=s&source=web&cd=8&cad=rja&uact=8&ved=0CFwQFjAHahUKEwiKq8vWn6DHAhVHIaYKHXIFAA4&url=https%3A%2F%2Flwn.net%2Fimages%2Fpdf%2FLDD3%2Fch18.pdf&ei=Z4HJVcrbM8fCmAXyioBw&usg=AFQjCNHg8aGyIBkWXmU3itJ1JY5xVD1usQ&sig2=gE3rZ1mU8oDQMsaoT49vEA
最後發現 UART 收到 data 後會呼叫 tty_flip_buffer_push(tty); 通知 tty layer 有 data 在 buffer 了.
tty_flip_buffer_push(tty) 內容主要有下列這段 code ( 2.6.32.19),
if (tty->low_latency)
flush_to_ldisc(&tty->buf.work.work);
else
schedule_delayed_work(&tty->buf.work, 1);
答案出來了 , 因為每次都排 schedule_delay_work 去收 buffer 的資料.
我們使用的 tick 是 100 , 所以每排一次約 10 mS, 難怪我會測量到 10 ~ 1 mS,並且和 CPU clock 無關.
好 , 那就想辦法設定 tty->low_latency 為 "1" 吧, 這樣就是直接呼叫,不使用排程.
發現 在 serial_core.c 中的 ioctl 有支援修改 serial 的參數 (TIOCSSERIAL), 設定的 flage 中有
UPF_LOW_LATENCY (新版的是使用 ASYNC_LOW_LATENCY, 都是設定相同的 bit ).
測試後發現, 被 CAP_SYS_ADMIN 擋住 , 不過設定完後先 close tty 在 open tty 卻有效.
static int uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd,
unsigned long arg)
{
.............
case TIOCSSERIAL:
ret = uart_set_info(state, uarg);
break;
..............
}
static int uart_set_info(struct uart_state *state,
struct serial_struct __user *newinfo)
{
.....................
if (!capable(CAP_SYS_ADMIN))
{
..........
goto exit;
.............
goto check_and_exit;
................
}
..............
if (port->tty)
port->tty->low_latency =
(uport->flags & UPF_LOW_LATENCY) ? 1 : 0;
..................
}
檢查 tty open 過程後 ,發現其時會將剛剛設定的 ASYNC_LOW_LATENCY 值給帶入,難怪設定後在 close / open 一次就會有效.
static int uart_open(struct tty_struct *tty, struct file *filp)
{
............................
tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0;
tty->alt_speed = 0;
................
}
因為我的 AP 並非 root 執行 , 所以也不研究 SYS_CAP_ADMIN的問題了 , AP 端就 open /close 一次就好了 , 下列是 AP 設定 ASYNC_LOW_LATENCY 的方式.
//---- turn on ASYNC_LOW_LATENCY mode.
ioctl(fd, TIOCGSERIAL, &ser_info);
ser_info.flags &= ~ASYNC_LOW_LATENCY;
#if defined(ENABLE_ASYNC_LOW_LATENCY)
ser_info.flags |= ASYNC_LOW_LATENCY;
#endif
ioctl(fd, TIOCSSERIAL, &ser_info);
DEBUG_MSG("%s,Low_latency:%s ",dev_name,
(ser_info.flags & ASYNC_LOW_LATENCY) ? "on" : "off");
close(fd);
修改後再次測量uart 訊號到 read(tty) 的時間 (這次使用 select() 方式去等 data ) ,
改善很多 , 整個 UART 傳輸速度也提升了 !
仔細想想 , 原本 UART Rx IRQ 用 schedule_delay_work() 排程去執行, 符合 Top and Bottom Halves 方式 , 打開 ASYNC_LOW_LATENCY 後卻是 IRQ 就直接做完, 導致有可能 UART 的 Rx IRQ 執行時間攏長.
所以要打開 ASYNC_LOW_LATENCY 要考慮一下整個系統的狀況 !!
沒有留言:
張貼留言