2021年9月26日 星期日

Linux SPI

每個 SPI Bus 都有控制器,其控制器驅動程式是 SPI master controller driver。而 SPI Bus 上週邊的驅動程式稱為 SPI protocol driver,存取是靠傳 SPI 訊息 (struct spi_message) 對特定 Bus 依序完成,每個 SPI 訊息由一個或以上 struct spi_transfer 組成,主要成員是 tx_buf 和 rx_buf 放要傳或收的資料。

spi_async() 是最底層的存取函數,執行 spi_master 的 transfer,可在任何 context (包括 irq handler)  執行,完成後 callback complete。

spi_sync() 則將 spi_async() 加以包裝,並等候 complete。另外 spi_write()、spi_read()、spi_write_then_read() 等,使用 spi_sync() 進行 SPI 存取。

送交 spi_message 的程式需要管理好記憶體,將每個沒用到的欄位清為 0 以相容將來 API 更新,送交後忽略直到 completion callback。

使用流程

  1. 可設定速度 spi -> max_speed_hz = 400000; spi_setup(spi);
  2. spi_message_init() 初始化 struct spi_message。
  3. 設定 spi_transfer,以寫為例提供了 tx_buf。
  4. spi_message_add_tail() 將 spi_transfer 加到 spi_message。
  5. spi_sync() 把 spi_message 傳出去。

參考

  1. Linux include/linux/spi/spi.h
  2. https://ithelp.ithome.com.tw/articles/10247656

Linux Documentation/spi/spi-summary

int // 0 成功,負數 errno status code
spi_write_then_read(
    struct spi_device * spi, // sip device
    const void *txbuf, unsigned n_tx,
    void *rxbuf, unsigned n_rx);

SPI 同步寫入 n_tx bytes txbuf,然後讀取 n_rx bytes (最大?) 到 rxbuf。txbuf 和 rxbuf 不需要 dma-safe。只能用在能 sleep 的 context。參數都會複製到小 buffer,考慮移植性不要超過 32 bytes。Performance-sensitive 或 bulk transfer code 應該使用 dma-safe buffers 的 spi_{async,sync}()。

spi_write_and_read()

        struct spi_transfer     t = {
                        .tx_buf         = buf,
                        .rx_buf         = buf,
                        .len            = len,
                };
        struct spi_message      m;

        spi_message_init(&m);
        spi_message_add_tail(&t, &m);
        return spi_sync(spi, &m);

 spi_write_then_read()

  • https://ithelp.ithome.com.tw/articles/10247182
  • 現成的驅動程式:spidev。

2021年9月25日 星期六

file_operations unlocked_ioctl

用 unlocked_ioctl() 取代 ioctl()

呼叫 ioctl() 會先用 lock_kernel() 來 lock,而呼叫 unlocked_ioctl() 沒有 lock ,unlocked_ioctl() 需自行確保有無問題,例如用 per-driver 或 per-device 的 mutexes 取代。unlocked_ioctl() 不再提供inode參數,但可以透過 filp->f_dentry->d_inode 取得。

lock_kernel()/unlock_kernel() 是 Big Kernel Lock (BKL),在 Linux v2.6.39 deprecated。

參考

  1. The new way of ioctl()

#if HAVE_UNLOCKED_IOCTL
#include <linux/mutex.h>
#else
#include <linux/smp_lock.h>
#endif

.
.
.

#if HAVE_UNLOCKED_IOCTL
mutex_lock(&fs_mutex);
#else
lock_kernel();
#endif

http://stackoverflow.com/questions/5956145/can-someone-help-me-replace-lock-kernel-on-a-block-device-driver

  • compat_ioctl() 是 32-bit process 在 64-bit 系統呼叫 ioctl() 用,和 unlocked_ioctl() 一樣沒有 BKL。
  • https://kernelnewbies.org/BigKernelLock:BKL 最終在 Linux v2.6.39 移除。Big Kernel Lock (BKL) 是想要 get rid of 的 old serialization method,用更 fine-grained locking 取代,如 mutex、spinlock 和 RCU。BKL 是 recursive lock,意思是可以從已經持有的 thread 取得。聽起來方便,但容易 introduces all sorts of bugs。另一個問題是,當 thread sleep,BKL 自動釋出。這避免 mutex 在有些情況 lock order 問題,但也 creates more problems because it makes it really hard to track what code is executed under the lock。

2021年9月17日 星期五

lock-free

術語比較:

  • lock-free:沒鎖死。以系統觀點,只要執行夠長的時間,至少會有一個執行緒有進展。
  • lockless (lock-freedom?):不用鎖。不用 lock 共用資料。不用鎖 (lockless) 未必不會鎖死 (lock-free),也可能鎖死。
  • wait-freedom?:不用等
  • obstruction-freedom
  • non-blocking (非阻塞):
    • 任何 thread 的失敗或暫停不會造成其它 thread 的失敗或暫停。non-blocking 演算法如果能保證 system-wide progress 是 lock-free,如果能進一步保證 per-thread progress 是 wait-free。
    • 在電信網路,是指建立新的連線路徑不用調整現有的連線。沒缺陷的電話交換機總是可以建立連線 (nonblocking minimal spanning switch)。

參考

  1. https://hackmd.io/@sysprog/concurrency-lockfree
  2. https://hackmd.io/@sysprog/concurrency-atomics

lock contention

微處理器專用 atomic 指令及軟硬體設計考量:ABA 問題, memory ordering 及 memory barrier、多核處理器特性、C11 標準的 <stdatomic.h>、Linux 核心介面

SPSC (single-producer and single-consumer)

SPMC (single-producer, multiple-consumer)

2021年9月11日 星期六

Linux File I/O

問題

  • read()、write()、ioctl() 有 race 問題嗎?

參考

TLPI §4 & §5

4 FILE I/O: THE UNIVERSAL I/O MODEL
4.1 Overview
4.2 Universality of I/O
4.3 Opening a File: open()
4.3.1 The open() flags Argument
4.3.2 Errors from open()
4.3.3 The creat() System Call
4.4 Reading from a File: read()
4.5 Writing to a File: write()
4.6 Closing a File: close()
4.7 Changing the File Offset: lseek()
4.8 Operations Outside the Universal I/O Model: ioctl()
4.9 Summary
4.10 Exercises

5 FILE I/O: FURTHER DETAILS
5.1 Atomicity and Race Conditions
5.2 File Control Operations: fcntl()
5.3 Open File Status Flags
5.4 Relationship Between File Descriptors and Open Files
5.5 Duplicating File Descriptors
5.6 File I/O at a Specified Offset: pread() and pwrite()
5.7 Scatter-Gather I/O: readv() and writev()
5.8 Truncating a File: truncate() and ftruncate()
5.9 Nonblocking I/O
5.10 I/O on Large Files
5.11 The /dev/fd Directory
5.12 Creating Temporary Files
5.13 Summary
5.14 Exercises

SIP header Via

所有 SIP 訊息 都要有 Via,縮寫 v。一開始的 UAC 和後續途經的每個 proxy 都會疊加一個 Via 放傳送的位址,依序作為回應的路徑。 格式 sent-protocol sent-by [ ;branch= branch ][ ; 參數 ...] s...