Initial commit

This commit is contained in:
Kaiwan N Billimoria
2022-12-21 16:14:42 +05:30
parent 0c1f8213fa
commit 77fff3e1fc
2 changed files with 339 additions and 0 deletions

View File

@@ -0,0 +1,183 @@
# ch6/foreach/thrd_showall/Makefile
# ***************************************************************
# This program is part of the source code released for the book
# "Linux Kernel Programming"
# (c) Author: Kaiwan N Billimoria
# Publisher: Packt
# GitHub repository:
# https://github.com/PacktPublishing/Linux-Kernel-Programming
#
# From: Ch 5 : Writing Your First Kernel Module LKMs, Part 2
# ***************************************************************
# Brief Description:
# A 'better' Makefile template for Linux LKMs (Loadable Kernel Modules); besides
# the 'usual' targets (the build, install and clean), we incorporate targets to
# do useful (and indeed required) stuff like:
# - adhering to kernel coding style (indent+checkpatch)
# - several static analysis targets (via sparse, gcc, flawfinder, cppcheck)
# - two 'dummy' dynamic analysis targets (KASAN, LOCKDEP)
# - a packaging (.tar.xz) target and
# - a help target.
#
# To get started, just type:
# make help
#
# For details, please refer the book, Ch 5.
# To support cross-compiling for kernel modules:
# For architecture (cpu) 'arch', invoke make as:
# make ARCH=<arch> CROSS_COMPILE=<cross-compiler-prefix>
ifeq ($(ARCH),arm)
# *UPDATE* 'KDIR' below to point to the ARM Linux kernel source tree on your box
KDIR ?= ~/rpi_work/kernel_rpi/linux
else ifeq ($(ARCH),arm64)
# *UPDATE* 'KDIR' below to point to the ARM64 (Aarch64) Linux kernel source
# tree on your box
KDIR ?= ~/kernel/linux-4.14
else ifeq ($(ARCH),powerpc)
# *UPDATE* 'KDIR' below to point to the PPC64 Linux kernel source tree on your box
KDIR ?= ~/kernel/linux-4.9.1
else
# 'KDIR' is the Linux 'kernel headers' package on your host system; this is
# usually an x86_64, but could be anything, really (f.e. building directly
# on a Raspberry Pi implies that it's the host)
KDIR ?= /lib/modules/$(shell uname -r)/build
endif
PWD := $(shell pwd)
obj-m += thrd_showall.o
EXTRA_CFLAGS += -DDEBUG
all:
@echo
@echo '--- Building : KDIR=${KDIR} ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} EXTRA_CFLAGS=${EXTRA_CFLAGS} ---'
@echo
make -C $(KDIR) M=$(PWD) modules
install:
@echo
@echo "--- installing ---"
@echo
make -C $(KDIR) M=$(PWD) modules_install
clean:
@echo
@echo "--- cleaning ---"
@echo
make -C $(KDIR) M=$(PWD) clean
rm -f *~ # from 'indent'
#--------------- More (useful) targets! -------------------------------
INDENT := indent
# code-style : "wrapper" target over the following kernel code style targets
code-style:
make indent
make checkpatch
# indent- "beautifies" C code - to conform to the the Linux kernel
# coding style guidelines.
# Note! original source file(s) is overwritten, so we back it up.
indent:
@echo
@echo "--- applying kernel code style indentation with indent ---"
@echo
mkdir bkp 2> /dev/null; cp -f *.[chsS] bkp/
${INDENT} -linux --line-length95 *.[chsS]
# add source files as required
# Detailed check on the source code styling / etc
checkpatch:
make clean
@echo
@echo "--- kernel code style check with checkpatch.pl ---"
@echo
$(KDIR)/scripts/checkpatch.pl --no-tree -f --max-line-length=95 *.[ch]
# add source files as required
#--- Static Analysis
# sa : "wrapper" target over the following kernel static analyzer targets
sa:
make sa_sparse
make sa_gcc
make sa_flawfinder
make sa_cppcheck
# static analysis with sparse
sa_sparse:
make clean
@echo
@echo "--- static analysis with sparse ---"
@echo
# if you feel it's too much, use C=1 instead
make C=2 CHECK="/usr/bin/sparse" -C $(KDIR) M=$(PWD) modules
# static analysis with gcc
sa_gcc:
make clean
@echo
@echo "--- static analysis with gcc ---"
@echo
make W=1 -C $(KDIR) M=$(PWD) modules
# static analysis with flawfinder
sa_flawfinder:
make clean
@echo
@echo "--- static analysis with flawfinder ---"
@echo
flawfinder *.[ch]
# static analysis with cppcheck
sa_cppcheck:
make clean
@echo
@echo "--- static analysis with cppcheck ---"
@echo
cppcheck -v --force .
# Packaging; just tar.xz as of now
PKG_NAME := lkm_template
tarxz-pkg:
rm -f ../${PKG_NAME}.tar.xz 2>/dev/null
make clean
@echo
@echo "--- packaging ---"
@echo
tar caf ../${PKG_NAME}.tar.xz *
ls -l ../${PKG_NAME}.tar.xz
@echo '=== package created: ../$(PKG_NAME).tar.xz ==='
help:
@echo '=== Makefile Help : additional targets available ==='
@echo
@echo 'TIP: type make <tab><tab> to show all valid targets'
@echo
@echo '--- 'usual' kernel LKM targets ---'
@echo 'typing "make" or "all" target : builds the kernel module object (the .ko)'
@echo 'install : installs the kernel module(s) to INSTALL_MOD_PATH (default: /lib/modules/$(shell uname -r)/)'
@echo 'clean : cleanup - remove all kernel objects, temp files/dirs, etc'
@echo
@echo '--- kernel code style targets ---'
@echo 'code-style : "wrapper" target over the following kernel code style targets'
@echo ' indent : run the $(INDENT) utility on source file(s) to indent them as per the kernel code style'
@echo ' checkpatch : run the kernel code style checker tool on source file(s)'
@echo
@echo '--- kernel static analyzer targets ---'
@echo 'sa : "wrapper" target over the following kernel static analyzer targets'
@echo ' sa_sparse : run the static analysis sparse tool on the source file(s)'
@echo ' sa_gcc : run gcc with option -W1 ("Generally useful warnings") on the source file(s)'
@echo ' sa_flawfinder : run the static analysis flawfinder tool on the source file(s)'
@echo 'TIP: use coccinelle as well (requires spatch): https://www.kernel.org/doc/html/v4.15/dev-tools/coccinelle.html'
@echo
@echo '--- kernel dynamic analysis targets ---'
@echo 'da_kasan : DUMMY target: this is to remind you to run your code with the dynamic analysis KASAN tool enabled; requires configuring the kernel with CONFIG_KASAN On, rebuild and boot it'
@echo 'da_lockdep : DUMMY target: this is to remind you to run your code with the dynamic analysis LOCKDEP tool (for deep locking issues analysis) enabled; requires configuring the kernel with CONFIG_PROVE_LOCKING On, rebuild and boot it'
@echo 'TIP: best to build a debug kernel with several kernel debug config options turned On, boot via it and run all your test cases'
@echo
@echo '--- misc targets ---'
@echo 'tarxz-pkg : tar and compress the LKM source files as a tar.xz into the dir above; allows one to transfer and build the module on another system'
@echo 'help : this help target'

