# 2022q1 Homework3 (fibdrv)
contributed by < [Herakleos](https://github.com/Herakleos/fibdrv) >
## Reqirements
- [x] 研讀上述 ==Linux 效能分析的提示== 描述,在自己的實體電腦運作 GNU/Linux,做好必要的設定和準備工作 $\to$ 從中也該理解為何不希望在虛擬機器中進行實驗;
- [ ] 研讀上述費氏數列相關材料 (包含論文),摘錄關鍵手法,並思考 [clz / ctz](https://en.wikipedia.org/wiki/Find_first_set) 一類的指令對 Fibonacci 數運算的幫助。請列出關鍵程式碼並解說
- [x] 複習 C 語言 [數值系統](https://hackmd.io/@sysprog/c-numerics) 和 [bitwise operation](https://hackmd.io/@sysprog/c-bitwise),思考 Fibonacci 數快速計算演算法的實作中如何減少乘法運算的成本;
- [ ] 研讀 [KYG-yaya573142 的報告](https://hackmd.io/@KYWeng/rkGdultSU),指出針對大數運算,有哪些加速運算和縮減記憶體操作成本的舉措?
- [ ] `lsmod` 的輸出結果有一欄名為 `Used by`,這是 "each module's use count and a list of referring modules",但如何實作出來呢?模組間的相依性和實際使用次數 ([reference counting](https://en.wikipedia.org/wiki/Reference_counting)) 在 Linux 核心如何追蹤呢?
> 搭配閱讀 [The Linux driver implementer’s API guide » Driver Basics](https://www.kernel.org/doc/html/latest/driver-api/basics.html)
- [ ] 注意到 `fibdrv.c` 存在著 `DEFINE_MUTEX`, `mutex_trylock`, `mutex_init`, `mutex_unlock`, `mutex_destroy` 等字樣,什麼場景中會需要呢?撰寫多執行緒的 userspace 程式來測試,觀察 Linux 核心模組若沒用到 mutex,到底會發生什麼問題。嘗試撰寫使用 [POSIX Thread](https://en.wikipedia.org/wiki/POSIX_Threads) 的程式碼來確認。
## Environment
The same as I mentioned in [lab0-c](https://hackmd.io/@herakleos/linux2022-lab0).
## Prerequisites
### Kernal-modules
Install `linux-headers` package and make sure the package has been installed correctly.
```shell
$ dpkg -L linux-headers-`uname -r` | grep "/lib/modules"
/lib/modules
/lib/modules/5.13.0-35-generic
/lib/modules/5.13.0-35-generic/build
```
After I compiled and test (`make check`) the original program, instead of showing `Passed [-]`, it turns out with several problems.
1. **Skipping BTF generation for ... due to unavailability of vmlinux**
:::info
I am still working on this problem.
:::
```shell
$ readelf -a ./fibdrv.ko | grep BTF
```
2. **insmod: ERROR: could not insert module fibdrv.ko: Operation not permitted**
> [Why do I get “Required key not available” when install 3rd party kernel modules or after a kernel upgrade?](https://askubuntu.com/questions/762254/why-do-i-get-required-key-not-available-when-install-3rd-party-kernel-modules)
As above, something went wrong while installing kernal-modules.
```shell
$ dmesg | tail -1
Lockdown: insmod: unsigned module loading is restricted; see man kernel_lockdown.7
```
```shell
$ sudo mokutil --sb-state
SecureBoot enabled
```
```shell
$ sudo mokutil --disable-validation
$ sudo reboot
```
```shell
$ sudo mokutil --sb-state
SecureBoot enabled
SecureBoot validation is disabled in shim
```
After all these steps, `Passed [-]` will be correctly displayed.
```shell
Passed [-]
f(93) fail
input: 7540113804746346429
expected: 12200160415121876738
```
### Reduce the external interference
* Modify `GRUB_CMDLINE_LINUX_DEFAULT` in `/etc/default/grub`.
```
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash isolcpus=3"
```
```shell
$ sudo update-grub
$ sudo reboot
```
```shell
$ cat /sys/devices/system/cpu/isolated
3
```
* Address space layout randomization.
```shell
$ sudo sh -c "echo 0 > /proc/sys/kernel/randomize_va_space"
```
* Disable turbo mode in Intel structure.
```shell
$ sudo sh -c "echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo"
```
```shell
$ sudo taskset -c 3 ./client
```
In order not to do this step repeatly, I put this line in `Makefile`.
### More about `taskset`
:::info
Working on...
:::
### Modify Timer (1)
Replace skipped operation fib_write() to get ktime.
```c
ktime_t kt = ktime_get();
fib_sequence(*offset);
kt = ktime_sub(ktime_get(), kt);
return (ssize_t) ktime_to_ns(kt);
```
This will cause line `diff` return make error, the `script/expected.txt` should therefore be refined.
> [Issue - Make error causes by diff](https://github.com/sysprog21/fibdrv/issues/4)
## Fibonacci
Fix the original fib_sequence(), C99 variable-length array (VLA) is not allowed in Linux kernel.
```c
if (k <= 2)
return !!k;
long long f[3]; // replace variable-length array (VLA)
f[0] = 0;
f[1] = 1;
for (int i = 2; i <= k; i++) {
f[2] = f[0] + f[1];
f[0] = f[1];
f[1] = f[2];
}
return f[2];
```
According to the article, we may calculate fibonacci number by the following formula.
$$
\begin{split}
F(2k) &= F(k)[2F(k+1) - F(k)] \\
F(2k+1) &= F(k+1)^2+F(k)^2
\end{split}
$$
After reading [Calculating Fibonacci Numbers by Fast Doubling](https://gist.github.com/ChunMinChang/f80ef4decca23b88df16f2f7846049b6), I tried to modify `fib_sequence()` as following.
```c
if (k <= 2)
return !!k;
long long f[2];
f[0] = 0; // F(n)
f[1] = 1; // F(n+1)
/* get MSB for the total round of the loop */
for (int i = __builtin_clzll(k) - 1; i >= 0; --i) {
/* F(2n) = F(n) * [ 2 * F(n+1) - F(n) ] */
long long f1 = f[0] * ((f[1] << 1) - f[0]);
/* F(2n+1) = F(n)^2 + F(n+1)^2 */
long long f2 = f[0] * f[0] + f[1] * f[1];
/* k >> i is odd or not */
if ((k >> i) & 1) {
f[0] = f2; // F(2n+1)
f[1] = f1 + f2; // F(2n+2)
} else {
f[0] = f1; // F(2n)
f[1] = f2; // F(2n+1)
}
}
return f[0]; // F(k)
```
By the way, the above [article](https://chunminchang.github.io/blog/post/calculating-fibonacci-numbers-by-fast-doubling) use a for loop to get the MSB.
```c
unsigned int h = 0;
for (unsigned int i = n ; i ; ++h, i >>= 1);
```
### Analysis `__builtin_clz` and other bitwise operation to find MSB
:::info
Working on...
:::
### Modify Timer (2)
Use `escape` (return of `fib_seq_fd_clz`) to avoid the function be removed after optimization.
```c
__attribute__((always_inline)) static inline void escape(void *p) {
__asm__ volatile ("" : : "g"(p) : "memory");
}
```
After add fib_sequence using fast doubling, fib_write should be modified.
```c
ktime_t kt;
long long result = 0;
switch (size) {
case 0:
kt = ktime_get();
fib_sequence(*offset);
kt = ktime_sub(ktime_get(), kt);
break;
case 1:
kt = ktime_get();
result = fib_seq_fd_clz(*offset);
kt = ktime_sub(ktime_get(), kt);
escape(&result);
break;
case 2:
return ktime_to_ns(ktime_get());
default:
return 1;
}
return (ssize_t) ktime_to_ns(kt);
```
:::info
To-do: statistics
:::
## Big Number
> Reading [KYG-yaya573142](https://hackmd.io/@KYWeng/rkGdultSU)
###### tags: `linux2022`