Kernel: using sigqueue-functions [duplicate] - c

This question already has answers here:
Can't access super_blocks list from kernel module: undefined!
(2 answers)
Closed 6 years ago.
I try to implement a signal call from a loadable kernel module to a user application. The application uses sigaction to handle the signal call and later it shall use sival_int to handle different actions by switchand case.
void signalHandler_function (int signum,
siginfo_t *siginfo,
void *ucontext)
{
printf("signum: %i\n", signum);
printf("sigval: %d\n", siginfo->si_value.sival_int);
}
int main(){
int ret;
int pid = getpid();
char pidc[4];
struct sigaction sig;
memcpy(pidc,&pid,4);
// configure signal handler
sig.sa_flags = SA_SIGINFO;
sig.sa_sigaction = signalHandler_function;
sigaction(SIGIO, &sig, NULL);
g_fdCharDev = open(EXPDEV_DEVPATHNAME, O_RDWR);
printf("My process ID : %d\n", pid);
ret = write(g_fdCharDev, &pid, 1);
...
}
On kernel side I try to use send_sigqueue() (together with sigqueue_alloc()and sigqueue_free()). This is declared in include/linux/sched.h, line 2320 as extern and defined in kernel/signal.c, line 1560. But the linker says this functions are undefined:
make -C /lib/modules/3.19.0-58-generic/build/ M=/home/alex/git/Kernel3/SignalHandling modules
make[1]: Verzeichnis »/usr/src/linux-headers-3.19.0-58-generic« wird betreten
CC [M] /home/alex/git/Kernel3/SignalHandling/ExpDev.o
Building modules, stage 2.
MODPOST 1 modules
WARNING: "sigqueue_free" [/home/alex/git/Kernel3/SignalHandling/ExpDev.ko] undefined!
WARNING: "sigqueue_alloc" [/home/alex/git/Kernel3/SignalHandling/ExpDev.ko] undefined!
WARNING: "send_sigqueue" [/home/alex/git/Kernel3/SignalHandling/ExpDev.ko] undefined!
CC /home/alex/git/Kernel3/SignalHandling/ExpDev.mod.o
LD [M] /home/alex/git/Kernel3/SignalHandling/ExpDev.ko
make[1]: Verzeichnis »/usr/src/linux-headers-3.19.0-58-generic« wird verlassen
Process terminated with status 0 (0 minute(s), 0 second(s))
0 error(s), 0 warning(s) (0 minute(s), 0 second(s))
Here comes the shortened source of the LKM:
#include <linux/init.h> /// Macros used to mark up functions e.g. __init __exit
#include <linux/module.h> /// Core header for loading LKMs into the kernel
#include <linux/device.h> /// Header to support the kernel Driver Model
#include <linux/kernel.h> /// Contains types, macros, functions for the kernel
#include <linux/fs.h> /// Header for the Linux file system support
#include <asm/uaccess.h> /// Required for the copy to user function
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/rcupdate.h>
#include <linux/slab.h>
struct siginfo *g_sig_info;
static struct sigqueue *g_sigqueue;
int g_usp_pid;
struct task_struct *g_sig_task;
static int __init expdev_init(void)
{
...
// setup signal
g_sigqueue = sigqueue_alloc();
g_sigqueue->info.si_signo = SIGIO;
g_sigqueue->info.si_signo = SI_QUEUE;
g_sigqueue->info.si_errno = 0;
...
}
static void __exit expdev_exit(void)
{
sigqueue_free(g_sigqueue);
...
}
static ssize_t dev_write(struct file *filep,
const char *buffer,
size_t len,
loff_t *offset)
{
...
memcpy(&g_usp_pid,buffer,4); // we know the PID comes with the buffer
// find task to the given PID
rcu_read_lock();
g_sig_task = pid_task(find_pid_ns(g_usp_pid, &init_pid_ns),
PIDTYPE_PID);
//send signal to user land
g_sigqueue->info.si_value.sival_int = 33;
ret = send_sigqueue(g_sigqueue, g_sig_task, 0);
...
}
Why it isn't linkable? Is my approach wrong?

