# 2019q1 Homework2 (fibdrv) contributed by < `TsundereChen` > ###### tags: `sysprog` `TsundereChen` ## Report ### `make check` command * When compiling, using different compiler will prompt different kinds of message * Using `clang v6.0.0-1ubuntu2` won't have error messages related to code * Using `gcc v8.2.1` would have warning message below ```bash make[1]: Entering directory '/usr/lib/modules/5.0.0-zen1-1-zen/build' CC [M] /home/tsundere/Workspace/linux_2019_spr/Week2/fibdrv/fibdrv.o /home/tsundere/Workspace/linux_2019_spr/Week2/fibdrv/fibdrv.c: In function ‘fib_sequence’: /home/tsundere/Workspace/linux_2019_spr/Week2/fibdrv/fibdrv.c:30:5: warning: ISO C90 forbids variable length array ‘f’ [-Wvla] long long f[k + 2]; ^~~~ ``` * Always use gcc ? ### Variable Length Arrays in Kernel * There's a talk called [Making C Less Dangerous in the Linux kernel](https://www.youtube.com/watch?v=FY9SbqTO5GQ) /[ Slide](https://outflux.net/slides/2019/lca/danger.pdf) in this year's [linux.conf.au](https://linux.conf.au) * In this talk, he mentioned several pitfalls in linux kernel development, one of them is **using VLA** * Using VLA can cause * Overflow * Slow * Using more instructions * We are going to fix this ### fibdrv.c #### MODULE_LICENSE, MODULE_AUTHOR, MODULE_DESCRIPTION * `MODULE_*` is defined in `include/linux/module.h` * Take a look at `MODULE_LICENSE` in `module.h` ```c #define MODULE_LICENSE(_license) MODULE_INFO(license, _license) ``` * Take a look at `MODULE_INFO` in `module.h` ```c #define MODULE_INFO(tag, info) __MODULE_INFO(tag, tag, info) ``` * Okay...where's `__MODULE_INFO` ? * Defined in `include/linux/moduleparam.h`, at line 20 ~ 36 ```c #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 #define __MODULE_PARM_TYPE(name, _type) \ __MODULE_INFO(parmtype, name##type, #name ":" _type) /* One for each parameter, describing how to use it. Some files do multiple of these per line, so can't just use MODULE_INFO. */ #define MODULE_PARM_DESC(_parm, desc) \ __MODULE_INFO(parm, _parm, #_parm ":" desc) ``` * `MODULE_AUTHOR` and `MODULE_DESCRIPTION` is same as `MODULE_LICENSE` #### MODULE_VERSION * There are two ways to define `MODULE_VERSION` ```c #if defined(MODULE) || !defined(CONFIG_SYSFS) #define MODULE_VERSION(_version) MODULE_INFO(version, _version) #else #define MODULE_VERSION(_version) \ static struct module_version_attribute ___modver_attr = { \ .mattr = { \ .attr = { \ .name = "version", \ .mode = S_IRUGO, \ }, \ .show = __modver_version_show, \ }, \ .module_name = KBUILD_MODNAME, \ .version = _version, \ }; \ static const struct module_version_attribute \ __used __attribute__ ((__section__ ("__modver"))) \ * __moduleparam_const __modver_attr = &___modver_attr #endif ``` * `MODULE_VERSION` would be different if `CONFIG_SYSFS` is defined * Most modern linux use virtual filesystem * Execute `mount` in your shell, if you see result like this ```bash sys on /sys type sysfs (rw,nosuid,nodev,noexec,relatime) ``` Then yeah, your system is using virtual filesystem * `S_IRUGO` * This is a macro defined in `include/linux/stat.h` * `IRUGO` means * `I` -> Inode * `R` -> Readonly * `U` -> User * `G` -> Group * `O` -> Others * So, `S_IRUGO` means the inode created is mode 0444 * **`stat.h` changes** * After linux 3.7, lots of header files are moved into `include/uapi` or `arch/xxxx/include/uapi` * This is trying to solve **include recursive** issue * Source: [Linux kernel uapi header](http://vh21.github.io/linux/2014/11/21/linux-kernel-uapi-include-file.html) ### Command `insmod` * By the flow below, we can see that `insmod` is going to call init function of that module ![insmod_rmmod_flow](https://www.oreilly.com/library/view/linux-device-drivers/0596005903/httpatomoreillycomsourceoreillyimages138389.png) Take a look at the end of `fibdrv.c`, we found these code ```c module_init(init_fib_dev); module_exit(exit_fib_dev); ``` Now, let's find how `module_init(function)` works * [`include/linux/module.h line 87`](https://elixir.bootlin.com/linux/latest/source/include/linux/module.h#L87) * ```c #define module_init(x) __initcall(x) ``` * [`include/linux/init.h line 231`](https://elixir.bootlin.com/linux/latest/source/include/linux/init.h#L231) * ```c #define __initcall(fn) device_initcall(fn) ``` * [`include/linux/init.h line 226`](https://elixir.bootlin.com/linux/latest/source/include/linux/init.h#L226) * ```c #define device_initcall(fn) __define_initcall(fn, 6) ``` * [`include/linux/init.h line 197`](https://elixir.bootlin.com/linux/latest/source/include/linux/init.h#L197) * ```c #define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id) ``` * [`include/linux/init.h line 185`](https://elixir.bootlin.com/linux/latest/source/include/linux/init.h#L185) * ```c #ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS #define ___define_initcall(fn, id, __sec) \ __ADDRESSABLE(fn) \ asm(".section \"" #__sec ".init\", \"a\" \n" \ "__initcall_" #fn #id ": \n" \ ".long " #fn " - . \n" \ ".previous \n"); #else #define ___define_initcall(fn, id, __sec) \ static initcall_t __initcall_##fn##id __used \ __attribute__((__section__(#__sec ".init"))) = fn; #endif ``` * So this code ```c module_init(init_fib_drv); ``` would eventually turn into this **(NEED TO CHECK)** ```c static initcall_t __initcall_init_fib_drv6 __used \ __attribute__((__section__(.initcall6.init))) = init_fib_drv; ``` * Okay...what does that mean ? * **gcc's [section attribute](https://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html)** * It's a way to let gcc know which section of the ELF you want that variable to live in * Code above would register this kernel module to kernel, let kernel know it can serve requests and terminates its initialization function right after registration * So, `module_init(init_fib_drv)` would register `init_fib_drv` to kernel, and initialize the module ### Register and initialize the device * We want to focus on function `static int __init init_fib_dev(void)` * `mutex_init(&fib_mutex)` * ### The Linux Virtual File System * Virtual File System (VFS) is designed to create a uniform way for clients to access different kinds of file system * It creates an interface between kernel and file system, so it's easy to support new file systems * Three classes of VFS * Disk-based filesystems * Ext4, NTFS, UDF, ZFS...etc * Network filesystems * NFS, CIFS...etc * Special filesystems * Filesystems that **do not manage disk space** ## TODO - [ ] Fix VLA in `fibdrv.c` ## Environment * Ubuntu Server using Linux 4.15.0-46-generic ```bash tsundere:~/ $ hostnamectl Static hostname: tsundere-ml350p-gen8 Icon name: computer-desktop Chassis: desktop Operating System: Ubuntu 18.04.2 LTS Kernel: Linux 4.15.0-46-generic Architecture: x86-64 tsundere:~/ $ lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): 12 On-line CPU(s) list: 0-11 Thread(s) per core: 2 Core(s) per socket: 6 Socket(s): 1 NUMA node(s): 1 Vendor ID: GenuineIntel CPU family: 6 Model: 62 Model name: Intel(R) Xeon(R) CPU E5-2630 v2 @ 2.60GHz Stepping: 4 CPU MHz: 1652.487 CPU max MHz: 3100.0000 CPU min MHz: 1200.0000 BogoMIPS: 5186.67 Virtualization: VT-x L1d cache: 32K L1i cache: 32K L2 cache: 256K L3 cache: 15360K NUMA node0 CPU(s): 0-11 Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm cons tant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm pcid dca sse4_1 s se4_2 x2apic popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm cpuid_fault epb pti intel_ppin ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid fsgsbase sm ep erms xsaveopt dtherm ida arat pln pts flush_l1d tsundere:~/ $ free -h total used free shared buff/cache available Mem: 7.7G 532M 139M 216K 7.1G 6.9G Swap: 4.0G 12M 4.0G ``` * Arch Linux with linux-5.0.0-zen ```bash tsundere:~/ $ hostnamectl Static hostname: Tsundere-X240s Icon name: computer-laptop Chassis: laptop Operating System: Arch Linux Kernel: Linux 5.0.0-zen1-1-zen Architecture: x86-64 tsundere:~/ $ lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian Address sizes: 39 bits physical, 48 bits virtual CPU(s): 4 On-line CPU(s) list: 0-3 Thread(s) per core: 2 Core(s) per socket: 2 Socket(s): 1 NUMA node(s): 1 Vendor ID: GenuineIntel CPU family: 6 Model: 69 Model name: Intel(R) Core(TM) i7-4500U CPU @ 1.80GHz Stepping: 1 CPU MHz: 1795.879 CPU max MHz: 3000.0000 CPU min MHz: 800.0000 BogoMIPS: 4790.23 Virtualization: VT-x L1d cache: 32K L1i cache: 32K L2 cache: 256K L3 cache: 4096K NUMA node0 CPU(s): 0-3 Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt dtherm ida arat pln pts flush_l1d tsundere:~/ $ free -h total used free shared buff/cache available Mem: 7.7Gi 1.9Gi 4.1Gi 493Mi 1.7Gi 5.1Gi Swap: 9Gi 0B 9Gi ``` ## Reference * [Making C Less Dangerous in the Linux kernel - linux.conf.au 2019](https://www.youtube.com/watch?v=FY9SbqTO5GQ) * [Linux kernel uapi header - Viller Hsiao](http://vh21.github.io/linux/2014/11/21/linux-kernel-uapi-include-file.html) * [The UAPI header file split[LWN.net]](https://lwn.net/Articles/507794/) * [1.2. How Do Modules Get Into The Kernel?](https://linux.die.net/lkmpg/x44.html) * [Linux Device Drivers, 3rd, 2. Building and Running Modules](https://www.oreilly.com/library/view/linux-device-drivers/0596005903/ch02.html) * [Linux驱动中module_init宏的解析](https://blog.csdn.net/armfpga123/article/details/54292961) * [Linuxのドライバの初期化が呼ばれる流れ](https://qiita.com/rarul/items/308d4eef138b511aa233) * [Mitchel Humpherys::Hacking Your ELF For Fun And Profit](http://mgalgs.github.io/2013/05/10/hacking-your-ELF-for-fun-and-profit.html)