mirror of
https://github.com/cirosantilli/linux-kernel-module-cheat.git
synced 2026-01-30 13:24:27 +01:00
poll kernel module: overhaul with prints everywhere
This commit is contained in:
72
README.adoc
72
README.adoc
@@ -7126,7 +7126,9 @@ Bibliography: https://stackoverflow.com/questions/5970595/how-to-create-a-device
|
|||||||
|
|
||||||
==== File operations
|
==== File operations
|
||||||
|
|
||||||
File operations are the main method of userland driver communication. `struct file_operations` determines what the kernel will do on filesystem system calls of <<pseudo-filesystems>>.
|
File operations are the main method of userland driver communication.
|
||||||
|
|
||||||
|
`struct file_operations` determines what the kernel will do on filesystem system calls of <<pseudo-filesystems>>.
|
||||||
|
|
||||||
This example illustrates the most basic system calls: `open`, `read`, `write`, `close` and `lseek`:
|
This example illustrates the most basic system calls: `open`, `read`, `write`, `close` and `lseek`:
|
||||||
|
|
||||||
@@ -7224,26 +7226,78 @@ cd
|
|||||||
|
|
||||||
==== poll
|
==== poll
|
||||||
|
|
||||||
The poll system call allows an user process to do a non-busy wait on a kernel event:
|
The poll system call allows an user process to do a non-busy wait on a kernel event.
|
||||||
|
|
||||||
....
|
|
||||||
./poll.sh
|
|
||||||
....
|
|
||||||
|
|
||||||
Outcome: `jiffies` gets printed to stdout every second from userland.
|
|
||||||
|
|
||||||
Sources:
|
Sources:
|
||||||
|
|
||||||
* link:kernel_modules/poll.c[]
|
* link:kernel_modules/poll.c[]
|
||||||
* link:rootfs_overlay/lkmc/poll.sh[]
|
* link:rootfs_overlay/lkmc/poll.sh[]
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
....
|
||||||
|
./poll.sh
|
||||||
|
....
|
||||||
|
|
||||||
|
Outcome: `jiffies` gets printed to stdout every second from userland, e.g.:
|
||||||
|
|
||||||
|
....
|
||||||
|
poll
|
||||||
|
<6>[ 4.275305] poll
|
||||||
|
<6>[ 4.275580] return POLLIN
|
||||||
|
revents = 1
|
||||||
|
POLLIN n=10 buf=4294893337
|
||||||
|
poll
|
||||||
|
<6>[ 4.276627] poll
|
||||||
|
<6>[ 4.276911] return 0
|
||||||
|
<6>[ 5.271193] wake_up
|
||||||
|
<6>[ 5.272326] poll
|
||||||
|
<6>[ 5.273207] return POLLIN
|
||||||
|
revents = 1
|
||||||
|
POLLIN n=10 buf=4294893588
|
||||||
|
poll
|
||||||
|
<6>[ 5.276367] poll
|
||||||
|
<6>[ 5.276618] return 0
|
||||||
|
<6>[ 6.275178] wake_up
|
||||||
|
<6>[ 6.276370] poll
|
||||||
|
<6>[ 6.277269] return POLLIN
|
||||||
|
revents = 1
|
||||||
|
POLLIN n=10 buf=4294893839
|
||||||
|
....
|
||||||
|
|
||||||
|
Force the poll <<file-operations,`file_operation`>> to return 0 to see what happens more clearly:
|
||||||
|
|
||||||
|
....
|
||||||
|
./poll.sh pol0=1
|
||||||
|
....
|
||||||
|
|
||||||
|
Sample output:
|
||||||
|
|
||||||
|
....
|
||||||
|
poll
|
||||||
|
<6>[ 85.674801] poll
|
||||||
|
<6>[ 85.675788] return 0
|
||||||
|
<6>[ 86.675182] wake_up
|
||||||
|
<6>[ 86.676431] poll
|
||||||
|
<6>[ 86.677373] return 0
|
||||||
|
<6>[ 87.679198] wake_up
|
||||||
|
<6>[ 87.680515] poll
|
||||||
|
<6>[ 87.681564] return 0
|
||||||
|
<6>[ 88.683198] wake_up
|
||||||
|
....
|
||||||
|
|
||||||
|
From this we see that control is not returned to userland: the kernel just keeps calling the poll `file_operation` again and again.
|
||||||
|
|
||||||
Typically, we are waiting for some hardware to make some piece of data available available to the kernel.
|
Typically, we are waiting for some hardware to make some piece of data available available to the kernel.
|
||||||
|
|
||||||
The hardware notifies the kernel that the data is ready with an interrupt.
|
The hardware notifies the kernel that the data is ready with an interrupt.
|
||||||
|
|
||||||
To simplify this example, we just fake the hardware interrupts with a <<kthread>> that sleeps for a second in an infinite loop.
|
To simplify this example, we just fake the hardware interrupts with a <<kthread>> that sleeps for a second in an infinite loop.
|
||||||
|
|
||||||
Bibliography: https://stackoverflow.com/questions/30035776/how-to-add-poll-function-to-the-kernel-module-code/44645336#44645336
|
Bibliography:
|
||||||
|
|
||||||
|
* https://stackoverflow.com/questions/30035776/how-to-add-poll-function-to-the-kernel-module-code/44645336#44645336
|
||||||
|
* https://stackoverflow.com/questions/30234496/why-do-we-need-to-call-poll-wait-in-poll/44645480#44645480
|
||||||
|
|
||||||
==== ioctl
|
==== ioctl
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,7 @@
|
|||||||
* Adapted from: https://github.com/torvalds/linux/blob/v4.17/samples/kprobes/kprobe_example.c
|
* Adapted from: https://github.com/torvalds/linux/blob/v4.17/samples/kprobes/kprobe_example.c
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/* NOTE: This example is works on x86 and powerpc.
|
||||||
* NOTE: This example is works on x86 and powerpc.
|
|
||||||
* Here's a sample kernel module showing the use of kprobes to dump a
|
* Here's a sample kernel module showing the use of kprobes to dump a
|
||||||
* stack trace and selected registers when _do_fork() is called.
|
* stack trace and selected registers when _do_fork() is called.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
#include <linux/seq_file.h> /* seq_read, seq_lseek, single_release */
|
#include <linux/seq_file.h> /* seq_read, seq_lseek, single_release */
|
||||||
#include <uapi/linux/stat.h> /* S_IRUSR | S_IWUSR */
|
#include <uapi/linux/stat.h> /* S_IRUSR | S_IWUSR */
|
||||||
|
|
||||||
static u32 i = 0;
|
static int i = 0;
|
||||||
static u32 j = 0;
|
static int j = 0;
|
||||||
module_param(i, int, S_IRUSR | S_IWUSR);
|
module_param(i, int, S_IRUSR | S_IWUSR);
|
||||||
module_param(j, int, S_IRUSR | S_IWUSR);
|
module_param(j, int, S_IRUSR | S_IWUSR);
|
||||||
MODULE_PARM_DESC(i, "my favorite int");
|
MODULE_PARM_DESC(i, "my favorite int");
|
||||||
|
|||||||
@@ -14,6 +14,10 @@
|
|||||||
#include <linux/wait.h> /* wait_queue_head_t, wait_event_interruptible, wake_up_interruptible */
|
#include <linux/wait.h> /* wait_queue_head_t, wait_event_interruptible, wake_up_interruptible */
|
||||||
#include <uapi/linux/stat.h> /* S_IRUSR */
|
#include <uapi/linux/stat.h> /* S_IRUSR */
|
||||||
|
|
||||||
|
static int ret0 = 0;
|
||||||
|
module_param(ret0, int, S_IRUSR | S_IWUSR);
|
||||||
|
MODULE_PARM_DESC(i, "if 1, always return 0 from poll");
|
||||||
|
|
||||||
static char readbuf[1024];
|
static char readbuf[1024];
|
||||||
static size_t readbuflen;
|
static size_t readbuflen;
|
||||||
static struct dentry *debugfs_file;
|
static struct dentry *debugfs_file;
|
||||||
@@ -34,24 +38,33 @@ static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If you return 0 here, then the kernel will sleep until an event happens in the queue.
|
/* If you return 0 here, then the kernel will sleep until an event
|
||||||
*
|
* happens in the queue. and then call this again, because of the call to poll_wait. */
|
||||||
* This gets called again every time an event happens in the wait queue.
|
|
||||||
*/
|
|
||||||
unsigned int poll(struct file *filp, struct poll_table_struct *wait)
|
unsigned int poll(struct file *filp, struct poll_table_struct *wait)
|
||||||
{
|
{
|
||||||
|
pr_info("poll\n");
|
||||||
|
/* This doesn't sleep. It just makes the kernel call poll again if we return 0. */
|
||||||
poll_wait(filp, &waitqueue, wait);
|
poll_wait(filp, &waitqueue, wait);
|
||||||
if (readbuflen)
|
if (readbuflen && !ret0) {
|
||||||
|
pr_info("return POLLIN\n");
|
||||||
return POLLIN;
|
return POLLIN;
|
||||||
else
|
} else {
|
||||||
|
pr_info("return 0\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kthread_func(void *data)
|
static int kthread_func(void *data)
|
||||||
{
|
{
|
||||||
while (!kthread_should_stop()) {
|
while (!kthread_should_stop()) {
|
||||||
readbuflen = snprintf(readbuf, sizeof(readbuf), "%llu", (unsigned long long)jiffies);
|
readbuflen = snprintf(
|
||||||
|
readbuf,
|
||||||
|
sizeof(readbuf),
|
||||||
|
"%llu",
|
||||||
|
(unsigned long long)jiffies
|
||||||
|
);
|
||||||
usleep_range(1000000, 1000001);
|
usleep_range(1000000, 1000001);
|
||||||
|
pr_info("wake_up\n");
|
||||||
wake_up(&waitqueue);
|
wake_up(&waitqueue);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# https://cirosantilli.com/linux-kernel-module-cheat#poll
|
# https://cirosantilli.com/linux-kernel-module-cheat#poll
|
||||||
set -e
|
set -e
|
||||||
insmod poll.ko
|
insmod poll.ko "$@"
|
||||||
./kernel_modules/poll.out /sys/kernel/debug/lkmc_poll
|
./kernel_modules/poll.out /sys/kernel/debug/lkmc_poll
|
||||||
|
# TODO capture Ctrl+C and do this automatically.
|
||||||
#rmmod poll
|
#rmmod poll
|
||||||
|
|||||||
@@ -26,13 +26,14 @@ int main(int argc, char **argv) {
|
|||||||
pfd.fd = fd;
|
pfd.fd = fd;
|
||||||
pfd.events = POLLIN;
|
pfd.events = POLLIN;
|
||||||
while (1) {
|
while (1) {
|
||||||
puts("loop");
|
puts("poll");
|
||||||
i = poll(&pfd, 1, -1);
|
i = poll(&pfd, 1, -1);
|
||||||
if (i == -1) {
|
if (i == -1) {
|
||||||
perror("poll");
|
perror("poll");
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
revents = pfd.revents;
|
revents = pfd.revents;
|
||||||
|
printf("revents = %d\n", revents);
|
||||||
if (revents & POLLIN) {
|
if (revents & POLLIN) {
|
||||||
n = read(pfd.fd, buf, sizeof(buf));
|
n = read(pfd.fd, buf, sizeof(buf));
|
||||||
printf("POLLIN n=%d buf=%.*s\n", n, n, buf);
|
printf("POLLIN n=%d buf=%.*s\n", n, n, buf);
|
||||||
|
|||||||
Reference in New Issue
Block a user