Initial commit
This commit is contained in:
297
ch13/2_percpu/Makefile
Normal file
297
ch13/2_percpu/Makefile
Normal file
@@ -0,0 +1,297 @@
|
||||
# ch5/lkm_template/Makefile
|
||||
# ***************************************************************
|
||||
# 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 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); just to remind you!
|
||||
# - a packaging (.tar.xz) target and
|
||||
# - a help target.
|
||||
#
|
||||
# To get started, just type:
|
||||
# make help
|
||||
#
|
||||
# For details, please refer the book, Ch 5, section
|
||||
# 'A "better" Makefile template for your kernel modules'.
|
||||
#
|
||||
# AUTHOR : Kaiwan N Billimoria
|
||||
# DESCRIPTION : A simple kernel module 'better' Makefile template
|
||||
# LICENSE : Dual MIT/GPL
|
||||
# VERSION : 0.2
|
||||
|
||||
#------------------------------------------------------------------
|
||||
# IMPORTANT : Set FNAME_C to the kernel module name source filename (without .c).
|
||||
# This enables you to use this Makefile as a template; just update this variable!
|
||||
# As well, the MYDEBUG variable (see it below) can be set to 'y' or 'n' (no being
|
||||
# the default)
|
||||
FNAME_C := percpu_var
|
||||
ifeq ($(FNAME_C),)
|
||||
$(error ERROR: you Must set the FNAME_C variable in the Makefile)
|
||||
endif
|
||||
#------------------------------------------------------------------
|
||||
|
||||
#--- To support cross-compiling for kernel modules
|
||||
# For architecture (cpu) 'arch', invoke make as:
|
||||
# make ARCH=<arch> CROSS_COMPILE=<cross-compiler-prefix>
|
||||
# F.e. to cross-compile for the AArch64:
|
||||
# make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-
|
||||
#
|
||||
# Alternately:
|
||||
# export ARCH=<arch>
|
||||
# export CROSS_COMPILE=<cross-compiler-prefix>
|
||||
# make
|
||||
#
|
||||
# The KDIR var is set to a sample path below; you're expected to update it on
|
||||
# your box to the appropriate path to the kernel source tree for that arch.
|
||||
ifeq ($(ARCH),arm)
|
||||
# *UPDATE* 'KDIR' below to point to the ARM Linux kernel source tree on your box
|
||||
KDIR ?= ~/arm_prj/kernel/linux
|
||||
else ifeq ($(ARCH),arm64)
|
||||
# *UPDATE* 'KDIR' below to point to the ARM64 (Aarch64) Linux kernel source
|
||||
# tree on your box
|
||||
KDIR ?= ~/arm64_prj/kernel/linux-5.10.60
|
||||
else ifeq ($(ARCH),powerpc)
|
||||
# *UPDATE* 'KDIR' below to point to the PPC64 Linux kernel source tree on your box
|
||||
KDIR ?= ~/ppc_prj/kernel/linux-5.4
|
||||
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
|
||||
#---
|
||||
|
||||
# Compiler
|
||||
CC := $(CROSS_COMPILE)gcc
|
||||
#CC := clang
|
||||
STRIP := ${CROSS_COMPILE}strip
|
||||
|
||||
PWD := $(shell pwd)
|
||||
obj-m += ${FNAME_C}.o
|
||||
|
||||
#--- Debug or production mode?
|
||||
# Set the MYDEBUG variable accordingly to y/n resp. We keep it off (n) by default.
|
||||
# (Actually, debug info is always going to be generated when you build the
|
||||
# module on a debug kernel, where CONFIG_DEBUG_INFO is defined, making this
|
||||
# setting of the ccflags-y (or the older EXTRA_CFLAGS) variable mostly redundant
|
||||
# (besides the still useful -DDEBUG).
|
||||
# This simply helps us influence the build on a production kernel, forcing
|
||||
# generation of debug symbols, if so required. Also, realize that the DEBUG
|
||||
# macro is turned on by many CONFIG_*DEBUG* options; hence, we use a different
|
||||
# macro var name, MYDEBUG).
|
||||
MYDEBUG := n
|
||||
DBG_STRIP := y
|
||||
ifeq (${MYDEBUG}, y)
|
||||
# https://www.kernel.org/doc/html/latest/kbuild/makefiles.html#compilation-flags
|
||||
# EXTRA_CFLAGS deprecated; use ccflags-y
|
||||
ccflags-y += -DDEBUG -g -ggdb -gdwarf-4 -Wall -fno-omit-frame-pointer -fvar-tracking-assignments
|
||||
DBG_STRIP := n
|
||||
else
|
||||
ccflags-y += -UDEBUG
|
||||
endif
|
||||
# We always keep the dynamic debug facility enabled; this allows us to turn
|
||||
# dynamically turn on/off debug printk's later... To disable it simply comment
|
||||
# out the following line
|
||||
ccflags-y += -DDYNAMIC_DEBUG_MODULE
|
||||
KMODDIR ?= /lib/modules/$(shell uname -r)
|
||||
|
||||
# Gain access to kernel configs
|
||||
include $(KDIR)/.config
|
||||
|
||||
# Strip the module? Note:
|
||||
# a) Only strip debug symbols else it won't load correctly
|
||||
# b) WARNING! Don't strip modules when using CONFIG_MODULE_SIG* crytographic security
|
||||
ifdef CONFIG_MODULE_SIG
|
||||
DBG_STRIP := n
|
||||
endif
|
||||
ifdef CONFIG_MODULE_SIG_ALL
|
||||
DBG_STRIP := n
|
||||
endif
|
||||
ifdef CONFIG_MODULE_SIG_FORCE
|
||||
DBG_STRIP := n
|
||||
endif
|
||||
|
||||
|
||||
all:
|
||||
@echo
|
||||
@echo '--- Building : KDIR=${KDIR} ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} ccflags-y="${ccflags-y}" MYDEBUG=${MYDEBUG} DBG_STRIP=${DBG_STRIP} ---'
|
||||
@${CC} --version|head -n1
|
||||
@echo
|
||||
make -C $(KDIR) M=$(PWD) modules
|
||||
if [ "${DBG_STRIP}" = "y" ]; then \
|
||||
${STRIP} --strip-debug ${FNAME_C}.ko ; \
|
||||
fi
|
||||
install:
|
||||
@echo
|
||||
@echo "--- installing ---"
|
||||
@echo " [First, invoking the 'make' ]"
|
||||
make
|
||||
@echo
|
||||
@echo " [Now for the 'sudo make install' ]"
|
||||
sudo make -C $(KDIR) M=$(PWD) modules_install
|
||||
sudo depmod
|
||||
@echo " [If !debug and !(module signing), stripping debug info from ${KMODDIR}/extra/${FNAME_C}.ko]"
|
||||
if [ "${DBG_STRIP}" = "y" ]; then \
|
||||
sudo ${STRIP} --strip-debug ${KMODDIR}/extra/${FNAME_C}.ko ; \
|
||||
fi
|
||||
nsdeps:
|
||||
@echo "--- nsdeps (namespace dependencies resolution; for possibly importing ns's) ---"
|
||||
make -C $(KDIR) M=$(PWD) nsdeps
|
||||
clean:
|
||||
@echo
|
||||
@echo "--- cleaning ---"
|
||||
@echo
|
||||
make -C $(KDIR) M=$(PWD) clean
|
||||
# from 'indent'; comment out if you want the backup kept
|
||||
rm -f *~
|
||||
|
||||
# Any usermode programs to build? Insert the build target(s) below
|
||||
|
||||
|
||||
#--------------- 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:
|
||||
ifeq (,$(shell which indent))
|
||||
$(error ERROR: install indent first)
|
||||
endif
|
||||
@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:
|
||||
ifeq (,$(shell which sparse))
|
||||
$(error ERROR: install sparse first)
|
||||
endif
|
||||
make clean
|
||||
@echo
|
||||
@echo "--- static analysis with sparse ---"
|
||||
@echo
|
||||
# If you feel it's too much, use C=1 instead
|
||||
# NOTE: deliberately IGNORING warnings from kernel headers!
|
||||
make -Wsparse-all C=2 CHECK="/usr/bin/sparse --os=linux --arch=$(ARCH)" -C $(KDIR) M=$(PWD) modules 2>&1 |egrep -v "^\./include/.*\.h|^\./arch/.*\.h"
|
||||
|
||||
# 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:
|
||||
ifeq (,$(shell which flawfinder))
|
||||
$(error ERROR: install flawfinder first)
|
||||
endif
|
||||
make clean
|
||||
@echo
|
||||
@echo "--- static analysis with flawfinder ---"
|
||||
@echo
|
||||
flawfinder *.[ch]
|
||||
|
||||
# static analysis with cppcheck
|
||||
sa_cppcheck:
|
||||
ifeq (,$(shell which cppcheck))
|
||||
$(error ERROR: install cppcheck first)
|
||||
endif
|
||||
make clean
|
||||
@echo
|
||||
@echo "--- static analysis with cppcheck ---"
|
||||
@echo
|
||||
cppcheck -v --force --enable=all -i .tmp_versions/ -i *.mod.c -i bkp/ --suppress=missingIncludeSystem .
|
||||
|
||||
# Packaging: just generates a tar.xz of the source as of now
|
||||
tarxz-pkg:
|
||||
rm -f ../${FNAME_C}.tar.xz 2>/dev/null
|
||||
make clean
|
||||
@echo
|
||||
@echo "--- packaging ---"
|
||||
@echo
|
||||
tar caf ../${FNAME_C}.tar.xz *
|
||||
ls -l ../${FNAME_C}.tar.xz
|
||||
@echo '=== package created: ../$(FNAME_C).tar.xz ==='
|
||||
@echo ' TIP: When extracting, to extract into a directory with the same name as the tar file, do this:'
|
||||
@echo ' tar -xvf ${FNAME_C}.tar.xz --one-top-level'
|
||||
|
||||
help:
|
||||
@echo '=== Makefile Help : additional targets available ==='
|
||||
@echo
|
||||
@echo 'TIP: Type make <tab><tab> to show all valid targets'
|
||||
@echo 'FYI: KDIR=${KDIR} ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} ccflags-y="${ccflags-y}" MYDEBUG=${MYDEBUG} DBG_STRIP=${DBG_STRIP}'
|
||||
|
||||
@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 here: /lib/modules/$(shell uname -r)/).'
|
||||
@echo ' : Takes care of performing debug-only symbols stripping iff MYDEBUG=n and not using module signature'
|
||||
@echo 'nsdeps : namespace dependencies resolution; for possibly importing namespaces'
|
||||
@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 ' sa_cppcheck : run the static analysis cppcheck tool on the source file(s)'
|
||||
@echo 'TIP: use Coccinelle as well: https://www.kernel.org/doc/html/v6.1/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 ' TIP: When extracting, to extract into a directory with the same name as the tar file, do this:'
|
||||
@echo ' tar -xvf ${FNAME_C}.tar.xz --one-top-level'
|
||||
@echo 'help : this help target'
|
||||
295
ch13/2_percpu/percpu_var.c
Normal file
295
ch13/2_percpu/percpu_var.c
Normal file
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* ch13/2_percpu/percpu_var.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 13 : Kernel Synchronization, Part 2
|
||||
****************************************************************
|
||||
* Brief Description:
|
||||
* FYI: we use a very hack-y approach to accessing the unexported symbol
|
||||
* sched_setaffinity(); details follow. We get away with it here, but
|
||||
* DON'T use this approach in production.
|
||||
*
|
||||
* For details, please refer the book, Ch 13.
|
||||
*/
|
||||
#define pr_fmt(fmt) "%s:%s(): " fmt, KBUILD_MODNAME, __func__
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/task.h> // {get,put}_task_struct()
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/delay.h>
|
||||
#include "../../convenient.h"
|
||||
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
MODULE_AUTHOR("Kaiwan N Billimoria");
|
||||
MODULE_DESCRIPTION("LKP 2E book:ch13/2_percpu: demo of using percpu variables");
|
||||
MODULE_LICENSE("Dual MIT/GPL");
|
||||
MODULE_VERSION("0.2");
|
||||
|
||||
#define SHOW_CPU_CTX() do { \
|
||||
pr_info("*** kthread PID %d on cpu %d now ***\n",\
|
||||
current->pid, raw_smp_processor_id()); \
|
||||
} while(0)
|
||||
|
||||
#define MAX_KTHRDS 2
|
||||
#define THRD0_ITERS 3
|
||||
#define THRD1_ITERS 3
|
||||
|
||||
static unsigned long func_ptr;
|
||||
module_param(func_ptr, ulong, 0);
|
||||
|
||||
// schedsa_ptr is our function pointer to the sched_setaffinity() function
|
||||
unsigned long (*schedsa_ptr)(pid_t, const struct cpumask *) = NULL;
|
||||
|
||||
static struct task_struct *arr_tsk[MAX_KTHRDS];
|
||||
|
||||
/*--- The percpu variables, an integer 'pcpa' and a data structure --- */
|
||||
/* This percpu integer 'pcpa' is statically allocated and initialized to 0;
|
||||
* one integer instance will be allocated per CPU core!
|
||||
*/
|
||||
DEFINE_PER_CPU(int, pcpa);
|
||||
|
||||
/* This "driver context" percpu structure will be dynamically allocated
|
||||
* via alloc_percpu()
|
||||
*/
|
||||
static struct drv_ctx {
|
||||
int tx, rx; /* here, as a demo, we just use these two members,
|
||||
ignoring the rest */
|
||||
int err, myword;
|
||||
u32 config1, config2;
|
||||
u64 config3;
|
||||
} *pcp_ctx;
|
||||
|
||||
/* Display our percpu variables */
|
||||
static inline void disp_our_percpu_vars(void)
|
||||
{
|
||||
int i, val, rx, tx;
|
||||
|
||||
PRINT_CTX();
|
||||
for_each_online_cpu(i) {
|
||||
val = per_cpu(pcpa, i);
|
||||
rx = per_cpu_ptr(pcp_ctx, i)->rx;
|
||||
tx = per_cpu_ptr(pcp_ctx, i)->tx;
|
||||
pr_info(" cpu %2d: pcpa = %+d, rx = %5d, tx = %5d\n",
|
||||
i, val, rx, tx);
|
||||
}
|
||||
}
|
||||
|
||||
static long set_cpuaffinity(unsigned int cpu)
|
||||
{
|
||||
struct cpumask mask;
|
||||
long ret = 0;
|
||||
|
||||
unsigned int euid = from_kuid(&init_user_ns, current_euid());
|
||||
struct cred *new;
|
||||
|
||||
/*
|
||||
* Not root? get root! (hey, we're in kernel mode :)
|
||||
* This isn't really required; we're just showing off...
|
||||
*/
|
||||
if (unlikely(euid)) {
|
||||
pr_info("%s(): before commit_creds(): uid=%u euid=%u\n",
|
||||
__func__, from_kuid(&init_user_ns, current_uid()),
|
||||
from_kuid(&init_user_ns, current_euid()));
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
new->fsuid = new->uid = new->euid = make_kuid(current_user_ns(), 0);
|
||||
if (!uid_valid(new->uid))
|
||||
return -1;
|
||||
commit_creds(new);
|
||||
pr_info("after commit_creds(): uid=%u euid=%u\n",
|
||||
from_kuid(&init_user_ns, current_uid()),
|
||||
from_kuid(&init_user_ns, current_euid()));
|
||||
}
|
||||
|
||||
/* pr_info("setting cpu mask to cpu #%u now...\n", cpu); */
|
||||
cpumask_clear(&mask);
|
||||
cpumask_set_cpu(cpu, &mask); // 1st param is the CPU number, not bitmask
|
||||
/* !HACK! sched_setaffinity() is NOT exported, we can't call it
|
||||
* sched_setaffinity(0, &mask); // 0 => on self
|
||||
* So we invoke it via it's function pointer!
|
||||
*/
|
||||
ret = (*schedsa_ptr)(0, &mask); // 0 => on self
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Our kernel thread worker routine.
|
||||
* The parameter is the thread number; it shoud be 0 or 1.
|
||||
*/
|
||||
static int thrd_work(void *arg)
|
||||
{
|
||||
int i, val;
|
||||
long thrd = (long)arg;
|
||||
struct drv_ctx *ctx;
|
||||
|
||||
if (thrd != 0 && thrd != 1) {
|
||||
pr_err("'thrd' value invalid (thrd=%ld)\n", thrd);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Set CPU affinity mask to 'thrd', which is either 0 or 1 */
|
||||
if (set_cpuaffinity(thrd) < 0) {
|
||||
pr_err("setting cpu affinity mask for our kthread %ld failed\n", thrd);
|
||||
return -ENOSYS;
|
||||
}
|
||||
SHOW_CPU_CTX();
|
||||
|
||||
if (thrd == 0) { /* our kthread #0 runs on CPU 0 */
|
||||
for (i=0; i<THRD0_ITERS; i++) {
|
||||
/* Operate on our per-cpu integer */
|
||||
val = ++ get_cpu_var(pcpa);
|
||||
pr_info(" thrd_0/cpu0: pcpa = %+d\n", val);
|
||||
put_cpu_var(pcpa);
|
||||
|
||||
/* Operate on our per-cpu structure */
|
||||
ctx = get_cpu_ptr(pcp_ctx);
|
||||
ctx->tx += 100;
|
||||
pr_info(" thrd_0/cpu0: pcp ctx: tx = %5d, rx = %5d\n",
|
||||
ctx->tx, ctx->rx);
|
||||
put_cpu_ptr(pcp_ctx);
|
||||
}
|
||||
} else if (thrd == 1) { /* our kthread #1 runs on CPU 1 */
|
||||
for (i=0; i<THRD1_ITERS; i++) {
|
||||
/* Operate on our per-cpu integer */
|
||||
val = -- get_cpu_var(pcpa);
|
||||
pr_info(" thrd_1/cpu1: pcpa = %+d\n", val);
|
||||
put_cpu_var(pcpa);
|
||||
|
||||
/* Operate on our per-cpu structure */
|
||||
ctx = get_cpu_ptr(pcp_ctx);
|
||||
#if 0
|
||||
/* If we try and run a blocking API within the per-CPU
|
||||
* 'critical section', we land up with a kernel bug; hence,
|
||||
* it's commented out by default.
|
||||
*/
|
||||
void *vp=vmalloc(1024*1024); // vmalloc(), vfree() are possibly blocking!
|
||||
mdelay(1); // mdelay(), printk() are non-blocking
|
||||
vfree(vp);
|
||||
#endif
|
||||
ctx->rx += 200;
|
||||
pr_info(" thrd_1/cpu1: pcp ctx: tx = %5d, rx = %5d\n",
|
||||
ctx->tx, ctx->rx);
|
||||
put_cpu_ptr(pcp_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
disp_our_percpu_vars();
|
||||
pr_info("Our kernel thread #%ld exiting now...\n", thrd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* run_kthrd()
|
||||
* Creates and wakes up a kernel thread; a simple wrapper over the kernel's
|
||||
* kthread_run() wrapper.
|
||||
* Be sure to call the kthread_stop() routine upon cleanup.
|
||||
* (Details on kthread management are covered in the companion volume-
|
||||
* Linux Kernel Programming - Part 2, in Chapter 5, Working with Kernel Timers,
|
||||
* Threads, and Workqueues).
|
||||
*/
|
||||
static int run_kthrd(char *kname, long thrdnum)
|
||||
{
|
||||
/* kthread_run(): 1st param is the function to execute, 2nd is the
|
||||
* (void * arg) to pass; ret val is task ptr on success
|
||||
*/
|
||||
arr_tsk[thrdnum] = kthread_run(thrd_work, (void *)thrdnum,
|
||||
"%s/%ld", kname, thrdnum);
|
||||
if (!arr_tsk[thrdnum]) {
|
||||
pr_err(" kthread_run() for our kthread %ld failed\n", thrdnum);
|
||||
return -1;
|
||||
}
|
||||
get_task_struct(arr_tsk[thrdnum]); /* increment refcnt, "take" the task
|
||||
* struct, ensuring that the task does not simply die */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int __init init_percpu_var(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("inserted\n");
|
||||
|
||||
/* !WARNING! This is considered a hack.
|
||||
* As sched_setaffinity() isn't exported, we don't have access to it
|
||||
* within this kernel module. So, here we resort to a hack:
|
||||
* a) Until 5.7, we could directly use the kallsyms_lookup_name() function
|
||||
* (which works when CONFIG_KALLSYMS is defined) to retrieve the function
|
||||
* pointer, and subsequently call the function via it's pointer (with 'C'
|
||||
* what you do is only limited by your imagination :).
|
||||
* b) From 5.7 on, the kernel devs unexported the kallsyms_lookup_name()!
|
||||
* (Commit id 0bd476e6c671. Rationale: https://lwn.net/Articles/813350/).
|
||||
* With it gone, or not, we now simply use this approach:
|
||||
* A helper script (named run) greps the sched_setaffinity() function's
|
||||
* address and passes it to this module as a parameter! Here, we equate
|
||||
* it to the expected function signature - that of sched_setaffinity() -
|
||||
* and use it.
|
||||
* *Not* pedantically right, but hey, it works. Don't do this in production.
|
||||
*/
|
||||
ret = -ENOSYS;
|
||||
if (!func_ptr) {
|
||||
pr_warn("couldn't obtain sched_setaffinity() addr via module param, aborting ...\n");
|
||||
return ret;
|
||||
}
|
||||
schedsa_ptr = (unsigned long (*)(pid_t pid, const struct cpumask *in_mask))func_ptr;
|
||||
|
||||
/* Dynamically allocate the percpu structures; one structure instance
|
||||
* will be allocated per CPU core!
|
||||
*/
|
||||
ret = -ENOMEM;
|
||||
pcp_ctx = (struct drv_ctx __percpu *) alloc_percpu(struct drv_ctx);
|
||||
if (!pcp_ctx) {
|
||||
pr_info("alloc_percpu() failed, aborting...\n");
|
||||
goto out1;
|
||||
}
|
||||
|
||||
/* Spawn two kernel threads */
|
||||
ret = -ENOMEM;
|
||||
if (run_kthrd("thrd_0", 0) < 0) {
|
||||
pr_info("kthread thrd #0 not created, aborting...\n");
|
||||
goto out2;
|
||||
}
|
||||
if (run_kthrd("thrd_1", 1) < 0) {
|
||||
pr_info("kthread thrd #1 not created, aborting...\n");
|
||||
goto out3;
|
||||
}
|
||||
|
||||
return 0; /* success */
|
||||
|
||||
out3:
|
||||
kthread_stop(arr_tsk[0]);
|
||||
out2:
|
||||
free_percpu(pcp_ctx);
|
||||
out1:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit exit_percpu_var(void)
|
||||
{
|
||||
kthread_stop(arr_tsk[0]);
|
||||
kthread_stop(arr_tsk[1]);
|
||||
disp_our_percpu_vars();
|
||||
free_percpu(pcp_ctx);
|
||||
pr_info("removed.\n");
|
||||
}
|
||||
|
||||
module_init(init_percpu_var);
|
||||
module_exit(exit_percpu_var);
|
||||
36
ch13/2_percpu/run
Executable file
36
ch13/2_percpu/run
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
# Wrapper script to correctly load up the percpu_var.ko module.
|
||||
name=$(basename $0)
|
||||
|
||||
# !WARNING! This is considered a hack.
|
||||
# As sched_setaffinity() isn't exported, we don't have access to it
|
||||
# within this kernel module. So, here we resort to a hack:
|
||||
# a) Until 5.7, we could directly use the kallsyms_lookup_name() function
|
||||
# (which works when CONFIG_KALLSYMS is defined) to retrieve the function
|
||||
# pointer, and subsequently call the function via it's pointer (with 'C'
|
||||
# what you do is only limited by your imagination :).
|
||||
# b) From 5.7 on, the kernel devs unexported the kallsyms_lookup_name()!
|
||||
# (Rationale: https://lwn.net/Articles/813350/).
|
||||
#
|
||||
# So, with it gone, or not, we now simply use this approach:
|
||||
# A helper script (this one!) greps the sched_setaffinity()
|
||||
# address and passes it to the module as a parameter! There, we equate it to
|
||||
# the expected function signature - that of sched_setaffinity() - and use it.
|
||||
# *Not* pedantically right, but hey, it works. Don't do this in production.
|
||||
#
|
||||
KMOD=percpu_var
|
||||
KFUNC=sched_setaffinity
|
||||
KFUNC_PTR=0x$(sudo grep -w "T ${KFUNC}" /proc/kallsyms |awk '{print $1}')
|
||||
[[ -z "${KFUNC_PTR}" ]] && {
|
||||
echo "${name}: lookup of kallsyms_lookup_name() failed, aborting..."
|
||||
exit 1
|
||||
}
|
||||
echo "sched_setaffinity() on $(uname -r): KFUNC_PTR=${KFUNC_PTR}
|
||||
WARNING! this is certainly an info-leak! Don't do stuff like this in production."
|
||||
|
||||
make clean
|
||||
make || exit 1
|
||||
sudo rmmod ${KMOD} 2>/dev/null
|
||||
sudo dmesg -C
|
||||
sudo insmod ./${KMOD}.ko func_ptr=${KFUNC_PTR}
|
||||
sudo dmesg
|
||||
Reference in New Issue
Block a user