# 2019q1 Homework2 (fibdrv)
contributed by < `guojiun` >
### Review by Zames-Chang
* 在 `int fib(int n)` 的函式中,因為第一個 `if` 函式會執行到 `2^K <= n && 2^K+1 > n` 所以你的時間複雜度仍然是 $O(n)$,要計算的總數為$N = (n-2^k) + logk$,其中 `if` 這段程式碼執行了 $logk$ 次 `else` 執行了 $(n-2^k)$ 次。不過因為$(2^k-logk) < 0$ 所以仍然有起到加速的作用。
* 使用 `int` 比原本使用 的` long long ` 可以處理的數值範圍更小,建議使用 `unsiged long long `,不過我想也是方便起見你才這樣寫的。
* 然後要 reject input 的 int < 0 的情況。
```cpp
while (i < n) {
/*
這一個 if 會執行到 2^K <= n 然後 2^K+1 > n
*/
if ((i << 1) <= n) {
t4 = t1 * t1 + t0 * t0;
t3 = t0 * (2 * t1 - t0);
t0 = t3;
t1 = t4;
i = i << 1;
/*
這一個 else 會執行從2^K -> n
*/
} else {
t0 = t3;
t3 = t4;
t4 = t0 + t4;
i++;
}
}
```
## 實驗環境
```shell
$ uname -r
4.15.0-45-generic
```
## 自我檢查清單
- [ ] **檔案 `fibdrv.c` 裡頭的 `MODULE_LICENSE`, `MODULE_AUTHOR`, `MODULE_DESCRIPTION`, `MODULE_VERSION` 等巨集做了什麼事,可以讓核心知曉呢?**
在 [/include/linux/module.h](https://elixir.bootlin.com/linux/v4.15/source/include/linux/module.h#L198) 中,定義了相關的 macro,以 `MODULE_LICENSE` 來說,其定義如下:
```clike
#define MODULE_LICENSE(_license) MODULE_INFO(license, _license)
#define MODULE_INFO(tag, info) __MODULE_INFO(tag, tag, info)
```
而 `__MODULE_INFO()` 則於 [/include/linux/moduleparam.h](https://elixir.bootlin.com/linux/v4.15/source/include/linux/moduleparam.h#L21) 之中:
```clike
#ifdef MODULE
#define __MODULE_INFO(tag, name, info) \
static const char __UNIQUE_ID(name)[] \
__used __attribute__((section(".modinfo"), unused, aligned(1))) \
= __stringify(tag) "=" info
#else /* !MODULE */
/* This struct is here for syntactic coherency, it is not used */
#define __MODULE_INFO(tag, name, info) \
struct __UNIQUE_ID(name) {}
#endif
```
`__stringify` [/include/linux/stringify.h](https://elixir.bootlin.com/linux/v4.15/source/include/linux/stringify.h#L10) :
```clike
#define __stringify_1(x...) #x
#define __stringify(x...) __stringify_1(x)
```
`__used` [/include/linux/compiler-gcc.h](https://elixir.bootlin.com/linux/v4.15/source/include/linux/compiler-gcc.h#L139) :
```clike
#if GCC_VERSION < 30300
# define __used __attribute__((__unused__))
#else
# define __used __attribute__((__used__))
#endif
```
最終,展開後,如下所示:
```clike
static const char __UNIQUE_ID_license23[] \
__attribute__((__used__)) \
__attribute__((section(".modinfo"), unused, aligned(1))) \
= "license = Dual MIT/GPL"
```
由此可知,上述 `MODULE_XXXX` 等 macro,將其對應的變量,如 `MODULE_LICENSE` 對應至__UNIQUE_ID_license23 (可由 .ko 之 symbol table 得知) 放至 `.modinfo` section 中。
- [ ] **insmod 這命令背後,對應 Linux 核心內部有什麼操作呢?請舉出相關 Linux 核心原始碼並解讀**
* **當我們透過 insmod 去載入一個核心模組時,為何 module_init 所設定的函式得以執行呢?Linux 核心做了什麼事呢?**
`insmod` 指令位於 /sbin 底下,實際上指向 /bin/kmod,而 `kmod` 是管理 linux 模組的命令集合 [kmod](https://packages.ubuntu.com/zh-tw/trusty/kmod)。我們不從 `insmod` 內部的實作入手,而是透過像 `strace` 這樣的工具來追蹤 `insmod` 命令在執行的過程中所呼叫的 system call 資訊:
```
sudo strace -o strace.txt insmod fibdrv.ko
...
finit_module(3, "", 0) = 0
...
```
查看 man page [finit_module](https://linux.die.net/man/2/finit_module)
finit_module :
* The finit_module() system call is like init_module(), but reads the module to be loaded from the file descriptor fd.
那 init_module 是做什麼的呢?
* loads an ELF image into kernel space
* performs anynecessary symbol relocations
* initializes module parameters to values
* runs the module's init function
`finit_module` 對應的 linux 實作 [/kernel/module.c](https://elixir.bootlin.com/linux/v4.15/source/kernel/module.c#L3846):(摘要)
```clike
SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags)
{
...
err = kernel_read_file_from_fd(fd, &hdr, &size, INT_MAX,
READING_MODULE);
...
return load_module(&info, uargs, flags);
}
```
接著呼叫 load_module [/kernel/module.c](https://elixir.bootlin.com/linux/v4.15/source/kernel/module.c#L3642) (摘要)
```clike
static int load_module(struct load_info *info, const char __user *uargs,
int flags)
{
...
/* Figure out module layout, and allocate all the memory. */
mod = layout_and_allocate(info, flags);
/* Now module is in final location, initialize linked lists, etc. */
err = module_unload_init(mod);
init_param_lock(mod);
/* Now we've got everything in the final locations, we can
* find optional sections. */
err = find_module_sections(mod, info);
err = check_module_license_and_versions(mod);
/* Set up MODINFO_ATTR fields */
setup_modinfo(mod, info);
/* Now copy in args */
mod->args = strndup_user(uargs, ~0UL >> 1);
/* Done! */
trace_module_load(mod);
return do_init_module(mod);
}
```
這時才呼叫 do_init_module() [/kernel/module.c](https://elixir.bootlin.com/linux/v4.15/source/kernel/module.c#L3426) (摘要)
```
static noinline int do_init_module(struct module *mod)
{
struct mod_initfree *freeinit;
freeinit = kmalloc(sizeof(*freeinit), GFP_KERNEL);
freeinit->module_init = mod->init_layout.base;
/* Start the module */
if (mod->init != NULL)
ret = do_one_initcall(mod->init);
...
return 0;
...
}
```
觀察到有個很特別的 function call `do_one_initcall(mod->init)` 可能是由此呼叫 module_init 所設定的函式。
- [ ] **試著執行 `$ readelf -a fibdrv.ko`, 觀察裡頭的資訊和原始程式碼及 modinfo 的關聯,搭配上述提問,解釋像 fibdrv.ko 這樣的 ELF 執行檔案是如何「植入」到 Linux 核心**
```shell
$readelf -a fibdrv.ko
...
Section Headers:
[Nr] Name Type Address Offset
...
[ 4] .init.text
....
Symbol table '.symtab' contains 59 entries:
...
22: 0000000000000000 339 FUNC LOCAL DEFAULT 4 init_fib_dev
```
`module_init` 與 `module_exit` 兩者都是 macro ,且被定義在 [include/linux/module.h](https://elixir.bootlin.com/linux/v4.15/source/include/linux/module.h#L85)
```clike
#define module_init(x) __initcall(x);
#define module_exit(x) __exitcall(x);
```
```clike
#define module_init(initfn) \
static inline initcall_t __maybe_unused __inittest(void) \
{ return initfn; } \
int init_module(void) __attribute__((alias(#initfn)));
```
因此,fibdrv.c 中 `module_init(init_fib_dev);` ,其展開後,如下:
```clike=
static inline initcall_t __maybe_unused __inittest(void) { return init_fib_dev;}
int init_module(void) __attribute__((alias("init_fib_dev")));
```
以 module_init(x) 為例,其 __initcall 另被定義在 [/include/linux/init.h](https://elixir.bootlin.com/linux/v4.15/source/include/linux/init.h#L199)
```clike
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn) __define_initcall(fn, 6)
#define __define_initcall(fn, id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" #id ".init"))) = fn;
```
其中,initcall_t 被定義於同個檔案 [/include/linux/init.h](https://elixir.bootlin.com/linux/v4.15/source/include/linux/init.h#L109)
```clike
typedef int (*initcall_t)(void);
```
__used 則被定義於 [/include/linux/compiler-gcc.h](https://elixir.bootlin.com/linux/v4.15/source/include/linux/compiler-gcc.h#L138)
```clike
#if GCC_VERSION < 30300
# define __used __attribute__((__unused__))
#else
# define __used __attribute__((__used__))
#endif
```
因此,`module_init(x)` 最終展開後,等同於 `__define_initcall(fn, 6)` 而其又等同於:
```clike
static int __initcall_fn_6(void) \
__attribute__((__used__)) \
__attribute__((__section__(".initcall" "6" ".init"))) = fn;
```
根據 [linux-insides](https://0xax.gitbooks.io/linux-insides/Concepts/linux-cpu-3.html) ,其中提及,在 [/include/asm-generic/vmlinux.lds.h](https://elixir.bootlin.com/linux/v4.15/source/include/asm-generic/vmlinux.lds.h#L749) linker script 中,`initcalls` sections 會透過 `data` sections 間接取得
```clike
#define INIT_CALLS \
VMLINUX_SYMBOL(__initcall_start) = .; \
KEEP(*(.initcallearly.init)) \
INIT_CALLS_LEVEL(0) \
INIT_CALLS_LEVEL(1) \
INIT_CALLS_LEVEL(2) \
INIT_CALLS_LEVEL(3) \
INIT_CALLS_LEVEL(4) \
INIT_CALLS_LEVEL(5) \
INIT_CALLS_LEVEL(rootfs) \
INIT_CALLS_LEVEL(6) \
INIT_CALLS_LEVEL(7) \
VMLINUX_SYMBOL(__initcall_end) = .;
...
#define INIT_DATA_SECTION(initsetup_align) \
.init.data : AT(ADDR(.init.data) - LOAD_OFFSET) { \
...
INIT_CALLS \
...
}
```
**小記**
* 原以為 ELF 檔中會存在 `initcall` section,然而根據 symbal table 得知,module_init() 所註冊的 function,其實是放在 `.init.text` section 中。尚不太清楚上述 `".initcall" "6" ".init"` 最終是如何對應至 ELF 檔的 section。
> 注意到 module.h 中第 76 行的 `#ifndef MODULE`,這用來區別需要編譯出靜態還是動態連結的ELF,而 kernel module 需要動態加載,所以這邊需要看[第 128 行](https://elixir.bootlin.com/linux/v4.15/source/include/linux/module.h#L128)的 macro 定義
> 然後可以去看看 `fibdrv.mod.c`
> [name=HexRabbit][color=purple]
* 到目前為止,已約略了解 fibdrv.c 與 其 ELF 檔 section 之間的對應關係,也初步了解,insmod 將 ELF 檔載入 kernel 的流程。不過細節的把握,仍迷惑不已。將繼續釐清... [Linux Loadable Kernel Module HOWTO](https://www.tldp.org/HOWTO/html_single/Module-HOWTO/#AEN627)
* 這個 fibdrv 名稱取自 Fibonacci driver 的簡稱,儘管在這裡顯然是為了展示和教學用途而存在,但針對若干關鍵的應用場景,特別去撰寫 Linux 核心模組,仍有其意義,請找出 Linux 核心的案例並解讀。
* 參考資料:[Random numbers from CPU execution time jitter](https://lwn.net/Articles/642166/)
* 名詞解釋:
* CPU execution time jitter: jitter is the deviation from true periodicity of a presumably periodic signal [wiki](https://en.wikipedia.org/wiki/Jitter)
* 摘要:
> The kernel's deterministic random bit generator (DRBG) is currently initialized by making a call to get_random_bytes(), which uses the non-blocking random number pool.
>
> Instead of reusing the existing blocking pool (i.e. the one that feeds `/dev/random`), though, his patch creates a new kernel_pool that is only accessible to the kernel itself. That eliminates a kind of denial-of-service attack where a user-space program can continuously read `/dev/random` to consume all of the entropy being generated by the system.
* 查閱 ktime 相關的 API,並找出使用案例 (需要有核心模組和簡化的程式碼來解說)
* clock_gettime 和 High Resolution TImers (HRT) 的關聯為何?請參閱 POSIX 文件並搭配程式碼解說
* fibdrv 如何透過 Linux Virtual File System 介面,讓計算出來的 Fibonacci 數列得以讓 userspace (使用者層級) 程式 (本例就是 client.c 程式) 得以存取呢?解釋原理,並撰寫或找出相似的 Linux 核心模組範例
* why considering writing kernel-mode code? [來源](https://stackoverflow.com/questions/149032/when-should-i-write-a-linux-kernel-module)
* Does it need access to extremely low-level resources, such as interrupts?
* Is your code defining a new interface/driver for hardware that cannot be built on top of currently exported functionality?
* Does your code require access to data structures or primitives that are not exported out of kernel space?
* Are you writing something that will be primarily used by other kernel subsystems, such as a scheduler or VM system?
## 實作:
* 未修改 :

```clike=
static long long fib_sequence(long long k)
{
/* FIXME: use clz/ctz and fast algorithms to speed up */
long long f[k + 2];
f[0] = 0;
f[1] = 1;
for (int i = 2; i <= k; i++) {
f[i] = f[i - 1] + f[i - 2];
}
return f[k];
}
```
* Fast-doubling : [參考資料](https://hackmd.io/s/rJ8BOjGGl#Fast-doubling)

```clike=
int fib(int n) {
if (n == 0) return 0;
int t0 = 1; // F(n)
int t1 = 1; // F(n + 1)
int t3 = 1; // F(2n)
int t4; // F(2n+1)
int i = 1;
while (i < n) {
if ((i << 1) <= n) {
t4 = t1 * t1 + t0 * t0;
t3 = t0 * (2 * t1 - t0);
t0 = t3;
t1 = t4;
i = i << 1;
} else {
t0 = t3;
t3 = t4;
t4 = t0 + t4;
i++;
}
}
return t3;
}
```
* 實作在 user space 上,尚未在 kernel space 上運行:
```clike=
void fib_luc(long *fib_result, long *luc_result, int n)
{
long tmp;
if (n == 0) {
*fib_result = 0;
*luc_result = 2;
return;
}
fib_luc(fib_result, luc_result, n/2);
if (n & 1) {
tmp = (*fib_result) * (*luc_result);
*luc_result = 5 * (*fib_result) * ((*luc_result) + (*fib_result)) / 2;
((n & 2) == 0) ? (*luc_result += 1) : (*luc_result -= 1);
*fib_result = *luc_result - 2 * tmp;
} else {
tmp = (*fib_result) * (*fib_result);
*fib_result = (*fib_result) * (*luc_result);
*luc_result = 5 * tmp;
((n & 2) == 0) ? (*luc_result += 2) : (*luc_result -= 2);
}
}
void fib(long *result, int n)
{
long luc, tmp;
fib_luc(result, &luc, n/2);
if (n % 2 != 0) {
*result = 5 * (*result) * (luc + *result) / 2 - 2 * luc * (*result);
if ((n & 2) == 0) {
*result = *result + 1;
} else {
*result = *result - 1;
}
} else {
*result = (*result) * (luc);
}
}
```
* 說明:
* 因目前版本為遞迴形式,須改為 iteration 後,再移植到 kernel module 中
* [參考資料(一)](https://bosker.wordpress.com/2011/07/27/computing-fibonacci-numbers-using-binet%E2%80%99s-formula/)
* [參考資料(二)](http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibFormulae.html#section2.3)