Inter-Module Communication in Linux Kernel

Implementing correct inter-module synchronization in Linux kernel

Traditionally IRQ handling in Linux is done in two parts:

  1. So called "upper-half" is actual working in IRQ context (IRQ handler itself). This part must exit as fast as possible. So it basically checks interrupt source and then starts bottom-half.

  2. "Bottom-half". It may be implemented as work queue. It is where actual job is done. It runs in normal context, so it can use blocking functions, etc.

If you only want to wait for IRQ in your worker thread, better to use special object called completion. It is exactly created for this task.

linux inter driver communication

It seems you want to receive input events from an unrelated module. But you cannot read /dev/input/event* from kernel space, so you have to options:

  1. Write a user mode daemon that reads the /dev/input/event* and forwards to your driver, maybe with a char device or a sysfs parameter.
  2. Hook on the input driver events from your driver.

Option 1 should be straightforward. I'll elaborate on option 2.

To hook an input device from kernel you use function input_register_handler() from your module init function (and input_unregister_handler() from your exit function, of course).

This function takes a struct input_handler as argument, it has a lot of members, but you probably only need to fill name, id_table, connect, disconnect and event.

Then, in your connect callback you call input_register_handle() (note the handle vs handler names) and input_open_device() and you will get input events in your event callback.

Of course, do not forget to undo that work in your disconnect callback.

There are several instances of this API usage in the kernel, but by far the easier one to read is the evbug driver: it just dumps all input events into the kernel log.

Communicating between multiple distinct kernel modules (drivers)

It looks like the Linux side has already been answered in comments.

For macOS kexts, the mechanism to use are the OSBundleLibraries and OSBundleCompatibleVersion Info.plist properties. The kext exporting symbols must have the OSBundleCompatibleVersion property set. This must be less than or equal to its CFBundleVersion and allows you to version your API.

The kext which wishes to import the other kext's symbols must list the exporting kext's bundle identifier in the OSBundleLibraries dictionary, with the appropriate version number.

Note that linking against another kext will import all of its public symbols, so I strongly recommend making all symbols default-hidden and providing an explicit exports file. To do this, enable "Symbols hidden by default" in the Xcode target settings, create a new .exp (or .exports) file, and declare it in the "Exported Symbols File" setting. At a minimum, you will need to add _kmod_info to this file. Then add all the symbols you wish to export, one on each line.

C functions and global variables will need to be prefixed with an underscore, C++ functions and static class member variables will need to be mangled in the usual way. You can use * as a (partial) wildcard, which is sometimes handy for C++ classes with a lot of member functions, for example. The xnu source distribution contains plenty of examples of exports files if you need a reference. You can use the nm tool to generate a list of all the symbols in your kext, from which you can then pick & choose; this saves you from manually mangling names.

Kexts can't circularly depend on one another. One will need to be the "library," the other the "user" of that library. If they need to interact, you will need to use callbacks, virtual functions, etc.

The "library" kext must be installed in /Library/Extensions (/System/Library/Extensions on OS X 10.8 or earlier) or the user of the library will not find it, potentially even if the library kext is already loaded. If your "user" kext specifies in its OSBundleRequired property that it may be needed for local or network booting, the libraries it depends on should declare the same or a superset of those conditions, or they may not be appropriately prelinked/kextcached.

Apple does have a small amount of documentation on designing kext libraries too.

How to make communication between user space program and Linux kernel module in order to print a message sent from user program to the kernel

First problem: you never modify message.

Then, you cannot use directly the user memory in the kernel context

You have to translate it before: copy_from_user is for that.

Your write function could looks like

#define MAX 256
/* here, message is defined as 256 bytes +1 one.
The extra char is here to be compatible with the `%s` formatter */
static char message[MAX+1] ="";

ssize_t exer_write(struct file *pfile, const char __user *buffer, size_t length, loff_t *offset) {
if (length > MAX)
return -EINVAL;

if (copy_from_user(message, buffer, length) != 0)
return -EFAULT;

printk(KERN_INFO "Received %s characters from the user\n", message);
return 0;

}

Structure of two interdependent Linux kernel modules?

I don't see any problem with your proposed solution of having one router.ko module that talks to the actual hardware, and submodules dev_A.ko and dev_B.ko that talk to router.ko. You just need to have router.ko export a "my_register_driver" function, analogous to the existing pci_register_driver function. You would pass in a structure that has an "id" member and a "add_device" function pointer; dev_A.ko would pass in id="A" and add_device=dev_A_add, and similarly for dev_B.ko.

Then when router.ko comes up, it discovers the hardware and creates virtual devices (your own context structure) for A and B. Then when a submodule comes along, router.ko just calls the appropriate add_device methods with the appropriate virtual device. router.ko should also export methods that the dev_A and dev_B modules can use to access the underlying hardware.

For an example of what I have in mind, you can look at the mlx4_core, mlx4_ib and mlx4_en modules in the upstream kernel (I wrote them so I choose this example :). The idea with them is that there is one PCI device that can be used as both an InfiniBand and Ethernet device; so mlx4_ib and mlx4_en both use mlx4_core to discover and access the underlying PCI device. The small bit of code that the sub-drivers use to ask for which devices exist is in drivers/net/mlx4/intf.c.

Also, as far as the existing "Device A" userspace interface, that shouldn't be a problem. You can just implement the same ABI in your dev_A.ko, unless there is some complication you didn't mention.

What options do we have for communication between a user program and a Linux Kernel Module?

Your option 3) is really a sub-option of option 1) - ioctl() is one way of interacting with a device file (read() and write() being the usual ways).

Two other ways worth considering are:

  • The sysfs filesystem;
  • Netlink sockets.


Related Topics



Leave a reply



Submit