Any helpful list_head or function that is exported for kernel modules are found as address in the System.map file.
$ sudo grep sigqueue /boot/System.map-3.19.0-58-generic
ffffffff81084880 T sigqueue_alloc
ffffffff810848b0 T sigqueue_free
ffffffff81084920 T send_sigqueue
This function-pointer-addresses can be used to local functions:
#include <linux/init.h> /// Macros used to mark up functions e.g. __init __exit
#include <linux/module.h> /// Core header for loading LKMs into the kernel
#include <linux/device.h> /// Header to support the kernel Driver Model
#include <linux/kernel.h> /// Contains types, macros, functions for the kernel
#include <linux/fs.h> /// Header for the Linux file system support
#include <asm/uaccess.h> /// Required for the copy to user function
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/rcupdate.h>
#include <linux/slab.h>
struct siginfo *g_sig_info;
static struct sigqueue *g_sigqueue;
int g_usp_pid;
struct task_struct *g_sig_task;
// ffffffff81084920 T send_sigqueue
int (*send_sigqueue_ptr)( struct sigqueue *,
struct task_struct *,
int group) = (void*)0xffffffff81084920;
// ffffffff81084880 T sigqueue_alloc
struct sigqueue *(*sigqueue_alloc_ptr)(void) = (void*)0xffffffff81084880;
// ffffffff810848b0 T sigqueue_free
void (*sigqueue_free_ptr)(struct sigqueue *) = (void*)0xffffffff810848b0;
static int __init expdev_init(void)
{
...
// setup signal
g_sigqueue = sigqueue_alloc_ptr();
g_sigqueue->info.si_signo = SIGIO;
g_sigqueue->info.si_code = SI_QUEUE;
g_sigqueue->info.si_errno = 0;
...
}
static void __exit expdev_exit(void)
{
sigqueue_free_ptr(g_sigqueue);
...
}
static ssize_t dev_write(struct file *filep,
const char *buffer,
size_t len,
loff_t *offset)
{
...
memcpy(&g_usp_pid,buffer,4); // we know the PID comes with the buffer
// find task to the given PID
rcu_read_lock();
g_sig_task = pid_task(find_pid_ns(g_usp_pid, &init_pid_ns),
PIDTYPE_PID);
//send signal to user land
g_sigqueue->info.si_value.sival_int = 33;
ret = send_sigqueue_ptr(g_sigqueue, g_sig_task, 0);
...
}
This will work only for this special Kernel version and is not very portable. If there any macro or another workaround do get this addresses or functions? Is it a good way to compile the kernel/signal.c as well?
If anyone knows a way to improve this. Please feel free to comment and edit.

Related

Is SCHED_DEADLINE officially supported in Ubuntu 16.04?

