Writing to Eventfd from Kernel Module

Writing to eventfd from kernel module

I finally figured out how to do this. I realized that each open file on a system could be identified by the pid of one of the processes which opened it and the fd corresponding to that file (within that process's context). So if my kernel module knows the pid and fd, it can look up the struct * task_struct of the process and from that the struct * files and finally using the fd, it can acquire the pointer to the eventfd's struct * file. Then, using this last pointer, it can write to the eventfd's counter.

Here are the codes for the userspace program and the kernel module that I wrote up to demonstrate the concept (which now work):

Userspace C code (efd_us.c):

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h> //Definition of uint64_t
#include <sys/eventfd.h>

int efd; //Eventfd file descriptor
uint64_t eftd_ctr;

int retval; //for select()
fd_set rfds; //for select()

int s;

int main() {


//Create eventfd
efd = eventfd(0,0);
if (efd == -1){
printf("\nUnable to create eventfd! Exiting...\n");
exit(EXIT_FAILURE);
}

printf("\nefd=%d pid=%d",efd,getpid());

//Watch efd
FD_ZERO(&rfds);
FD_SET(efd, &rfds);

printf("\nNow waiting on select()...");
fflush(stdout);

retval = select(efd+1, &rfds, NULL, NULL, NULL);

if (retval == -1){
printf("\nselect() error. Exiting...");
exit(EXIT_FAILURE);
} else if (retval > 0) {
printf("\nselect() says data is available now. Exiting...");
printf("\nreturned from select(), now executing read()...");
s = read(efd, &eftd_ctr, sizeof(uint64_t));
if (s != sizeof(uint64_t)){
printf("\neventfd read error. Exiting...");
} else {
printf("\nReturned from read(), value read = %lld",eftd_ctr);
}
} else if (retval == 0) {
printf("\nselect() says that no data was available");
}

printf("\nClosing eventfd. Exiting...");
close(efd);
printf("\n");
exit(EXIT_SUCCESS);
}

Kernel Module C code (efd_lkm.c):

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pid.h>
#include <linux/sched.h>
#include <linux/fdtable.h>
#include <linux/rcupdate.h>
#include <linux/eventfd.h>

//Received from userspace. Process ID and eventfd's File descriptor are enough to uniquely identify an eventfd object.
int pid;
int efd;

//Resolved references...
struct task_struct * userspace_task = NULL; //...to userspace program's task struct
struct file * efd_file = NULL; //...to eventfd's file struct
struct eventfd_ctx * efd_ctx = NULL; //...and finally to eventfd context

//Increment Counter by 1
static uint64_t plus_one = 1;

int init_module(void) {
printk(KERN_ALERT "~~~Received from userspace: pid=%d efd=%d\n",pid,efd);

userspace_task = pid_task(find_vpid(pid), PIDTYPE_PID);
printk(KERN_ALERT "~~~Resolved pointer to the userspace program's task struct: %p\n",userspace_task);

printk(KERN_ALERT "~~~Resolved pointer to the userspace program's files struct: %p\n",userspace_task->files);

rcu_read_lock();
efd_file = fcheck_files(userspace_task->files, efd);
rcu_read_unlock();
printk(KERN_ALERT "~~~Resolved pointer to the userspace program's eventfd's file struct: %p\n",efd_file);


efd_ctx = eventfd_ctx_fileget(efd_file);
if (!efd_ctx) {
printk(KERN_ALERT "~~~eventfd_ctx_fileget() Jhol, Bye.\n");
return -1;
}
printk(KERN_ALERT "~~~Resolved pointer to the userspace program's eventfd's context: %p\n",efd_ctx);

eventfd_signal(efd_ctx, plus_one);

printk(KERN_ALERT "~~~Incremented userspace program's eventfd's counter by 1\n");

eventfd_ctx_put(efd_ctx);

return 0;
}


void cleanup_module(void) {
printk(KERN_ALERT "~~~Module Exiting...\n");
}

MODULE_LICENSE("GPL");
module_param(pid, int, 0);
module_param(efd, int, 0);

To run this, carry out the following steps:

  1. Compile the userspace program (efd_us.out) and the kernel module (efd_lkm.ko)
  2. Run the userspace program (./efd_us.out) and note the pid and efd values that it print. (for eg. "pid=2803 efd=3". The userspace program will wait endlessly on select()
  3. Open a new terminal window and insert the kernel module passing the pid and efd as params: sudo insmod efd_lkm.ko pid=2803 efd=3
  4. Switch back to the userspace program window and you will see that the userspace program has broken out of select and exited.

Using socket api, how to pass in an extra data structure to a fd in kernel module?

Your understanding is correct, you could pass anything to your ioctl() handler from userspace, it's then up to the kernel module to correctly handle whatever command and argument you pass. However, since you are working with sockets and writing your own protocol, it would be more appropriate to implement this functionality through getsockopt(2)/setsockopt(2). The argument to setsockopt(2) can be anything you want. From userspace you would do something like:

res = setsockopt(sock_fd, &your_struct, sizeof(your_struct));

Passing enum from kernel module to UserSpace program

Let's first address the issue of what copy_to_user does and why is it needed?

Modern computers work with a mechanism that's called Virtual Memory. This is a mechanism that allows 2 main things - The separation of memory between different processes, and the allocation of more virtual memory than there is physical memory on the machine.

For each process, there's a a distinct virtual memory space, and different processes could have the same Virtual Address point to different Physical addresses.

The kernel maps between the Virtual address spaces of processes to their Physical addresses. But what happens when you pass the Kernel a pointer?

The pointer you pass to the kernel is a virtual address in the user space processes virtual memory. The kernel needs to translate that address into the corresponding physical address in order to fill that address with the result.
For that operation copy_to_user was created - It takes a pointer in the Kernel's address space and copies it into a pointer in the user processes address space.

From all written above you should already understand that your question is invalid - an Enum value is not a pointer, so there's no need to call copy_to_user on it, you can just return it as is.

The next thing we need to discuss is what's an ENUM. An enum is a syntactic sugar provided by many modern languages to allow the definition of values as human-readable identifiers.
The enum keys don't exist past-compilation, there's just integer values passed between functions. They are translated much like #define into their value, and the named key doesn't matter anymore.

The only thing you need to do when returning an ENUM from the kernel, is make sure you #include the proper header in the user program so that you can translate the numbers which the enum keys represent correctly in your program. In runtime - numbers are all that's being passed.

How to send a signal / interrupt from kernel built in module to a loadable kernel module?

Also for an efficient solution, you can make use of function pointers from kernel source code.

But the problem here is you have to modify Linux source code. Be very careful when you do such modifications.

  1. Implement a NULL function pointer from you Linux source code - Call to this function pointer from the kernel routine (check for NULL and do)
  2. Export the symbol
  3. Provide a local function address to this symbol from your loadable module.

That's it...!!! You will get a function call from the kernel routine. Also make sure that when you exit the module, put the symbol back to NULL, otherwise kernel will crash.



Related Topics



Leave a reply



Submit