# 2021q1 Homework3 (fibdrv) contributed by < `gyes00205` > ###### tags: `linux2021` > [作業要求](https://hackmd.io/@sysprog/linux2021-fibdrv) ## 安裝工具 ```shell $ sudo apt install linux-headers-`uname -r` $ sudo apt-get update` $ sudo apt install util-linux strace gnuplot-nox ``` ## 遇到問題 * 執行 `$ make check` ```shell $ make check cc -o client client.c make -C /lib/modules/5.4.0-67-generic/build M=fibdrv modules make[1]: Entering directory '/usr/src/linux-headers-5.4.0-67-generic' CC [M] fibdrv/fibdrv.o fibdrv/fibdrv.c: In function ‘fib_sequence’: fibdrv/fibdrv.c:30:5: warning: ISO C90 forbids variable length array ‘f’ [-Wvla] 30 | long long f[k + 2]; | ^~~~ Building modules, stage 2. MODPOST 1 modules CC [M] fibdrv/fibdrv.mod.o LD [M] fibdrv/fibdrv.ko make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-67-generic' make unload make[1]: Entering directory 'fibdrv' sudo rmmod fibdrv || true >/dev/null rmmod: ERROR: Module fibdrv is not currently loaded make[1]: Leaving directory 'fibdrv' make load make[1]: Entering directory 'fibdrv' sudo insmod fibdrv.ko insmod: ERROR: could not insert module fibdrv.ko: Operation not permitted make[1]: *** [Makefile:23: load] Error 1 make[1]: Leaving directory 'fibdrv' make: *** [Makefile:37: check] Error 2 ``` 原來是忘記關閉 Secure Boot * 關閉方式: ``` $ sudo apt install mokutil $ sudo mokutil --disable-validation ``` 接著重新啟動,按 `Esc` 鍵選擇 `Change Secure Boot State` * 重新 `$ make check` ```shell $ make check make -C /lib/modules/5.4.0-70-generic/build M=fibdrv modules make[1]: Entering directory '/usr/src/linux-headers-5.4.0-70-generic' Building modules, stage 2. MODPOST 1 modules make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-70-generic' make unload make[1]: Entering directory 'fibdrv' sudo rmmod fibdrv || true >/dev/null [sudo] password for gyes00205: rmmod: ERROR: Module fibdrv is not currently loaded make[1]: Leaving directory 'fibdrv' make load make[1]: Entering directory 'fibdrv' sudo insmod fibdrv.ko make[1]: Leaving directory 'fibdrv' sudo ./client > out make unload make[1]: Entering directory 'fibdrv' sudo rmmod fibdrv || true >/dev/null make[1]: Leaving directory 'fibdrv' Passed [-] f(93) fail input: 7540113804746346429 expected: 12200160415121876738 ``` ### Fast doubling 利用 `__builtin_clzll(k)` 找出 leading zero number 。 將 `long long` 改為 `__uint128_t` , `long long` 可以表達 64 bit 的有號數,因此可表達的最大正整數為 $2^{63} - 1$ $=$ $9,223,372,036,854,775,807$ , `__uint128_t` 可表達最大正整數為 $2^{128} - 1$ 。 ```cpp static __uint128_t fib_fast_doubling(long long k) { __uint128_t a = 0, b = 1, t1, t2; for (long long i = 1 << (63 - __builtin_clzll(k)); i > 0; i >>= 1) { t1 = a * (2 * b - a); t2 = b * b + a * a; a = t1; b = t2; if (i & k) { t1 = a + b; a = b; b = t1; } } return a; } ``` ### 印出 128 bit 數字: 參考同學[hankluo6](https://hackmd.io/@hankluo6/fibdrv#%E5%A4%A7%E6%95%B8%E9%81%8B%E7%AE%97)的方法。 `ssize_t` 只能表達 64 bit 的整數,所以用 `copy_to_user` (需要 `#include <linux/uaccess.h>`) 取代 `ssize_t` 將 128 bit 傳到 `buf` 。 ```cpp static ssize_t fib_read(struct file *file, char *buf, size_t size, loff_t *offset) { __uint128_t ret = fib_fast_doubling(*offset); copy_to_user(buf, &ret, sizeof(ret)); return 1; // return (ssize_t) fib_sequence(*offset); } ``` 另外在 `client.c` 實做 `print_u128_u()` ```cpp static int print_u128_u(__uint128_t u128) { int rc; if (u128 > UINT64_MAX) { __uint128_t leading = u128 / P10_UINT64; uint64_t trailing = u128 % P10_UINT64; rc = print_u128_u(leading); rc += printf("%." TO_STRING(E10_UINT64) PRIu64, trailing); } else { uint64_t u64 = u128; rc = printf("%" PRIu64, u64); } return rc; } ``` 之後將 scripts/expected.txt 改為正確答案,執行 `make check`,成功 `Passed[-]` 。 ```shell $ make check make -C /lib/modules/5.4.0-71-generic/build M=fibdrv modules make[1]: Entering directory '/usr/src/linux-headers-5.4.0-71-generic' Building modules, stage 2. MODPOST 1 modules make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-71-generic' make unload make[1]: Entering directory 'fibdrv' sudo rmmod fibdrv || true >/dev/null [sudo] password for gyes00205: rmmod: ERROR: Module fibdrv is not currently loaded make[1]: Leaving directory 'fibdrv' make load make[1]: Entering directory 'fibdrv' sudo insmod fibdrv.ko make[1]: Leaving directory 'fibdrv' sudo ./client > out make unload make[1]: Entering directory 'fibdrv' sudo rmmod fibdrv || true >/dev/null make[1]: Leaving directory 'fibdrv' Passed [-] ```