Currently I'm running Ubuntu 16.04 with linux kernel version to be 4.16. I wrote a dummy program that changes its scheduler to SCHED_DEADLINE. But when I tried to compile it, it cannot find definition of structs and macros needed for SCHED_DEADLINE. Most of the code snippet was taken from here (page 24). Below is the test program:
#define _GNU_SOURCE
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sched.h>
int main(int argc, char* argv[]) {
struct sched_attr attr;
attr.size = sizeof(attr);
attr.sched_policy = SCHED_DEADLINE;
attr.sched_runtime = 30000000;
attr.sched_period = 100000000;
attr.sched_deadline = attr.sched_period;
if (sched_setattr(gettid(), &attr, 0))
perror("sched_setattr()");
return 0;
}
Here's the output of the compilation:
sched_deadline.c: In function ‘main’:
sched_deadline.c:11:20: error: storage size of ‘attr’ isn’t known
struct sched_attr attr;
^
sched_deadline.c:12:21: error: invalid application of ‘sizeof’ to incomplete type ‘struct attr’
attr.size = sizeof(struct attr);
^
sched_deadline.c:13:22: error: ‘SCHED_DEADLINE’ undeclared (first use in this function)
attr.sched_policy = SCHED_DEADLINE;
My gcc version:
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)
However, the sample code posted in the official website works for me, but the sample code manually defines all the needed macros and system calls in the program. My goal was to compile the application without adding those definitions, which should already be included in the newest kernel version. I have seen various places saying that SCHED_DEADLINE is officially supported after Linux 3.14.10, and upgrading the kernel would automatically solve this issue.
Things I've tried:
Recompiling 4.16 kernel. Previously I thought I need to turn on a switch in config file, but I was not able to find it.
Look into /usr/include/linux/sched.h. Clearly the macros are defined in this header file, but somehow my compiler cannot find it.
I also looked into other posts in the community, but all those questions are for older linux (pre 3.14.10).
You need to include #include <linux/sched.h>
But for the definition of sched_setattr() and gettid(), see the link posted by #CraigEstey
The reason about that, it that glibc will not add function wrappers of linux specific syscall.
For example for gettid(), in the manual we can read this:
Note: There is no glibc wrapper for this system call; see NOTES.
Glibc does not provide a wrapper for this system call; call it using
syscall(2).
The thread ID returned by this call is not the same thing as a POSIX thread ID
Have a look at this article: https://lwn.net/Articles/711058/
#define _GNU_SOURCE
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sched.h>
#include <linux/sched.h>
#include <sys/types.h>
struct sched_attr {
uint32_t size;
uint32_t sched_policy;
uint64_t sched_flags;
/* SCHED_NORMAL, SCHED_BATCH */
int32_t sched_nice;
/* SCHED_FIFO, SCHED_RR */
uint32_t sched_priority;
/* SCHED_DEADLINE (nsec) */
uint64_t sched_runtime;
uint64_t sched_deadline;
uint64_t sched_period;
};
int sched_setattr(pid_t pid, const struct sched_attr *attr, unsigned int flags)
{
return syscall(__NR_sched_setattr, pid, attr, flags);
}
int main(int argc, char* argv[]) {
struct sched_attr attr = {
.size = sizeof(attr),
.sched_policy = SCHED_DEADLINE,
.sched_runtime = 30000000,
.sched_period = 100000000,
.sched_deadline = 100000000
};
pid_t tid = syscall(SYS_gettid);
if (sched_setattr(tid, &attr, 0))
perror("sched_setattr()");
return 0;
}
Or a more shorter code, without the redefinition of struct sched_attr
#define _GNU_SOURCE
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/sched/types.h>
#include <linux/sched.h>
#include <sys/types.h>
int sched_setattr(pid_t pid, const struct sched_attr *attr, unsigned int flags)
{
return syscall(__NR_sched_setattr, pid, attr, flags);
}
int main(int argc, char* argv[]) {
struct sched_attr attr = {
.size = sizeof(attr),
.sched_policy = SCHED_DEADLINE,
.sched_runtime = 30000000,
.sched_period = 100000000,
.sched_deadline = 100000000
};
pid_t tid = syscall(SYS_gettid);
if (sched_setattr(tid, &attr, 0))
perror("sched_setattr()");
return 0;
}
But this needs to be executed as root, otherwise I got sched_setattr(): Operation not permitted
Or the application needs to have the right linux capabilities.

Build against newer linux headers than libc is built using

