Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call a userspace function from within a Linux kernel module

I'm programming a simple Linux character device driver to output data to a piece of hardware via I/O ports. I have a function which performs floating point operations to calculate the correct output for the hardware; unfortunately this means I need to keep this function in userspace since the Linux kernel doesn't handle floating point operations very nicely.

Here's a pseudo representation of the setup (note that this code doesn't do anything specific, it just shows the relative layout of my code):

Userspace function:

char calculate_output(char x){
    double y = 2.5*x;
    double z = sqrt(y);
    
    char output = 0xA3;

    if(z > 35.67){
        output = 0xC0;
    }
    
    return output;
}

Kernelspace code:

unsigned i;
for(i = 0; i < 300; i++){
    if(inb(INPUT_PORT) & NEED_DATA){
        char seed = inb(SEED_PORT);
        char output = calculate_output(seed);
        outb(output, OUTPUT_PORT);
    }

    /* do some random stuff here */
}

I thought about using ioctl to pass in the data from the userspace function, but I'm not sure how to handle the fact that the function call is in a loop and more code executes before the next call to calculate_output occurs.

The way I envision this working is:

  1. main userspace program will start the kernelspace code (perhaps via ioctl)
  2. userspace program blocks and waits for kernelspace code
    • kernelspace program asks userspace program for output data, and blocks to wait
    • userspace program unblocks, calculates and sends data (ioctl?), then blocks again
    • kernelspace program unblocks and continues
  3. kernelspace program finishes and notifies userspace
  4. userspace unblocks and continues to next task

So how do I have the communication between kernelspace and userspace, and also have blocking so that I don't have the userspace continually polling a device file to see if it needs to send data?


A caveat: while fixed point arithmetic would work quite well in my example code, it is not an option in the real code; I require the large range that floating point provides and -- even if not -- I'm afraid rewriting the code to use fixed point arithmetic would obfuscate the algorithm for future maintainers.

like image 980
Vilhelm Gray Avatar asked Apr 17 '13 18:04

Vilhelm Gray


People also ask

How to call user space function from kernel?

You cannot call a kernel function from user space, you need to go through one of the existing mechanisms. You'll need to write at least some glue code in the kernel, to provide a way to trigger the execution of the kernel code, and pass parameters and results around.

What is the difference between userspace and kernel space?

Kernel space is strictly reserved for running a privileged operating system kernel, kernel extensions, and most device drivers. In contrast, user space is the memory area where application software and some drivers execute.

What is Usermode helper?

Usermode Helper API is for creating a user mode process from kernel space. Data structure that is used for the API is struct subprocess_info . /linux/include/kmod.


1 Answers

I think the simplest solution would be to create a character device in your kernel driver, with your own file operations for a virtual file. Then userspace can open this device O_RDWR. You have to implement two main file operations:

  • read -- this is how the kernel passes data back up to userspace. This function is run in the context of the userspace thread calling the read() system call, and in your case it should block until the kernel has another seed value that it needs to know the output for.

  • write-- this is how userspace passes data into the kernel. In your case, the kernel would just take the response to the previous read and pass it onto the hardware.

Then you end up with a simple loop in userspace:

while (1) {
    read(fd, buf, sizeof buf);
    calculate_output(buf, output);
    write(fd, output, sizeof output);
}

and no loop at all in the kernel -- everything runs in the context of the userspace process that is driving things, and the kernel driver is just responsible for moving the data to/from the hardware.

Depending on what your "do some random stuff here" on the kernel side is, it might not be possible to do it quite so simply. If you really need the kernel loop, then you need to create a kernel thread to run that loop, and then have some variables along the lines of input_data, input_ready, output_data and output_ready, along with a couple of waitqueues and whatever locking you need.

When the kernel thread reads data, you put the data in input_ready and set the input_ready flag and signal the input waitqueue, and then do wait_event(<output_ready is set>). The read file operation would do a wait_event(<input_ready is set>) and return the data to userspace when it becomes ready. Similarly the write file operation would put the data it gets from userspace into output_data and set output_ready and signal the output waitqueue.

Another (uglier, less portable) way is to use something like ioperm, iopl or /dev/port to do everything completely in userspace, including the low-level hardware access.

like image 167
Roland Avatar answered Nov 08 '22 21:11

Roland