Writing to eventfd from kernel module

后端 未结 3 1117
無奈伤痛
無奈伤痛 2020-12-24 08:45

I have created an eventfd instance in a userspace program using eventfd(). Is there a way in which I can pass some reference (a pointer to its struct or pid+fd pair) to this

相关标签:
3条回答
  • 2020-12-24 09:16

    Consult the kernel source here:

    http://lxr.free-electrons.com/source/fs/eventfd.c

    Basically, send your userspace file descriptor, as produced by eventfd(), to your module via ioctl() or some other path. From the kernel, call eventfd_ctx_fdget() to get an eventfd context, then eventfd_signal() on the resulting context. Don't forget eventfd_ctx_put() when you're done with the context.

    0 讨论(0)
  • 2020-12-24 09:23

    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.
    0 讨论(0)
  • 2020-12-24 09:28

    how do I resolve the "struct file *" pointers to these eventfds from kernelspace

    You must resolve those pointers into data structures that this interface you've created has published (create new types and read the fields you want from struct file into it).

    Is there better way to signal events to userspace from kernelspace?

    Netlink sockets are another convenient way for the kernel to communicate with userspace. "Better" is in the eye of the beholder.

    0 讨论(0)
提交回复
热议问题