1 |
Author: mpagano |
2 |
Date: 2012-02-01 15:31:40 +0000 (Wed, 01 Feb 2012) |
3 |
New Revision: 2075 |
4 |
|
5 |
Added: |
6 |
genpatches-2.6/trunk/3.2/2400_kcopy-patch-for-infiniband-driver.patch |
7 |
Modified: |
8 |
genpatches-2.6/trunk/3.2/0000_README |
9 |
Log: |
10 |
Zero copy for infiniband psm userspace driver |
11 |
|
12 |
Modified: genpatches-2.6/trunk/3.2/0000_README |
13 |
=================================================================== |
14 |
--- genpatches-2.6/trunk/3.2/0000_README 2012-01-30 21:07:06 UTC (rev 2074) |
15 |
+++ genpatches-2.6/trunk/3.2/0000_README 2012-02-01 15:31:40 UTC (rev 2075) |
16 |
@@ -49,7 +49,7 @@ |
17 |
|
18 |
Patch: 2400_kcopy-patch-for-infiniband-driver.patch |
19 |
From: Alexey Shvetsov <alexxy@g.o> |
20 |
-Desc: ALPS Touchpad Patchset |
21 |
+Desc: Zero copy for infiniband psm userspace driver |
22 |
|
23 |
Patch: 2600_Input-ALPS-synaptics-touchpad.patch |
24 |
From: http://bugs.gentoo.org/show_bug.cgi?id=318567 |
25 |
|
26 |
Added: genpatches-2.6/trunk/3.2/2400_kcopy-patch-for-infiniband-driver.patch |
27 |
=================================================================== |
28 |
--- genpatches-2.6/trunk/3.2/2400_kcopy-patch-for-infiniband-driver.patch (rev 0) |
29 |
+++ genpatches-2.6/trunk/3.2/2400_kcopy-patch-for-infiniband-driver.patch 2012-02-01 15:31:40 UTC (rev 2075) |
30 |
@@ -0,0 +1,725 @@ |
31 |
+From 3b2e8091390000a15bdceccb57a6e3956a751134 Mon Sep 17 00:00:00 2001 |
32 |
+From: Alexey Shvetsov <alexxy@g.o> |
33 |
+Date: Tue, 17 Jan 2012 21:08:49 +0400 |
34 |
+Subject: [PATCH] [kcopy] Add kcopy driver |
35 |
+ |
36 |
+Add kcopy driver from qlogic to implement zero copy for infiniband psm |
37 |
+userspace driver |
38 |
+ |
39 |
+Signed-off-by: Alexey Shvetsov <alexxy@g.o> |
40 |
+--- |
41 |
+ drivers/char/Kconfig | 2 + |
42 |
+ drivers/char/Makefile | 2 + |
43 |
+ drivers/char/kcopy/Kconfig | 17 ++ |
44 |
+ drivers/char/kcopy/Makefile | 4 + |
45 |
+ drivers/char/kcopy/kcopy.c | 646 +++++++++++++++++++++++++++++++++++++++++++ |
46 |
+ 5 files changed, 671 insertions(+), 0 deletions(-) |
47 |
+ create mode 100644 drivers/char/kcopy/Kconfig |
48 |
+ create mode 100644 drivers/char/kcopy/Makefile |
49 |
+ create mode 100644 drivers/char/kcopy/kcopy.c |
50 |
+ |
51 |
+diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig |
52 |
+index 4364303..f206545 100644 |
53 |
+--- a/drivers/char/Kconfig |
54 |
++++ b/drivers/char/Kconfig |
55 |
+@@ -6,6 +6,8 @@ menu "Character devices" |
56 |
+ |
57 |
+ source "drivers/tty/Kconfig" |
58 |
+ |
59 |
++source "drivers/char/kcopy/Kconfig" |
60 |
++ |
61 |
+ config DEVKMEM |
62 |
+ bool "/dev/kmem virtual device support" |
63 |
+ default y |
64 |
+--- a/drivers/char/Makefile 2012-02-01 10:18:43.568153662 -0500 |
65 |
++++ b/drivers/char/Makefile 2012-02-01 10:19:40.726839554 -0500 |
66 |
+@@ -65,3 +65,4 @@ obj-$(CONFIG_JS_RTC) += js-rtc.o |
67 |
+ js-rtc-y = rtc.o |
68 |
+ |
69 |
+ obj-$(CONFIG_TILE_SROM) += tile-srom.o |
70 |
++obj-$(CONFIG_KCOPY) += kcopy/ |
71 |
+diff --git a/drivers/char/kcopy/Kconfig b/drivers/char/kcopy/Kconfig |
72 |
+new file mode 100644 |
73 |
+index 0000000..453ae52 |
74 |
+--- /dev/null |
75 |
++++ b/drivers/char/kcopy/Kconfig |
76 |
+@@ -0,0 +1,17 @@ |
77 |
++# |
78 |
++# KCopy character device configuration |
79 |
++# |
80 |
++ |
81 |
++menu "KCopy" |
82 |
++ |
83 |
++config KCOPY |
84 |
++ tristate "Memory-to-memory copies using kernel assist" |
85 |
++ default m |
86 |
++ ---help--- |
87 |
++ High-performance inter-process memory copies. Can often save a |
88 |
++ memory copy to shared memory in the application. Useful at least |
89 |
++ for MPI applications where the point-to-point nature of vmsplice |
90 |
++ and pipes can be a limiting factor in performance. |
91 |
++ |
92 |
++endmenu |
93 |
++ |
94 |
+diff --git a/drivers/char/kcopy/Makefile b/drivers/char/kcopy/Makefile |
95 |
+new file mode 100644 |
96 |
+index 0000000..9cb269b |
97 |
+--- /dev/null |
98 |
++++ b/drivers/char/kcopy/Makefile |
99 |
+@@ -0,0 +1,4 @@ |
100 |
++# |
101 |
++# Makefile for the kernel character device drivers. |
102 |
++# |
103 |
++obj-$(CONFIG_KCOPY) += kcopy.o |
104 |
+diff --git a/drivers/char/kcopy/kcopy.c b/drivers/char/kcopy/kcopy.c |
105 |
+new file mode 100644 |
106 |
+index 0000000..a9f915c |
107 |
+--- /dev/null |
108 |
++++ b/drivers/char/kcopy/kcopy.c |
109 |
+@@ -0,0 +1,646 @@ |
110 |
++#include <linux/module.h> |
111 |
++#include <linux/fs.h> |
112 |
++#include <linux/cdev.h> |
113 |
++#include <linux/device.h> |
114 |
++#include <linux/mutex.h> |
115 |
++#include <linux/mman.h> |
116 |
++#include <linux/highmem.h> |
117 |
++#include <linux/spinlock.h> |
118 |
++#include <linux/sched.h> |
119 |
++#include <linux/rbtree.h> |
120 |
++#include <linux/rcupdate.h> |
121 |
++#include <linux/uaccess.h> |
122 |
++#include <linux/slab.h> |
123 |
++ |
124 |
++MODULE_LICENSE("GPL"); |
125 |
++MODULE_AUTHOR("Arthur Jones <arthur.jones@××××××.com>"); |
126 |
++MODULE_DESCRIPTION("QLogic kcopy driver"); |
127 |
++ |
128 |
++#define KCOPY_ABI 1 |
129 |
++#define KCOPY_MAX_MINORS 64 |
130 |
++ |
131 |
++struct kcopy_device { |
132 |
++ struct cdev cdev; |
133 |
++ struct class *class; |
134 |
++ struct device *devp[KCOPY_MAX_MINORS]; |
135 |
++ dev_t dev; |
136 |
++ |
137 |
++ struct kcopy_file *kf[KCOPY_MAX_MINORS]; |
138 |
++ struct mutex open_lock; |
139 |
++}; |
140 |
++ |
141 |
++static struct kcopy_device kcopy_dev; |
142 |
++ |
143 |
++/* per file data / one of these is shared per minor */ |
144 |
++struct kcopy_file { |
145 |
++ int count; |
146 |
++ |
147 |
++ /* pid indexed */ |
148 |
++ struct rb_root live_map_tree; |
149 |
++ |
150 |
++ struct mutex map_lock; |
151 |
++}; |
152 |
++ |
153 |
++struct kcopy_map_entry { |
154 |
++ int count; |
155 |
++ struct task_struct *task; |
156 |
++ pid_t pid; |
157 |
++ struct kcopy_file *file; /* file backpointer */ |
158 |
++ |
159 |
++ struct list_head list; /* free map list */ |
160 |
++ struct rb_node node; /* live map tree */ |
161 |
++}; |
162 |
++ |
163 |
++#define KCOPY_GET_SYSCALL 1 |
164 |
++#define KCOPY_PUT_SYSCALL 2 |
165 |
++#define KCOPY_ABI_SYSCALL 3 |
166 |
++ |
167 |
++struct kcopy_syscall { |
168 |
++ __u32 tag; |
169 |
++ pid_t pid; |
170 |
++ __u64 n; |
171 |
++ __u64 src; |
172 |
++ __u64 dst; |
173 |
++}; |
174 |
++ |
175 |
++static const void __user *kcopy_syscall_src(const struct kcopy_syscall *ks) |
176 |
++{ |
177 |
++ return (const void __user *) (unsigned long) ks->src; |
178 |
++} |
179 |
++ |
180 |
++static void __user *kcopy_syscall_dst(const struct kcopy_syscall *ks) |
181 |
++{ |
182 |
++ return (void __user *) (unsigned long) ks->dst; |
183 |
++} |
184 |
++ |
185 |
++static unsigned long kcopy_syscall_n(const struct kcopy_syscall *ks) |
186 |
++{ |
187 |
++ return (unsigned long) ks->n; |
188 |
++} |
189 |
++ |
190 |
++static struct kcopy_map_entry *kcopy_create_entry(struct kcopy_file *file) |
191 |
++{ |
192 |
++ struct kcopy_map_entry *kme = |
193 |
++ kmalloc(sizeof(struct kcopy_map_entry), GFP_KERNEL); |
194 |
++ |
195 |
++ if (!kme) |
196 |
++ return NULL; |
197 |
++ |
198 |
++ kme->count = 1; |
199 |
++ kme->file = file; |
200 |
++ kme->task = current; |
201 |
++ kme->pid = current->tgid; |
202 |
++ INIT_LIST_HEAD(&kme->list); |
203 |
++ |
204 |
++ return kme; |
205 |
++} |
206 |
++ |
207 |
++static struct kcopy_map_entry * |
208 |
++kcopy_lookup_pid(struct rb_root *root, pid_t pid) |
209 |
++{ |
210 |
++ struct rb_node *node = root->rb_node; |
211 |
++ |
212 |
++ while (node) { |
213 |
++ struct kcopy_map_entry *kme = |
214 |
++ container_of(node, struct kcopy_map_entry, node); |
215 |
++ |
216 |
++ if (pid < kme->pid) |
217 |
++ node = node->rb_left; |
218 |
++ else if (pid > kme->pid) |
219 |
++ node = node->rb_right; |
220 |
++ else |
221 |
++ return kme; |
222 |
++ } |
223 |
++ |
224 |
++ return NULL; |
225 |
++} |
226 |
++ |
227 |
++static int kcopy_insert(struct rb_root *root, struct kcopy_map_entry *kme) |
228 |
++{ |
229 |
++ struct rb_node **new = &(root->rb_node); |
230 |
++ struct rb_node *parent = NULL; |
231 |
++ |
232 |
++ while (*new) { |
233 |
++ struct kcopy_map_entry *tkme = |
234 |
++ container_of(*new, struct kcopy_map_entry, node); |
235 |
++ |
236 |
++ parent = *new; |
237 |
++ if (kme->pid < tkme->pid) |
238 |
++ new = &((*new)->rb_left); |
239 |
++ else if (kme->pid > tkme->pid) |
240 |
++ new = &((*new)->rb_right); |
241 |
++ else { |
242 |
++ printk(KERN_INFO "!!! debugging: bad rb tree !!!\n"); |
243 |
++ return -EINVAL; |
244 |
++ } |
245 |
++ } |
246 |
++ |
247 |
++ rb_link_node(&kme->node, parent, new); |
248 |
++ rb_insert_color(&kme->node, root); |
249 |
++ |
250 |
++ return 0; |
251 |
++} |
252 |
++ |
253 |
++static int kcopy_open(struct inode *inode, struct file *filp) |
254 |
++{ |
255 |
++ int ret; |
256 |
++ const int minor = iminor(inode); |
257 |
++ struct kcopy_file *kf = NULL; |
258 |
++ struct kcopy_map_entry *kme; |
259 |
++ struct kcopy_map_entry *okme; |
260 |
++ |
261 |
++ if (minor < 0 || minor >= KCOPY_MAX_MINORS) |
262 |
++ return -ENODEV; |
263 |
++ |
264 |
++ mutex_lock(&kcopy_dev.open_lock); |
265 |
++ |
266 |
++ if (!kcopy_dev.kf[minor]) { |
267 |
++ kf = kmalloc(sizeof(struct kcopy_file), GFP_KERNEL); |
268 |
++ |
269 |
++ if (!kf) { |
270 |
++ ret = -ENOMEM; |
271 |
++ goto bail; |
272 |
++ } |
273 |
++ |
274 |
++ kf->count = 1; |
275 |
++ kf->live_map_tree = RB_ROOT; |
276 |
++ mutex_init(&kf->map_lock); |
277 |
++ kcopy_dev.kf[minor] = kf; |
278 |
++ } else { |
279 |
++ if (filp->f_flags & O_EXCL) { |
280 |
++ ret = -EBUSY; |
281 |
++ goto bail; |
282 |
++ } |
283 |
++ kcopy_dev.kf[minor]->count++; |
284 |
++ } |
285 |
++ |
286 |
++ kme = kcopy_create_entry(kcopy_dev.kf[minor]); |
287 |
++ if (!kme) { |
288 |
++ ret = -ENOMEM; |
289 |
++ goto err_free_kf; |
290 |
++ } |
291 |
++ |
292 |
++ kf = kcopy_dev.kf[minor]; |
293 |
++ |
294 |
++ mutex_lock(&kf->map_lock); |
295 |
++ |
296 |
++ okme = kcopy_lookup_pid(&kf->live_map_tree, kme->pid); |
297 |
++ if (okme) { |
298 |
++ /* pid already exists... */ |
299 |
++ okme->count++; |
300 |
++ kfree(kme); |
301 |
++ kme = okme; |
302 |
++ } else |
303 |
++ ret = kcopy_insert(&kf->live_map_tree, kme); |
304 |
++ |
305 |
++ mutex_unlock(&kf->map_lock); |
306 |
++ |
307 |
++ filp->private_data = kme; |
308 |
++ |
309 |
++ ret = 0; |
310 |
++ goto bail; |
311 |
++ |
312 |
++err_free_kf: |
313 |
++ if (kf) { |
314 |
++ kcopy_dev.kf[minor] = NULL; |
315 |
++ kfree(kf); |
316 |
++ } |
317 |
++bail: |
318 |
++ mutex_unlock(&kcopy_dev.open_lock); |
319 |
++ return ret; |
320 |
++} |
321 |
++ |
322 |
++static int kcopy_flush(struct file *filp, fl_owner_t id) |
323 |
++{ |
324 |
++ struct kcopy_map_entry *kme = filp->private_data; |
325 |
++ struct kcopy_file *kf = kme->file; |
326 |
++ |
327 |
++ if (file_count(filp) == 1) { |
328 |
++ mutex_lock(&kf->map_lock); |
329 |
++ kme->count--; |
330 |
++ |
331 |
++ if (!kme->count) { |
332 |
++ rb_erase(&kme->node, &kf->live_map_tree); |
333 |
++ kfree(kme); |
334 |
++ } |
335 |
++ mutex_unlock(&kf->map_lock); |
336 |
++ } |
337 |
++ |
338 |
++ return 0; |
339 |
++} |
340 |
++ |
341 |
++static int kcopy_release(struct inode *inode, struct file *filp) |
342 |
++{ |
343 |
++ const int minor = iminor(inode); |
344 |
++ |
345 |
++ mutex_lock(&kcopy_dev.open_lock); |
346 |
++ kcopy_dev.kf[minor]->count--; |
347 |
++ if (!kcopy_dev.kf[minor]->count) { |
348 |
++ kfree(kcopy_dev.kf[minor]); |
349 |
++ kcopy_dev.kf[minor] = NULL; |
350 |
++ } |
351 |
++ mutex_unlock(&kcopy_dev.open_lock); |
352 |
++ |
353 |
++ return 0; |
354 |
++} |
355 |
++ |
356 |
++static void kcopy_put_pages(struct page **pages, int npages) |
357 |
++{ |
358 |
++ int j; |
359 |
++ |
360 |
++ for (j = 0; j < npages; j++) |
361 |
++ put_page(pages[j]); |
362 |
++} |
363 |
++ |
364 |
++static int kcopy_validate_task(struct task_struct *p) |
365 |
++{ |
366 |
++ return p && ((current_euid() == task_euid(p)) || (current_euid() == task_uid(p))); |
367 |
++} |
368 |
++ |
369 |
++static int kcopy_get_pages(struct kcopy_file *kf, pid_t pid, |
370 |
++ struct page **pages, void __user *addr, |
371 |
++ int write, size_t npages) |
372 |
++{ |
373 |
++ int err; |
374 |
++ struct mm_struct *mm; |
375 |
++ struct kcopy_map_entry *rkme; |
376 |
++ |
377 |
++ mutex_lock(&kf->map_lock); |
378 |
++ |
379 |
++ rkme = kcopy_lookup_pid(&kf->live_map_tree, pid); |
380 |
++ if (!rkme || !kcopy_validate_task(rkme->task)) { |
381 |
++ err = -EINVAL; |
382 |
++ goto bail_unlock; |
383 |
++ } |
384 |
++ |
385 |
++ mm = get_task_mm(rkme->task); |
386 |
++ if (unlikely(!mm)) { |
387 |
++ err = -ENOMEM; |
388 |
++ goto bail_unlock; |
389 |
++ } |
390 |
++ |
391 |
++ down_read(&mm->mmap_sem); |
392 |
++ err = get_user_pages(rkme->task, mm, |
393 |
++ (unsigned long) addr, npages, write, 0, |
394 |
++ pages, NULL); |
395 |
++ |
396 |
++ if (err < npages && err > 0) { |
397 |
++ kcopy_put_pages(pages, err); |
398 |
++ err = -ENOMEM; |
399 |
++ } else if (err == npages) |
400 |
++ err = 0; |
401 |
++ |
402 |
++ up_read(&mm->mmap_sem); |
403 |
++ |
404 |
++ mmput(mm); |
405 |
++ |
406 |
++bail_unlock: |
407 |
++ mutex_unlock(&kf->map_lock); |
408 |
++ |
409 |
++ return err; |
410 |
++} |
411 |
++ |
412 |
++static unsigned long kcopy_copy_pages_from_user(void __user *src, |
413 |
++ struct page **dpages, |
414 |
++ unsigned doff, |
415 |
++ unsigned long n) |
416 |
++{ |
417 |
++ struct page *dpage = *dpages; |
418 |
++ char *daddr = kmap(dpage); |
419 |
++ int ret = 0; |
420 |
++ |
421 |
++ while (1) { |
422 |
++ const unsigned long nleft = PAGE_SIZE - doff; |
423 |
++ const unsigned long nc = (n < nleft) ? n : nleft; |
424 |
++ |
425 |
++ /* if (copy_from_user(daddr + doff, src, nc)) { */ |
426 |
++ if (__copy_from_user_nocache(daddr + doff, src, nc)) { |
427 |
++ ret = -EFAULT; |
428 |
++ goto bail; |
429 |
++ } |
430 |
++ |
431 |
++ n -= nc; |
432 |
++ if (n == 0) |
433 |
++ break; |
434 |
++ |
435 |
++ doff += nc; |
436 |
++ doff &= ~PAGE_MASK; |
437 |
++ if (doff == 0) { |
438 |
++ kunmap(dpage); |
439 |
++ dpages++; |
440 |
++ dpage = *dpages; |
441 |
++ daddr = kmap(dpage); |
442 |
++ } |
443 |
++ |
444 |
++ src += nc; |
445 |
++ } |
446 |
++ |
447 |
++bail: |
448 |
++ kunmap(dpage); |
449 |
++ |
450 |
++ return ret; |
451 |
++} |
452 |
++ |
453 |
++static unsigned long kcopy_copy_pages_to_user(void __user *dst, |
454 |
++ struct page **spages, |
455 |
++ unsigned soff, |
456 |
++ unsigned long n) |
457 |
++{ |
458 |
++ struct page *spage = *spages; |
459 |
++ const char *saddr = kmap(spage); |
460 |
++ int ret = 0; |
461 |
++ |
462 |
++ while (1) { |
463 |
++ const unsigned long nleft = PAGE_SIZE - soff; |
464 |
++ const unsigned long nc = (n < nleft) ? n : nleft; |
465 |
++ |
466 |
++ if (copy_to_user(dst, saddr + soff, nc)) { |
467 |
++ ret = -EFAULT; |
468 |
++ goto bail; |
469 |
++ } |
470 |
++ |
471 |
++ n -= nc; |
472 |
++ if (n == 0) |
473 |
++ break; |
474 |
++ |
475 |
++ soff += nc; |
476 |
++ soff &= ~PAGE_MASK; |
477 |
++ if (soff == 0) { |
478 |
++ kunmap(spage); |
479 |
++ spages++; |
480 |
++ spage = *spages; |
481 |
++ saddr = kmap(spage); |
482 |
++ } |
483 |
++ |
484 |
++ dst += nc; |
485 |
++ } |
486 |
++ |
487 |
++bail: |
488 |
++ kunmap(spage); |
489 |
++ |
490 |
++ return ret; |
491 |
++} |
492 |
++ |
493 |
++static unsigned long kcopy_copy_to_user(void __user *dst, |
494 |
++ struct kcopy_file *kf, pid_t pid, |
495 |
++ void __user *src, |
496 |
++ unsigned long n) |
497 |
++{ |
498 |
++ struct page **pages; |
499 |
++ const int pages_len = PAGE_SIZE / sizeof(struct page *); |
500 |
++ int ret = 0; |
501 |
++ |
502 |
++ pages = (struct page **) __get_free_page(GFP_KERNEL); |
503 |
++ if (!pages) { |
504 |
++ ret = -ENOMEM; |
505 |
++ goto bail; |
506 |
++ } |
507 |
++ |
508 |
++ while (n) { |
509 |
++ const unsigned long soff = (unsigned long) src & ~PAGE_MASK; |
510 |
++ const unsigned long spages_left = |
511 |
++ (soff + n + PAGE_SIZE - 1) >> PAGE_SHIFT; |
512 |
++ const unsigned long spages_cp = |
513 |
++ min_t(unsigned long, spages_left, pages_len); |
514 |
++ const unsigned long sbytes = |
515 |
++ PAGE_SIZE - soff + (spages_cp - 1) * PAGE_SIZE; |
516 |
++ const unsigned long nbytes = min_t(unsigned long, sbytes, n); |
517 |
++ |
518 |
++ ret = kcopy_get_pages(kf, pid, pages, src, 0, spages_cp); |
519 |
++ if (unlikely(ret)) |
520 |
++ goto bail_free; |
521 |
++ |
522 |
++ ret = kcopy_copy_pages_to_user(dst, pages, soff, nbytes); |
523 |
++ kcopy_put_pages(pages, spages_cp); |
524 |
++ if (ret) |
525 |
++ goto bail_free; |
526 |
++ dst = (char *) dst + nbytes; |
527 |
++ src = (char *) src + nbytes; |
528 |
++ |
529 |
++ n -= nbytes; |
530 |
++ } |
531 |
++ |
532 |
++bail_free: |
533 |
++ free_page((unsigned long) pages); |
534 |
++bail: |
535 |
++ return ret; |
536 |
++} |
537 |
++ |
538 |
++static unsigned long kcopy_copy_from_user(const void __user *src, |
539 |
++ struct kcopy_file *kf, pid_t pid, |
540 |
++ void __user *dst, |
541 |
++ unsigned long n) |
542 |
++{ |
543 |
++ struct page **pages; |
544 |
++ const int pages_len = PAGE_SIZE / sizeof(struct page *); |
545 |
++ int ret = 0; |
546 |
++ |
547 |
++ pages = (struct page **) __get_free_page(GFP_KERNEL); |
548 |
++ if (!pages) { |
549 |
++ ret = -ENOMEM; |
550 |
++ goto bail; |
551 |
++ } |
552 |
++ |
553 |
++ while (n) { |
554 |
++ const unsigned long doff = (unsigned long) dst & ~PAGE_MASK; |
555 |
++ const unsigned long dpages_left = |
556 |
++ (doff + n + PAGE_SIZE - 1) >> PAGE_SHIFT; |
557 |
++ const unsigned long dpages_cp = |
558 |
++ min_t(unsigned long, dpages_left, pages_len); |
559 |
++ const unsigned long dbytes = |
560 |
++ PAGE_SIZE - doff + (dpages_cp - 1) * PAGE_SIZE; |
561 |
++ const unsigned long nbytes = min_t(unsigned long, dbytes, n); |
562 |
++ |
563 |
++ ret = kcopy_get_pages(kf, pid, pages, dst, 1, dpages_cp); |
564 |
++ if (unlikely(ret)) |
565 |
++ goto bail_free; |
566 |
++ |
567 |
++ ret = kcopy_copy_pages_from_user((void __user *) src, |
568 |
++ pages, doff, nbytes); |
569 |
++ kcopy_put_pages(pages, dpages_cp); |
570 |
++ if (ret) |
571 |
++ goto bail_free; |
572 |
++ |
573 |
++ dst = (char *) dst + nbytes; |
574 |
++ src = (char *) src + nbytes; |
575 |
++ |
576 |
++ n -= nbytes; |
577 |
++ } |
578 |
++ |
579 |
++bail_free: |
580 |
++ free_page((unsigned long) pages); |
581 |
++bail: |
582 |
++ return ret; |
583 |
++} |
584 |
++ |
585 |
++static int kcopy_do_get(struct kcopy_map_entry *kme, pid_t pid, |
586 |
++ const void __user *src, void __user *dst, |
587 |
++ unsigned long n) |
588 |
++{ |
589 |
++ struct kcopy_file *kf = kme->file; |
590 |
++ int ret = 0; |
591 |
++ |
592 |
++ if (n == 0) { |
593 |
++ ret = -EINVAL; |
594 |
++ goto bail; |
595 |
++ } |
596 |
++ |
597 |
++ ret = kcopy_copy_to_user(dst, kf, pid, (void __user *) src, n); |
598 |
++ |
599 |
++bail: |
600 |
++ return ret; |
601 |
++} |
602 |
++ |
603 |
++static int kcopy_do_put(struct kcopy_map_entry *kme, const void __user *src, |
604 |
++ pid_t pid, void __user *dst, |
605 |
++ unsigned long n) |
606 |
++{ |
607 |
++ struct kcopy_file *kf = kme->file; |
608 |
++ int ret = 0; |
609 |
++ |
610 |
++ if (n == 0) { |
611 |
++ ret = -EINVAL; |
612 |
++ goto bail; |
613 |
++ } |
614 |
++ |
615 |
++ ret = kcopy_copy_from_user(src, kf, pid, (void __user *) dst, n); |
616 |
++ |
617 |
++bail: |
618 |
++ return ret; |
619 |
++} |
620 |
++ |
621 |
++static int kcopy_do_abi(u32 __user *dst) |
622 |
++{ |
623 |
++ u32 val = KCOPY_ABI; |
624 |
++ int err; |
625 |
++ |
626 |
++ err = put_user(val, dst); |
627 |
++ if (err) |
628 |
++ return -EFAULT; |
629 |
++ |
630 |
++ return 0; |
631 |
++} |
632 |
++ |
633 |
++ssize_t kcopy_write(struct file *filp, const char __user *data, size_t cnt, |
634 |
++ loff_t *o) |
635 |
++{ |
636 |
++ struct kcopy_map_entry *kme = filp->private_data; |
637 |
++ struct kcopy_syscall ks; |
638 |
++ int err = 0; |
639 |
++ const void __user *src; |
640 |
++ void __user *dst; |
641 |
++ unsigned long n; |
642 |
++ |
643 |
++ if (cnt != sizeof(struct kcopy_syscall)) { |
644 |
++ err = -EINVAL; |
645 |
++ goto bail; |
646 |
++ } |
647 |
++ |
648 |
++ err = copy_from_user(&ks, data, cnt); |
649 |
++ if (unlikely(err)) |
650 |
++ goto bail; |
651 |
++ |
652 |
++ src = kcopy_syscall_src(&ks); |
653 |
++ dst = kcopy_syscall_dst(&ks); |
654 |
++ n = kcopy_syscall_n(&ks); |
655 |
++ if (ks.tag == KCOPY_GET_SYSCALL) |
656 |
++ err = kcopy_do_get(kme, ks.pid, src, dst, n); |
657 |
++ else if (ks.tag == KCOPY_PUT_SYSCALL) |
658 |
++ err = kcopy_do_put(kme, src, ks.pid, dst, n); |
659 |
++ else if (ks.tag == KCOPY_ABI_SYSCALL) |
660 |
++ err = kcopy_do_abi(dst); |
661 |
++ else |
662 |
++ err = -EINVAL; |
663 |
++ |
664 |
++bail: |
665 |
++ return err ? err : cnt; |
666 |
++} |
667 |
++ |
668 |
++static const struct file_operations kcopy_fops = { |
669 |
++ .owner = THIS_MODULE, |
670 |
++ .open = kcopy_open, |
671 |
++ .release = kcopy_release, |
672 |
++ .flush = kcopy_flush, |
673 |
++ .write = kcopy_write, |
674 |
++}; |
675 |
++ |
676 |
++static int __init kcopy_init(void) |
677 |
++{ |
678 |
++ int ret; |
679 |
++ const char *name = "kcopy"; |
680 |
++ int i; |
681 |
++ int ninit = 0; |
682 |
++ |
683 |
++ mutex_init(&kcopy_dev.open_lock); |
684 |
++ |
685 |
++ ret = alloc_chrdev_region(&kcopy_dev.dev, 0, KCOPY_MAX_MINORS, name); |
686 |
++ if (ret) |
687 |
++ goto bail; |
688 |
++ |
689 |
++ kcopy_dev.class = class_create(THIS_MODULE, (char *) name); |
690 |
++ |
691 |
++ if (IS_ERR(kcopy_dev.class)) { |
692 |
++ ret = PTR_ERR(kcopy_dev.class); |
693 |
++ printk(KERN_ERR "kcopy: Could not create " |
694 |
++ "device class (err %d)\n", -ret); |
695 |
++ goto bail_chrdev; |
696 |
++ } |
697 |
++ |
698 |
++ cdev_init(&kcopy_dev.cdev, &kcopy_fops); |
699 |
++ ret = cdev_add(&kcopy_dev.cdev, kcopy_dev.dev, KCOPY_MAX_MINORS); |
700 |
++ if (ret < 0) { |
701 |
++ printk(KERN_ERR "kcopy: Could not add cdev (err %d)\n", |
702 |
++ -ret); |
703 |
++ goto bail_class; |
704 |
++ } |
705 |
++ |
706 |
++ for (i = 0; i < KCOPY_MAX_MINORS; i++) { |
707 |
++ char devname[8]; |
708 |
++ const int minor = MINOR(kcopy_dev.dev) + i; |
709 |
++ const dev_t dev = MKDEV(MAJOR(kcopy_dev.dev), minor); |
710 |
++ |
711 |
++ snprintf(devname, sizeof(devname), "kcopy%02d", i); |
712 |
++ kcopy_dev.devp[i] = |
713 |
++ device_create(kcopy_dev.class, NULL, |
714 |
++ dev, NULL, devname); |
715 |
++ |
716 |
++ if (IS_ERR(kcopy_dev.devp[i])) { |
717 |
++ ret = PTR_ERR(kcopy_dev.devp[i]); |
718 |
++ printk(KERN_ERR "kcopy: Could not create " |
719 |
++ "devp %d (err %d)\n", i, -ret); |
720 |
++ goto bail_cdev_add; |
721 |
++ } |
722 |
++ |
723 |
++ ninit++; |
724 |
++ } |
725 |
++ |
726 |
++ ret = 0; |
727 |
++ goto bail; |
728 |
++ |
729 |
++bail_cdev_add: |
730 |
++ for (i = 0; i < ninit; i++) |
731 |
++ device_unregister(kcopy_dev.devp[i]); |
732 |
++ |
733 |
++ cdev_del(&kcopy_dev.cdev); |
734 |
++bail_class: |
735 |
++ class_destroy(kcopy_dev.class); |
736 |
++bail_chrdev: |
737 |
++ unregister_chrdev_region(kcopy_dev.dev, KCOPY_MAX_MINORS); |
738 |
++bail: |
739 |
++ return ret; |
740 |
++} |
741 |
++ |
742 |
++static void __exit kcopy_fini(void) |
743 |
++{ |
744 |
++ int i; |
745 |
++ |
746 |
++ for (i = 0; i < KCOPY_MAX_MINORS; i++) |
747 |
++ device_unregister(kcopy_dev.devp[i]); |
748 |
++ |
749 |
++ cdev_del(&kcopy_dev.cdev); |
750 |
++ class_destroy(kcopy_dev.class); |
751 |
++ unregister_chrdev_region(kcopy_dev.dev, KCOPY_MAX_MINORS); |
752 |
++} |
753 |
++ |
754 |
++module_init(kcopy_init); |
755 |
++module_exit(kcopy_fini); |