mirror of
https://github.com/bashrc/LKMPG.git
synced 2018-06-11 03:06:54 +02:00
Atomic operations
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -2670,9 +2670,9 @@ So even though /flywheel_thread/ is started first you should notice if you load
|
||||
|
||||
There are other variations upon the /wait_for_completion/ function, which include timeouts or being interrupted, but this basic mechanism is enough for many common situations without adding a lot of complexity.
|
||||
|
||||
** Avoiding Deadlock
|
||||
* Avoiding Collisions and Deadlocks
|
||||
If processes running on different CPUs or in different threads try to access the same memory then it's possible that strange things can happen or your system can lock up. To avoid this various types of mutual exclusion kernel functions are possible. These indicate if a section of code is "locked" or "unlocked" so that simultaneous attempts to run it can't happen.
|
||||
*** Spinlocks
|
||||
** Spinlocks
|
||||
As the name suggests, spinlocks lock up the CPU that the code is running on, taking 100% of its resources. Because of this you should only use the spinlock mechanism around code which is likely to take no more than a few milliseconds to run and so won't noticably slow anything down from the user's point of view.
|
||||
|
||||
The example here is /"irq safe"/ in that if interrupts happen during the lock then they won't be forgotten and will activate when the unlock happens, using the /flags/ variable to retain their state.
|
||||
@@ -2743,7 +2743,7 @@ MODULE_DESCRIPTION("Spinlock example");
|
||||
MODULE_LICENSE("GPL");
|
||||
#+end_src
|
||||
|
||||
*** Read and write locks
|
||||
** Read and write locks
|
||||
Read and write locks are specialised kinds of spinlocks so that you can exclusively read from something or write to something. Like the earlier spinlocks example the one below shows an "irq safe" situation in which if other functions were triggered from irqs which might also read and write to whatever you are concerned with then they wouldn't disrupt the logic. As before it's a good idea to keep anything done within the lock as short as possible so that it doesn't hang up the system and cause users to start revolting against the tyranny of your module.
|
||||
|
||||
#+begin_src C file:example_rwlock.c
|
||||
@@ -2803,6 +2803,87 @@ MODULE_LICENSE("GPL");
|
||||
#+end_src
|
||||
|
||||
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)/ and /read_unlock(&myrwlock)/ or the corresponding write functions.
|
||||
** Atomic operations
|
||||
If you're doing simple arithmetic: adding, subtracting or bitwise operations then there's another way in the multi-CPU and multi-hyperthreaded world to stop other parts of the system from messing with your mojo. By using atomic operations you can be confident that your addition, subtraction or bit flip did actually happen and wasn't overwritten by some other shenanigans. An example is shown below.
|
||||
|
||||
#+begin_src C file:example_atomic.c
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
|
||||
#define BYTE_TO_BINARY(byte) \
|
||||
(byte & 0x80 ? '1' : '0'), \
|
||||
(byte & 0x40 ? '1' : '0'), \
|
||||
(byte & 0x20 ? '1' : '0'), \
|
||||
(byte & 0x10 ? '1' : '0'), \
|
||||
(byte & 0x08 ? '1' : '0'), \
|
||||
(byte & 0x04 ? '1' : '0'), \
|
||||
(byte & 0x02 ? '1' : '0'), \
|
||||
(byte & 0x01 ? '1' : '0')
|
||||
|
||||
static void atomic_add_subtract(void)
|
||||
{
|
||||
atomic_t debbie;
|
||||
atomic_t chris = ATOMIC_INIT(50);
|
||||
|
||||
atomic_set(&debbie, 45);
|
||||
|
||||
/* subtract one */
|
||||
atomic_dec(&debbie);
|
||||
|
||||
atomic_add(7, &debbie);
|
||||
|
||||
/* add one */
|
||||
atomic_inc(&debbie);
|
||||
|
||||
printk("chris: %d, debbie: %d\n",
|
||||
atomic_read(&chris), atomic_read(&debbie));
|
||||
}
|
||||
|
||||
static void atomic_bitwise(void)
|
||||
{
|
||||
unsigned long word = 0;
|
||||
|
||||
printk("Bits 0: "BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word));
|
||||
set_bit(3, &word);
|
||||
set_bit(5, &word);
|
||||
printk("Bits 1: "BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word));
|
||||
clear_bit(5, &word);
|
||||
printk("Bits 2: "BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word));
|
||||
change_bit(3, &word);
|
||||
|
||||
printk("Bits 3: "BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word));
|
||||
if (test_and_set_bit(3, &word))
|
||||
printk("wrong\n");
|
||||
printk("Bits 4: "BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word));
|
||||
|
||||
word = 255;
|
||||
printk("Bits 5: "BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word));
|
||||
}
|
||||
|
||||
static int example_atomic_init(void)
|
||||
{
|
||||
printk("example_atomic started\n");
|
||||
|
||||
atomic_add_subtract();
|
||||
atomic_bitwise();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void example_atomic_exit(void)
|
||||
{
|
||||
printk("example_atomic exit\n");
|
||||
}
|
||||
|
||||
module_init(example_atomic_init);
|
||||
module_exit(example_atomic_exit);
|
||||
|
||||
MODULE_AUTHOR("Bob Mottram");
|
||||
MODULE_DESCRIPTION("Atomic operations example");
|
||||
MODULE_LICENSE("GPL");
|
||||
#+end_src
|
||||
* Replacing Printks
|
||||
** Replacing printk
|
||||
In Section 1.2.1.2, I said that X and kernel module programming don't mix. That's true for developing kernel modules, but in actual use, you want to be able to send messages to whichever tty[fn:15] the command to load the module came from.
|
||||
|
||||
@@ -25,6 +25,7 @@ obj-m += example_tasklet.o
|
||||
obj-m += devicemodel.o
|
||||
obj-m += example_spinlock.o
|
||||
obj-m += example_rwlock.o
|
||||
obj-m += example_atomic.o
|
||||
|
||||
all:
|
||||
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
|
||||
|
||||
76
4.9.11/examples/example_atomic.c
Normal file
76
4.9.11/examples/example_atomic.c
Normal file
@@ -0,0 +1,76 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
|
||||
#define BYTE_TO_BINARY(byte) \
|
||||
(byte & 0x80 ? '1' : '0'), \
|
||||
(byte & 0x40 ? '1' : '0'), \
|
||||
(byte & 0x20 ? '1' : '0'), \
|
||||
(byte & 0x10 ? '1' : '0'), \
|
||||
(byte & 0x08 ? '1' : '0'), \
|
||||
(byte & 0x04 ? '1' : '0'), \
|
||||
(byte & 0x02 ? '1' : '0'), \
|
||||
(byte & 0x01 ? '1' : '0')
|
||||
|
||||
static void atomic_add_subtract(void)
|
||||
{
|
||||
atomic_t debbie;
|
||||
atomic_t chris = ATOMIC_INIT(50);
|
||||
|
||||
atomic_set(&debbie, 45);
|
||||
|
||||
/* subtract one */
|
||||
atomic_dec(&debbie);
|
||||
|
||||
atomic_add(7, &debbie);
|
||||
|
||||
/* add one */
|
||||
atomic_inc(&debbie);
|
||||
|
||||
printk("chris: %d, debbie: %d\n",
|
||||
atomic_read(&chris), atomic_read(&debbie));
|
||||
}
|
||||
|
||||
static void atomic_bitwise(void)
|
||||
{
|
||||
unsigned long word = 0;
|
||||
|
||||
printk("Bits 0: "BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word));
|
||||
set_bit(3, &word);
|
||||
set_bit(5, &word);
|
||||
printk("Bits 1: "BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word));
|
||||
clear_bit(5, &word);
|
||||
printk("Bits 2: "BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word));
|
||||
change_bit(3, &word);
|
||||
|
||||
printk("Bits 3: "BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word));
|
||||
if (test_and_set_bit(3, &word))
|
||||
printk("wrong\n");
|
||||
printk("Bits 4: "BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word));
|
||||
|
||||
word = 255;
|
||||
printk("Bits 5: "BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word));
|
||||
}
|
||||
|
||||
static int example_atomic_init(void)
|
||||
{
|
||||
printk("example_atomic started\n");
|
||||
|
||||
atomic_add_subtract();
|
||||
atomic_bitwise();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void example_atomic_exit(void)
|
||||
{
|
||||
printk("example_atomic exit\n");
|
||||
}
|
||||
|
||||
module_init(example_atomic_init);
|
||||
module_exit(example_atomic_exit);
|
||||
|
||||
MODULE_AUTHOR("Bob Mottram");
|
||||
MODULE_DESCRIPTION("Atomic operations example");
|
||||
MODULE_LICENSE("GPL");
|
||||
Reference in New Issue
Block a user