Imported Upstream version 16.07.2
[deb_dpdk.git] / lib / librte_vhost / eventfd_link / eventfd_link.c
1 /*-
2  * GPL LICENSE SUMMARY
3  *
4  *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of version 2 of the GNU General Public License as
8  *   published by the Free Software Foundation.
9  *
10  *   This program is distributed in the hope that it will be useful, but
11  *   WITHOUT ANY WARRANTY; without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *   General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License
16  *   along with this program; if not, write to the Free Software
17  *   Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
18  *   The full GNU General Public License is included in this distribution
19  *   in the file called LICENSE.GPL.
20  *
21  *   Contact Information:
22  *   Intel Corporation
23  */
24
25 #include <linux/miscdevice.h>
26 #include <linux/module.h>
27 #include <linux/file.h>
28 #include <linux/fdtable.h>
29 #include <linux/syscalls.h>
30
31 #include "eventfd_link.h"
32
33
34 /*
35  * get_files_struct is copied from fs/file.c
36  */
37 struct files_struct *
38 get_files_struct(struct task_struct *task)
39 {
40         struct files_struct *files;
41
42         task_lock(task);
43         files = task->files;
44         if (files)
45                 atomic_inc(&files->count);
46         task_unlock(task);
47
48         return files;
49 }
50
51 /*
52  * put_files_struct is extracted from fs/file.c
53  */
54 void
55 put_files_struct(struct files_struct *files)
56 {
57         if (atomic_dec_and_test(&files->count))
58                 BUG();
59 }
60
61 static struct file *
62 fget_from_files(struct files_struct *files, unsigned fd)
63 {
64         struct file *file;
65
66         rcu_read_lock();
67         file = fcheck_files(files, fd);
68         if (file) {
69                 if (file->f_mode & FMODE_PATH ||
70                         !atomic_long_inc_not_zero(&file->f_count)) {
71
72                         file = NULL;
73                 }
74         }
75         rcu_read_unlock();
76
77         return file;
78 }
79
80 static long
81 eventfd_link_ioctl_copy2(unsigned long arg)
82 {
83         void __user *argp = (void __user *) arg;
84         struct task_struct *task_target = NULL;
85         struct file *file;
86         struct files_struct *files;
87         struct eventfd_copy2 eventfd_copy2;
88         long ret = -EFAULT;
89
90         if (copy_from_user(&eventfd_copy2, argp, sizeof(struct eventfd_copy2)))
91                 goto out;
92
93         /*
94          * Find the task struct for the target pid
95          */
96         ret = -ESRCH;
97
98         task_target =
99                 get_pid_task(find_vpid(eventfd_copy2.pid), PIDTYPE_PID);
100         if (task_target == NULL) {
101                 pr_info("Unable to find pid %d\n", eventfd_copy2.pid);
102                 goto out;
103         }
104
105         ret = -ESTALE;
106         files = get_files_struct(task_target);
107         if (files == NULL) {
108                 pr_info("Failed to get target files struct\n");
109                 goto out_task;
110         }
111
112         ret = -EBADF;
113         file = fget_from_files(files, eventfd_copy2.fd);
114         put_files_struct(files);
115
116         if (file == NULL) {
117                 pr_info("Failed to get fd %d from target\n", eventfd_copy2.fd);
118                 goto out_task;
119         }
120
121         /*
122          * Install the file struct from the target process into the
123          * newly allocated file desciptor of the source process.
124          */
125         ret = get_unused_fd_flags(eventfd_copy2.flags);
126         if (ret < 0) {
127                 fput(file);
128                 goto out_task;
129         }
130         fd_install(ret, file);
131
132 out_task:
133         put_task_struct(task_target);
134 out:
135         return ret;
136 }
137
138 static long
139 eventfd_link_ioctl_copy(unsigned long arg)
140 {
141         void __user *argp = (void __user *) arg;
142         struct task_struct *task_target = NULL;
143         struct file *file;
144         struct files_struct *files;
145         struct fdtable *fdt;
146         struct eventfd_copy eventfd_copy;
147         long ret = -EFAULT;
148
149         if (copy_from_user(&eventfd_copy, argp, sizeof(struct eventfd_copy)))
150                 goto out;
151
152         /*
153          * Find the task struct for the target pid
154          */
155         ret = -ESRCH;
156
157         task_target =
158                 get_pid_task(find_vpid(eventfd_copy.target_pid), PIDTYPE_PID);
159         if (task_target == NULL) {
160                 pr_info("Unable to find pid %d\n", eventfd_copy.target_pid);
161                 goto out;
162         }
163
164         ret = -ESTALE;
165         files = get_files_struct(current);
166         if (files == NULL) {
167                 pr_info("Failed to get current files struct\n");
168                 goto out_task;
169         }
170
171         ret = -EBADF;
172         file = fget_from_files(files, eventfd_copy.source_fd);
173
174         if (file == NULL) {
175                 pr_info("Failed to get fd %d from source\n",
176                         eventfd_copy.source_fd);
177                 put_files_struct(files);
178                 goto out_task;
179         }
180
181         /*
182          * Release the existing eventfd in the source process
183          */
184         spin_lock(&files->file_lock);
185         fput(file);
186         filp_close(file, files);
187         fdt = files_fdtable(files);
188         fdt->fd[eventfd_copy.source_fd] = NULL;
189         spin_unlock(&files->file_lock);
190
191         put_files_struct(files);
192
193         /*
194          * Find the file struct associated with the target fd.
195          */
196
197         ret = -ESTALE;
198         files = get_files_struct(task_target);
199         if (files == NULL) {
200                 pr_info("Failed to get target files struct\n");
201                 goto out_task;
202         }
203
204         ret = -EBADF;
205         file = fget_from_files(files, eventfd_copy.target_fd);
206         put_files_struct(files);
207
208         if (file == NULL) {
209                 pr_info("Failed to get fd %d from target\n",
210                         eventfd_copy.target_fd);
211                 goto out_task;
212         }
213
214         /*
215          * Install the file struct from the target process into the
216          * file desciptor of the source process,
217          */
218
219         fd_install(eventfd_copy.source_fd, file);
220         ret = 0;
221
222 out_task:
223         put_task_struct(task_target);
224 out:
225         return ret;
226 }
227
228 static long
229 eventfd_link_ioctl(struct file *f, unsigned int ioctl, unsigned long arg)
230 {
231         long ret = -ENOIOCTLCMD;
232
233         switch (ioctl) {
234         case EVENTFD_COPY:
235                 ret = eventfd_link_ioctl_copy(arg);
236                 break;
237         case EVENTFD_COPY2:
238                 ret = eventfd_link_ioctl_copy2(arg);
239                 break;
240         }
241
242         return ret;
243 }
244
245 static const struct file_operations eventfd_link_fops = {
246         .owner = THIS_MODULE,
247         .unlocked_ioctl = eventfd_link_ioctl,
248 };
249
250
251 static struct miscdevice eventfd_link_misc = {
252         .minor = MISC_DYNAMIC_MINOR,
253         .name = "eventfd-link",
254         .fops = &eventfd_link_fops,
255 };
256
257 static int __init
258 eventfd_link_init(void)
259 {
260         return misc_register(&eventfd_link_misc);
261 }
262
263 module_init(eventfd_link_init);
264
265 static void __exit
266 eventfd_link_exit(void)
267 {
268         misc_deregister(&eventfd_link_misc);
269 }
270
271 module_exit(eventfd_link_exit);
272
273 MODULE_VERSION("0.0.1");
274 MODULE_LICENSE("GPL v2");
275 MODULE_AUTHOR("Anthony Fee");
276 MODULE_DESCRIPTION("Link eventfd");
277 MODULE_ALIAS("devname:eventfd-link");