顯示具有 Linux-Kernel 標籤的文章。 顯示所有文章
顯示具有 Linux-Kernel 標籤的文章。 顯示所有文章

2023年10月21日 星期六

adjtimex()

adjtimex() 是 Linux 才有的調校時間系統呼叫,使用 David L. Mills 的 clock adjustment algorithm (見 RFC 5905),透過 struct timex * 設定參數和回傳資料。ntp_adjtime() 只是用不同的 mode 名稱,偏好用在 NTP daemon。

#include <sys/timex.h>
int adjtimex(struct timex *buf);
int ntp_adjtime(struct timex *buf);

struct timex {
    int  modes;      /* Mode selector */
    long offset;     /* Time offset; nanoseconds (有設 STA_NANO) 或 microseconds */
    long freq;       /* Frequency offset; see NOTES for units */
    long maxerror;   /* Maximum error (microseconds) */
    long esterror;   /* Estimated error (microseconds) */
    int  status;     /* Clock command/status */
    long constant;   /* PLL time constant */
    long precision;  /* Clock precision
                        (microseconds, read-only) */
    long tolerance;  /* Clock frequency tolerance (read-only);
                        see NOTES for units */
    struct timeval time;
                     /* Current time (read-only, except for
                        ADJ_SETOFFSET); upon return, time.tv_usec
                        contains nanoseconds, if STA_NANO status
                        flag is set, otherwise microseconds */
    long tick;       /* Microseconds between clock ticks */
    long ppsfreq;    /* PPS (pulse per second) frequency
                        (read-only); see NOTES for units */
    long jitter;     /* PPS jitter (read-only); nanoseconds, if
                        STA_NANO status flag is set, otherwise
                        microseconds */
    int  shift;      /* PPS interval duration
                        (seconds, read-only) */
    long stabil;     /* PPS stability (read-only);
                        see NOTES for units */
    long jitcnt;     /* PPS count of jitter limit exceeded
                        events (read-only) */
    long calcnt;     /* PPS count of calibration intervals
                        (read-only) */
    long errcnt;     /* PPS count of calibration errors
                        (read-only) */
    long stbcnt;     /* PPS count of stability limit exceeded
                        events (read-only) */
    int tai;         /* TAI offset, as set by previous ADJ_TAI
                        operation (seconds, read-only,
                        since Linux 2.6.26) */
    /* Further padding bytes to allow for future expansion */
};

modes 決定哪些參數需要設,是下列的 bit‐wise 組合:

  • ADJ_OFFSET:offset 設定時間偏移。Linux 2.6.26 以後會切去大於 ±0.5s 的部份,之前超出回 EINVAL。
  • ADJ_FREQUENCY:freq 設定頻率偏移。Linux 2.6.26 以後會切去大於 ±32768000 的部份,之前超出範圍回 EINVAL。
  • ADJ_MAXERROR:maxerror 設定 maximum time error
  • ADJ_ESTERROR:esterror 設定 estimated time error
  • ADJ_STATUS:status 設定 clock status bits,如下:
    • The buf.status field is a bit mask that is used to set and/or  retrieve
             status  bits  associated with the NTP implementation.  Some bits in the
             mask are both readable and settable, while others are read-only.
      
             STA_PLL (read-write)
                    Enable phase-locked loop (PLL) updates via ADJ_OFFSET.
      
             STA_PPSFREQ (read-write)
                    Enable PPS (pulse-per-second) frequency discipline.
      
             STA_PPSTIME (read-write)
                    Enable PPS time discipline.
      
             STA_FLL (read-write)
                    Select frequency-locked loop (FLL) mode.
      
             STA_INS (read-write)
                    Insert a leap second after the last second of the UTC day,  thus
                    extending the last minute of the day by one second.  Leap-second
                    insertion will occur each day, so long as this flag remains set.
      
             STA_DEL (read-write)
                    Delete a leap second at the last second of the  UTC  day.   Leap
                    second  deletion  will  occur each day, so long as this flag re‐
                    mains set.
      
             STA_UNSYNC (read-write)
                    Clock unsynchronized.
      
             STA_FREQHOLD (read-write)
                    Hold frequency.  Normally adjustments made via ADJ_OFFSET result
                    in  dampened frequency adjustments also being made.  So a single
                    call corrects the current offset, but as offsets in the same di‐
                    rection  are  made  repeatedly,  the small frequency adjustments
                    will accumulate to fix the long-term skew.
      
                    This flag prevents the small  frequency  adjustment  from  being
                    made when correcting for an ADJ_OFFSET value.
      
             STA_PPSSIGNAL (read-only)
                    A valid PPS (pulse-per-second) signal is present.
      
             STA_PPSJITTER (read-only)
                    PPS signal jitter exceeded.
      
             STA_PPSWANDER (read-only)
                    PPS signal wander exceeded.
      
             STA_PPSERROR (read-only)
                    PPS signal calibration error.
      
             STA_CLOCKERR (read-only)
                    Clock hardware fault.
      
             STA_NANO (read-only; since Linux 2.6.26)
                    Resolution   (0  =  microsecond,  1  =  nanoseconds).   Set  via
                    ADJ_NANO, cleared via ADJ_MICRO.
      
             STA_MODE (since Linux 2.6.26)
                    Mode (0 = Phase Locked Loop, 1 = Frequency Locked Loop).
      
             STA_CLK (read-only; since Linux 2.6.26)
                    Clock source (0 = A, 1 = B); currently unused.
      
             Attempts to set read-only status bits are silently ignored.
  • ADJ_TIMECONST:constant 設定 PLL time constant。If the STA_NANO status flag (see below) is clear, the kernel adds 4 to this value.
  • ADJ_SETOFFSET (Linux 2.6.39+):time 增加到 current time. If buf.status includes the ADJ_NANO flag, then buf.time.tv_usec is interpreted as a nanosecond value; otherwise it is interpreted as microseconds.
  • ADJ_MICRO、ADJ_NANO (Linux 2.6.26+):分別選擇 microsecond 或 nanosecond resolution,兩者不能同時使用。
  • ADJ_TAI (Linux 2.6.26+):constant 設 TAI (Atomic International Time) offset。 ADJ_TAI should not be used in conjunction with ADJ_TIMECONST, since the latter mode also employs the buf.constant field. For a complete explanation of TAI and the difference between TAI and UTC, see BIPM ⟨http://www.bipm.org/en/bipm/tai/tai.html⟩
  • ADJ_TICK:tick 設定 tick value。

另外,modes 可以使用下列多 bit 組成的值,此時其它 bit 不能使用:

  • ADJ_OFFSET_SINGLESHOT (含有 ADJ_OFFSET):傳統 adjtime() 方式,使用 offset 的 µs,kernel 進行每次最多 MAX_TICKADJ 逐步調整。offset 回傳之前剩餘未調整的。
  • ADJ_OFFSET_SS_READ (Linux 2.6.28+):offset 回傳先前 ADJ_OFFSET_SINGLESHOT 還有多少未調整。

一般使用者 modes 只能用 0 或 ADJ_OFFSET_SS_READ,其它要 superuser。

回傳值:成功回傳如下 clock state,失敗回傳 -1 並設 errno。

  • TIME_OK:Clock synchronized, no leap second adjustment pending.
  • TIME_INS:Indicates that a leap second will be added at the end of the UTC day.
  • TIME_DEL:Indicates that a leap second will be deleted at the end of the UTC day.
  • TIME_OOP:Insertion of a leap second is in progress.
  • TIME_WAIT:A leap-second insertion or deletion has been completed. This value will be returned until the next ADJ_STATUS oper‐ ation clears the STA_INS and STA_DEL flags.
  • TIME_ERROR (或 TIME_BAD):The system clock is not synchronized to a reliable server. This value is returned when any of the following holds true:
    • Either STA_UNSYNC or STA_CLOCKERR is set.
    • STA_PPSSIGNAL is clear and either STA_PPSFREQ or STA_PPSTIME is set.
    • STA_PPSTIME and STA_PPSJITTER are both set.
    • STA_PPSFREQ is set and either STA_PPSWANDER or STA_PPSJITTER is set.

註:Linux 3.4 開始,the call operates asynchronously and the return value usually will not reflect a state change caused by the call itself.

失敗 errno 有:

  • EFAULT:參數不可寫。
  • EINVAL (kernels before Linux 2.6.26) An attempt was made to set buf.freq to a value outside the range (-33554432, +33554432).
  • EINVAL (kernels before Linux 2.6.26) An attempt was made to set buf.offset to a value outside the permitted range. In kernels before Linux 2.0, the permitted range was (-131072, +131072). From Linux 2.0 onwards, the per‐ mitted range was (-512000, +512000).
  • EINVAL An attempt was made to set buf.status to a value other than those listed above.
  • EINVAL An attempt was made to set buf.tick to a value outside the range 900000/HZ to 1100000/HZ, where HZ is the system timer interrupt frequency.
  • EPERM buf.modes is neither 0 nor ADJ_OFFSET_SS_READ, and the caller does not have sufficient privilege. Under Linux, the CAP_SYS_TIME capability is required.
NOTES
       In  struct timex, freq, ppsfreq, and stabil are ppm (parts per million)
       with a 16-bit fractional part, which means that a value of 1 in one  of
       those  fields  actually means 2^-16 ppm, and 2^16=65536 is 1 ppm.  This
       is the case for both input values (in the case of freq) and output val‐
       ues.

       The  leap-second processing triggered by STA_INS and STA_DEL is done by
       the kernel in timer context.  Thus, it will take one tick into the sec‐
       ond for the leap second to be inserted or deleted.

SEE ALSO
       settimeofday(2),  adjtime(3), ntp_gettime(3), capabilities(7), time(7),
       adjtimex(8), hwclock(8)

       NTP "Kernel Application Program Interface"
       ⟨http://www.slac.stanford.edu/comp/unix/package/rtems/src/ssrlApps/
       ntpNanoclock/api.htm⟩

#define ADJ_OFFSET              0x0001  /* time offset */
#define ADJ_FREQUENCY           0x0002  /* frequency offset */
#define ADJ_MAXERROR            0x0004  /* maximum time error */
#define ADJ_ESTERROR            0x0008  /* estimated time error */
#define ADJ_STATUS              0x0010  /* clock status */
#define ADJ_TIMECONST           0x0020  /* pll time constant */
#define ADJ_TICK                0x4000  /* tick value */
#define ADJ_OFFSET_SINGLESHOT   0x8001  /* old-fashioned adjtime */

asmlinkage long sys_adjtimex(struct timex __user *txc_p)
{
        struct timex txc;               /* Local copy of parameter */
        int ret;

        /* Copy the user data space into the kernel copy
         * structure. But bear in mind that the structures
         * may change
         */
        if(copy_from_user(&txc, txc_p, sizeof(struct timex)))
                return -EFAULT;
        ret = do_adjtimex(&txc);
        return copy_to_user(txc_p, &txc, sizeof(struct timex)) ? -EFAULT : ret;
}

參考

  1. man adjtimex
  2. Linux kernel 原始碼
  • 軟體時鐘使用系統呼叫 adjtimex 來同步其它外部時鐘來源時,可以每 11 分鐘去調整硬體時鐘。

2020年12月11日 星期五

Read-Copy-Update

Read-Copy-Update (RCU) 是 Linux kernel 一種同步讀取和更新的機制,適合大多為讀取的情況。

一個資料結構有 reader 讀取和 updater 更新,RCU 的更新是透過覆蓋指標值來把舊資料換掉。

參考

  1. https://www.kernel.org/doc/Documentation/RCU/whatisRCU.txt

2020年5月10日 星期日

Linux Method Dispatch

一種在 C 實作 Object 的方式是宣告一個 struct,含有一些函數指標,這些函數運作在 struct 自己 (也就是第一個參數是 struct 自己),但這不是 Linux 主要採用的樣式。
就同樣 class 的 Object,Method 不像狀態一般不會變,通常收集在另一個 struct,有時稱為「virtual function table (vtable)」,原本 struct 只需要一個指標到這個 table 而節省記憶體。

pure vtable:都是函數指標的 struct,這些函數的第一個引數指到一個 struct,其包含一個指標指到此 vtable。
例如:file_lock_operations 包含兩個函數指標,運作在 struct file_lock
例如:seq_operations 包含四個函數指標,運作在 struct seq_file
vtable 取名習慣是運作的 struct 名稱或 abbreviated 加上「_operations」,在 Linux 2.6.39 大約有 30 個「*_operations」。此外有超過 100 個「*_ops」,大部分也是。但也有幾個如 struct mdk_personality,並沒有規則。

