Skip to content

Instantly share code, notes, and snippets.

@laoar
Last active June 3, 2024 09:07
Show Gist options
  • Save laoar/4a7110dcd65dbf2aefb3231146458b39 to your computer and use it in GitHub Desktop.
Save laoar/4a7110dcd65dbf2aefb3231146458b39 to your computer and use it in GitHub Desktop.
an example of kernel space to user space zero-copy via mmap, and also the comparing mmap with read/write
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#define MAX_SIZE (PAGE_SIZE * 2) /* max size mmaped to userspace */
#define DEVICE_NAME "mchar"
#define CLASS_NAME "mogu"
static struct class* class;
static struct device* device;
static int major;
static char *sh_mem = NULL;
static DEFINE_MUTEX(mchar_mutex);
/* executed once the device is closed or releaseed by userspace
* @param inodep: pointer to struct inode
* @param filep: pointer to struct file
*/
static int mchar_release(struct inode *inodep, struct file *filep)
{
mutex_unlock(&mchar_mutex);
pr_info("mchar: Device successfully closed\n");
return 0;
}
/* executed once the device is opened.
*
*/
static int mchar_open(struct inode *inodep, struct file *filep)
{
int ret = 0;
if(!mutex_trylock(&mchar_mutex)) {
pr_alert("mchar: device busy!\n");
ret = -EBUSY;
goto out;
}
pr_info("mchar: Device opened\n");
out:
return ret;
}
/* mmap handler to map kernel space to user space
*
*/
static int mchar_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret = 0;
struct page *page = NULL;
unsigned long size = (unsigned long)(vma->vm_end - vma->vm_start);
if (size > MAX_SIZE) {
ret = -EINVAL;
goto out;
}
page = virt_to_page((unsigned long)sh_mem + (vma->vm_pgoff << PAGE_SHIFT));
ret = remap_pfn_range(vma, vma->vm_start, page_to_pfn(page), size, vma->vm_page_prot);
if (ret != 0) {
goto out;
}
out:
return ret;
}
static ssize_t mchar_read(struct file *filep, char *buffer, size_t len, loff_t *offset)
{
int ret;
if (len > MAX_SIZE) {
pr_info("read overflow!\n");
ret = -EFAULT;
goto out;
}
if (copy_to_user(buffer, sh_mem, len) == 0) {
pr_info("mchar: copy %u char to the user\n", len);
ret = len;
} else {
ret = -EFAULT;
}
out:
return ret;
}
static ssize_t mchar_write(struct file *filep, const char *buffer, size_t len, loff_t *offset)
{
int ret;
if (copy_from_user(sh_mem, buffer, len)) {
pr_err("mchar: write fault!\n");
ret = -EFAULT;
goto out;
}
pr_info("mchar: copy %d char from the user\n", len);
ret = len;
out:
return ret;
}
static const struct file_operations mchar_fops = {
.open = mchar_open,
.read = mchar_read,
.write = mchar_write,
.release = mchar_release,
.mmap = mchar_mmap,
/*.unlocked_ioctl = mchar_ioctl,*/
.owner = THIS_MODULE,
};
static int __init mchar_init(void)
{
int ret = 0;
major = register_chrdev(0, DEVICE_NAME, &mchar_fops);
if (major < 0) {
pr_info("mchar: fail to register major number!");
ret = major;
goto out;
}
class = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(class)){
unregister_chrdev(major, DEVICE_NAME);
pr_info("mchar: failed to register device class");
ret = PTR_ERR(class);
goto out;
}
device = device_create(class, NULL, MKDEV(major, 0), NULL, DEVICE_NAME);
if (IS_ERR(device)) {
class_destroy(class);
unregister_chrdev(major, DEVICE_NAME);
ret = PTR_ERR(device);
goto out;
}
/* init this mmap area */
sh_mem = kmalloc(MAX_SIZE, GFP_KERNEL);
if (sh_mem == NULL) {
ret = -ENOMEM;
goto out;
}
sprintf(sh_mem, "xyz\n");
mutex_init(&mchar_mutex);
out:
return ret;
}
static void __exit mchar_exit(void)
{
mutex_destroy(&mchar_mutex);
device_destroy(class, MKDEV(major, 0));
class_unregister(class);
class_destroy(class);
unregister_chrdev(major, DEVICE_NAME);
pr_info("mchar: unregistered!");
}
module_init(mchar_init);
module_exit(mchar_exit);
MODULE_LICENSE("GPL");
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdio.h>
#include <string.h>
#include<errno.h>
#define DEVICE_FILENAME "/dev/mchar"
int main()
{
int fd;
int ret;
char *p = NULL;
char buff[64];
fd = open(DEVICE_FILENAME, O_RDWR|O_NDELAY);
if(fd >= 0) {
p = (char*)mmap(0,
4096,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
0);
printf("%s", p);
munmap(p, 4096);
close(fd);
}
fd = open(DEVICE_FILENAME, O_RDWR|O_NDELAY);
ret = write(fd, "abcd", strlen("abcd"));
if (ret < 0) {
printf("write error!\n");
ret = errno;
goto out;
}
ret = read(fd, buff, 64);
if (ret < 0) {
printf("read error!\n");
ret = errno;
goto out;
}
printf("read: %s\n", buff);
out:
return ret;
}
@evanlu14
Copy link

How to compile this code? Appreciate if adding Makefile.

@vinaykumar-eximius
Copy link

Please find the Makefile for module and test file compilation.
all: kernel_modules

obj-m := mmap_zcopy.o

KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

%.ko: %.o
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) $@

kernel_modules:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
gcc test.c -o test

clean:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean
rm -f test

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment