7836894a64
Problem:if I firstly insert my kmod_test.ko, then insert eventfd_link.ko, error will happen with hint "Device or resource busy". This is because the default minor device number, 0, has been occupied by my kmod_test.ko . root@distro:~/test$ lsmod Module Size Used by kmod_test 927 0 vboxsf 35930 4 vboxguest 222130 1 vboxsf microcode 10315 0 autofs4 25051 0 root@distro:~/test$ insmod ./eventfd_link.ko insmod: ERROR: could not insert module ./eventfd_link.ko: Device or resource busy Explanation: For miscdevices, the major device_no is same, so the minor device_no should be set to ditinguish different misc devices; if not set the minor, it may fail while insmod due to the default minor value, 0, has been used by other miscdevice. MISC_DYNAMIC_MINOR means to let Linux kernel dynamically assign one minor devide number while loading. Signed-off-by: Xiaobo Chi <xiaobo.chi@nokia.com> Acked-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
278 lines
5.8 KiB
C
278 lines
5.8 KiB
C
/*-
|
|
* GPL LICENSE SUMMARY
|
|
*
|
|
* Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of version 2 of the GNU General Public License as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
|
* The full GNU General Public License is included in this distribution
|
|
* in the file called LICENSE.GPL.
|
|
*
|
|
* Contact Information:
|
|
* Intel Corporation
|
|
*/
|
|
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/module.h>
|
|
#include <linux/file.h>
|
|
#include <linux/fdtable.h>
|
|
#include <linux/syscalls.h>
|
|
|
|
#include "eventfd_link.h"
|
|
|
|
|
|
/*
|
|
* get_files_struct is copied from fs/file.c
|
|
*/
|
|
struct files_struct *
|
|
get_files_struct(struct task_struct *task)
|
|
{
|
|
struct files_struct *files;
|
|
|
|
task_lock(task);
|
|
files = task->files;
|
|
if (files)
|
|
atomic_inc(&files->count);
|
|
task_unlock(task);
|
|
|
|
return files;
|
|
}
|
|
|
|
/*
|
|
* put_files_struct is extracted from fs/file.c
|
|
*/
|
|
void
|
|
put_files_struct(struct files_struct *files)
|
|
{
|
|
if (atomic_dec_and_test(&files->count))
|
|
BUG();
|
|
}
|
|
|
|
static struct file *
|
|
fget_from_files(struct files_struct *files, unsigned fd)
|
|
{
|
|
struct file *file;
|
|
|
|
rcu_read_lock();
|
|
file = fcheck_files(files, fd);
|
|
if (file) {
|
|
if (file->f_mode & FMODE_PATH ||
|
|
!atomic_long_inc_not_zero(&file->f_count)) {
|
|
|
|
file = NULL;
|
|
}
|
|
}
|
|
rcu_read_unlock();
|
|
|
|
return file;
|
|
}
|
|
|
|
static long
|
|
eventfd_link_ioctl_copy2(unsigned long arg)
|
|
{
|
|
void __user *argp = (void __user *) arg;
|
|
struct task_struct *task_target = NULL;
|
|
struct file *file;
|
|
struct files_struct *files;
|
|
struct eventfd_copy2 eventfd_copy2;
|
|
long ret = -EFAULT;
|
|
|
|
if (copy_from_user(&eventfd_copy2, argp, sizeof(struct eventfd_copy2)))
|
|
goto out;
|
|
|
|
/*
|
|
* Find the task struct for the target pid
|
|
*/
|
|
ret = -ESRCH;
|
|
|
|
task_target =
|
|
get_pid_task(find_vpid(eventfd_copy2.pid), PIDTYPE_PID);
|
|
if (task_target == NULL) {
|
|
pr_info("Unable to find pid %d\n", eventfd_copy2.pid);
|
|
goto out;
|
|
}
|
|
|
|
ret = -ESTALE;
|
|
files = get_files_struct(task_target);
|
|
if (files == NULL) {
|
|
pr_info("Failed to get target files struct\n");
|
|
goto out_task;
|
|
}
|
|
|
|
ret = -EBADF;
|
|
file = fget_from_files(files, eventfd_copy2.fd);
|
|
put_files_struct(files);
|
|
|
|
if (file == NULL) {
|
|
pr_info("Failed to get fd %d from target\n", eventfd_copy2.fd);
|
|
goto out_task;
|
|
}
|
|
|
|
/*
|
|
* Install the file struct from the target process into the
|
|
* newly allocated file desciptor of the source process.
|
|
*/
|
|
ret = get_unused_fd_flags(eventfd_copy2.flags);
|
|
if (ret < 0) {
|
|
fput(file);
|
|
goto out_task;
|
|
}
|
|
fd_install(ret, file);
|
|
|
|
out_task:
|
|
put_task_struct(task_target);
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static long
|
|
eventfd_link_ioctl_copy(unsigned long arg)
|
|
{
|
|
void __user *argp = (void __user *) arg;
|
|
struct task_struct *task_target = NULL;
|
|
struct file *file;
|
|
struct files_struct *files;
|
|
struct fdtable *fdt;
|
|
struct eventfd_copy eventfd_copy;
|
|
long ret = -EFAULT;
|
|
|
|
if (copy_from_user(&eventfd_copy, argp, sizeof(struct eventfd_copy)))
|
|
goto out;
|
|
|
|
/*
|
|
* Find the task struct for the target pid
|
|
*/
|
|
ret = -ESRCH;
|
|
|
|
task_target =
|
|
get_pid_task(find_vpid(eventfd_copy.target_pid), PIDTYPE_PID);
|
|
if (task_target == NULL) {
|
|
pr_info("Unable to find pid %d\n", eventfd_copy.target_pid);
|
|
goto out;
|
|
}
|
|
|
|
ret = -ESTALE;
|
|
files = get_files_struct(current);
|
|
if (files == NULL) {
|
|
pr_info("Failed to get current files struct\n");
|
|
goto out_task;
|
|
}
|
|
|
|
ret = -EBADF;
|
|
file = fget_from_files(files, eventfd_copy.source_fd);
|
|
|
|
if (file == NULL) {
|
|
pr_info("Failed to get fd %d from source\n",
|
|
eventfd_copy.source_fd);
|
|
put_files_struct(files);
|
|
goto out_task;
|
|
}
|
|
|
|
/*
|
|
* Release the existing eventfd in the source process
|
|
*/
|
|
spin_lock(&files->file_lock);
|
|
fput(file);
|
|
filp_close(file, files);
|
|
fdt = files_fdtable(files);
|
|
fdt->fd[eventfd_copy.source_fd] = NULL;
|
|
spin_unlock(&files->file_lock);
|
|
|
|
put_files_struct(files);
|
|
|
|
/*
|
|
* Find the file struct associated with the target fd.
|
|
*/
|
|
|
|
ret = -ESTALE;
|
|
files = get_files_struct(task_target);
|
|
if (files == NULL) {
|
|
pr_info("Failed to get target files struct\n");
|
|
goto out_task;
|
|
}
|
|
|
|
ret = -EBADF;
|
|
file = fget_from_files(files, eventfd_copy.target_fd);
|
|
put_files_struct(files);
|
|
|
|
if (file == NULL) {
|
|
pr_info("Failed to get fd %d from target\n",
|
|
eventfd_copy.target_fd);
|
|
goto out_task;
|
|
}
|
|
|
|
/*
|
|
* Install the file struct from the target process into the
|
|
* file desciptor of the source process,
|
|
*/
|
|
|
|
fd_install(eventfd_copy.source_fd, file);
|
|
ret = 0;
|
|
|
|
out_task:
|
|
put_task_struct(task_target);
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static long
|
|
eventfd_link_ioctl(struct file *f, unsigned int ioctl, unsigned long arg)
|
|
{
|
|
long ret = -ENOIOCTLCMD;
|
|
|
|
switch (ioctl) {
|
|
case EVENTFD_COPY:
|
|
ret = eventfd_link_ioctl_copy(arg);
|
|
break;
|
|
case EVENTFD_COPY2:
|
|
ret = eventfd_link_ioctl_copy2(arg);
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct file_operations eventfd_link_fops = {
|
|
.owner = THIS_MODULE,
|
|
.unlocked_ioctl = eventfd_link_ioctl,
|
|
};
|
|
|
|
|
|
static struct miscdevice eventfd_link_misc = {
|
|
.minor = MISC_DYNAMIC_MINOR,
|
|
.name = "eventfd-link",
|
|
.fops = &eventfd_link_fops,
|
|
};
|
|
|
|
static int __init
|
|
eventfd_link_init(void)
|
|
{
|
|
return misc_register(&eventfd_link_misc);
|
|
}
|
|
|
|
module_init(eventfd_link_init);
|
|
|
|
static void __exit
|
|
eventfd_link_exit(void)
|
|
{
|
|
misc_deregister(&eventfd_link_misc);
|
|
}
|
|
|
|
module_exit(eventfd_link_exit);
|
|
|
|
MODULE_VERSION("0.0.1");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_AUTHOR("Anthony Fee");
|
|
MODULE_DESCRIPTION("Link eventfd");
|
|
MODULE_ALIAS("devname:eventfd-link");
|