Запись в eventfd из модуля ядра

Я создал экземпляр eventfd в пользовательской программе, используя eventfd(). Есть ли способ, которым я могу передать некоторую ссылку (указатель на ее структуру или пару pid + fd) на этот созданный экземпляр eventfd на модуль ядра, чтобы он мог обновить значение счетчика?

Вот что я хочу сделать: Я разрабатываю программу пользовательского пространства, которая должна обмениваться данными и сигналами с модулем пространства ядра, который я написал. Для передачи данных я уже использую ioctl. Но я хочу, чтобы модуль ядра мог сигнализировать программе пользовательского пространства всякий раз, когда новые данные готовы для использования в ioctl.

Для этого моя пользовательская программа создаст несколько eventfds в разных потоках. Эти потоки будут ждать эти eventfds, используя select(), и всякий раз, когда модуль ядра обновляет счетчики этих событий, они будут продолжать потреблять данные, запросив их через ioctl.

Проблема в том, как мне разрешить указатели "struct file *" на эти eventfds из kernelspace? Какую информацию вы можете получить в eventfds для модулей ядра, чтобы они могли получить указатели на eventfds? какие функции я бы использовал в модуле ядра для получения этих указателей?

Есть ли лучший способ передать события в пользовательское пространство из ядра? Я не могу отменить использование select().

Ответы

Ответ 1

Наконец-то я понял, как это сделать. Я понял, что каждый открытый файл в системе может быть идентифицирован pid одного из процессов, которые его открыли, и fd, соответствующего этому файлу (в этом контексте процесса). Поэтому, если мой модуль ядра знает pid и fd, он может искать struct * task_struct процесса и из него struct * files и, наконец, использовать fd, it может получить указатель на файл eventfd struct *. Затем, используя этот последний указатель, он может записать счетчик eventfd.

Вот коды для программы userpace и модуля ядра, которые я написал, чтобы продемонстрировать концепцию (которая теперь работает):

Код пользователя C (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);
}

Код модуля C ядра (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 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 task struct
struct file * efd_file = NULL;          //...to eventfd 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 task struct: %p\n",userspace_task);

    printk(KERN_ALERT "~~~Resolved pointer to the userspace program 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 eventfd 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 eventfd context: %p\n",efd_ctx);

    eventfd_signal(efd_ctx, plus_one);

    printk(KERN_ALERT "~~~Incremented userspace program eventfd 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);

Чтобы выполнить это, выполните следующие шаги:

  • Скомпилируйте программу пользовательского пространства (efd_us.out) и модуль ядра (efd_lkm.ko)
  • Запустите программу пользовательского пространства (./efd_us.out) и обратите внимание на значения pid и efd, которые он печатает. (например, "pid = 2803 efd = 3". Программа пользовательского пространства будет бесконечно ждать при выборе()
  • Откройте новое окно терминала и вставьте модуль ядра, передающий pid и efd в качестве параметров: sudo insmod efd_lkm.ko pid = 2803 efd = 3
  • Вернитесь в окно программы пользовательской программы, и вы увидите, что программа пользовательского пространства вышла из выбора и вышла.

Ответ 2

Обратитесь к источнику ядра здесь:

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

В принципе, отправьте свой файловый дескриптор пользовательского пространства, созданный eventfd(), в ваш модуль через ioctl() или какой-либо другой путь. Из ядра вызовите eventfd_ctx_fdget(), чтобы получить контекст eventfd, а затем eventfd_signal() в результирующем контексте. Не забывайте eventfd_ctx_put(), когда вы закончите с контекстом.

Ответ 3

как мне разрешить указатели "struct file *" на эти eventfds из kernelelspace

Вы должны разрешить эти указатели в структурах данных, которые опубликовал этот интерфейс (создать новые типы и прочитать нужные поля из struct file).

Есть ли лучший способ сигнализировать события в пользовательском пространстве из kernelelspace?

Сокеты Netlink - еще один удобный способ для ядра взаимодействовать с пользовательским пространством. "Лучше" в глазах смотрящего.