# 2019q1 Homework2 (kcalc)
contributed by < `jeffcarl67` >
## 環境
* Linux 4.15.0-45-generic #48~16.04.1-Ubuntu SMP
* gcc (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609
* 相關程式碼引用自 [linux v5.0](https://elixir.bootlin.com/linux/v5.0/source)
## 作業要求
* 回答「自我檢查清單」的所有問題,需要附上對應的參考資料和必要的程式碼,以第一手材料 (包含自己設計的實驗) 為佳
* 在 GitHub 上 fork [kcalc](https://github.com/sysprog21/kcalc),主要目標是將 `MathEx` 整合到 `calc.c` 中 (作為 LKM 的形式),過程中需要一併完成以下:
* 將 `MathEx` 的浮點數運算換為 fixed point,應該先在使用者層級驗證,然後再搬到 Linux 核心中。可斟酌移除 `MathEx` 裡頭的功能,但需要充分解釋;
* 量化 `MathEx` 在核心的執行時間,搭配 [fibdrv](https://hackmd.io/s/SJ2DKLZ8E) 撰寫的工具程式使用;
* 設計 micro-benchmarking 實驗,用以驗證 `MathEx` 移植到 Linux 核心後的表現;
* 嘗試解讀上述時間分佈,特別是隨著 Fibonacci 數列增長後,對於 Linux 核心的影響;
* 改善 `MathEx` 的執行效率;
* 請善用 [perf](http://wiki.csie.ncku.edu.tw/embedded/perf-tutorial) 一類地效能分析工具;
## 自我檢查清單
* 解釋浮點運算在 Linux 核心中為何需要特別對待,以及 context switch 的過程中,涉及到 FPU/SIMD context,該注意什麼?
* 提示: 參照 [Lazy FP state restore](https://en.wikipedia.org/wiki/Lazy_FP_state_restore) 和上方參考資料
* 應該撰寫對應包含浮點運算的 Linux 核心模組,實際編譯和測試
* 在給定的 `calc.c` 檔案中,和 [fibdrv](https://hackmd.io/s/SJ2DKLZ8E) 一樣有 character device,但註冊用的 kernel API 不同 (`register_chrdev` vs. `alloc_chrdev_region`),能否解釋其差異和適用場合呢?
查看源碼後可以發現, `register_chrdev` 實際調用了函式 `__register_chrdev`, 函式 `__register_chrdev` 與 `alloc_chrdev_region` 皆定義在 `linux/fs/xhar_dev.c` 中,
以下是兩個函數的相關註解:
* `__register_chrdev`
```clike
/**
* __register_chrdev() - create and register a cdev occupying a range of minors
* @major: major device number or 0 for dynamic allocation
* @baseminor: first of the requested range of minor numbers
* @count: the number of minor numbers required
* @name: name of this range of devices
* @fops: file operations associated with this devices
*
* If @major == 0 this functions will dynamically allocate a major and return
* its number.
*
* If @major > 0 this function will attempt to reserve a device with the given
* major number and will return zero on success.
*
* Returns a -ve errno on failure.
*
* The name of this device has nothing to do with the name of the device in
* /dev. It only helps to keep track of the different owners of devices. If
* your module name has only one type of devices it's ok to use e.g. the name
* of the module here.
*/
```
* `alloc_chrdev_region`
```clike
/**
* alloc_chrdev_region() - register a range of char device numbers
* @dev: output parameter for first assigned number
* @baseminor: first of the requested range of minor numbers
* @count: the number of minor numbers required
* @name: the name of the associated device or driver
*
* Allocates a range of char device numbers. The major number will be
* chosen dynamically, and returned (along with the first minor number)
* in @dev. Returns zero or a negative error code.
*/
```
比較註解與相關程式碼後可知, 兩個函式都有動態分配 major device number 的能力, 皆調用函式 `__register_chrdev_region` 以取得 device number, 但在 `__register_chrdev` 還會分配一個 `struct cdev` 結構, 而在 `alloc_chrdev_region` 中只是單純取得 device number
* 在 `scripts/test.sh` 檔案中,有一道命令為 `sudo chmod 0666`,這個作用為何?對於我們測試有何幫助?能否對 [fibdrv](https://hackmd.io/s/SJ2DKLZ8E) 建立的 `/dev/fibonacci` device file 也套用類似修改呢?另外,請解釋 device file 在核心及使用者層級的功能
執行命令 `sudo chmod 0666` 後會使指定的文件權限變為對所有人可讀可寫, 意味著在使用這個設備時任何用戶都能直接讀寫文件, 而查看 `/dev/fibonacci` 的權限後
```shell
$ ls -l /dev/fibonacci
crw------- 1 root root 242, 0 3月 19 05:16 /dev/fibonacci
```
可知只有 root 有對 `/dev/fibonacci` 讀寫的權限, 導致每次執行 `client` 時都需要使用 `sudo` 命令提昇普通用戶的權限, 如此一來在測試時較為不便, 嘗試對 `/dev/fibonacci` 執行相同操作後
```shell
$ sudo chmod 0666 /dev/fibonacci
$ ls -l /dev/fibonacci
crw-rw-rw- 1 root root 242, 0 3月 19 05:22 /dev/fibonacci
```
可以發現執行 `client` 時不再需要使用 `sudo` 命列提昇權限
* 在 `calc.c` 檔案中,用到 `copy_to_user` 這個 kernel API,其作用為何?本例使用該 API 做了什麼事?若是資料量增長,是否會有效能的嚴重衝擊呢?
`copy_to_user` 定義在 `linux/include/linux/uaccess.h` 中, 其最終會調用定義於 `linux/arch/x86/include/asm/uaccess_64.h` 的函式 `raw_copy_to_user` 執行實際操作, 在 x86 架構中, 此函式利用如下所定義的巨集依據所要傳送的資料長度使用不同的 x86 傳送指令, 例如若欲傳送的資料為 1 byte , 則巨集展開成 `movb %b0,%1` 的指令
```cp
#define __put_user_goto(x, addr, itype, rtype, ltype, label) \
asm_volatile_goto("\n" \
"1: mov"itype" %"rtype"0,%1\n" \
_ASM_EXTABLE_UA(1b, %l2) \
: : ltype(x), "m" (__m(addr)) \
: : label)
#define __put_user_failed(x, addr, itype, rtype, ltype, errret) \
({ __label__ __puflab; \
int __pufret = errret; \
__put_user_goto(x,addr,itype,rtype,ltype,__puflab); \
__pufret = 0; \
__puflab: __pufret; })
#define __put_user_asm(x, addr, retval, itype, rtype, ltype, errret) do { \
retval = __put_user_failed(x, addr, itype, rtype, ltype, errret); \
} while (0)
```
* 找出至少 3 個 Linux 核心中使用定點數的案例,搭配程式碼解說
* 提示: 參照 [Linux Kernel Load Average 計算分析
](http://brytonlee.github.io/blog/2014/05/07/linux-kernel-load-average-calc/), [What Every Computer Scientist Should Know About Floating-Point Arithmetic](https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html), [Load average explained](https://wiki.nix-pro.com/view/Load_average_explained)
* 是否知曉 MathEx 如何解析給定字串,從而分離出變數和對應數學表示法呢?
* 如何對 `MathEx` 進行效能分析?能否找出相似的 math expression 來分析比較呢?
* 提示: 參照 [muparserSSE - A Math Expression Compiler](http://beltoforion.de/article.php?a=muparsersse)
* 在 `MathEx` 原始程式碼的 `expression.[ch]` 裡頭 `vec` 相關的程式碼,主要做什麼事?有沒有發現類似 [list](https://hackmd.io/s/S12jCWKHN) 使用到的技巧呢?
* 提示: 參照 `mathex/test-unit.c` 的測試項目
* 解釋 `MathEx` 一類的 math expression 在真實世界有哪些應用?甚至,是否在 Linux 核心就存在類似的程式碼?
* 提示: 參照 [A thorough introduction to eBPF](https://lwn.net/Articles/740157/)
* 如果要將使用者層級的 C 語言程式,搬到 Linux 核心作為核心模組 (LKM),該注意哪些議題呢?請舉例說明
* 提示: 注意 `__KERNEL__` 巨集的定義, [kmalloc](https://www.kernel.org/doc/htmldocs/kernel-api/API-kmalloc.html) 的使用, [vmalloc](https://www.kernel.org/doc/htmldocs/kernel-api/API-vmalloc.html) 的使用 (以及後兩者的差異)
* [fixed point arithmetic for RT-Linux](http://netwinder.osuosl.org/pub/netwinder/docs/nw/rt_fixed/doc/html/rt_fix.html) 的運作原理為何?給定的程式碼已經存在超過 20 年,很多細節已有出入,可否嘗試移植到 Linux `v4.15+` 呢?