I want to write a program using the new SCHED_DEADLINE scheduling policy available since Linux 3.14.
I start out with a simple program trying to use the sched_setattr function.
#include <sched.h>
int main(void)
{
// struct sched_attr attr;
// attr.size = sizeof(struct sched_attr);
// attr.sched_policy = SCHED_DEADLINE;
sched_setattr(0, (void*)0, 0);
return 0;
}
However when compiling I get the following error:
$gcc dead.c
dead.c: In function ‘main’:
dead.c:8:2: warning: implicit declaration of function ‘sched_setattr’ [-Wimplicit-function-declaration]
sched_setattr(0, (void*)0, 0);
^~~~~~~~~~~~~
/tmp/ccGxWxZE.o: In function `main':
dead.c:(.text+0x19): undefined reference to `sched_setattr'
collect2: error: ld returned 1 exit status
My system is running Ubuntu 16.10 Yakkety, with kernel 4.8.0-59-generic. The sched.h file included is found in /usr/include/sched.h and is provided by the package libc6-dev. This headerfile does not contain the function sched_setattr and friends that I am trying to use.
However the kernel (and kernel headers) I have installed comes with a sched.h header file containing the definitions I need. It is located at /usr/src/linux-headers-4.8.0-58/include/linux/sched.h, on my system.
So I naively think lets just build against the newer linux headers instead of the libc6-dev provided headers. My program will only run on this or newer kernels, but that is just fine.
I modify the first line to be: #include <linux/sched.h> and execute:
gcc -I/usr/src/linux-headers-$(uname -r)/include -I/usr/src/linux-headers-$(unam -r)/arch/x86/include dead.c
Now I am getting page after page of errors and warning. This does not seem the way to go.
What is the correct way to build a userspace program against a newer Linux headers than those that are provided by libc?
And subsequently how do I build the program above?
sched_setattr() is a syscall and doesn't seem to have one-to-one libc wrapper. You could do the wrapper yourself, something like this:
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <linux/sched.h>
#include <sys/syscall.h>
#include <sys/types.h>
struct sched_attr {
uint32_t size; /* Size of this structure */
uint32_t sched_policy; /* Policy (SCHED_*) */
uint64_t sched_flags; /* Flags */
int32_t sched_nice; /* Nice value (SCHED_OTHER, SCHED_BATCH) */
uint32_t sched_priority; /* Static priority (SCHED_FIFO, SCHED_RR) */
/* Remaining fields are for SCHED_DEADLINE */
uint64_t sched_runtime;
uint64_t sched_deadline;
uint64_t sched_period;
};
static int sched_setattr (pid_t pid, const struct sched_attr *attr, unsigned int flags)
{
return syscall (SYS_sched_setattr, pid, attr, flags);
}
int main (int argc, char *argv[])
{
struct sched_attr attr;
int res;
memset (&attr, 0, sizeof (struct sched_attr));
attr.size = sizeof (struct sched_attr);
res = sched_setattr (getpid (), &attr, 0);
if (res < 0) {
perror ("sched_setattr");
return 1;
}
return 0;
}
Looking at the errors reported when trying to include kernel header files required to get the definition of struct sched_attr and reading the comments found by Googling "kernel headers in user space", I really can't suggest trying to include kernel header files just for this.

Instrument linux kernel local interrupt handling on x86-64

I would like to write some code (for example as a small kernel module) to instrument local interrupts on Linux running on the x86-64 architecture, i.e. I would like to write some kind of handler that is called by the kernel every time a local interrupt is triggered by the APIC.
The handler would then check whether a certain process is currently running and inspect said process' memory.
I realize that what I am trying to do may not be good engineering practice, but my aim is to create a hacky one-off solution for exploration/research purposes.
In the ideal case, there would be some kind of function similar to request_irq [1] (which as far as I can tell is used for handling interrupts from devices like keyboards, network cards, ...) allowing me to tell the kernel to run my code every time a local timer interrupt occurs.
Does anybody have any pointers on how to accomplish this?
Does the kernel provide an API for registering a handler for local interrupts?
If not, I could directly modify the kernel's source code for handling interrupts. Where in the kernel would I find this code?
Edit: Here is what my research so far has found.
TLDR:request_irq is not the way to hook local timer interrupts.
According to Understanding the Linux Kernel, Table 4.2 [2], the interrupt vector 0xef is allocated to Local APIC timer interrupts. The kernel source confirms this [3].
Since request_irq takes an interrupt vector as its first argument, let's try registering a handler for this vector inside a kernel module:
#include <linux/module.h>
#include <linux/kernel.h> // included for KERN_INFO
#include <linux/init.h> // included for __init and __exit macros
#include <linux/interrupt.h> // included for IRQF_ and request_irq
#include <linux/irqreturn.h> // included for IRQ_NONE
#include <asm/irq_vectors.h> // included for LOCAL_TIMER_VECTOR
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Instrument local timer interrupts");
static int DEVICE_COOKIE = 1;
static int count = 0;
static irqreturn_t handler(int irq, void *dev) {
count++;
return IRQ_NONE;
}
static int __init test_init(void) {
int status;
printk(KERN_INFO "Running request_irq\n");
status = request_irq(
LOCAL_TIMER_VECTOR,
&handler,
IRQF_TIMER,
"foobar",
&DEVICE_COOKIE);
if (status == 0) {
printk(KERN_INFO "Successfully installed handler\n");
return 0;
} else {
printk(
KERN_INFO "Failed to install handler. error code: %d\n",
status);
return -1;
}
}
static void __exit test_cleanup(void) {
free_irq(LOCAL_TIMER_VECTOR, &DEVICE_COOKIE);
printk(KERN_INFO "Goodbye kernel. I saw %d interrupts.\n", count);
}
module_init(test_init);
module_exit(test_cleanup);
When we try to insert the module with insmod, request_irq returns -EINVAL:
[74890.287173] Running request_irq
[74890.287174] Failed to install handler. error code: -22
So where does the -EINVAL come from?
Reading through the kernel source, we find that request_irq is just a wrapper around request_threaded_irq [4]. request_threaded_irq calls irq_to_desc and returns -EINVAL if the call fails. We can easily check whether this is the case with another small kernel module:
#include <linux/kernel.h> // included for KERN_INFO
#include <linux/init.h> // included for __init and __exit macros
#include <linux/interrupt.h> // included for IRQF_ and request_irq
#include <linux/irqreturn.h> // included for IRQ_NONE
#include <asm/irq_vectors.h> // included for LOCAL_TIMER_VECTOR
#include <linux/irqnr.h> // included for irq_to-desc
#include <linux/irqdesc.h> // included for irq_desc
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Call irq_to_desc(LOCAL_TIMER_VECTOR)");
static int __init test_init(void) {
struct irq_desc *desc;
desc = irq_to_desc(LOCAL_TIMER_VECTOR);
if (!desc) {
printk(KERN_INFO "irq_to_desc(LOCAL_TIMER_VECTOR) failed");
return -1;
}
return 0;
}
static void __exit test_cleanup(void) {
}
module_init(test_init);
module_exit(test_cleanup);
And indeed it fails:
[75787.142533] irq_to_desc(LOCAL_TIMER_VECTOR) failed
[1] www.makelinux.net/books/lkd2/ch06lev1sec3
[2] www.safaribooksonline.com/library/view/understanding-the-linux/0596005652/ch04s06.html
[3] lxr.free-electrons.com/source/arch/x86/include/asm/irq_vectors.h?v=4.8#L108
[4] lxr.free-electrons.com/source/kernel/irq/manage.c?v=4.8#L1634