函數指標為 NULL:
  • 呼叫前要檢查。
  • 與其間接執行一個沒功能的函數,檢查是否 NULL 較有效率。
  • 如果 NULL 只是少數例外,檢查 NULL 確實浪費程式空間及執行時間。
  • 可能有別的方式得知為 NULL,不用實際去檢查,也節省了 memory fetch。
原因:持續發展的結果。新加一個函數指標,舊有程式預設初始化為 NULL,不好一一指定一個存在、但沒實際動作的函數。 
原因:特定 Method 對有些使用情況是無意義的,測試為 NULL 需回傳錯誤。例如:在 inode_operations 的 create() 只有在 inode 是目錄才有意義,所以執行 vfs_create() 檢查為 NULL 而回傳 -EACCES。

原因:界面函數轉換。例如:file_operations 的 ioctl(),在 2.6.11 新增 unlocked_ioctl() 不用 big kernel lock,在 2.6.36 所有驅動程式和檔案系統完成轉換而移除 ioctl()。轉換過程中,一個檔案系統一般只有二者之一,讓另一個為 NULL。

例如:file_operations 的 read()/write() 和 aio_read()/aio_write() 只需要二者之一。aio_read() 用來支援 asynchronous IO,此時 the regular synchronous read() 是不需要的 (實際上是透過 do_sync_read() 使用 aio_read()),但似乎沒有移除 read() 的打算,有些情況 async IO 是 not relevant,例如特殊檔案系統 procfs 和 sysfs。

不純的 vtable:含有其它成員。

例如:struct mdk_personality 提供 particular software RAID level 操作,包含了「owner」、「name」、以及「list」「owner」是提供實作的模組,「name」只是識別(有些 vtable 是字串、有些數字,有些稱作「版本」、「family」、「drvname」、或「level」。)

Object-oriented design patterns in the kernel, part 1

2019年5月25日 星期六

prefetch

在 Linux Kernel,為了減少 cache miss 來增進效能,可將預期會用到的資料用 prefetch() 事先讀取,但有些只是想像效能會較好,沒有實證反而可能有反效果。

例如 <linux/list.h> 的 list_for_each() macro:
    #define list_for_each(pos, head) \
 for (pos = (head)->next; prefetch(pos->next), pos != (head); \
            pos = pos->next)
經過實證反而效能較差 (短 list、null prefetch),硬體自己做的不會較差。

其它:
  • reordering structures that commonly accessed together fields are found in the same cache line
  • linked-list => cache-unfriendly
  • singly-linked hlist hash table list 
  • likely()
參考:
The problem with prefetch

2015年8月22日 星期六

Linux Device Drivers

An Introduction to Device Drivers (LDD3 ch1 PDF)
  • The Role of the Device Driver
  • Splitting the Kernel
  • Classes of Devices and Modules
  • Security Issues
  • Version Numbering
  • License Terms
  • Joining the Kernel Development Community
  • Overview of the Book
Building and Running Modules (LDD3 ch2 PDF)
  • 建立測試系統
  • 簡易模組:hello
  • Kernel Modules Versus Applications
  • Compiling and Loading
  • The Kernel Symbol Table
  • Preliminaries
  • Initialization and Shutdown
  • 模組參數
  • Doing It in User Space
  • Quick Reference
  • Linux Modules(1.1)module parameters
