diff --git a/index.html b/index.html index c09a05c..5f0ed00 100644 --- a/index.html +++ b/index.html @@ -20,7 +20,7 @@
-
1/* -2 * sleep.c - create a /proc file, and if several processes try to open it -3 * at the same time, put all but one to sleep. -4 */ +1/* +2 * sleep.c - create a /proc file, and if several processes try to open it +3 * at the same time, put all but one to sleep. +4 */ 5 -6#include <linux/kernel.h> /* We're doing kernel work */ -7#include <linux/module.h> /* Specifically, a module */ -8#include <linux/proc_fs.h> /* Necessary because we use proc fs */ -9#include <linux/sched.h> /* For putting processes to sleep and -10 waking them up */ -11#include <linux/uaccess.h> /* for get_user and put_user */ -12#include <linux/version.h> +6#include <linux/kernel.h> /* We're doing kernel work */ +7#include <linux/module.h> /* Specifically, a module */ +8#include <linux/proc_fs.h> /* Necessary because we use proc fs */ +9#include <linux/sched.h> /* For putting processes to sleep and +10 waking them up */ +11#include <linux/uaccess.h> /* for get_user and put_user */ +12#include <linux/version.h> 13 -14#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) -15#define HAVE_PROC_OPS -16#endif +14#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +15#define HAVE_PROC_OPS +16#endif 17 -18/* Here we keep the last message received, to prove that we can process our -19 * input. -20 */ -21#define MESSAGE_LENGTH 80 -22static char Message[MESSAGE_LENGTH]; +18/* Here we keep the last message received, to prove that we can process our +19 * input. +20 */ +21#define MESSAGE_LENGTH 80 +22static char Message[MESSAGE_LENGTH]; 23 -24static struct proc_dir_entry *Our_Proc_File; -25#define PROC_ENTRY_FILENAME "sleep" +24static struct proc_dir_entry *Our_Proc_File; +25#define PROC_ENTRY_FILENAME "sleep" 26 -27/* Since we use the file operations struct, we can't use the special proc -28 * output provisions - we have to use a standard read function, which is this -29 * function. -30 */ -31static ssize_t module_output(struct file *file, /* see include/linux/fs.h */ -32 char *buf, /* The buffer to put data to -33 (in the user segment) */ -34 size_t len, /* The length of the buffer */ +27/* Since we use the file operations struct, we can't use the special proc +28 * output provisions - we have to use a standard read function, which is this +29 * function. +30 */ +31static ssize_t module_output(struct file *file, /* see include/linux/fs.h */ +32 char *buf, /* The buffer to put data to +33 (in the user segment) */ +34 size_t len, /* The length of the buffer */ 35 loff_t *offset) 36{ -37 static int finished = 0; -38 int i; -39 char message[MESSAGE_LENGTH + 30]; +37 static int finished = 0; +38 int i; +39 char message[MESSAGE_LENGTH + 30]; 40 -41 /* Return 0 to signify end of file - that we have nothing more to say -42 * at this point. -43 */ -44 if (finished) { +41 /* Return 0 to signify end of file - that we have nothing more to say +42 * at this point. +43 */ +44 if (finished) { 45 finished = 0; -46 return 0; +46 return 0; 47 } 48 -49 sprintf(message, "Last input:%s\n", Message); -50 for (i = 0; i < len && message[i]; i++) +49 sprintf(message, "Last input:%s\n", Message); +50 for (i = 0; i < len && message[i]; i++) 51 put_user(message[i], buf + i); 52 53 finished = 1; -54 return i; /* Return the number of bytes "read" */ +54 return i; /* Return the number of bytes "read" */ 55} 56 -57/* This function receives input from the user when the user writes to the -58 * /proc file. -59 */ -60static ssize_t module_input(struct file *file, /* The file itself */ -61 const char *buf, /* The buffer with input */ -62 size_t length, /* The buffer's length */ -63 loff_t *offset) /* offset to file - ignore */ +57/* This function receives input from the user when the user writes to the +58 * /proc file. +59 */ +60static ssize_t module_input(struct file *file, /* The file itself */ +61 const char *buf, /* The buffer with input */ +62 size_t length, /* The buffer's length */ +63 loff_t *offset) /* offset to file - ignore */ 64{ -65 int i; +65 int i; 66 -67 /* Put the input into Message, where module_output will later be able -68 * to use it. -69 */ -70 for (i = 0; i < MESSAGE_LENGTH - 1 && i < length; i++) +67 /* Put the input into Message, where module_output will later be able +68 * to use it. +69 */ +70 for (i = 0; i < MESSAGE_LENGTH - 1 && i < length; i++) 71 get_user(Message[i], buf + i); -72 /* we want a standard, zero terminated string */ -73 Message[i] = '\0'; +72 /* we want a standard, zero terminated string */ +73 Message[i] = '\0'; 74 -75 /* We need to return the number of input characters used */ -76 return i; +75 /* We need to return the number of input characters used */ +76 return i; 77} 78 -79/* 1 if the file is currently open by somebody */ -80int Already_Open = 0; +79/* 1 if the file is currently open by somebody */ +80int Already_Open = 0; 81 -82/* Queue of processes who want our file */ +82/* Queue of processes who want our file */ 83DECLARE_WAIT_QUEUE_HEAD(WaitQ); 84 -85/* Called when the /proc file is opened */ -86static int module_open(struct inode *inode, struct file *file) +85/* Called when the /proc file is opened */ +86static int module_open(struct inode *inode, struct file *file) 87{ -88 /* If the file's flags include O_NONBLOCK, it means the process does not -89 * want to wait for the file. In this case, if the file is already open, -90 * we should fail with -EAGAIN, meaning "you will have to try again", -91 * instead of blocking a process which would rather stay awake. -92 */ -93 if ((file->f_flags & O_NONBLOCK) && Already_Open) -94 return -EAGAIN; +88 /* If the file's flags include O_NONBLOCK, it means the process does not +89 * want to wait for the file. In this case, if the file is already open, +90 * we should fail with -EAGAIN, meaning "you will have to try again", +91 * instead of blocking a process which would rather stay awake. +92 */ +93 if ((file->f_flags & O_NONBLOCK) && Already_Open) +94 return -EAGAIN; 95 -96 /* This is the correct place for try_module_get(THIS_MODULE) because if -97 * a process is in the loop, which is within the kernel module, -98 * the kernel module must not be removed. -99 */ +96 /* This is the correct place for try_module_get(THIS_MODULE) because if +97 * a process is in the loop, which is within the kernel module, +98 * the kernel module must not be removed. +99 */ 100 try_module_get(THIS_MODULE); 101 -102 /* If the file is already open, wait until it is not. */ -103 while (Already_Open) { -104 int i, is_sig = 0; +102 /* If the file is already open, wait until it is not. */ +103 while (Already_Open) { +104 int i, is_sig = 0; 105 -106 /* This function puts the current process, including any system -107 * calls, such as us, to sleep. Execution will be resumed right -108 * after the function call, either because somebody called -109 * wake_up(&WaitQ) (only module_close does that, when the file -110 * is closed) or when a signal, such as Ctrl-C, is sent -111 * to the process -112 */ +106 /* This function puts the current process, including any system +107 * calls, such as us, to sleep. Execution will be resumed right +108 * after the function call, either because somebody called +109 * wake_up(&WaitQ) (only module_close does that, when the file +110 * is closed) or when a signal, such as Ctrl-C, is sent +111 * to the process +112 */ 113 wait_event_interruptible(WaitQ, !Already_Open); 114 -115 /* If we woke up because we got a signal we're not blocking, -116 * return -EINTR (fail the system call). This allows processes -117 * to be killed or stopped. -118 */ -119 for (i = 0; i < _NSIG_WORDS && !is_sig; i++) +115 /* If we woke up because we got a signal we're not blocking, +116 * return -EINTR (fail the system call). This allows processes +117 * to be killed or stopped. +118 */ +119 for (i = 0; i < _NSIG_WORDS && !is_sig; i++) 120 is_sig = current->pending.signal.sig[i] & ~current->blocked.sig[i]; 121 -122 if (is_sig) { -123 /* It is important to put module_put(THIS_MODULE) here, because -124 * for processes where the open is interrupted there will never -125 * be a corresponding close. If we do not decrement the usage -126 * count here, we will be left with a positive usage count -127 * which we will have no way to bring down to zero, giving us -128 * an immortal module, which can only be killed by rebooting -129 * the machine. -130 */ +122 if (is_sig) { +123 /* It is important to put module_put(THIS_MODULE) here, because +124 * for processes where the open is interrupted there will never +125 * be a corresponding close. If we do not decrement the usage +126 * count here, we will be left with a positive usage count +127 * which we will have no way to bring down to zero, giving us +128 * an immortal module, which can only be killed by rebooting +129 * the machine. +130 */ 131 module_put(THIS_MODULE); -132 return -EINTR; +132 return -EINTR; 133 } 134 } 135 -136 /* If we got here, Already_Open must be zero. */ +136 /* If we got here, Already_Open must be zero. */ 137 -138 /* Open the file */ +138 /* Open the file */ 139 Already_Open = 1; -140 return 0; /* Allow the access */ +140 return 0; /* Allow the access */ 141} 142 -143/* Called when the /proc file is closed */ -144int module_close(struct inode *inode, struct file *file) +143/* Called when the /proc file is closed */ +144int module_close(struct inode *inode, struct file *file) 145{ -146 /* Set Already_Open to zero, so one of the processes in the WaitQ will -147 * be able to set Already_Open back to one and to open the file. All -148 * the other processes will be called when Already_Open is back to one, -149 * so they'll go back to sleep. -150 */ +146 /* Set Already_Open to zero, so one of the processes in the WaitQ will +147 * be able to set Already_Open back to one and to open the file. All +148 * the other processes will be called when Already_Open is back to one, +149 * so they'll go back to sleep. +150 */ 151 Already_Open = 0; 152 -153 /* Wake up all the processes in WaitQ, so if anybody is waiting for the -154 * file, they can have it. -155 */ +153 /* Wake up all the processes in WaitQ, so if anybody is waiting for the +154 * file, they can have it. +155 */ 156 wake_up(&WaitQ); 157 158 module_put(THIS_MODULE); 159 -160 return 0; /* success */ +160 return 0; /* success */ 161} 162 -163/* Structures to register as the /proc file, with pointers to all the relevant -164 * functions. -165 */ +163/* Structures to register as the /proc file, with pointers to all the relevant +164 * functions. +165 */ 166 -167/* File operations for our proc file. This is where we place pointers to all -168 * the functions called when somebody tries to do something to our file. NULL -169 * means we don't want to deal with something. -170 */ -171#ifdef HAVE_PROC_OPS -172static const struct proc_ops File_Ops_4_Our_Proc_File = { -173 .proc_read = module_output, /* "read" from the file */ -174 .proc_write = module_input, /* "write" to the file */ -175 .proc_open = module_open, /* called when the /proc file is opened */ -176 .proc_release = module_close, /* called when it's closed */ +167/* File operations for our proc file. This is where we place pointers to all +168 * the functions called when somebody tries to do something to our file. NULL +169 * means we don't want to deal with something. +170 */ +171#ifdef HAVE_PROC_OPS +172static const struct proc_ops File_Ops_4_Our_Proc_File = { +173 .proc_read = module_output, /* "read" from the file */ +174 .proc_write = module_input, /* "write" to the file */ +175 .proc_open = module_open, /* called when the /proc file is opened */ +176 .proc_release = module_close, /* called when it's closed */ 177}; -178#else -179static const struct file_operations File_Ops_4_Our_Proc_File = { +178#else +179static const struct file_operations File_Ops_4_Our_Proc_File = { 180 .read = module_output, 181 .write = module_input, 182 .open = module_open, 183 .release = module_close, 184}; -185#endif +185#endif 186 -187/* Initialize the module - register the proc file */ -188static int __init sleep_init(void) +187/* Initialize the module - register the proc file */ +188static int __init sleep_init(void) 189{ 190 Our_Proc_File = 191 proc_create(PROC_ENTRY_FILENAME, 0644, NULL, &File_Ops_4_Our_Proc_File); -192 if (Our_Proc_File == NULL) { +192 if (Our_Proc_File == NULL) { 193 remove_proc_entry(PROC_ENTRY_FILENAME, NULL); -194 pr_debug("Error: Could not initialize /proc/%s\n", PROC_ENTRY_FILENAME); -195 return -ENOMEM; +194 pr_debug("Error: Could not initialize /proc/%s\n", PROC_ENTRY_FILENAME); +195 return -ENOMEM; 196 } 197 proc_set_size(Our_Proc_File, 80); 198 proc_set_user(Our_Proc_File, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID); 199 -200 pr_info("/proc/%s created\n", PROC_ENTRY_FILENAME); +200 pr_info("/proc/%s created\n", PROC_ENTRY_FILENAME); 201 -202 return 0; +202 return 0; 203} 204 -205/* Cleanup - unregister our file from /proc. This could get dangerous if -206 * there are still processes waiting in WaitQ, because they are inside our -207 * open function, which will get unloaded. I'll explain how to avoid removal -208 * of a kernel module in such a case in chapter 10. -209 */ -210static void __exit sleep_exit(void) +205/* Cleanup - unregister our file from /proc. This could get dangerous if +206 * there are still processes waiting in WaitQ, because they are inside our +207 * open function, which will get unloaded. I'll explain how to avoid removal +208 * of a kernel module in such a case in chapter 10. +209 */ +210static void __exit sleep_exit(void) 211{ 212 remove_proc_entry(PROC_ENTRY_FILENAME, NULL); -213 pr_debug("/proc/%s removed\n", PROC_ENTRY_FILENAME); +213 pr_debug("/proc/%s removed\n", PROC_ENTRY_FILENAME); 214} 215 216module_init(sleep_init); 217module_exit(sleep_exit); 218 -219MODULE_LICENSE("GPL");+219MODULE_LICENSE("GPL");
-
1/* -2 * cat_nonblock.c - open a file and display its contents, but exit rather than -3 * wait for input. -4 */ -5#include <errno.h> /* for errno */ -6#include <fcntl.h> /* for open */ -7#include <stdio.h> /* standard I/O */ -8#include <stdlib.h> /* for exit */ -9#include <unistd.h> /* for read */ +-1/* +2 * cat_nonblock.c - open a file and display its contents, but exit rather than +3 * wait for input. +4 */ +5#include <errno.h> /* for errno */ +6#include <fcntl.h> /* for open */ +7#include <stdio.h> /* standard I/O */ +8#include <stdlib.h> /* for exit */ +9#include <unistd.h> /* for read */ 10 -11#define MAX_BYTES 1024 * 4 +11#define MAX_BYTES 1024 * 4 12 -13int main(int argc, char *argv[]) +13int main(int argc, char *argv[]) 14{ -15 int fd; /* The file descriptor for the file to read */ -16 size_t bytes; /* The number of bytes read */ -17 char buffer[MAX_BYTES]; /* The buffer for the bytes */ +15 int fd; /* The file descriptor for the file to read */ +16 size_t bytes; /* The number of bytes read */ +17 char buffer[MAX_BYTES]; /* The buffer for the bytes */ 18 -19 /* Usage */ -20 if (argc != 2) { -21 printf("Usage: %s <filename>\n", argv[0]); -22 puts("Reads the content of a file, but doesn't wait for input"); +19 /* Usage */ +20 if (argc != 2) { +21 printf("Usage: %s <filename>\n", argv[0]); +22 puts("Reads the content of a file, but doesn't wait for input"); 23 exit(-1); 24 } 25 -26 /* Open the file for reading in non blocking mode */ +26 /* Open the file for reading in non blocking mode */ 27 fd = open(argv[1], O_RDONLY | O_NONBLOCK); 28 -29 /* If open failed */ -30 if (fd == -1) { -31 puts(errno == EAGAIN ? "Open would block" : "Open failed"); +29 /* If open failed */ +30 if (fd == -1) { +31 puts(errno == EAGAIN ? "Open would block" : "Open failed"); 32 exit(-1); 33 } 34 -35 /* Read the file and output its contents */ -36 do { -37 /* Read characters from the file */ +35 /* Read the file and output its contents */ +36 do { +37 /* Read characters from the file */ 38 bytes = read(fd, buffer, MAX_BYTES); 39 -40 /* If there's an error, report it and die */ -41 if (bytes == -1) { -42 if (errno = EAGAIN) -43 puts("Normally I'd block, but you told me not to"); -44 else -45 puts("Another read error"); +40 /* If there's an error, report it and die */ +41 if (bytes == -1) { +42 if (errno = EAGAIN) +43 puts("Normally I'd block, but you told me not to"); +44 else +45 puts("Another read error"); 46 exit(-1); 47 } 48 -49 /* Print the characters */ -50 if (bytes > 0) { -51 for (int i = 0; i < bytes; i++) +49 /* Print the characters */ +50 if (bytes > 0) { +51 for (int i = 0; i < bytes; i++) 52 putchar(buffer[i]); 53 } 54 -55 /* While there are no errors and the file isn't over */ -56 } while (bytes > 0); +55 /* While there are no errors and the file isn't over */ +56 } while (bytes > 0); 57 -58 return 0; +58 return 0; 59}@@ -3516,82 +3525,82 @@ another. -
1/* -2 * completions.c -3 */ -4#include <linux/completion.h> -5#include <linux/init.h> -6#include <linux/kernel.h> -7#include <linux/kthread.h> -8#include <linux/module.h> +1/* +2 * completions.c +3 */ +4#include <linux/completion.h> +5#include <linux/init.h> +6#include <linux/kernel.h> +7#include <linux/kthread.h> +8#include <linux/module.h> 9 -10static struct { -11 struct completion crank_comp; -12 struct completion flywheel_comp; +10static struct { +11 struct completion crank_comp; +12 struct completion flywheel_comp; 13} machine; 14 -15static int machine_crank_thread(void *arg) +15static int machine_crank_thread(void *arg) 16{ -17 pr_info("Turn the crank\n"); +17 pr_info("Turn the crank\n"); 18 19 complete_all(&machine.crank_comp); 20 complete_and_exit(&machine.crank_comp, 0); 21} 22 -23static int machine_flywheel_spinup_thread(void *arg) +23static int machine_flywheel_spinup_thread(void *arg) 24{ 25 wait_for_completion(&machine.crank_comp); 26 -27 pr_info("Flywheel spins up\n"); +27 pr_info("Flywheel spins up\n"); 28 29 complete_all(&machine.flywheel_comp); 30 complete_and_exit(&machine.flywheel_comp, 0); 31} 32 -33static int completions_init(void) +33static int completions_init(void) 34{ -35 struct task_struct *crank_thread; -36 struct task_struct *flywheel_thread; +35 struct task_struct *crank_thread; +36 struct task_struct *flywheel_thread; 37 -38 pr_info("completions example\n"); +38 pr_info("completions example\n"); 39 40 init_completion(&machine.crank_comp); 41 init_completion(&machine.flywheel_comp); 42 -43 crank_thread = kthread_create(machine_crank_thread, NULL, "KThread Crank"); -44 if (IS_ERR(crank_thread)) -45 goto ERROR_THREAD_1; +43 crank_thread = kthread_create(machine_crank_thread, NULL, "KThread Crank"); +44 if (IS_ERR(crank_thread)) +45 goto ERROR_THREAD_1; 46 47 flywheel_thread = kthread_create(machine_flywheel_spinup_thread, NULL, -48 "KThread Flywheel"); -49 if (IS_ERR(flywheel_thread)) -50 goto ERROR_THREAD_2; +48 "KThread Flywheel"); +49 if (IS_ERR(flywheel_thread)) +50 goto ERROR_THREAD_2; 51 52 wake_up_process(flywheel_thread); 53 wake_up_process(crank_thread); 54 -55 return 0; +55 return 0; 56 57ERROR_THREAD_2: 58 kthread_stop(crank_thread); 59ERROR_THREAD_1: 60 -61 return -1; +61 return -1; 62} 63 -64void completions_exit(void) +64void completions_exit(void) 65{ 66 wait_for_completion(&machine.crank_comp); 67 wait_for_completion(&machine.flywheel_comp); 68 -69 pr_info("completions exit\n"); +69 pr_info("completions exit\n"); 70} 71 72module_init(completions_init); 73module_exit(completions_exit); 74 -75MODULE_DESCRIPTION("Completions example"); -76MODULE_LICENSE("GPL");+75MODULE_DESCRIPTION("Completions example"); +76MODULE_LICENSE("GPL");The
machine
structure stores the completion states for the two threads. At the exit point of each thread the respective completion state is updated, and @@ -3621,47 +3630,47 @@ might deploy them in userland. This may be all that is needed to avoid collision most cases.-
1/* -2 * example_mutex.c -3 */ -4#include <linux/init.h> -5#include <linux/kernel.h> -6#include <linux/module.h> -7#include <linux/mutex.h> +1/* +2 * example_mutex.c +3 */ +4#include <linux/init.h> +5#include <linux/kernel.h> +6#include <linux/module.h> +7#include <linux/mutex.h> 8 9DEFINE_MUTEX(mymutex); 10 -11static int example_mutex_init(void) +11static int example_mutex_init(void) 12{ -13 int ret; +13 int ret; 14 -15 pr_info("example_mutex init\n"); +15 pr_info("example_mutex init\n"); 16 17 ret = mutex_trylock(&mymutex); -18 if (ret != 0) { -19 pr_info("mutex is locked\n"); +18 if (ret != 0) { +19 pr_info("mutex is locked\n"); 20 -21 if (mutex_is_locked(&mymutex) == 0) -22 pr_info("The mutex failed to lock!\n"); +21 if (mutex_is_locked(&mymutex) == 0) +22 pr_info("The mutex failed to lock!\n"); 23 24 mutex_unlock(&mymutex); -25 pr_info("mutex is unlocked\n"); -26 } else -27 pr_info("Failed to lock\n"); +25 pr_info("mutex is unlocked\n"); +26 } else +27 pr_info("Failed to lock\n"); 28 -29 return 0; +29 return 0; 30} 31 -32static void example_mutex_exit(void) +32static void example_mutex_exit(void) 33{ -34 pr_info("example_mutex exit\n"); +34 pr_info("example_mutex exit\n"); 35} 36 37module_init(example_mutex_init); 38module_exit(example_mutex_exit); 39 -40MODULE_DESCRIPTION("Mutex example"); -41MODULE_LICENSE("GPL");+40MODULE_DESCRIPTION("Mutex example"); +41MODULE_LICENSE("GPL");
0.12.2 Spinlocks
@@ -3679,69 +3688,69 @@ they will not be forgotten and will activate when the unlock happens, using the variable to retain their state.-
1/* -2 * example_spinlock.c -3 */ -4#include <linux/init.h> -5#include <linux/interrupt.h> -6#include <linux/kernel.h> -7#include <linux/module.h> -8#include <linux/spinlock.h> +1/* +2 * example_spinlock.c +3 */ +4#include <linux/init.h> +5#include <linux/interrupt.h> +6#include <linux/kernel.h> +7#include <linux/module.h> +8#include <linux/spinlock.h> 9 10DEFINE_SPINLOCK(sl_static); 11spinlock_t sl_dynamic; 12 -13static void example_spinlock_static(void) +13static void example_spinlock_static(void) 14{ -15 unsigned long flags; +15 unsigned long flags; 16 17 spin_lock_irqsave(&sl_static, flags); -18 pr_info("Locked static spinlock\n"); +18 pr_info("Locked static spinlock\n"); 19 -20 /* Do something or other safely. Because this uses 100% CPU time, this -21 * code should take no more than a few milliseconds to run. -22 */ +20 /* Do something or other safely. Because this uses 100% CPU time, this +21 * code should take no more than a few milliseconds to run. +22 */ 23 24 spin_unlock_irqrestore(&sl_static, flags); -25 pr_info("Unlocked static spinlock\n"); +25 pr_info("Unlocked static spinlock\n"); 26} 27 -28static void example_spinlock_dynamic(void) +28static void example_spinlock_dynamic(void) 29{ -30 unsigned long flags; +30 unsigned long flags; 31 32 spin_lock_init(&sl_dynamic); 33 spin_lock_irqsave(&sl_dynamic, flags); -34 pr_info("Locked dynamic spinlock\n"); +34 pr_info("Locked dynamic spinlock\n"); 35 -36 /* Do something or other safely. Because this uses 100% CPU time, this -37 * code should take no more than a few milliseconds to run. -38 */ +36 /* Do something or other safely. Because this uses 100% CPU time, this +37 * code should take no more than a few milliseconds to run. +38 */ 39 40 spin_unlock_irqrestore(&sl_dynamic, flags); -41 pr_info("Unlocked dynamic spinlock\n"); +41 pr_info("Unlocked dynamic spinlock\n"); 42} 43 -44static int example_spinlock_init(void) +44static int example_spinlock_init(void) 45{ -46 pr_info("example spinlock started\n"); +46 pr_info("example spinlock started\n"); 47 48 example_spinlock_static(); 49 example_spinlock_dynamic(); 50 -51 return 0; +51 return 0; 52} 53 -54static void example_spinlock_exit(void) +54static void example_spinlock_exit(void) 55{ -56 pr_info("example spinlock exit\n"); +56 pr_info("example spinlock exit\n"); 57} 58 59module_init(example_spinlock_init); 60module_exit(example_spinlock_exit); 61 -62MODULE_DESCRIPTION("Spinlock example"); -63MODULE_LICENSE("GPL");+62MODULE_DESCRIPTION("Spinlock example"); +63MODULE_LICENSE("GPL");
0.12.3 Read and write locks
@@ -3755,61 +3764,61 @@ the system and cause users to start revolting against the tyranny of your module.-
1/* -2 * example_rwlock.c -3 */ -4#include <linux/interrupt.h> -5#include <linux/kernel.h> -6#include <linux/module.h> +1/* +2 * example_rwlock.c +3 */ +4#include <linux/interrupt.h> +5#include <linux/kernel.h> +6#include <linux/module.h> 7 8DEFINE_RWLOCK(myrwlock); 9 -10static void example_read_lock(void) +10static void example_read_lock(void) 11{ -12 unsigned long flags; +12 unsigned long flags; 13 14 read_lock_irqsave(&myrwlock, flags); -15 pr_info("Read Locked\n"); +15 pr_info("Read Locked\n"); 16 -17 /* Read from something */ +17 /* Read from something */ 18 19 read_unlock_irqrestore(&myrwlock, flags); -20 pr_info("Read Unlocked\n"); +20 pr_info("Read Unlocked\n"); 21} 22 -23static void example_write_lock(void) +23static void example_write_lock(void) 24{ -25 unsigned long flags; +25 unsigned long flags; 26 27 write_lock_irqsave(&myrwlock, flags); -28 pr_info("Write Locked\n"); +28 pr_info("Write Locked\n"); 29 -30 /* Write to something */ +30 /* Write to something */ 31 32 write_unlock_irqrestore(&myrwlock, flags); -33 pr_info("Write Unlocked\n"); +33 pr_info("Write Unlocked\n"); 34} 35 -36static int example_rwlock_init(void) +36static int example_rwlock_init(void) 37{ -38 pr_info("example_rwlock started\n"); +38 pr_info("example_rwlock started\n"); 39 40 example_read_lock(); 41 example_write_lock(); 42 -43 return 0; +43 return 0; 44} 45 -46static void example_rwlock_exit(void) +46static void example_rwlock_exit(void) 47{ -48 pr_info("example_rwlock exit\n"); +48 pr_info("example_rwlock exit\n"); 49} 50 51module_init(example_rwlock_init); 52module_exit(example_rwlock_exit); 53 -54MODULE_DESCRIPTION("Read/Write locks example"); -55MODULE_LICENSE("GPL");+54MODULE_DESCRIPTION("Read/Write locks example"); +55MODULE_LICENSE("GPL");Of course, if you know for sure that there are no functions triggered by irqs which could possibly interfere with your logic then you can use the simpler
read_lock(&myrwlock) @@ -3825,80 +3834,80 @@ and was not overwritten by some other shenanigans. An example is shown below.
-
1/* -2 * example_atomic.c -3 */ -4#include <linux/interrupt.h> -5#include <linux/kernel.h> -6#include <linux/module.h> +@@ -3922,83 +3931,83 @@ a pointer to a string write function, which we use to write a string to the tty.1/* +2 * example_atomic.c +3 */ +4#include <linux/interrupt.h> +5#include <linux/kernel.h> +6#include <linux/module.h> 7 -8#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c" -9#define BYTE_TO_BINARY(byte) \ -10 (byte & 0x80 ? '1' : '0'), (byte & 0x40 ? '1' : '0'), \ -11 (byte & 0x20 ? '1' : '0'), (byte & 0x10 ? '1' : '0'), \ -12 (byte & 0x08 ? '1' : '0'), (byte & 0x04 ? '1' : '0'), \ -13 (byte & 0x02 ? '1' : '0'), (byte & 0x01 ? '1' : '0') +8#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c" +9#define BYTE_TO_BINARY(byte) \ +10 (byte & 0x80 ? '1' : '0'), (byte & 0x40 ? '1' : '0'), \ +11 (byte & 0x20 ? '1' : '0'), (byte & 0x10 ? '1' : '0'), \ +12 (byte & 0x08 ? '1' : '0'), (byte & 0x04 ? '1' : '0'), \ +13 (byte & 0x02 ? '1' : '0'), (byte & 0x01 ? '1' : '0') 14 -15static void atomic_add_subtract(void) +15static void atomic_add_subtract(void) 16{ 17 atomic_t debbie; 18 atomic_t chris = ATOMIC_INIT(50); 19 20 atomic_set(&debbie, 45); 21 -22 /* subtract one */ +22 /* subtract one */ 23 atomic_dec(&debbie); 24 25 atomic_add(7, &debbie); 26 -27 /* add one */ +27 /* add one */ 28 atomic_inc(&debbie); 29 -30 pr_info("chris: %d, debbie: %d\n", atomic_read(&chris), +30 pr_info("chris: %d, debbie: %d\n", atomic_read(&chris), 31 atomic_read(&debbie)); 32} 33 -34static void atomic_bitwise(void) +34static void atomic_bitwise(void) 35{ -36 unsigned long word = 0; +36 unsigned long word = 0; 37 -38 pr_info("Bits 0: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word)); +38 pr_info("Bits 0: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word)); 39 set_bit(3, &word); 40 set_bit(5, &word); -41 pr_info("Bits 1: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word)); +41 pr_info("Bits 1: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word)); 42 clear_bit(5, &word); -43 pr_info("Bits 2: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word)); +43 pr_info("Bits 2: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word)); 44 change_bit(3, &word); 45 -46 pr_info("Bits 3: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word)); -47 if (test_and_set_bit(3, &word)) -48 pr_info("wrong\n"); -49 pr_info("Bits 4: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word)); +46 pr_info("Bits 3: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word)); +47 if (test_and_set_bit(3, &word)) +48 pr_info("wrong\n"); +49 pr_info("Bits 4: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word)); 50 51 word = 255; -52 pr_info("Bits 5: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word)); +52 pr_info("Bits 5: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word)); 53} 54 -55static int example_atomic_init(void) +55static int example_atomic_init(void) 56{ -57 pr_info("example_atomic started\n"); +57 pr_info("example_atomic started\n"); 58 59 atomic_add_subtract(); 60 atomic_bitwise(); 61 -62 return 0; +62 return 0; 63} 64 -65static void example_atomic_exit(void) +65static void example_atomic_exit(void) 66{ -67 pr_info("example_atomic exit\n"); +67 pr_info("example_atomic exit\n"); 68} 69 70module_init(example_atomic_init); 71module_exit(example_atomic_exit); 72 -73MODULE_DESCRIPTION("Atomic operations example"); -74MODULE_LICENSE("GPL");+73MODULE_DESCRIPTION("Atomic operations example"); +74MODULE_LICENSE("GPL");-
1/* -2 * print_string.c - Send output to the tty we're running on, regardless if -3 * it is through X11, telnet, etc. We do this by printing the string to the -4 * tty associated with the current task. -5 */ -6#include <linux/init.h> -7#include <linux/kernel.h> -8#include <linux/module.h> -9#include <linux/sched.h> /* For current */ -10#include <linux/tty.h> /* For the tty declarations */ +1/* +2 * print_string.c - Send output to the tty we're running on, regardless if +3 * it is through X11, telnet, etc. We do this by printing the string to the +4 * tty associated with the current task. +5 */ +6#include <linux/init.h> +7#include <linux/kernel.h> +8#include <linux/module.h> +9#include <linux/sched.h> /* For current */ +10#include <linux/tty.h> /* For the tty declarations */ 11 -12static void print_string(char *str) +12static void print_string(char *str) 13{ -14 struct tty_struct *my_tty; -15 const struct tty_operations *ttyops; +14 struct tty_struct *my_tty; +15 const struct tty_operations *ttyops; 16 -17 /* The tty for the current task, for 2.6.6+ kernels */ +17 /* The tty for the current task, for 2.6.6+ kernels */ 18 my_tty = get_current_tty(); 19 ttyops = my_tty->driver->ops; 20 -21 /* If my_tty is NULL, the current task has no tty you can print to (i.e., -22 * if it is a daemon). If so, there is nothing we can do. -23 */ -24 if (my_tty) { -25 /* my_tty->driver is a struct which holds the tty's functions, -26 * one of which (write) is used to write strings to the tty. -27 * It can be used to take a string either from the user's or -28 * kernel's memory segment. -29 * -30 * The function's 1st parameter is the tty to write to, because the -31 * same function would normally be used for all tty's of a certain -32 * type. -33 * The 2nd parameter is a pointer to a string. -34 * The 3rd parameter is the length of the string. -35 * -36 * As you will see below, sometimes it's necessary to use -37 * preprocessor stuff to create code that works for different -38 * kernel versions. The (naive) approach we've taken here does not -39 * scale well. The right way to deal with this is described in -40 * section 2 of -41 * linux/Documentation/SubmittingPatches -42 */ -43 (ttyops->write)(my_tty, /* The tty itself */ -44 str, /* String */ -45 strlen(str)); /* Length */ +21 /* If my_tty is NULL, the current task has no tty you can print to (i.e., +22 * if it is a daemon). If so, there is nothing we can do. +23 */ +24 if (my_tty) { +25 /* my_tty->driver is a struct which holds the tty's functions, +26 * one of which (write) is used to write strings to the tty. +27 * It can be used to take a string either from the user's or +28 * kernel's memory segment. +29 * +30 * The function's 1st parameter is the tty to write to, because the +31 * same function would normally be used for all tty's of a certain +32 * type. +33 * The 2nd parameter is a pointer to a string. +34 * The 3rd parameter is the length of the string. +35 * +36 * As you will see below, sometimes it's necessary to use +37 * preprocessor stuff to create code that works for different +38 * kernel versions. The (naive) approach we've taken here does not +39 * scale well. The right way to deal with this is described in +40 * section 2 of +41 * linux/Documentation/SubmittingPatches +42 */ +43 (ttyops->write)(my_tty, /* The tty itself */ +44 str, /* String */ +45 strlen(str)); /* Length */ 46 -47 /* ttys were originally hardware devices, which (usually) strictly -48 * followed the ASCII standard. In ASCII, to move to a new line you -49 * need two characters, a carriage return and a line feed. On Unix, -50 * the ASCII line feed is used for both purposes - so we can not -51 * just use \n, because it would not have a carriage return and the -52 * next line will start at the column right after the line feed. -53 * -54 * This is why text files are different between Unix and MS Windows. -55 * In CP/M and derivatives, like MS-DOS and MS Windows, the ASCII -56 * standard was strictly adhered to, and therefore a newline requirs -57 * both a LF and a CR. -58 */ -59 (ttyops->write)(my_tty, "\015\012", 2); +47 /* ttys were originally hardware devices, which (usually) strictly +48 * followed the ASCII standard. In ASCII, to move to a new line you +49 * need two characters, a carriage return and a line feed. On Unix, +50 * the ASCII line feed is used for both purposes - so we can not +51 * just use \n, because it would not have a carriage return and the +52 * next line will start at the column right after the line feed. +53 * +54 * This is why text files are different between Unix and MS Windows. +55 * In CP/M and derivatives, like MS-DOS and MS Windows, the ASCII +56 * standard was strictly adhered to, and therefore a newline requirs +57 * both a LF and a CR. +58 */ +59 (ttyops->write)(my_tty, "\015\012", 2); 60 } 61} 62 -63static int __init print_string_init(void) +63static int __init print_string_init(void) 64{ -65 print_string("The module has been inserted. Hello world!"); -66 return 0; +65 print_string("The module has been inserted. Hello world!"); +66 return 0; 67} 68 -69static void __exit print_string_exit(void) +69static void __exit print_string_exit(void) 70{ -71 print_string("The module has been removed. Farewell world!"); +71 print_string("The module has been removed. Farewell world!"); 72} 73 74module_init(print_string_init); 75module_exit(print_string_exit); 76 -77MODULE_LICENSE("GPL");+77MODULE_LICENSE("GPL");
0.13.2 Flashing keyboard LEDs
@@ -4012,49 +4021,49 @@ file. loaded, starts blinking the keyboard LEDs until it is unloaded.-
1/* -2 * kbleds.c - Blink keyboard leds until the module is unloaded. -3 */ +@@ -4137,43 +4146,50 @@ better suited to running multiple things in a sequence. function continues to the exit point.1/* +2 * kbleds.c - Blink keyboard leds until the module is unloaded. +3 */ 4 -5#include <linux/init.h> -6#include <linux/kd.h> /* For KDSETLED */ -7#include <linux/module.h> -8#include <linux/tty.h> /* For fg_console, MAX_NR_CONSOLES */ -9#include <linux/vt.h> -10#include <linux/vt_kern.h> /* for fg_console */ +5#include <linux/init.h> +6#include <linux/kd.h> /* For KDSETLED */ +7#include <linux/module.h> +8#include <linux/tty.h> /* For fg_console, MAX_NR_CONSOLES */ +9#include <linux/vt.h> +10#include <linux/vt_kern.h> /* for fg_console */ 11 -12#include <linux/console_struct.h> /* For vc_cons */ +12#include <linux/console_struct.h> /* For vc_cons */ 13 -14MODULE_DESCRIPTION("Example module illustrating the use of Keyboard LEDs."); +14MODULE_DESCRIPTION("Example module illustrating the use of Keyboard LEDs."); 15 -16struct timer_list my_timer; -17struct tty_driver *my_driver; -18char kbledstatus = 0; +16struct timer_list my_timer; +17struct tty_driver *my_driver; +18char kbledstatus = 0; 19 -20#define BLINK_DELAY HZ / 5 -21#define ALL_LEDS_ON 0x07 -22#define RESTORE_LEDS 0xFF +20#define BLINK_DELAY HZ / 5 +21#define ALL_LEDS_ON 0x07 +22#define RESTORE_LEDS 0xFF 23 -24/* Function my_timer_func blinks the keyboard LEDs periodically by invoking -25 * command KDSETLED of ioctl() on the keyboard driver. To learn more on virtual -26 * terminal ioctl operations, please see file: -27 * drivers/tty/vt/vt_ioctl.c, function vt_ioctl(). -28 * -29 * The argument to KDSETLED is alternatively set to 7 (thus causing the led -30 * mode to be set to LED_SHOW_IOCTL, and all the leds are lit) and to 0xFF -31 * (any value above 7 switches back the led mode to LED_SHOW_FLAGS, thus -32 * the LEDs reflect the actual keyboard status). To learn more on this, -33 * please see file: drivers/tty/vt/keyboard.c, function setledstate(). -34 */ +24/* Function my_timer_func blinks the keyboard LEDs periodically by invoking +25 * command KDSETLED of ioctl() on the keyboard driver. To learn more on virtual +26 * terminal ioctl operations, please see file: +27 * drivers/tty/vt/vt_ioctl.c, function vt_ioctl(). +28 * +29 * The argument to KDSETLED is alternatively set to 7 (thus causing the led +30 * mode to be set to LED_SHOW_IOCTL, and all the leds are lit) and to 0xFF +31 * (any value above 7 switches back the led mode to LED_SHOW_FLAGS, thus +32 * the LEDs reflect the actual keyboard status). To learn more on this, +33 * please see file: drivers/tty/vt/keyboard.c, function setledstate(). +34 */ 35 -36static void my_timer_func(unsigned long ptr) +36static void my_timer_func(unsigned long ptr) 37{ -38 unsigned long *pstatus = (unsigned long *) ptr; -39 struct tty_struct *t = vc_cons[fg_console].d->port.tty; +38 unsigned long *pstatus = (unsigned long *) ptr; +39 struct tty_struct *t = vc_cons[fg_console].d->port.tty; 40 -41 if (*pstatus == ALL_LEDS_ON) +41 if (*pstatus == ALL_LEDS_ON) 42 *pstatus = RESTORE_LEDS; -43 else +43 else 44 *pstatus = ALL_LEDS_ON; 45 46 (my_driver->ops->ioctl)(t, KDSETLED, *pstatus); @@ -4063,35 +4072,35 @@ loaded, starts blinking the keyboard LEDs until it is unloaded. 49 add_timer(&my_timer); 50} 51 -52static int __init kbleds_init(void) +52static int __init kbleds_init(void) 53{ -54 int i; +54 int i; 55 -56 pr_info("kbleds: loading\n"); -57 pr_info("kbleds: fgconsole is %x\n", fg_console); -58 for (i = 0; i < MAX_NR_CONSOLES; i++) { -59 if (!vc_cons[i].d) -60 break; -61 pr_info("poet_atkm: console[%i/%i] #%i, tty %lx\n", i, MAX_NR_CONSOLES, -62 vc_cons[i].d->vc_num, (unsigned long) vc_cons[i].d->port.tty); +56 pr_info("kbleds: loading\n"); +57 pr_info("kbleds: fgconsole is %x\n", fg_console); +58 for (i = 0; i < MAX_NR_CONSOLES; i++) { +59 if (!vc_cons[i].d) +60 break; +61 pr_info("poet_atkm: console[%i/%i] #%i, tty %lx\n", i, MAX_NR_CONSOLES, +62 vc_cons[i].d->vc_num, (unsigned long) vc_cons[i].d->port.tty); 63 } -64 pr_info("kbleds: finished scanning consoles\n"); +64 pr_info("kbleds: finished scanning consoles\n"); 65 66 my_driver = vc_cons[fg_console].d->port.tty->driver; -67 pr_info("kbleds: tty driver magic %x\n", my_driver->magic); +67 pr_info("kbleds: tty driver magic %x\n", my_driver->magic); 68 -69 /* Set up the LED blink timer the first time. */ -70 timer_setup(&my_timer, (void *) &my_timer_func, -71 (unsigned long) &kbledstatus); +69 /* Set up the LED blink timer the first time. */ +70 timer_setup(&my_timer, (void *) &my_timer_func, +71 (unsigned long) &kbledstatus); 72 my_timer.expires = jiffies + BLINK_DELAY; 73 add_timer(&my_timer); 74 -75 return 0; +75 return 0; 76} 77 -78static void __exit kbleds_cleanup(void) +78static void __exit kbleds_cleanup(void) 79{ -80 pr_info("kbleds: unloading...\n"); +80 pr_info("kbleds: unloading...\n"); 81 del_timer(&my_timer); 82 (my_driver->ops->ioctl)(vc_cons[fg_console].d->port.tty, KDSETLED, 83 RESTORE_LEDS); @@ -4100,7 +4109,7 @@ loaded, starts blinking the keyboard LEDs until it is unloaded. 86module_init(kbleds_init); 87module_exit(kbleds_cleanup); 88 -89MODULE_LICENSE("GPL");+89MODULE_LICENSE("GPL");-
1/* -2 * example_tasklet.c -3 */ -4#include <linux/delay.h> -5#include <linux/interrupt.h> -6#include <linux/kernel.h> -7#include <linux/module.h> +1/* +2 * example_tasklet.c +3 */ +4#include <linux/delay.h> +5#include <linux/interrupt.h> +6#include <linux/kernel.h> +7#include <linux/module.h> 8 -9static void tasklet_fn(unsigned long data) -10{ -11 pr_info("Example tasklet starts\n"); -12 mdelay(5000); -13 pr_info("Example tasklet ends\n"); -14} +9/* Macro DECLARE_TASKLET_OLD exists for compatibiity. +10 * See https://lwn.net/Articles/830964/ +11 */ +12#ifndef DECLARE_TASKLET_OLD +13#define DECLARE_TASKLET_OLD(arg1, arg2) DECLARE_TASKLET(arg1, arg2, 0L) +14#endif 15 -16DECLARE_TASKLET(mytask, tasklet_fn, 0L); -17 -18static int example_tasklet_init(void) -19{ -20 pr_info("tasklet example init\n"); -21 tasklet_schedule(&mytask); -22 mdelay(200); -23 pr_info("Example tasklet init continues...\n"); -24 return 0; -25} -26 -27static void example_tasklet_exit(void) -28{ -29 pr_info("tasklet example exit\n"); -30 tasklet_kill(&mytask); -31} -32 -33module_init(example_tasklet_init); -34module_exit(example_tasklet_exit); -35 -36MODULE_DESCRIPTION("Tasklet example"); -37MODULE_LICENSE("GPL");+16static void tasklet_fn(unsigned long data) +17{ +18 pr_info("Example tasklet starts\n"); +19 mdelay(5000); +20 pr_info("Example tasklet ends\n"); +21} +22 +23DECLARE_TASKLET_OLD(mytask, tasklet_fn); +24 +25static int example_tasklet_init(void) +26{ +27 pr_info("tasklet example init\n"); +28 tasklet_schedule(&mytask); +29 mdelay(200); +30 pr_info("Example tasklet init continues...\n"); +31 return 0; +32} +33 +34static void example_tasklet_exit(void) +35{ +36 pr_info("tasklet example exit\n"); +37 tasklet_kill(&mytask); +38} +39 +40module_init(example_tasklet_init); +41module_exit(example_tasklet_exit); +42 +43MODULE_DESCRIPTION("Tasklet example"); +44MODULE_LICENSE("GPL");So with this example loaded
dmesg
should show: @@ -4186,39 +4202,51 @@ Example tasklet starts Example tasklet init continues... Example tasklet ends
-
+
Although tasklet is easy to use, it comes with several defators, and developers are +discussing about getting rid of tasklet in linux kernel. The tasklet callback +runs in atomic context, inside a software interrupt, meaning that it cannot +sleep or access user-space data, so not all work can be done in a tasklet +handler. Also, the kernel only allows one instance of any given tasklet to be +running at any given time; multiple different tasklet callbacks can run in +parallel. +
In recent kernels, tasklets can be replaced by workqueues, timers, or threaded
+interrupts.1
+While the removal of tasklets remains a longer-term goal, the current kernel contains more
+than a hundred uses of tasklets. Now developers are proceeding with the API changes and
+the macro DECLARE_TASKLET_OLD
+
exists for compatibiity. For further information, see https://lwn.net/Articles/830964/.
+
To add a task to the scheduler we can use a workqueue. The kernel then uses the +
To add a task to the scheduler we can use a workqueue. The kernel then uses the Completely Fair Scheduler (CFS) to execute work within the queue.
-
1/* -2 * sched.c -3 */ -4#include <linux/init.h> -5#include <linux/module.h> -6#include <linux/workqueue.h> ++1/* +2 * sched.c +3 */ +4#include <linux/init.h> +5#include <linux/module.h> +6#include <linux/workqueue.h> 7 -8static struct workqueue_struct *queue = NULL; -9static struct work_struct work; +8static struct workqueue_struct *queue = NULL; +9static struct work_struct work; 10 -11static void work_handler(struct work_struct *data) +11static void work_handler(struct work_struct *data) 12{ -13 pr_info("work handler function.\n"); +13 pr_info("work handler function.\n"); 14} 15 -16static int __init sched_init(void) +16static int __init sched_init(void) 17{ -18 queue = alloc_workqueue("HELLOWORLD", WQ_UNBOUND, 1); +18 queue = alloc_workqueue("HELLOWORLD", WQ_UNBOUND, 1); 19 INIT_WORK(&work, work_handler); 20 schedule_work(&work); 21 -22 return 0; +22 return 0; 23} 24 -25static void __exit sched_exit(void) +25static void __exit sched_exit(void) 26{ 27 destroy_workqueue(queue); 28} @@ -4226,38 +4254,38 @@ Completely Fair Scheduler (CFS) to execute work within the queue. 30module_init(sched_init); 31module_exit(sched_exit); 32 -33MODULE_LICENSE("GPL"); -34MODULE_DESCRIPTION("Workqueue example");-+33MODULE_LICENSE("GPL"); +34MODULE_DESCRIPTION("Workqueue example");
+
+ + +
Except for the last chapter, everything we did in the kernel so far we have done as a +
Except for the last chapter, everything we did in the kernel so far we have done as a
response to a process asking for it, either by dealing with a special file, sending an
ioctl()
, or issuing a system call. But the job of the kernel is not just to respond to process
requests. Another job, which is every bit as important, is to speak to the hardware
connected to the machine.
-
There are two types of interaction between the CPU and the rest of the +
There are two types of interaction between the CPU and the rest of the computer’s hardware. The first type is when the CPU gives orders to the hardware, the order is when the hardware needs to tell the CPU something. The second, called interrupts, is much harder to implement because it has to be dealt with when convenient for the hardware, not the CPU. Hardware devices typically have a very small amount of RAM, and if you do not read their information when available, it is lost. - - - -
Under Linux, hardware interrupts are called IRQ’s (Interrupt ReQuests). There +
Under Linux, hardware interrupts are called IRQ’s (Interrupt ReQuests). There are two types of IRQ’s, short and long. A short IRQ is one which is expected to take a very short period of time, during which the rest of the machine will be blocked and no other interrupts will be handled. A long IRQ is one which can take longer, and during which other interrupts may occur (but not interrupts from the same device). If at all possible, it is better to declare an interrupt handler to be long. -
When the CPU receives an interrupt, it stops whatever it is doing (unless it is +
When the CPU receives an interrupt, it stops whatever it is doing (unless it is processing a more important interrupt, in which case it will deal with this one only when the more important one is done), saves certain parameters on the stack and calls the interrupt handler. This means that certain things @@ -4269,10 +4297,10 @@ the new information at a later time (this is called the "bottom half") and return. The kernel is then guaranteed to call the bottom half as soon as possible – and when it does, everything allowed in kernel modules will be allowed. -
The way to implement this is to call +
The way to implement this is to call
request_irq()
to get your interrupt handler called when the relevant IRQ is received.
-
In practice IRQ handling can be a bit more complex. Hardware is often +
In practice IRQ handling can be a bit more complex. Hardware is often designed in a way that chains two interrupt controllers, so that all the IRQs from interrupt controller B are cascaded to a certain IRQ from interrupt controller A. Of course, that requires that the kernel finds out which IRQ it @@ -4282,11 +4310,14 @@ them requires handlers to be written in assembler, so they do not really fit into the kernel. They can be made to work similar to the others, but after that procedure, they are no longer any faster than "common" IRQs. SMP enabled kernels running on systems with more than one processor + + + need to solve another truckload of problems. It is not enough to know if a certain IRQs has happened, it’s also important to know what CPU(s) it was for. People still interested in more details, might want to refer to "APIC" now. -
This function receives the IRQ number, the name of the function, +
This function receives the IRQ number, the name of the function,
flags, a name for /proc/interrupts and a parameter to be passed to the
interrupt handler. Usually there is a certain number of IRQs available.
How many IRQs there are is hardware-dependent. The flags can include
@@ -4296,132 +4327,129 @@ How many IRQs there are is hardware-dependent. The flags can include
SA_INTERRUPT
to indicate this is a fast interrupt. This function will only succeed if there is not
already a handler on this IRQ, or if you are both willing to share.
-
-
-
-
+
Many popular single board computers, such as Raspberry Pi or Beagleboards, have a +
Many popular single board computers, such as Raspberry Pi or Beagleboards, have a bunch of GPIO pins. Attaching buttons to those and then having a button press do something is a classic case in which you might need to use interrupts, so that instead of having the CPU waste time and battery power polling for a change in input state, it is better for the input to trigger the CPU to then run a particular handling function. -
Here is an example where buttons are connected to GPIO numbers 17 and 18 and +
Here is an example where buttons are connected to GPIO numbers 17 and 18 and an LED is connected to GPIO 4. You can change those numbers to whatever is appropriate for your board.
-
1/* -2 * intrpt.c - Handling GPIO with interrupts -3 * -4 * Based upon the RPi example by Stefan Wendler (devnull@kaltpost.de) -5 * from: -6 * https://github.com/wendlers/rpi-kmod-samples -7 * -8 * Press one button to turn on a LED and another to turn it off. -9 */ ++1/* +2 * intrpt.c - Handling GPIO with interrupts +3 * +4 * Based upon the RPi example by Stefan Wendler (devnull@kaltpost.de) +5 * from: +6 * https://github.com/wendlers/rpi-kmod-samples +7 * +8 * Press one button to turn on a LED and another to turn it off. +9 */ 10 -11#include <linux/gpio.h> -12#include <linux/interrupt.h> -13#include <linux/kernel.h> -14#include <linux/module.h> +11#include <linux/gpio.h> +12#include <linux/interrupt.h> +13#include <linux/kernel.h> +14#include <linux/module.h> 15 -16static int button_irqs[] = {-1, -1}; +16static int button_irqs[] = {-1, -1}; 17 -18/* Define GPIOs for LEDs. -19 * TODO: Change the numbers for the GPIO on your board. -20 */ -21static struct gpio leds[] = {{4, GPIOF_OUT_INIT_LOW, "LED 1"}}; +18/* Define GPIOs for LEDs. +19 * TODO: Change the numbers for the GPIO on your board. +20 */ +21static struct gpio leds[] = {{4, GPIOF_OUT_INIT_LOW, "LED 1"}}; 22 -23/* Define GPIOs for BUTTONS -24 * TODO: Change the numbers for the GPIO on your board. -25 */ -26static struct gpio buttons[] = {{17, GPIOF_IN, "LED 1 ON BUTTON"}, -27 {18, GPIOF_IN, "LED 1 OFF BUTTON"}}; +23/* Define GPIOs for BUTTONS +24 * TODO: Change the numbers for the GPIO on your board. +25 */ +26static struct gpio buttons[] = {{17, GPIOF_IN, "LED 1 ON BUTTON"}, +27 {18, GPIOF_IN, "LED 1 OFF BUTTON"}}; 28 -29/* interrupt function triggered when a button is pressed. */ -30static irqreturn_t button_isr(int irq, void *data) +29/* interrupt function triggered when a button is pressed. */ +30static irqreturn_t button_isr(int irq, void *data) 31{ -32 /* first button */ -33 if (irq == button_irqs[0] && !gpio_get_value(leds[0].gpio)) +32 /* first button */ +33 if (irq == button_irqs[0] && !gpio_get_value(leds[0].gpio)) 34 gpio_set_value(leds[0].gpio, 1); -35 /* second button */ -36 else if (irq == button_irqs[1] && gpio_get_value(leds[0].gpio)) +35 /* second button */ +36 else if (irq == button_irqs[1] && gpio_get_value(leds[0].gpio)) 37 gpio_set_value(leds[0].gpio, 0); 38 -39 return IRQ_HANDLED; +39 return IRQ_HANDLED; 40} 41 -42static int __init intrpt_init(void) +42static int __init intrpt_init(void) 43{ -44 int ret = 0; +44 int ret = 0; 45 -46 pr_info("%s\n", __func__); +46 pr_info("%s\n", __func__); 47 -48 /* register LED gpios */ +48 /* register LED gpios */ 49 ret = gpio_request_array(leds, ARRAY_SIZE(leds)); 50 -51 if (ret) { -52 pr_err("Unable to request GPIOs for LEDs: %d\n", ret); -53 return ret; +51 if (ret) { +52 pr_err("Unable to request GPIOs for LEDs: %d\n", ret); +53 return ret; 54 } 55 -56 /* register BUTTON gpios */ +56 /* register BUTTON gpios */ 57 ret = gpio_request_array(buttons, ARRAY_SIZE(buttons)); 58 -59 if (ret) { -60 pr_err("Unable to request GPIOs for BUTTONs: %d\n", ret); -61 goto fail1; +59 if (ret) { +60 pr_err("Unable to request GPIOs for BUTTONs: %d\n", ret); +61 goto fail1; 62 } 63 -64 pr_info("Current button1 value: %d\n", gpio_get_value(buttons[0].gpio)); +64 pr_info("Current button1 value: %d\n", gpio_get_value(buttons[0].gpio)); 65 66 ret = gpio_to_irq(buttons[0].gpio); 67 -68 if (ret < 0) { -69 pr_err("Unable to request IRQ: %d\n", ret); -70 goto fail2; +68 if (ret < 0) { +69 pr_err("Unable to request IRQ: %d\n", ret); +70 goto fail2; 71 } 72 73 button_irqs[0] = ret; 74 -75 pr_info("Successfully requested BUTTON1 IRQ # %d\n", button_irqs[0]); +75 pr_info("Successfully requested BUTTON1 IRQ # %d\n", button_irqs[0]); 76 77 ret = request_irq(button_irqs[0], button_isr, 78 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, -79 "gpiomod#button1", NULL); +79 "gpiomod#button1", NULL); 80 -81 if (ret) { -82 pr_err("Unable to request IRQ: %d\n", ret); -83 goto fail2; +81 if (ret) { +82 pr_err("Unable to request IRQ: %d\n", ret); +83 goto fail2; 84 } 85 86 87 ret = gpio_to_irq(buttons[1].gpio); 88 -89 if (ret < 0) { -90 pr_err("Unable to request IRQ: %d\n", ret); -91 goto fail2; +89 if (ret < 0) { +90 pr_err("Unable to request IRQ: %d\n", ret); +91 goto fail2; 92 } 93 94 button_irqs[1] = ret; 95 -96 pr_info("Successfully requested BUTTON2 IRQ # %d\n", button_irqs[1]); +96 pr_info("Successfully requested BUTTON2 IRQ # %d\n", button_irqs[1]); 97 98 ret = request_irq(button_irqs[1], button_isr, 99 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, -100 "gpiomod#button2", NULL); +100 "gpiomod#button2", NULL); 101 -102 if (ret) { -103 pr_err("Unable to request IRQ: %d\n", ret); -104 goto fail3; +102 if (ret) { +103 pr_err("Unable to request IRQ: %d\n", ret); +104 goto fail3; 105 } 106 -107 return 0; +107 return 0; 108 -109/* cleanup what has been setup so far */ +109/* cleanup what has been setup so far */ 110fail3: 111 free_irq(button_irqs[0], NULL); 112 @@ -4431,24 +4459,24 @@ appropriate for your board. 116fail1: 117 gpio_free_array(leds, ARRAY_SIZE(leds)); 118 -119 return ret; +119 return ret; 120} 121 -122static void __exit intrpt_exit(void) +122static void __exit intrpt_exit(void) 123{ -124 int i; +124 int i; 125 -126 pr_info("%s\n", __func__); +126 pr_info("%s\n", __func__); 127 -128 /* free irqs */ +128 /* free irqs */ 129 free_irq(button_irqs[0], NULL); 130 free_irq(button_irqs[1], NULL); 131 -132 /* turn all LEDs off */ -133 for (i = 0; i < ARRAY_SIZE(leds); i++) +132 /* turn all LEDs off */ +133 for (i = 0; i < ARRAY_SIZE(leds); i++) 134 gpio_set_value(leds[i].gpio, 0); 135 -136 /* unregister */ +136 /* unregister */ 137 gpio_free_array(leds, ARRAY_SIZE(leds)); 138 gpio_free_array(buttons, ARRAY_SIZE(buttons)); 139} @@ -4456,480 +4484,492 @@ appropriate for your board. 141module_init(intrpt_init); 142module_exit(intrpt_exit); 143 -144MODULE_LICENSE("GPL"); -145MODULE_DESCRIPTION("Handle some GPIO interrupts");-+144MODULE_LICENSE("GPL"); +145MODULE_DESCRIPTION("Handle some GPIO interrupts");
Suppose you want to do a bunch of stuff inside of an interrupt routine. A common +
Suppose you want to do a bunch of stuff inside of an interrupt routine. A common way to do that without rendering the interrupt unavailable for a significant duration is to combine it with a tasklet. This pushes the bulk of the work off into the scheduler. -
The example below modifies the previous example to also run an additional task +
The example below modifies the previous example to also run an additional task when an interrupt is triggered.
+ + +
-1/* -2 * bottomhalf.c - Top and bottom half interrupt handling -3 * -4 * Based upon the RPi example by Stefan Wendler (devnull@kaltpost.de) -5 * from: -6 * https://github.com/wendlers/rpi-kmod-samples -7 * -8 * Press one button to turn on a LED and another to turn it off -9 */ ++1/* +2 * bottomhalf.c - Top and bottom half interrupt handling +3 * +4 * Based upon the RPi example by Stefan Wendler (devnull@kaltpost.de) +5 * from: +6 * https://github.com/wendlers/rpi-kmod-samples +7 * +8 * Press one button to turn on a LED and another to turn it off +9 */ 10 -11#include <linux/delay.h> -12#include <linux/gpio.h> -13#include <linux/interrupt.h> -14#include <linux/kernel.h> -15#include <linux/module.h> +11#include <linux/delay.h> +12#include <linux/gpio.h> +13#include <linux/interrupt.h> +14#include <linux/kernel.h> +15#include <linux/module.h> 16 -17static int button_irqs[] = {-1, -1}; -18 -19/* Define GPIOs for LEDs. -20 * TODO: Change the numbers for the GPIO on your board. -21 */ -22static struct gpio leds[] = {{4, GPIOF_OUT_INIT_LOW, "LED 1"}}; +17/* Macro DECLARE_TASKLET_OLD exists for compatibiity. +18 * See https://lwn.net/Articles/830964/ +19 */ +20#ifndef DECLARE_TASKLET_OLD +21#define DECLARE_TASKLET_OLD(arg1, arg2) DECLARE_TASKLET(arg1, arg2, 0L) +22#endif 23 -24/* Define GPIOs for BUTTONS -25 * TODO: Change the numbers for the GPIO on your board. -26 */ -27static struct gpio buttons[] = { -28 {17, GPIOF_IN, "LED 1 ON BUTTON"}, -29 {18, GPIOF_IN, "LED 1 OFF BUTTON"}, -30}; -31 -32/* Tasklet containing some non-trivial amount of processing */ -33static void bottomhalf_tasklet_fn(unsigned long data) -34{ -35 pr_info("Bottom half tasklet starts\n"); -36 /* do something which takes a while */ -37 mdelay(500); -38 pr_info("Bottom half tasklet ends\n"); -39} -40 -41DECLARE_TASKLET(buttontask, bottomhalf_tasklet_fn, 0L); -42 -43/* interrupt function triggered when a button is pressed */ -44static irqreturn_t button_isr(int irq, void *data) -45{ -46 /* Do something quickly right now */ -47 if (irq == button_irqs[0] && !gpio_get_value(leds[0].gpio)) -48 gpio_set_value(leds[0].gpio, 1); -49 else if (irq == button_irqs[1] && gpio_get_value(leds[0].gpio)) -50 gpio_set_value(leds[0].gpio, 0); -51 -52 /* Do the rest at leisure via the scheduler */ -53 tasklet_schedule(&buttontask); -54 -55 return IRQ_HANDLED; -56} -57 -58static int __init bottomhalf_init(void) -59{ -60 int ret = 0; +24static int button_irqs[] = {-1, -1}; +25 +26/* Define GPIOs for LEDs. +27 * TODO: Change the numbers for the GPIO on your board. +28 */ +29static struct gpio leds[] = {{4, GPIOF_OUT_INIT_LOW, "LED 1"}}; +30 +31/* Define GPIOs for BUTTONS +32 * TODO: Change the numbers for the GPIO on your board. +33 */ +34static struct gpio buttons[] = { +35 {17, GPIOF_IN, "LED 1 ON BUTTON"}, +36 {18, GPIOF_IN, "LED 1 OFF BUTTON"}, +37}; +38 +39/* Tasklet containing some non-trivial amount of processing */ +40static void bottomhalf_tasklet_fn(unsigned long data) +41{ +42 pr_info("Bottom half tasklet starts\n"); +43 /* do something which takes a while */ +44 mdelay(500); +45 pr_info("Bottom half tasklet ends\n"); +46} +47 +48DECLARE_TASKLET_OLD(buttontask, bottomhalf_tasklet_fn); +49 +50/* interrupt function triggered when a button is pressed */ +51static irqreturn_t button_isr(int irq, void *data) +52{ +53 /* Do something quickly right now */ +54 if (irq == button_irqs[0] && !gpio_get_value(leds[0].gpio)) +55 gpio_set_value(leds[0].gpio, 1); +56 else if (irq == button_irqs[1] && gpio_get_value(leds[0].gpio)) +57 gpio_set_value(leds[0].gpio, 0); +58 +59 /* Do the rest at leisure via the scheduler */ +60 tasklet_schedule(&buttontask); 61 -62 pr_info("%s\n", __func__); -63 -64 /* register LED gpios */ -65 ret = gpio_request_array(leds, ARRAY_SIZE(leds)); -66 -67 if (ret) { -68 pr_err("Unable to request GPIOs for LEDs: %d\n", ret); -69 return ret; -70 } -71 -72 /* register BUTTON gpios */ -73 ret = gpio_request_array(buttons, ARRAY_SIZE(buttons)); -74 -75 if (ret) { -76 pr_err("Unable to request GPIOs for BUTTONs: %d\n", ret); -77 goto fail1; -78 } -79 -80 pr_info("Current button1 value: %d\n", gpio_get_value(buttons[0].gpio)); +62 return IRQ_HANDLED; +63} +64 +65static int __init bottomhalf_init(void) +66{ +67 int ret = 0; +68 +69 pr_info("%s\n", __func__); +70 +71 /* register LED gpios */ +72 ret = gpio_request_array(leds, ARRAY_SIZE(leds)); +73 +74 if (ret) { +75 pr_err("Unable to request GPIOs for LEDs: %d\n", ret); +76 return ret; +77 } +78 +79 /* register BUTTON gpios */ +80 ret = gpio_request_array(buttons, ARRAY_SIZE(buttons)); 81 -82 ret = gpio_to_irq(buttons[0].gpio); -83 -84 if (ret < 0) { -85 pr_err("Unable to request IRQ: %d\n", ret); -86 goto fail2; -87 } +82 if (ret) { +83 pr_err("Unable to request GPIOs for BUTTONs: %d\n", ret); +84 goto fail1; +85 } +86 +87 pr_info("Current button1 value: %d\n", gpio_get_value(buttons[0].gpio)); 88 -89 button_irqs[0] = ret; +89 ret = gpio_to_irq(buttons[0].gpio); 90 -91 pr_info("Successfully requested BUTTON1 IRQ # %d\n", button_irqs[0]); -92 -93 ret = request_irq(button_irqs[0], button_isr, -94 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, -95 "gpiomod#button1", NULL); -96 -97 if (ret) { -98 pr_err("Unable to request IRQ: %d\n", ret); -99 goto fail2; -100 } -101 -102 -103 ret = gpio_to_irq(buttons[1].gpio); -104 -105 if (ret < 0) { -106 pr_err("Unable to request IRQ: %d\n", ret); -107 goto fail2; -108 } +91 if (ret < 0) { +92 pr_err("Unable to request IRQ: %d\n", ret); +93 goto fail2; +94 } +95 +96 button_irqs[0] = ret; +97 +98 pr_info("Successfully requested BUTTON1 IRQ # %d\n", button_irqs[0]); +99 +100 ret = request_irq(button_irqs[0], button_isr, +101 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, +102 "gpiomod#button1", NULL); +103 +104 if (ret) { +105 pr_err("Unable to request IRQ: %d\n", ret); +106 goto fail2; +107 } +108 109 -110 button_irqs[1] = ret; +110 ret = gpio_to_irq(buttons[1].gpio); 111 -112 pr_info("Successfully requested BUTTON2 IRQ # %d\n", button_irqs[1]); -113 -114 ret = request_irq(button_irqs[1], button_isr, -115 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, -116 "gpiomod#button2", NULL); -117 -118 if (ret) { -119 pr_err("Unable to request IRQ: %d\n", ret); -120 goto fail3; -121 } -122 -123 return 0; +112 if (ret < 0) { +113 pr_err("Unable to request IRQ: %d\n", ret); +114 goto fail2; +115 } +116 +117 button_irqs[1] = ret; +118 +119 pr_info("Successfully requested BUTTON2 IRQ # %d\n", button_irqs[1]); +120 +121 ret = request_irq(button_irqs[1], button_isr, +122 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, +123 "gpiomod#button2", NULL); 124 -125/* cleanup what has been setup so far */ -126fail3: -127 free_irq(button_irqs[0], NULL); -128 -129fail2: -130 gpio_free_array(buttons, ARRAY_SIZE(leds)); +125 if (ret) { +126 pr_err("Unable to request IRQ: %d\n", ret); +127 goto fail3; +128 } +129 +130 return 0; 131 -132fail1: -133 gpio_free_array(leds, ARRAY_SIZE(leds)); -134 -135 return ret; -136} -137 -138static void __exit bottomhalf_exit(void) -139{ -140 int i; +132/* cleanup what has been setup so far */ +133fail3: +134 free_irq(button_irqs[0], NULL); +135 +136fail2: +137 gpio_free_array(buttons, ARRAY_SIZE(leds)); +138 +139fail1: +140 gpio_free_array(leds, ARRAY_SIZE(leds)); 141 -142 pr_info("%s\n", __func__); -143 -144 /* free irqs */ -145 free_irq(button_irqs[0], NULL); -146 free_irq(button_irqs[1], NULL); -147 -148 /* turn all LEDs off */ -149 for (i = 0; i < ARRAY_SIZE(leds); i++) -150 gpio_set_value(leds[i].gpio, 0); -151 -152 /* unregister */ -153 gpio_free_array(leds, ARRAY_SIZE(leds)); -154 gpio_free_array(buttons, ARRAY_SIZE(buttons)); -155} -156 -157module_init(bottomhalf_init); -158module_exit(bottomhalf_exit); -159 -160MODULE_LICENSE("GPL"); -161MODULE_DESCRIPTION("Interrupt with top and bottom half");-+142 return ret; +143} +144 +145static void __exit bottomhalf_exit(void) +146{ +147 int i; +148 +149 pr_info("%s\n", __func__); +150 +151 /* free irqs */ +152 free_irq(button_irqs[0], NULL); +153 free_irq(button_irqs[1], NULL); +154 +155 /* turn all LEDs off */ +156 for (i = 0; i < ARRAY_SIZE(leds); i++) +157 gpio_set_value(leds[i].gpio, 0); +158 +159 /* unregister */ +160 gpio_free_array(leds, ARRAY_SIZE(leds)); +161 gpio_free_array(buttons, ARRAY_SIZE(buttons)); +162} +163 +164module_init(bottomhalf_init); +165module_exit(bottomhalf_exit); +166 +167MODULE_LICENSE("GPL"); +168MODULE_DESCRIPTION("Interrupt with top and bottom half");
At the dawn of the internet, everybody trusted everybody completely…but that did +
At the dawn of the internet, everybody trusted everybody completely…but that did not work out so well. When this guide was originally written, it was a more innocent era in which almost nobody actually gave a damn about crypto - least of all kernel developers. That is certainly no longer the case now. To handle crypto stuff, the kernel has its own API enabling common methods of encryption, decryption and your favourite hash functions. - - - -
+
Calculating and checking the hashes of things is a common operation. Here is a +
Calculating and checking the hashes of things is a common operation. Here is a demonstration of how to calculate a sha256 hash within a kernel module.
-
1/* -2 * cryptosha256.c -3 */ -4#include <crypto/internal/hash.h> -5#include <linux/module.h> ++1/* +2 * cryptosha256.c +3 */ +4#include <crypto/internal/hash.h> +5#include <linux/module.h> 6 -7#define SHA256_LENGTH 32 +7#define SHA256_LENGTH 32 8 -9static void show_hash_result(char *plaintext, char *hash_sha256) +9static void show_hash_result(char *plaintext, char *hash_sha256) 10{ -11 int i; -12 char str[SHA256_LENGTH * 2 + 1]; +11 int i; +12 char str[SHA256_LENGTH * 2 + 1]; 13 -14 pr_info("sha256 test for string: \"%s\"\n", plaintext); -15 for (i = 0; i < SHA256_LENGTH; i++) -16 sprintf(&str[i * 2], "%02x", (unsigned char) hash_sha256[i]); +14 pr_info("sha256 test for string: \"%s\"\n", plaintext); +15 for (i = 0; i < SHA256_LENGTH; i++) +16 sprintf(&str[i * 2], "%02x", (unsigned char) hash_sha256[i]); 17 str[i * 2] = 0; -18 pr_info("%s\n", str); +18 pr_info("%s\n", str); 19} 20 -21int cryptosha256_init(void) +21int cryptosha256_init(void) 22{ -23 char *plaintext = "This is a test"; -24 char hash_sha256[SHA256_LENGTH]; -25 struct crypto_shash *sha256; -26 struct shash_desc *shash; +23 char *plaintext = "This is a test"; +24 char hash_sha256[SHA256_LENGTH]; +25 struct crypto_shash *sha256; +26 struct shash_desc *shash; 27 -28 sha256 = crypto_alloc_shash("sha256", 0, 0); -29 if (IS_ERR(sha256)) -30 return -1; +28 sha256 = crypto_alloc_shash("sha256", 0, 0); +29 if (IS_ERR(sha256)) +30 return -1; 31 -32 shash = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(sha256), +32 shash = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(sha256), 33 GFP_KERNEL); -34 if (!shash) -35 return -ENOMEM; +34 if (!shash) +35 return -ENOMEM; 36 37 shash->tfm = sha256; 38 -39 if (crypto_shash_init(shash)) -40 return -1; +39 if (crypto_shash_init(shash)) +40 return -1; 41 -42 if (crypto_shash_update(shash, plaintext, strlen(plaintext))) -43 return -1; +42 if (crypto_shash_update(shash, plaintext, strlen(plaintext))) +43 return -1; 44 -45 if (crypto_shash_final(shash, hash_sha256)) -46 return -1; +45 if (crypto_shash_final(shash, hash_sha256)) +46 return -1; 47 48 kfree(shash); 49 crypto_free_shash(sha256); 50 51 show_hash_result(plaintext, hash_sha256); 52 -53 return 0; +53 return 0; 54} 55 -56void cryptosha256_exit(void) {} +56void cryptosha256_exit(void) {} 57 58module_init(cryptosha256_init); 59module_exit(cryptosha256_exit); 60 -61MODULE_DESCRIPTION("sha256 hash test"); -62MODULE_LICENSE("GPL");-Make and install the module: +61MODULE_DESCRIPTION("sha256 hash test"); +62MODULE_LICENSE("GPL");
Make and install the module:
1make 2sudo insmod cryptosha256.ko 3dmesg-
And you should see that the hash was calculated for the test string. -
Finally, remove the test module: +
And you should see that the hash was calculated for the test string. +
Finally, remove the test module:
1sudo rmmod cryptosha256-
+
Here is an example of symmetrically encrypting a string using the AES algorithm +
Here is an example of symmetrically encrypting a string using the AES algorithm and a password.
+ + +
-1/* -2 * cryptosk.c -3 */ -4#include <crypto/internal/skcipher.h> -5#include <linux/crypto.h> -6#include <linux/module.h> -7 -8#define SYMMETRIC_KEY_LENGTH 32 -9#define CIPHER_BLOCK_SIZE 16 -10 -11struct tcrypt_result { -12 struct completion completion; -13 int err; -14}; -15 -16struct skcipher_def { -17 struct scatterlist sg; -18 struct crypto_skcipher *tfm; -19 struct skcipher_request *req; -20 struct tcrypt_result result; -21 char *scratchpad; -22 char *ciphertext; -23 char *ivdata; -24}; -25 -26static struct skcipher_def sk; ++1/* +2 * cryptosk.c +3 */ +4#include <crypto/internal/skcipher.h> +5#include <linux/crypto.h> +6#include <linux/module.h> +7#include <linux/random.h> +8#include <linux/scatterlist.h> +9 +10#define SYMMETRIC_KEY_LENGTH 32 +11#define CIPHER_BLOCK_SIZE 16 +12 +13struct tcrypt_result { +14 struct completion completion; +15 int err; +16}; +17 +18struct skcipher_def { +19 struct scatterlist sg; +20 struct crypto_skcipher *tfm; +21 struct skcipher_request *req; +22 struct tcrypt_result result; +23 char *scratchpad; +24 char *ciphertext; +25 char *ivdata; +26}; 27 -28static void test_skcipher_finish(struct skcipher_def *sk) -29{ -30 if (sk->tfm) -31 crypto_free_skcipher(sk->tfm); -32 if (sk->req) -33 skcipher_request_free(sk->req); -34 if (sk->ivdata) -35 kfree(sk->ivdata); -36 if (sk->scratchpad) -37 kfree(sk->scratchpad); -38 if (sk->ciphertext) -39 kfree(sk->ciphertext); -40} -41 -42static int test_skcipher_result(struct skcipher_def *sk, int rc) -43{ -44 switch (rc) { -45 case 0: -46 break; -47 case -EINPROGRESS || -EBUSY: -48 rc = wait_for_completion_interruptible(&sk->result.completion); -49 if (!rc && !sk->result.err) { -50 reinit_completion(&sk->result.completion); -51 break; -52 } -53 pr_info("skcipher encrypt returned with %d result %d\n", rc, -54 sk->result.err); -55 break; -56 default: -57 pr_info("skcipher encrypt returned with %d result %d\n", rc, -58 sk->result.err); -59 break; -60 } -61 -62 init_completion(&sk->result.completion); +28static struct skcipher_def sk; +29 +30static void test_skcipher_finish(struct skcipher_def *sk) +31{ +32 if (sk->tfm) +33 crypto_free_skcipher(sk->tfm); +34 if (sk->req) +35 skcipher_request_free(sk->req); +36 if (sk->ivdata) +37 kfree(sk->ivdata); +38 if (sk->scratchpad) +39 kfree(sk->scratchpad); +40 if (sk->ciphertext) +41 kfree(sk->ciphertext); +42} +43 +44static int test_skcipher_result(struct skcipher_def *sk, int rc) +45{ +46 switch (rc) { +47 case 0: +48 break; +49 case -EINPROGRESS || -EBUSY: +50 rc = wait_for_completion_interruptible(&sk->result.completion); +51 if (!rc && !sk->result.err) { +52 reinit_completion(&sk->result.completion); +53 break; +54 } +55 pr_info("skcipher encrypt returned with %d result %d\n", rc, +56 sk->result.err); +57 break; +58 default: +59 pr_info("skcipher encrypt returned with %d result %d\n", rc, +60 sk->result.err); +61 break; +62 } 63 -64 return rc; -65} -66 -67static void test_skcipher_callback(struct crypto_async_request *req, int error) -68{ -69 struct tcrypt_result *result = req->data; -70 -71 if (error == -EINPROGRESS) -72 return; -73 -74 result->err = error; -75 complete(&result->completion); -76 pr_info("Encryption finished successfully\n"); -77 -78 /* decrypt data */ -79#if 0 -80 memset((void*)sk.scratchpad, '-', CIPHER_BLOCK_SIZE); -81 ret = crypto_skcipher_decrypt(sk.req); -82 ret = test_skcipher_result(&sk, ret); -83 if (ret) -84 return; -85 -86 sg_copy_from_buffer(&sk.sg, 1, sk.scratchpad, CIPHER_BLOCK_SIZE); -87 sk.scratchpad[CIPHER_BLOCK_SIZE-1] = 0; -88 -89 pr_info("Decryption request successful\n"); -90 pr_info("Decrypted: %s\n", sk.scratchpad); -91#endif -92} -93 -94static int test_skcipher_encrypt(char *plaintext, -95 char *password, -96 struct skcipher_def *sk) -97{ -98 int ret = -EFAULT; -99 unsigned char key[SYMMETRIC_KEY_LENGTH]; -100 -101 if (!sk->tfm) { -102 sk->tfm = crypto_alloc_skcipher("cbc-aes-aesni", 0, 0); -103 if (IS_ERR(sk->tfm)) { -104 pr_info("could not allocate skcipher handle\n"); -105 return PTR_ERR(sk->tfm); -106 } -107 } -108 -109 if (!sk->req) { -110 sk->req = skcipher_request_alloc(sk->tfm, GFP_KERNEL); -111 if (!sk->req) { -112 pr_info("could not allocate skcipher request\n"); -113 ret = -ENOMEM; -114 goto out; -115 } -116 } -117 -118 skcipher_request_set_callback(sk->req, CRYPTO_TFM_REQ_MAY_BACKLOG, -119 test_skcipher_callback, &sk->result); -120 -121 /* clear the key */ -122 memset((void *) key, '\0', SYMMETRIC_KEY_LENGTH); -123 -124 /* Use the world's favourite password */ -125 sprintf((char *) key, "%s", password); -126 -127 /* AES 256 with given symmetric key */ -128 if (crypto_skcipher_setkey(sk->tfm, key, SYMMETRIC_KEY_LENGTH)) { -129 pr_info("key could not be set\n"); -130 ret = -EAGAIN; -131 goto out; -132 } -133 pr_info("Symmetric key: %s\n", key); -134 pr_info("Plaintext: %s\n", plaintext); -135 -136 if (!sk->ivdata) { -137 /* see https://en.wikipedia.org/wiki/Initialization_vector */ -138 sk->ivdata = kmalloc(CIPHER_BLOCK_SIZE, GFP_KERNEL); -139 if (!sk->ivdata) { -140 pr_info("could not allocate ivdata\n"); -141 goto out; -142 } -143 get_random_bytes(sk->ivdata, CIPHER_BLOCK_SIZE); -144 } -145 -146 if (!sk->scratchpad) { -147 /* The text to be encrypted */ -148 sk->scratchpad = kmalloc(CIPHER_BLOCK_SIZE, GFP_KERNEL); -149 if (!sk->scratchpad) { -150 pr_info("could not allocate scratchpad\n"); -151 goto out; -152 } -153 } -154 sprintf((char *) sk->scratchpad, "%s", plaintext); -155 -156 sg_init_one(&sk->sg, sk->scratchpad, CIPHER_BLOCK_SIZE); -157 skcipher_request_set_crypt(sk->req, &sk->sg, &sk->sg, CIPHER_BLOCK_SIZE, -158 sk->ivdata); -159 init_completion(&sk->result.completion); -160 -161 /* encrypt data */ -162 ret = crypto_skcipher_encrypt(sk->req); -163 ret = test_skcipher_result(sk, ret); -164 if (ret) -165 goto out; -166 -167 pr_info("Encryption request successful\n"); +64 init_completion(&sk->result.completion); +65 +66 return rc; +67} +68 +69static void test_skcipher_callback(struct crypto_async_request *req, int error) +70{ +71 struct tcrypt_result *result = req->data; +72 +73 if (error == -EINPROGRESS) +74 return; +75 +76 result->err = error; +77 complete(&result->completion); +78 pr_info("Encryption finished successfully\n"); +79 +80 /* decrypt data */ +81#if 0 +82 memset((void*)sk.scratchpad, '-', CIPHER_BLOCK_SIZE); +83 ret = crypto_skcipher_decrypt(sk.req); +84 ret = test_skcipher_result(&sk, ret); +85 if (ret) +86 return; +87 +88 sg_copy_from_buffer(&sk.sg, 1, sk.scratchpad, CIPHER_BLOCK_SIZE); +89 sk.scratchpad[CIPHER_BLOCK_SIZE-1] = 0; +90 +91 pr_info("Decryption request successful\n"); +92 pr_info("Decrypted: %s\n", sk.scratchpad); +93#endif +94} +95 +96static int test_skcipher_encrypt(char *plaintext, +97 char *password, +98 struct skcipher_def *sk) +99{ +100 int ret = -EFAULT; +101 unsigned char key[SYMMETRIC_KEY_LENGTH]; +102 +103 if (!sk->tfm) { +104 sk->tfm = crypto_alloc_skcipher("cbc-aes-aesni", 0, 0); +105 if (IS_ERR(sk->tfm)) { +106 pr_info("could not allocate skcipher handle\n"); +107 return PTR_ERR(sk->tfm); +108 } +109 } +110 +111 if (!sk->req) { +112 sk->req = skcipher_request_alloc(sk->tfm, GFP_KERNEL); +113 if (!sk->req) { +114 pr_info("could not allocate skcipher request\n"); +115 ret = -ENOMEM; +116 goto out; +117 } +118 } +119 +120 skcipher_request_set_callback(sk->req, CRYPTO_TFM_REQ_MAY_BACKLOG, +121 test_skcipher_callback, &sk->result); +122 +123 /* clear the key */ +124 memset((void *) key, '\0', SYMMETRIC_KEY_LENGTH); +125 +126 /* Use the world's favourite password */ +127 sprintf((char *) key, "%s", password); +128 +129 /* AES 256 with given symmetric key */ +130 if (crypto_skcipher_setkey(sk->tfm, key, SYMMETRIC_KEY_LENGTH)) { +131 pr_info("key could not be set\n"); +132 ret = -EAGAIN; +133 goto out; +134 } +135 pr_info("Symmetric key: %s\n", key); +136 pr_info("Plaintext: %s\n", plaintext); +137 +138 if (!sk->ivdata) { +139 /* see https://en.wikipedia.org/wiki/Initialization_vector */ +140 sk->ivdata = kmalloc(CIPHER_BLOCK_SIZE, GFP_KERNEL); +141 if (!sk->ivdata) { +142 pr_info("could not allocate ivdata\n"); +143 goto out; +144 } +145 get_random_bytes(sk->ivdata, CIPHER_BLOCK_SIZE); +146 } +147 +148 if (!sk->scratchpad) { +149 /* The text to be encrypted */ +150 sk->scratchpad = kmalloc(CIPHER_BLOCK_SIZE, GFP_KERNEL); +151 if (!sk->scratchpad) { +152 pr_info("could not allocate scratchpad\n"); +153 goto out; +154 } +155 } +156 sprintf((char *) sk->scratchpad, "%s", plaintext); +157 +158 sg_init_one(&sk->sg, sk->scratchpad, CIPHER_BLOCK_SIZE); +159 skcipher_request_set_crypt(sk->req, &sk->sg, &sk->sg, CIPHER_BLOCK_SIZE, +160 sk->ivdata); +161 init_completion(&sk->result.completion); +162 +163 /* encrypt data */ +164 ret = crypto_skcipher_encrypt(sk->req); +165 ret = test_skcipher_result(sk, ret); +166 if (ret) +167 goto out; 168 -169out: -170 return ret; -171} -172 -173int cryptoapi_init(void) -174{ -175 /* The world's favorite password */ -176 char *password = "password123"; -177 -178 sk.tfm = NULL; -179 sk.req = NULL; -180 sk.scratchpad = NULL; -181 sk.ciphertext = NULL; -182 sk.ivdata = NULL; -183 -184 test_skcipher_encrypt("Testing", password, &sk); -185 return 0; -186} -187 -188void cryptoapi_exit(void) -189{ -190 test_skcipher_finish(&sk); -191} -192 -193module_init(cryptoapi_init); -194module_exit(cryptoapi_exit); -195 -196MODULE_DESCRIPTION("Symmetric key encryption example"); -197MODULE_LICENSE("GPL");-+169 pr_info("Encryption request successful\n"); +170 +171out: +172 return ret; +173} +174 +175int cryptoapi_init(void) +176{ +177 /* The world's favorite password */ +178 char *password = "password123"; +179 +180 sk.tfm = NULL; +181 sk.req = NULL; +182 sk.scratchpad = NULL; +183 sk.ciphertext = NULL; +184 sk.ivdata = NULL; +185 +186 test_skcipher_encrypt("Testing", password, &sk); +187 return 0; +188} +189 +190void cryptoapi_exit(void) +191{ +192 test_skcipher_finish(&sk); +193} +194 +195module_init(cryptoapi_init); +196module_exit(cryptoapi_exit); +197 +198MODULE_DESCRIPTION("Symmetric key encryption example"); +199MODULE_LICENSE("GPL");
Up to this point we have seen all kinds of modules doing all kinds of things, but there +
Up to this point we have seen all kinds of modules doing all kinds of things, but there was no consistency in their interfaces with the rest of the kernel. To impose some consistency such that there is at minimum a standardized way to start, suspend and resume a device a device model was added. An example is shown below, and you can @@ -4937,59 +4977,59 @@ use this as a template to add your own suspend, resume or other interface functions.
-
1/* -2 * devicemodel.c -3 */ -4#include <linux/kernel.h> -5#include <linux/module.h> -6#include <linux/platform_device.h> ++1/* +2 * devicemodel.c +3 */ +4#include <linux/kernel.h> +5#include <linux/module.h> +6#include <linux/platform_device.h> 7 -8struct devicemodel_data { -9 char *greeting; -10 int number; +8struct devicemodel_data { +9 char *greeting; +10 int number; 11}; 12 -13static int devicemodel_probe(struct platform_device *dev) +13static int devicemodel_probe(struct platform_device *dev) 14{ -15 struct devicemodel_data *pd = -16 (struct devicemodel_data *) (dev->dev.platform_data); +15 struct devicemodel_data *pd = +16 (struct devicemodel_data *) (dev->dev.platform_data); 17 -18 pr_info("devicemodel probe\n"); -19 pr_info("devicemodel greeting: %s; %d\n", pd->greeting, pd->number); +18 pr_info("devicemodel probe\n"); +19 pr_info("devicemodel greeting: %s; %d\n", pd->greeting, pd->number); 20 -21 /* Your device initialization code */ +21 /* Your device initialization code */ 22 -23 return 0; +23 return 0; 24} 25 -26static int devicemodel_remove(struct platform_device *dev) +26static int devicemodel_remove(struct platform_device *dev) 27{ -28 pr_info("devicemodel example removed\n"); +28 pr_info("devicemodel example removed\n"); 29 -30 /* Your device removal code */ +30 /* Your device removal code */ 31 -32 return 0; +32 return 0; 33} 34 -35static int devicemodel_suspend(struct device *dev) +35static int devicemodel_suspend(struct device *dev) 36{ -37 pr_info("devicemodel example suspend\n"); +37 pr_info("devicemodel example suspend\n"); 38 -39 /* Your device suspend code */ +39 /* Your device suspend code */ 40 -41 return 0; +41 return 0; 42} 43 -44static int devicemodel_resume(struct device *dev) +44static int devicemodel_resume(struct device *dev) 45{ -46 pr_info("devicemodel example resume\n"); +46 pr_info("devicemodel example resume\n"); 47 -48 /* Your device resume code */ +48 /* Your device resume code */ 49 -50 return 0; +50 return 0; 51} 52 -53static const struct dev_pm_ops devicemodel_pm_ops = { +53static const struct dev_pm_ops devicemodel_pm_ops = { 54 .suspend = devicemodel_suspend, 55 .resume = devicemodel_resume, 56 .poweroff = devicemodel_suspend, @@ -4998,10 +5038,10 @@ functions. 59 .restore = devicemodel_resume, 60}; 61 -62static struct platform_driver devicemodel_driver = { +62static struct platform_driver devicemodel_driver = { 63 .driver = 64 { -65 .name = "devicemodel_example", +65 .name = "devicemodel_example", 66 .owner = THIS_MODULE, 67 .pm = &devicemodel_pm_ops, 68 }, @@ -5009,43 +5049,40 @@ functions. 70 .remove = devicemodel_remove, 71}; 72 -73static int devicemodel_init(void) +73static int devicemodel_init(void) 74{ -75 int ret; +75 int ret; 76 -77 pr_info("devicemodel init\n"); +77 pr_info("devicemodel init\n"); 78 79 ret = platform_driver_register(&devicemodel_driver); 80 -81 if (ret) { -82 pr_err("Unable to register driver\n"); -83 return ret; +81 if (ret) { +82 pr_err("Unable to register driver\n"); +83 return ret; 84 } 85 -86 return 0; +86 return 0; 87} 88 -89static void devicemodel_exit(void) +89static void devicemodel_exit(void) 90{ -91 pr_info("devicemodel exit\n"); +91 pr_info("devicemodel exit\n"); 92 platform_driver_unregister(&devicemodel_driver); 93} 94 95module_init(devicemodel_init); 96module_exit(devicemodel_exit); 97 -98MODULE_LICENSE("GPL"); -99MODULE_DESCRIPTION("Linux Device Model example");- - - -+98MODULE_LICENSE("GPL"); +99MODULE_DESCRIPTION("Linux Device Model example");
+
Sometimes you might want your code to run as quickly as possible, +
Sometimes you might want your code to run as quickly as possible, especially if it is handling an interrupt or doing something which might cause noticeable latency. If your code contains boolean conditions and if you know that the conditions are almost always likely to evaluate as either @@ -5059,56 +5096,63 @@ to succeed.
1bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx); -2if (unlikely(!bvl)) { +2if (unlikely(!bvl)) { 3 mempool_free(bio, bio_pool); 4 bio = NULL; -5 goto out; +5 goto out; 6}-
When the When the
+
+
You can not do that. In a kernel module, you can only use kernel functions which are
+ You can not do that. In a kernel module, you can only use kernel functions which are
the functions you can see in /proc/kallsyms.
-
-
-
-
+
You might need to do this for a short time and that is OK, but if you do not enable
+ You might need to do this for a short time and that is OK, but if you do not enable
them afterwards, your system will be stuck and you will have to power it
off.
-
+
For people seriously interested in kernel programming, I recommend kernelnewbies.org
+ For people seriously interested in kernel programming, I recommend kernelnewbies.org
and the Documentation subdirectory within the kernel source code which is not
always easy to understand but can be a starting point for further investigation. Also,
as Linus Torvalds said, the best way to learn the kernel is to read the source code
yourself.
- If you are interested in more examples of short kernel modules then searching on
+ If you are interested in more examples of short kernel modules then searching on
sites such as Github and Gitlab is a good way to start, although there is a lot of
duplication of older LKMPG examples which may not compile with newer kernel
versions. You will also be able to find examples of the use of kernel modules to attack
or compromise systems or exfiltrate data and those can be useful for thinking about
how to defend systems and learning about existing security mechanisms within the
kernel.
- I hope I have helped you in your quest to become a better programmer, or at
+
+
+
+ I hope I have helped you in your quest to become a better programmer, or at
least to have fun through technology. And, if you do write useful kernel modules, I
hope you publish them under the GPL, so I can use them too.
- If you would like to contribute to this guide or notice anything glaringly wrong,
+ If you would like to contribute to this guide or notice anything glaringly wrong,
please create an issue at https://github.com/sysprog21/lkmpg.
- Happy hacking!
+ Happy hacking!
1The goal of threaded interrupts is to push more of the work to separate threads, so that the
+minimum needed for acknowledging an interrupt is reduced, and therefore the time spent handling
+the interrupt (where it can’t handle any other interrupts at the same time) is reduced. See
+https://lwn.net/Articles/302043/ unlikely
+
+
+
+
unlikely
macro is used, the compiler alters its machine instruction output, so that it
continues along the false branch and only jumps if the condition is true. That
avoids flushing the processor pipeline. The opposite happens if you use the
likely
macro.
-0.19 Common Pitfalls
-0.19.1 Using standard libraries
-0.19.2 Disabling interrupts
-0.20 Where To Go From Here?
-