View File

@@ -0,0 +1,156 @@
/*
* ch6/foreach/thrd_showall/thrd_showall.c
***************************************************************
* This program is part of the source code released for the book
* "Linux Kernel Programming" 2E
* (c) Author: Kaiwan N Billimoria
* Publisher: Packt
* GitHub repository:
* https://github.com/PacktPublishing/Linux-Kernel-Programming_2E
*
* From: Ch 6 : Kernel and MM Internals Essentials
****************************************************************
* Brief Description:
* This kernel module iterates over the task structures of all *threads*
* currently alive on the box, printing out some details.
* We use the do_each_thread() { ... } while_each_thread() macros to do
* so here.
*
* For details, please refer the book, Ch 6.
*/
//#define pr_fmt(fmt) "%s: " fmt, KBUILD_MODNAME
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h> /* current() */
#include <linux/version.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 10, 0)
#include <linux/sched/signal.h>
#endif
MODULE_AUTHOR("Kaiwan N Billimoria");
MODULE_DESCRIPTION("LKP 2E:ch6/foreach/thrd_showall: \
demo to display all threads by iterating over the task list");
MODULE_LICENSE("Dual MIT/GPL");
MODULE_VERSION("0.2");
/* Display just CPU 0's idle thread, i.e., the pid 0 task,
* the (terribly named) 'swapper/n'; n = 0, 1, 2,...
* Again, init_task is always the task structure of the first CPU's
* idle thread, i.e., we're referencing swapper/0.
*/
static inline void disp_idle_thread(void)
{
struct task_struct *t = &init_task;
/* We know that the swapper is a kernel thread */
pr_info("%8d %8d 0x%px 0x%px [%16s]\n",
t->pid, t->pid, t, t->stack, t->comm);
}
static int showthrds(void)
{
struct task_struct *g = NULL, *t = NULL; /* 'g' : process ptr; 't': thread ptr */
int nr_thrds = 1, total = 1; /* total init to 1 for the idle thread */
#define BUFMAX 256
#define TMPMAX 128
char buf[BUFMAX], tmp[TMPMAX];
const char hdr[] =
"------------------------------------------------------------------------------------------\n"
" TGID PID current stack-start Thread Name MT? # thrds\n"
"------------------------------------------------------------------------------------------\n";
pr_info("%s", hdr);
disp_idle_thread();
/*
* The do_each_thread() / while_each_thread() is a pair of macros that iterates over
* _all_ task structures in memory.
* The task structs are global of course; this implies we should hold a lock of some
* sort while working on them (even if only reading!). So, doing
* read_lock(&tasklist_lock);
* [...]
* read_unlock(&tasklist_lock);
* BUT, this lock - tasklist_lock - isn't exported and thus unavailable to modules.
* So, using an RCU read lock is indicated here (this has been added later to this code).
* FYI: a) Ch 12 and Ch 13 cover the details on kernel synchronization.
* b) Read Copy Update (RCU) is a complex synchronization mechanism; it's
* conceptually explained really well in this blog article:
* https://reberhardt.com/blog/2020/11/18/my-first-kernel-module.html
*/
rcu_read_lock();
do_each_thread(g, t) { /* 'g' : process ptr; 't': thread ptr */
// task_lock(t);
get_task_struct(t); // take a reference to the task struct
snprintf(buf, BUFMAX-1, "%8d %8d ", g->tgid, t->pid);
/* task_struct addr and kernel-mode stack addr */
snprintf(tmp, TMPMAX-1, " 0x%px", t);
/*
* To concatenate the temp string to our buffer, we could go with the
* strncat() here; flawfinder, though, points out this is potentially
* dangerous; so we simply use another snprintf() to achieve the same.
* Why not use strlcat() instead? Here, it runs into trouble - being
* called in an atomic context, which isn't ok (due to the
* might_sleep() within it's code)...
*/
snprintf(buf, BUFMAX-1, "%s%s 0x%px", buf, tmp, t->stack);
if (!g->mm) { // kernel thread
/* One might question why we don't use the get_task_comm() to obtain
* the task's name here; the short reason: it causes a deadlock! We
* shall explore this (and how to avoid it) in some detail in Ch 17 -
* Kernel Synchronization Part 2. For now, we just do it the simple way
*/
snprintf(tmp, TMPMAX-1, " [%16s]", t->comm);
} else {
snprintf(tmp, TMPMAX-1, " %16s ", t->comm);
}
snprintf(buf, BUFMAX-1, "%s%s", buf, tmp);
/* Is this the "main" thread of a multithreaded process?
* We check by seeing if (a) it's a userspace thread,
* (b) it's TGID == it's PID, and (c), there are >1 threads in
* the process.
* If so, display the number of threads in the overall process
* to the right..
*/
nr_thrds = get_nr_threads(g);
if (g->mm && (g->tgid == t->pid) && (nr_thrds > 1)) {
snprintf(tmp, TMPMAX-1, " %3d", nr_thrds);
snprintf(buf, BUFMAX-1, "%s%s", buf, tmp);
}
snprintf(buf, BUFMAX-1, "%s\n", buf);
pr_info("%s", buf);
total++;
memset(buf, 0, sizeof(buf));
memset(tmp, 0, sizeof(tmp));
put_task_struct(t); // release reference to the task struct
// task_unlock(t);
} while_each_thread(g, t);
rcu_read_unlock();
return total;
}
static int __init thrd_showall_init(void)
{
int total;
pr_info("%s: inserted\n", KBUILD_MODNAME);
total = showthrds();
pr_info("%s: total # of threads on the system: %d\n", KBUILD_MODNAME, total);
return 0; /* success */
}
static void __exit thrd_showall_exit(void)
{
pr_info("%s: removed\n", KBUILD_MODNAME);
}
module_init(thrd_showall_init);
module_exit(thrd_showall_exit);