Char Drivers (LDD3 ch3 PDF)
  • scull (Simple Character Utility for Loading Localities) 是用一塊取得的記憶體作為 device 的 char driver,有多種形式示範不同功能:
    • scull0 ~ scull3:每個包含 global 且 persistent 的記憶體區塊。global 表示 device 開啟後,記憶體區塊是共享的。persistent 表示 device 關閉後再開啟,資料還是在。可用 cp、cat、shell I/O 轉向等指令存取和 tested。
    • scullpipe0 ~ scullpipe3 (ch6 A Blocking I/O Example):FIFO (first-in-first-out) device,行為類似 pipe,一個 process 讀另一個 process 寫的。如果多個 process 同時讀,競爭取得資料。scullpipe 內部不用中斷實作 blocking and non-blocking read and write,雖然真實 driver 使用硬體中斷同步 device, the topic of blocking and nonblocking operations is an important one and is separate from interrupt handling (covered in Chapter 10).
    • scullsingle, scullpriv, sculluid, scullwuid (ch6 Access Control on a Device File):類似 scull0 但有一些開啟限制。scullsingle 只允許一個 process 使用。scullpriv 對每個 virtual console 或 X terminal session 會取到不同記憶體區域而各自獨立。sculluid 和 scullwuid 同一個 user 可以開啟多次,但如果有其它 user 使用時開啟,前者會回「Device Busy」的錯誤,後者實作會 blocking。雖然看起來混淆 policy 和 mechanism,但一些真實 device 需要這類的管理而值得了解。
  • Major and Minor Numbers
  • Some Important Data Structures:包括 File Operations、The file Structure、The inode Structure
  • Char Device Registration
  • open and release
  • scull’s Memory Usage
  • read and write
  • Playing with the New Devices
  • Quick Reference

Debugging Techniques (LDD3 ch4 PDF)
Concurrency and Race Conditions (LDD3 ch5PDF)
Advanced Char Driver Operations (LDD3 ch6 PDF)

  • 在 LDD Chap. 3 建立了一個可以 read()/write() 的 device driver,但真實的 device 提供的功能不只這樣。現在可以進一步,萬一出現 concurrency 問題造成混亂,也有除錯工具可徹底了解。ioctl() 系統呼叫是 device 的共通控制界面。synchronizing with user space 的各種方式; 如何 put processes to sleep (and wake them up), implement nonblocking I/O, and inform user space when your devices are available for reading or writing. a look at how to implement a few different device access policies within drivers. 這些透過 a couple of modified versions of the scull driver 示範,所有實作仍使用 in-memory virtual devices,而不需要特殊硬體。LDD Chap. 9 才會用到真正的硬體。
  • ioctlioctl()
  • Blocking I/O
  • poll and select
  • Asynchronous Notification
  • Seeking a Device
  • Access Control on a Device File
  • Quick Reference

Time, Delays, and Deferred Work (LDD3 ch7 PDF)

Allocating Memory (LDD3 ch8 PDF)
Communicating with Hardware(LDD3 ch9 PDF)
Interrupt Handling (LDD3 ch10 PDF)
Data Types in the Kernel (LDD3 ch11 PDF)
PCI Drivers (LDD3 ch12 PDF)
USB Drivers (LDD3 ch13 PDF)
The Linux Device Model (LDD3 ch14 PDF)
Memory Mapping and DMA (LDD3 ch15 PDF)
Block Drivers (LDD3 ch16 PDF)
Network Drivers (LDD3 ch17 PDF)
TTY Drivers (LDD3 ch18 PDF)

參考來源

  1. https://lwn.net/Kernel/LDD3/ (Table of ContentsIndex、調整過的範例程式:martinezjavier, jesstess)
  • Free Tutorials: Linux Device Drivers
  • Linux Device Drivers 4th, ISBN 978-1-4493-7161-6, Published 2017 O'Reilly Media, Authors:Jessica McKellarAlessandro RubiniJonathan CorbetGreg Kroah-Hartman

2013年8月16日 星期五

Linux timeout by jiffies

之前這篇提到,Linux 的 udelay() 以迴圈延遲 1µs。每個 tick 可以跑 loops_per_jiffy 個延遲迴圈,一秒就是 loops_per_jiffy * HZ 個迴圈,1µs 就是 loops_per_jiffy * HZ / 1000000 個迴圈。mdelay() 或 ndelay() 也是類似的作法。但這些函數都是 busy waiting,等待時不能做其它事。

jiffies 是 Linux 全域變數,每 1/HZ 秒會自動加一,可用來判斷 timeout。範例如下:

unsigned long timeout;

timeout = jiffies + HZ/2; /* 500 ms timeout */

do{
        /* do something */
        if (some_state)
                break;
} while (time_after(timeout, jiffies)); /* 500ms timeout */

jiffies 跟 time_after 定義在 inclue/linux/jiffies.h。

相關文章

SIP header Via

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