Reputation: 325
I have shared memory segment created in kernel using mmap. I need to access this mapped memory from both kernel and user space. What mechanism should I use to protect the memory from concurrent access ? I want to have something like:
Kernel module:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/mm.h>
#ifndef VM_RESERVED
# define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP)
#endif
struct dentry *file;
struct mmap_info
{
char *data;
int reference;
};
void mmap_open(struct vm_area_struct *vma)
{
struct mmap_info *info = (struct mmap_info *)vma->vm_private_data;
info->reference++;
}
void mmap_close(struct vm_area_struct *vma)
{
struct mmap_info *info = (struct mmap_info *)vma->vm_private_data;
info->reference--;
}
static int mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct page *page;
struct mmap_info *info;
info = (struct mmap_info *)vma->vm_private_data;
if (!info->data)
{
printk("No data\n");
return 0;
}
page = virt_to_page(info->data);
get_page(page);
vmf->page = page;
return 0;
}
struct vm_operations_struct mmap_vm_ops =
{
.open = mmap_open,
.close = mmap_close,
.fault = mmap_fault,
};
int op_mmap(struct file *filp, struct vm_area_struct *vma)
{
vma->vm_ops = &mmap_vm_ops;
vma->vm_flags |= VM_RESERVED;
vma->vm_private_data = filp->private_data;
mmap_open(vma);
return 0;
}
int mmapfop_close(struct inode *inode, struct file *filp)
{
struct mmap_info *info = filp->private_data;
free_page((unsigned long)info->data);
kfree(info);
filp->private_data = NULL;
return 0;
}
int mmapfop_open(struct inode *inode, struct file *filp)
{
struct mmap_info *info = kmalloc(sizeof(struct mmap_info), GFP_KERNEL);
info->data = (char *)get_zeroed_page(GFP_KERNEL);
memcpy(info->data, "hello from kernel this is file: ", 32);
memcpy(info->data + 32, filp->f_dentry->d_name.name, strlen(filp->f_dentry->d_name.name));
/* assign this info struct to the file */
filp->private_data = info;
return 0;
}
static const struct file_operations mmap_fops = {
.open = mmapfop_open,
.release = mmapfop_close,
.mmap = op_mmap,
};
static int __init mmapexample_module_init(void)
{
file = debugfs_create_file("mmap_example", 0644, NULL, NULL, &mmap_fops);
return 0;
}
static void __exit mmapexample_module_exit(void)
{
debugfs_remove(file);
}
module_init(mmapexample_module_init);
module_exit(mmapexample_module_exit);
MODULE_LICENSE("GPL");
User space:
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#define PAGE_SIZE 4096
int main ( int argc, char **argv )
{
int configfd;
char * address = NULL;
configfd = open("/sys/kernel/debug/mmap_example", O_RDWR);
if(configfd < 0)
{
perror("Open call failed");
return -1;
}
address = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, configfd, 0);
if (address == MAP_FAILED)
{
perror("mmap operation failed");
return -1;
}
printf("Initial message: %s\n", address);
memcpy(address + 11 , "*user*", 6);
printf("Changed message: %s\n", address);
close(configfd);
return 0;
}
but with locks.
Upvotes: 3
Views: 2076
Reputation: 2408
The problem with the ioctl is you need a kernel switch every time you want to access the share info->data. If that is okay then the ioctl is good - but then why not just do a standard character read/write file operation instead?
You can also try a lock-less mechanism. In the shared info->data area add a barrier variable. When the user needs access, it will do an atomic_compare_and_xchg on the barrier variable until it is set to 0 (unused) and then set it to 1. When the kernel needs access it will do the same but set it to 2. See the gcc atomic builtin documentation.
Upvotes: 1
Reputation: 65880
Kernel space and user space have no shared mechanisms for concurrent access protection. If you want them, you need to implement them by yourself.
It can be some sort of mutex, implemented within you kernel module, and accessed from user space via special ioctl requests:
Kernel:
DECLARE_WAIT_QUEUE_HEAD(wq);
int my_mutex_val = 0;
/*
* Lock mutex.
*
* May be used directly by the kernel or via 'ioctl(MY_CMD_LOCK)' by user.
*/
void my_mutex_lock(void)
{
spin_lock(&wq.lock);
wait_event_interruptible_locked(&wq, my_mutex_val == 0);
my_mutex_val = 1;
spin_unlock(&wq.lock);
}
/*
* Unlock mutex.
*
* May be used directly by the kernel or via 'ioctl(MY_CMD_UNLOCK)' by user.
*/
void my_mutex_unlock(void)
{
spin_lock(&wq.lock);
my_mutex_val = 0;
wake_up(&wq);
spin_unlock(&wq.lock);
}
long unlocked_ioctl (struct file * filp, unsigned int cmd, unsigned long val)
{
switch(cmd) {
case MY_CMD_LOCK:
my_mutex_lock();
break;
case MY_CMD_UNLOCK:
my_mutex_unlock();
break;
}
}
User:
int main()
{
...
ioctl(MY_CMD_LOCK);
<read data>
ioctl(MY_CMD_UNLOCK);
...
}
It can be some sort of spinlock, which value is stored in mmap-ed area (so visible both for kernel space and user space).
In any case, kernel module should be prepared for the case, when user space application doesn't follow locking conventions. This, probably, would cancel any expectation about mmap-ed area content, generated by the kernel, but kernel module shouldn't crash in that case. [This is why standard kernel's struct mutex
is not used in the code above: user space may use it incorrectly].
Upvotes: 1