Linux Kernel Module/IOCTL: inappropriate ioctl for device

I am in the process of writing a Linux Kernel Module (LKM) serving as a pseudo-driver - I am unable to figure out how to make IOCTL calls between the LKM (wait.c) and the user-level program (user.c).
The magic number for the device driver is 0xBF - the LKM does not communicate with a physical block/char device, it is simply an exercise. From what I can tell, the IOCTL call to KERN_IOCTL_CREATE_EVENT is not formatted properly & the magic number is incorrect.
The IOCTL call that I am attempting to use is:
#include <sys/ioctl.h>
#define KERN_IOCTL_CREATE_EVENT _IOWR(WAIT_DEVICE_MAGIC, 1, int)
int main(){
int ret;
int fd;
fd = open("/dev/wait", 0);
if(fd < 0){
return -1;
}
ret = ioctl(fd, KERN_IOCTL_CREATE_EVENT, 0);
Error:
[fail]: KERN_IOCTL_CREATE_EVENT: Inappropriate ioctl for device
The user-mode application can open/close a file descriptor pointing to the device: /dev/wait but the case/switch statement isn't accepting the IOCTL call. Any suggestions?
Here is the output of # uname -a
Linux vagrant-ubuntu-trusty-64 3.13.11.11+ #1 SMP Mon Dec 1 20:50:23 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
wait.c
#include <linux/miscdevice.h>
#include <linux/moduleparam.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/fs.h>
#include "wait.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tyler Fisher <tyler#tylerfisher.org>");
MODULE_DESCRIPTION("In-kernel wait queue");
static unsigned long event_table_size = 50;
module_param(event_table_size, ulong, (S_IRUSR | S_IRGRP | S_IROTH));
MODULE_PARM_DESC(event_table_size, "Size of event table (i.e. how many processes can be blocking)");
/* IOCTL function headers */
static int wait_open(struct inode *, struct file *);
static int wait_close(struct inode *, struct file *);
static long wait_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
/* other function headers */
static long event_open(int event_id);
/* file operations */
static struct file_operations wait_fops = {
.owner = THIS_MODULE,
.open = wait_open,
.release = wait_close,
.llseek = noop_llseek,
.unlocked_ioctl = wait_ioctl
};
/* device handler */
static struct miscdevice wait_misc_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = WAIT_DEVICE_NAME,
.fops = &wait_fops
};
/* open wait device */
static int wait_open(struct inode *inode, struct file *file){
dev_t node = iminor(inode);
if(MINOR(node) != WAIT_DEVICE_MINOR){
return -ENODEV;
}
return 0;
}
static int wait_close(struct inode *inode, struct file *file){
return 0;
}
static long wait_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long sub_cmd){
switch(cmd){
case KERN_IOCTL_CREATE_EVENT:
printk(KERN_INFO "[wait device]: create event %lu\n", sub_cmd);
return event_open(sub_cmd);
default:
return -ENOENT;
}
}
static long event_open(int id){
return 0;
}
static long __init wait_init(void){
if(misc_register(&wait_misc_device) < 0){
printk(KERN_ERR "[wait device] failed to register device\n");
return -1;
}
printk(KERN_INFO "[wait device] has been registered\n");
return 0;
}
static void __exit wait_exit(void){
misc_deregister(&wait_misc_device);
printk(KERN_INFO "[wait device] has been unregistered\n");
}
module_init(wait_init);
module_exit(wait_exit);
wait.h
#include <linux/ioctl.h>
#define WAIT_DEVICE_NAME "wait"
#define WAIT_DEVICE_MAGIC 0xBF
#define WAIT_DEVICE_MAJOR 200
#define WAIT_DEVICE_MINOR 0
#define KERN_IOCTL_CREATE_EVENT _IOWR(WAIT_DEVICE_MAGIC, 0x01, int)
#define MAX_WAITING 5
The test program for the IOCTL calls:
user.c
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#define WAIT_DEVICE_MAGIC 0xBF
#define KERN_IOCTL_CREATE_EVENT _IOWR(WAIT_DEVICE_MAGIC, 0x01, int)
#define KERN_IOCTL_DESTROY_EVENT _IOWR(WAIT_DEVICE_MAGIC, 0x02, int)
#define KERN_IOCTL_LOCK_EVENT _IOWR(WAIT_DEVICE_MAGIC, 0x03, int)
#define KERN_IOCTL_UNLOCK_EVENT _IOWR(WAIT_DEVICE_MAGIC, 0x04, int)
int main(){
int fd;
if(fd = open("/dev/wait", O_RDWR) < 0){
perror("failed to open /dev/wait");
return -1;
}
/* test IOCTL: event creation */
if(ioctl(fd, KERN_IOCTL_CREATE_EVENT, 0) < 0){
perror("[fail]: KERN_IOCTL_CREATE_EVENT");
return -1;
}
return 0;
}
Makefile
obj-m += wait.o
CFLAGS_wait.o += -DDEBUG
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
In order to test out the LKM - clear dmesg, compile & execute user.c w/GCC:
# dmesg -c > /dev/null 2>&1
# make
# rmmod wait.ko
# insmod wait.ko
# gcc user.c -o user && ./user
The amount of debugging errors is embarassing. I feel bad for sharing this - and realize that this may cause the issue to be closed/downvoted into oblivion.
# sh test.sh
[+] cleared dmesg
make -C /lib/modules/3.13.11.11+/build M=/home/vagrant/PROG40000-kernel-synchronization modules
make[1]: Entering directory `/home/vagrant/ubuntu-trusty'
CC [M] /home/vagrant/PROG40000-kernel-synchronization/wait.o
/home/vagrant/PROG40000-kernel-synchronization/wait.c:61:1: warning: initialization from incompatible pointer type [enabled by default]
};
^
/home/vagrant/PROG40000-kernel-synchronization/wait.c:61:1: warning: (near initialization for ‘wait_fops.unlocked_ioctl’) [enabled by default]
In file included from include/linux/moduleparam.h:4:0,
from /home/vagrant/PROG40000-kernel-synchronization/wait.c:11:
/home/vagrant/PROG40000-kernel-synchronization/wait.c: In function ‘__inittest’:
include/linux/init.h:297:4: warning: return from incompatible pointer type [enabled by default]
{ return initfn; } \
^
/home/vagrant/PROG40000-kernel-synchronization/wait.c:167:1: note: in expansion of macro ‘module_init’
module_init(wait_init);
^
Building modules, stage 2.
MODPOST 1 modules
CC /home/vagrant/PROG40000-kernel-synchronization/wait.mod.o
LD [M] /home/vagrant/PROG40000-kernel-synchronization/wait.ko
make[1]: Leaving directory `/home/vagrant/ubuntu-trusty'
[--dmesg--]
[13112.810008] [wait device] has been unregistered
[13112.819049] [wait device] has been registered
[-/dmesg--]
[+] compiled user-mode program
-----
[fail]: KERN_IOCTL_CREATE_EVENT: Inappropriate ioctl for device
[fail]: KERN_IOCTL_CREATE_EVENT: Inappropriate ioctl for device
[+] executed user-mode program
-----
[--dmesg--]
[13112.810008] [wait device] has been unregistered
[13112.819049] [wait device] has been registered
[13112.893049] SOMEONE DARE READ FROM ME!?
[13112.893057] [wait device] invalid magic number: 0:0:191
[13112.893535] [wait device] invalid magic number: 0:0:191
[-/dmesg--]
Okay. So. Here's the solution.
In Linux kernel 2.6.x the declaration for _ioctl calls changed from
static long wait_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
To:
static long wait_ioctl(struct file *, unsigned int, unsigned long);
The fix is thus:
...
static long wait_ioctl(struct file *, unsigned int, unsigned long);
...
static long wait_ioctl(struct file *file, unsigned int cmd, unsigned long sub_cmd){
if(_IOC_TYPE(cmd) != WAIT_DEVICE_MAGIC){
printk(KERN_INFO "[wait device] invalid magic number: %u:%u:%u", _IOC_TYPE(cmd), cmd, WAIT_DEVICE_MAGIC);
return -ENOTTY;
}
....
.compat_ioctl
Also make sure to implement this file_operation if you are making 32-bit calls to a 64-bit kernel.
The symptom is that your ioctl handler is never run.

Simple interrupt handler: request_irq returns error code -22

I am writing a simple kernel module, which could register an interrupt and handle it.
However, when I try to register interrupt by calling the request_irq function,
it returns error code -22 :
ERROR: Cannot request IRQ 30 - code -22 , EIO 5 , EINVAL 22
I believe, this error code is equal to EINVAL (invalid argument)
Please tell me, what I am doing wrong. Here is a module:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/irqdomain.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <asm/exception.h>
#include <asm/mach/irq.h>
void int068_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
printk("Interrupt should be handled there\n");
}
static int __init
clcdint_init(void)
{
unsigned int irq;
unsigned int irqflags;
int ret;
irq=68;
irqflags=IRQF_SHARED | IRQF_NO_SUSPEND;
ret = request_irq(irq, int068_interrupt,
irqflags, "clcdint-int068", NULL);
if (ret!=0) {
printk("ERROR: Cannot request IRQ %d", irq);
printk(" - code %d , EIO %d , EINVAL %d\n", ret, EIO, EINVAL);
}
printk("CLCDINT_INIT\n");
return 0;
}
module_init(clcdint_init);
static void __exit
clcdint_exit(void)
{
unsigned int irq;
irq=68;
free_irq(irq, NULL);
printk("CLCDINT_EXIT\n");
}
module_exit(clcdint_exit);
You can't pass a NULL context (last parameters of the request_irq() call) when dealing with a shared interrupt line (IRQF_SHARED flag is on).
To understand why consider the following scenario: you have two identical network cards sharing the same IRQ. The same driver will pass the same interrupt handler function, the same irq number and the same description. There is no way to distinguish the two instances of the registration except via the context parameter.
Therefore, as a precaution, you can't pass a NULL context parameter if you pass the IRQF_SHARED flag.
irqflags has a type of unsigned int, but originally it had type long.
Try the following statement, it will definitely work:
request_irq(irq, int068_interrupt,IRQF_SHARED | IRQF_NO_SUSPEND, "clcdint-int068", NULL);

Resources