How to add poll function to the kernel module code

2019-01-07 18:23发布

问题:

as I know, to inform the user space from kernel space, one way is to using poll. That means kernel driver should provide poll method first. Below code is found from internet, and it really works!

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
#include <asm/uaccess.h>

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Fortune Cookie Kernel Module");
MODULE_AUTHOR("M. Tim Jones");

#define MAX_COOKIE_LENGTH       PAGE_SIZE

static struct proc_dir_entry *proc_entry;
static char *cookie_buf;  // Space for fortune strings
static int write_index;   // Index to write next fortune
static int read_index;    // Index to read next fortune

ssize_t fortune_write( struct file *filp, const char __user *buff,
                        unsigned long len, void *data )
// Refer to: ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
{
  int space_available = (MAX_COOKIE_LENGTH-write_index);

  if (len > space_available) {
    printk(KERN_INFO "fortune: cookie buffer is full!\n");
    return -ENOSPC;
  }

  if (copy_from_user( &cookie_buf[write_index], buff, len )) {
    return -EFAULT;
  }

  write_index += len;
  cookie_buf[write_index-1] = 0;

  return len;
}

ssize_t fortune_read(struct file *file, char *buf, size_t count, loff_t *f_pos){
// Refer to: ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    int len;

    //there's no fortune or a fortune has already been read
    //the *f_pos > 0 hack is needed because `cat /proc/fortune` would otherwise
    //display every thing in the cookie_buf
    if(write_index == 0 || *f_pos > 0){
        return 0;
    }

    // cicle through fortunes
    if(read_index >= write_index){
        read_index = 0;
    }

    len = sprintf(buf, "%s\n", &cookie_buf[read_index]);

    read_index += len;
    *f_pos += len;

    return len;
}

static const struct file_operations proc_test_fops = {
   .owner        = THIS_MODULE,
//    .open        = led_proc_open,
   .read        = fortune_read,
//    .llseek        = seq_lseek,
//    .release    = single_release,
   .write        = fortune_write,
//    unsigned int (*poll) (struct file *, struct poll_table_struct *);
//    int (*fasync) (int, struct file *, int);
};

int __init init_fortune_module( void )
{
    int ret = 0;
    cookie_buf = (char *)vmalloc( MAX_COOKIE_LENGTH );
    if (!cookie_buf) {
        ret = -ENOMEM;
    } else {
        memset( cookie_buf, 0, MAX_COOKIE_LENGTH );
//        proc_entry = create_proc_entry( "fortune", 0644, NULL );
        proc_entry = proc_create( "fortune", 0644, NULL, &proc_test_fops );

        if (proc_entry == NULL) {
            ret = -ENOMEM;
            vfree(cookie_buf);
            printk(KERN_INFO "fortune: Couldn't create proc entry\n");
        } else {
            write_index = 0;
            read_index = 0;
            printk(KERN_INFO "fortune: Module loaded.\n");
        }
    }

    return ret;
}

void __exit exit_fortune_module( void )
{
//    remove_proc_entry("fortune", &proc_entry);
    proc_remove(proc_entry);
    vfree(cookie_buf);
    printk(KERN_INFO "fortune: Module unloaded.\n");
}

module_init( init_fortune_module );
module_exit( exit_fortune_module );

I can do like this to make it work:

echo "hello" > /proc/fortune

And then

cat /proc/fortune

to see the result.

But how to add poll method to it? I tried some times, but still failed. Could anyone help? Thanks!

回答1:

You can find some good examples in kernel itself. Take a look at next files:

  • drivers/char/rtc.c
  • fs/proc/kmsg.c
  • drivers/char/random.c

To add poll() function to your code follow next steps.

  1. Include needed headers:

    #include <linux/wait.h>
    #include <linux/poll.h>
    
  2. Declare waitqueue variable:

    static DECLARE_WAIT_QUEUE_HEAD(fortune_wait);
    
  3. Add fortune_poll() function and add it (as .poll callback) to your file operations structure:

    static unsigned int fortune_poll(struct file *file, poll_table *wait)
    {
        poll_wait(file, &fortune_wait, wait);
        if (new-data-is-ready)
            return POLLIN | POLLRDNORM;
        return 0;
    }
    
    static const struct file_operations proc_test_fops = {
        ....
        .poll = fortune_poll,
    };
    

    Note that you should return POLLIN | POLLRDNORM if you have some new data to read, and 0 in case there is no new data to read (poll() call timed-out). See man 2 poll for details.

  4. Notify your waitqueue once you have new data:

    wake_up_interruptible(&fortune_wait);
    

That's the basic stuff about implementing poll() operation. Depending on your task, you may be needed to use some waitqueue API in your .read function (like wait_event_interruptible()).


See also related question: Implementing poll in a Linux kernel module.



回答2:

Minimal runnable example

Usage:

insmod /poll.ko
mount -t debugfs none /sys/kernel/debug
/poll.out /sys/kernel/debug/lkmc_poll/f

Outcome: every one second, the following is printed to screen:

loop
POLLIN n=10 buf=<jiffies>

GitHub upstream with QEMU + Buildroot boilerplate: poll.ko, poll.out

In this simplified example, we generate poll events from a separate thread. In real life, poll events will likely be triggered by interrupts, when the hardware has finished some job, and new data became available for userland to read.

poll.ko:

#include <asm/uaccess.h> /* copy_from_user, copy_to_user */
#include <linux/debugfs.h>
#include <linux/delay.h> /* usleep_range */
#include <linux/errno.h> /* EFAULT */
#include <linux/fs.h>
#include <linux/jiffies.h>
#include <linux/kernel.h> /* min */
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/printk.h> /* printk */
#include <linux/wait.h> /* wait_queue_head_t, wait_event_interruptible, wake_up_interruptible  */
#include <uapi/linux/stat.h> /* S_IRUSR */

MODULE_LICENSE("GPL");

static char readbuf[1024];
static size_t readbuflen;
static struct dentry *dir;
static struct task_struct *kthread;
static wait_queue_head_t waitqueue;

static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
    ssize_t ret;
    if (copy_to_user(buf, readbuf, readbuflen)) {
        ret = -EFAULT;
    } else {
        ret = readbuflen;
    }
    /* This is normal pipe behaviour: data gets drained once a reader reads from it. */
    /* https://stackoverflow.com/questions/1634580/named-pipes-fifos-on-unix-with-multiple-readers */
    readbuflen = 0;
    return ret;
}

/*
If you return 0 here, then the kernel will sleep until an event happens in the queue.

This gets called again every time an event happens in the wait queue.
*/
unsigned int poll(struct file *filp, struct poll_table_struct *wait)
{
    poll_wait(filp, &waitqueue, wait);
    if (readbuflen)
        return POLLIN;
    else
        return 0;
}

static int kthread_func(void *data)
{
    while (!kthread_should_stop()) {
        readbuflen = snprintf(readbuf, sizeof(readbuf), "%llu", (unsigned long long)jiffies);
        usleep_range(1000000, 1000001);
        wake_up(&waitqueue);
    }
    return 0;
}

static const struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = read,
    .poll = poll
};

static int myinit(void)
{
    dir = debugfs_create_dir("lkmc_poll", 0);
    debugfs_create_file("f", S_IRUSR | S_IWUSR, dir, NULL, &fops);
    init_waitqueue_head(&waitqueue);
    kthread = kthread_create(kthread_func, NULL, "mykthread");
    wake_up_process(kthread);
    return 0;
}

static void myexit(void)
{
    kthread_stop(kthread);
    debugfs_remove_recursive(dir);
}

module_init(myinit)
module_exit(myexit)

poll.out userland:

#define _XOPEN_SOURCE 700
#include <fcntl.h> /* creat, O_CREAT */
#include <poll.h> /* poll */
#include <stdio.h> /* printf, puts, snprintf */
#include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */
#include <unistd.h> /* read */

int main(int argc, char **argv) {
    char buf[1024], path[1024];
    int fd, i, n;
    short revents;
    struct pollfd pfd;

    fd = open(argv[1], O_RDONLY | O_NONBLOCK);
    if (fd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }
    pfd.fd = fd;
    pfd.events = POLLIN;
    while (1) {
        puts("loop");
        i = poll(&pfd, 1, -1);
        if (i == -1) {
            perror("poll");
            exit(EXIT_FAILURE);
        }
        revents = pfd.revents;
        if (revents & POLLIN) {
            n = read(pfd.fd, buf, sizeof(buf));
            printf("POLLIN n=%d buf=%.*s\n", n, n, buf);
        }
    }
}