Line 0
Link Here
|
0 |
- |
1 |
#include <linux/module.h> |
|
|
2 |
#include <linux/fs.h> |
3 |
#include <linux/cdev.h> |
4 |
#include <linux/device.h> |
5 |
#include <linux/mutex.h> |
6 |
#include <linux/mman.h> |
7 |
#include <linux/highmem.h> |
8 |
#include <linux/spinlock.h> |
9 |
#include <linux/sched.h> |
10 |
#include <linux/rbtree.h> |
11 |
#include <linux/rcupdate.h> |
12 |
#include <linux/uaccess.h> |
13 |
#include <linux/slab.h> |
14 |
|
15 |
MODULE_LICENSE("GPL"); |
16 |
MODULE_AUTHOR("Arthur Jones <arthur.jones@qlogic.com>"); |
17 |
MODULE_DESCRIPTION("QLogic kcopy driver"); |
18 |
|
19 |
#define KCOPY_ABI 1 |
20 |
#define KCOPY_MAX_MINORS 64 |
21 |
|
22 |
struct kcopy_device { |
23 |
struct cdev cdev; |
24 |
struct class *class; |
25 |
struct device *devp[KCOPY_MAX_MINORS]; |
26 |
dev_t dev; |
27 |
|
28 |
struct kcopy_file *kf[KCOPY_MAX_MINORS]; |
29 |
struct mutex open_lock; |
30 |
}; |
31 |
|
32 |
static struct kcopy_device kcopy_dev; |
33 |
|
34 |
/* per file data / one of these is shared per minor */ |
35 |
struct kcopy_file { |
36 |
int count; |
37 |
|
38 |
/* pid indexed */ |
39 |
struct rb_root live_map_tree; |
40 |
|
41 |
struct mutex map_lock; |
42 |
}; |
43 |
|
44 |
struct kcopy_map_entry { |
45 |
int count; |
46 |
struct task_struct *task; |
47 |
pid_t pid; |
48 |
struct kcopy_file *file; /* file backpointer */ |
49 |
|
50 |
struct list_head list; /* free map list */ |
51 |
struct rb_node node; /* live map tree */ |
52 |
}; |
53 |
|
54 |
#define KCOPY_GET_SYSCALL 1 |
55 |
#define KCOPY_PUT_SYSCALL 2 |
56 |
#define KCOPY_ABI_SYSCALL 3 |
57 |
|
58 |
struct kcopy_syscall { |
59 |
__u32 tag; |
60 |
pid_t pid; |
61 |
__u64 n; |
62 |
__u64 src; |
63 |
__u64 dst; |
64 |
}; |
65 |
|
66 |
static const void __user *kcopy_syscall_src(const struct kcopy_syscall *ks) |
67 |
{ |
68 |
return (const void __user *) (unsigned long) ks->src; |
69 |
} |
70 |
|
71 |
static void __user *kcopy_syscall_dst(const struct kcopy_syscall *ks) |
72 |
{ |
73 |
return (void __user *) (unsigned long) ks->dst; |
74 |
} |
75 |
|
76 |
static unsigned long kcopy_syscall_n(const struct kcopy_syscall *ks) |
77 |
{ |
78 |
return (unsigned long) ks->n; |
79 |
} |
80 |
|
81 |
static struct kcopy_map_entry *kcopy_create_entry(struct kcopy_file *file) |
82 |
{ |
83 |
struct kcopy_map_entry *kme = |
84 |
kmalloc(sizeof(struct kcopy_map_entry), GFP_KERNEL); |
85 |
|
86 |
if (!kme) |
87 |
return NULL; |
88 |
|
89 |
kme->count = 1; |
90 |
kme->file = file; |
91 |
kme->task = current; |
92 |
kme->pid = current->tgid; |
93 |
INIT_LIST_HEAD(&kme->list); |
94 |
|
95 |
return kme; |
96 |
} |
97 |
|
98 |
static struct kcopy_map_entry * |
99 |
kcopy_lookup_pid(struct rb_root *root, pid_t pid) |
100 |
{ |
101 |
struct rb_node *node = root->rb_node; |
102 |
|
103 |
while (node) { |
104 |
struct kcopy_map_entry *kme = |
105 |
container_of(node, struct kcopy_map_entry, node); |
106 |
|
107 |
if (pid < kme->pid) |
108 |
node = node->rb_left; |
109 |
else if (pid > kme->pid) |
110 |
node = node->rb_right; |
111 |
else |
112 |
return kme; |
113 |
} |
114 |
|
115 |
return NULL; |
116 |
} |
117 |
|
118 |
static int kcopy_insert(struct rb_root *root, struct kcopy_map_entry *kme) |
119 |
{ |
120 |
struct rb_node **new = &(root->rb_node); |
121 |
struct rb_node *parent = NULL; |
122 |
|
123 |
while (*new) { |
124 |
struct kcopy_map_entry *tkme = |
125 |
container_of(*new, struct kcopy_map_entry, node); |
126 |
|
127 |
parent = *new; |
128 |
if (kme->pid < tkme->pid) |
129 |
new = &((*new)->rb_left); |
130 |
else if (kme->pid > tkme->pid) |
131 |
new = &((*new)->rb_right); |
132 |
else { |
133 |
printk(KERN_INFO "!!! debugging: bad rb tree !!!\n"); |
134 |
return -EINVAL; |
135 |
} |
136 |
} |
137 |
|
138 |
rb_link_node(&kme->node, parent, new); |
139 |
rb_insert_color(&kme->node, root); |
140 |
|
141 |
return 0; |
142 |
} |
143 |
|
144 |
static int kcopy_open(struct inode *inode, struct file *filp) |
145 |
{ |
146 |
int ret; |
147 |
const int minor = iminor(inode); |
148 |
struct kcopy_file *kf = NULL; |
149 |
struct kcopy_map_entry *kme; |
150 |
struct kcopy_map_entry *okme; |
151 |
|
152 |
if (minor < 0 || minor >= KCOPY_MAX_MINORS) |
153 |
return -ENODEV; |
154 |
|
155 |
mutex_lock(&kcopy_dev.open_lock); |
156 |
|
157 |
if (!kcopy_dev.kf[minor]) { |
158 |
kf = kmalloc(sizeof(struct kcopy_file), GFP_KERNEL); |
159 |
|
160 |
if (!kf) { |
161 |
ret = -ENOMEM; |
162 |
goto bail; |
163 |
} |
164 |
|
165 |
kf->count = 1; |
166 |
kf->live_map_tree = RB_ROOT; |
167 |
mutex_init(&kf->map_lock); |
168 |
kcopy_dev.kf[minor] = kf; |
169 |
} else { |
170 |
if (filp->f_flags & O_EXCL) { |
171 |
ret = -EBUSY; |
172 |
goto bail; |
173 |
} |
174 |
kcopy_dev.kf[minor]->count++; |
175 |
} |
176 |
|
177 |
kme = kcopy_create_entry(kcopy_dev.kf[minor]); |
178 |
if (!kme) { |
179 |
ret = -ENOMEM; |
180 |
goto err_free_kf; |
181 |
} |
182 |
|
183 |
kf = kcopy_dev.kf[minor]; |
184 |
|
185 |
mutex_lock(&kf->map_lock); |
186 |
|
187 |
okme = kcopy_lookup_pid(&kf->live_map_tree, kme->pid); |
188 |
if (okme) { |
189 |
/* pid already exists... */ |
190 |
okme->count++; |
191 |
kfree(kme); |
192 |
kme = okme; |
193 |
} else |
194 |
ret = kcopy_insert(&kf->live_map_tree, kme); |
195 |
|
196 |
mutex_unlock(&kf->map_lock); |
197 |
|
198 |
filp->private_data = kme; |
199 |
|
200 |
ret = 0; |
201 |
goto bail; |
202 |
|
203 |
err_free_kf: |
204 |
if (kf) { |
205 |
kcopy_dev.kf[minor] = NULL; |
206 |
kfree(kf); |
207 |
} |
208 |
bail: |
209 |
mutex_unlock(&kcopy_dev.open_lock); |
210 |
return ret; |
211 |
} |
212 |
|
213 |
static int kcopy_flush(struct file *filp, fl_owner_t id) |
214 |
{ |
215 |
struct kcopy_map_entry *kme = filp->private_data; |
216 |
struct kcopy_file *kf = kme->file; |
217 |
|
218 |
if (file_count(filp) == 1) { |
219 |
mutex_lock(&kf->map_lock); |
220 |
kme->count--; |
221 |
|
222 |
if (!kme->count) { |
223 |
rb_erase(&kme->node, &kf->live_map_tree); |
224 |
kfree(kme); |
225 |
} |
226 |
mutex_unlock(&kf->map_lock); |
227 |
} |
228 |
|
229 |
return 0; |
230 |
} |
231 |
|
232 |
static int kcopy_release(struct inode *inode, struct file *filp) |
233 |
{ |
234 |
const int minor = iminor(inode); |
235 |
|
236 |
mutex_lock(&kcopy_dev.open_lock); |
237 |
kcopy_dev.kf[minor]->count--; |
238 |
if (!kcopy_dev.kf[minor]->count) { |
239 |
kfree(kcopy_dev.kf[minor]); |
240 |
kcopy_dev.kf[minor] = NULL; |
241 |
} |
242 |
mutex_unlock(&kcopy_dev.open_lock); |
243 |
|
244 |
return 0; |
245 |
} |
246 |
|
247 |
static void kcopy_put_pages(struct page **pages, int npages) |
248 |
{ |
249 |
int j; |
250 |
|
251 |
for (j = 0; j < npages; j++) |
252 |
put_page(pages[j]); |
253 |
} |
254 |
|
255 |
static int kcopy_validate_task(struct task_struct *p) |
256 |
{ |
257 |
return p && (uid_eq(current_euid(), task_euid(p)) || uid_eq(current_euid(), task_uid(p))); |
258 |
} |
259 |
|
260 |
static int kcopy_get_pages(struct kcopy_file *kf, pid_t pid, |
261 |
struct page **pages, void __user *addr, |
262 |
int write, size_t npages) |
263 |
{ |
264 |
int err; |
265 |
struct mm_struct *mm; |
266 |
struct kcopy_map_entry *rkme; |
267 |
|
268 |
mutex_lock(&kf->map_lock); |
269 |
|
270 |
rkme = kcopy_lookup_pid(&kf->live_map_tree, pid); |
271 |
if (!rkme || !kcopy_validate_task(rkme->task)) { |
272 |
err = -EINVAL; |
273 |
goto bail_unlock; |
274 |
} |
275 |
|
276 |
mm = get_task_mm(rkme->task); |
277 |
if (unlikely(!mm)) { |
278 |
err = -ENOMEM; |
279 |
goto bail_unlock; |
280 |
} |
281 |
|
282 |
down_read(&mm->mmap_sem); |
283 |
err = get_user_pages(rkme->task, mm, |
284 |
(unsigned long) addr, npages, write, 0, |
285 |
pages, NULL); |
286 |
|
287 |
if (err < npages && err > 0) { |
288 |
kcopy_put_pages(pages, err); |
289 |
err = -ENOMEM; |
290 |
} else if (err == npages) |
291 |
err = 0; |
292 |
|
293 |
up_read(&mm->mmap_sem); |
294 |
|
295 |
mmput(mm); |
296 |
|
297 |
bail_unlock: |
298 |
mutex_unlock(&kf->map_lock); |
299 |
|
300 |
return err; |
301 |
} |
302 |
|
303 |
static unsigned long kcopy_copy_pages_from_user(void __user *src, |
304 |
struct page **dpages, |
305 |
unsigned doff, |
306 |
unsigned long n) |
307 |
{ |
308 |
struct page *dpage = *dpages; |
309 |
char *daddr = kmap(dpage); |
310 |
int ret = 0; |
311 |
|
312 |
while (1) { |
313 |
const unsigned long nleft = PAGE_SIZE - doff; |
314 |
const unsigned long nc = (n < nleft) ? n : nleft; |
315 |
|
316 |
/* if (copy_from_user(daddr + doff, src, nc)) { */ |
317 |
if (__copy_from_user_nocache(daddr + doff, src, nc)) { |
318 |
ret = -EFAULT; |
319 |
goto bail; |
320 |
} |
321 |
|
322 |
n -= nc; |
323 |
if (n == 0) |
324 |
break; |
325 |
|
326 |
doff += nc; |
327 |
doff &= ~PAGE_MASK; |
328 |
if (doff == 0) { |
329 |
kunmap(dpage); |
330 |
dpages++; |
331 |
dpage = *dpages; |
332 |
daddr = kmap(dpage); |
333 |
} |
334 |
|
335 |
src += nc; |
336 |
} |
337 |
|
338 |
bail: |
339 |
kunmap(dpage); |
340 |
|
341 |
return ret; |
342 |
} |
343 |
|
344 |
static unsigned long kcopy_copy_pages_to_user(void __user *dst, |
345 |
struct page **spages, |
346 |
unsigned soff, |
347 |
unsigned long n) |
348 |
{ |
349 |
struct page *spage = *spages; |
350 |
const char *saddr = kmap(spage); |
351 |
int ret = 0; |
352 |
|
353 |
while (1) { |
354 |
const unsigned long nleft = PAGE_SIZE - soff; |
355 |
const unsigned long nc = (n < nleft) ? n : nleft; |
356 |
|
357 |
if (copy_to_user(dst, saddr + soff, nc)) { |
358 |
ret = -EFAULT; |
359 |
goto bail; |
360 |
} |
361 |
|
362 |
n -= nc; |
363 |
if (n == 0) |
364 |
break; |
365 |
|
366 |
soff += nc; |
367 |
soff &= ~PAGE_MASK; |
368 |
if (soff == 0) { |
369 |
kunmap(spage); |
370 |
spages++; |
371 |
spage = *spages; |
372 |
saddr = kmap(spage); |
373 |
} |
374 |
|
375 |
dst += nc; |
376 |
} |
377 |
|
378 |
bail: |
379 |
kunmap(spage); |
380 |
|
381 |
return ret; |
382 |
} |
383 |
|
384 |
static unsigned long kcopy_copy_to_user(void __user *dst, |
385 |
struct kcopy_file *kf, pid_t pid, |
386 |
void __user *src, |
387 |
unsigned long n) |
388 |
{ |
389 |
struct page **pages; |
390 |
const int pages_len = PAGE_SIZE / sizeof(struct page *); |
391 |
int ret = 0; |
392 |
|
393 |
pages = (struct page **) __get_free_page(GFP_KERNEL); |
394 |
if (!pages) { |
395 |
ret = -ENOMEM; |
396 |
goto bail; |
397 |
} |
398 |
|
399 |
while (n) { |
400 |
const unsigned long soff = (unsigned long) src & ~PAGE_MASK; |
401 |
const unsigned long spages_left = |
402 |
(soff + n + PAGE_SIZE - 1) >> PAGE_SHIFT; |
403 |
const unsigned long spages_cp = |
404 |
min_t(unsigned long, spages_left, pages_len); |
405 |
const unsigned long sbytes = |
406 |
PAGE_SIZE - soff + (spages_cp - 1) * PAGE_SIZE; |
407 |
const unsigned long nbytes = min_t(unsigned long, sbytes, n); |
408 |
|
409 |
ret = kcopy_get_pages(kf, pid, pages, src, 0, spages_cp); |
410 |
if (unlikely(ret)) |
411 |
goto bail_free; |
412 |
|
413 |
ret = kcopy_copy_pages_to_user(dst, pages, soff, nbytes); |
414 |
kcopy_put_pages(pages, spages_cp); |
415 |
if (ret) |
416 |
goto bail_free; |
417 |
dst = (char *) dst + nbytes; |
418 |
src = (char *) src + nbytes; |
419 |
|
420 |
n -= nbytes; |
421 |
} |
422 |
|
423 |
bail_free: |
424 |
free_page((unsigned long) pages); |
425 |
bail: |
426 |
return ret; |
427 |
} |
428 |
|
429 |
static unsigned long kcopy_copy_from_user(const void __user *src, |
430 |
struct kcopy_file *kf, pid_t pid, |
431 |
void __user *dst, |
432 |
unsigned long n) |
433 |
{ |
434 |
struct page **pages; |
435 |
const int pages_len = PAGE_SIZE / sizeof(struct page *); |
436 |
int ret = 0; |
437 |
|
438 |
pages = (struct page **) __get_free_page(GFP_KERNEL); |
439 |
if (!pages) { |
440 |
ret = -ENOMEM; |
441 |
goto bail; |
442 |
} |
443 |
|
444 |
while (n) { |
445 |
const unsigned long doff = (unsigned long) dst & ~PAGE_MASK; |
446 |
const unsigned long dpages_left = |
447 |
(doff + n + PAGE_SIZE - 1) >> PAGE_SHIFT; |
448 |
const unsigned long dpages_cp = |
449 |
min_t(unsigned long, dpages_left, pages_len); |
450 |
const unsigned long dbytes = |
451 |
PAGE_SIZE - doff + (dpages_cp - 1) * PAGE_SIZE; |
452 |
const unsigned long nbytes = min_t(unsigned long, dbytes, n); |
453 |
|
454 |
ret = kcopy_get_pages(kf, pid, pages, dst, 1, dpages_cp); |
455 |
if (unlikely(ret)) |
456 |
goto bail_free; |
457 |
|
458 |
ret = kcopy_copy_pages_from_user((void __user *) src, |
459 |
pages, doff, nbytes); |
460 |
kcopy_put_pages(pages, dpages_cp); |
461 |
if (ret) |
462 |
goto bail_free; |
463 |
|
464 |
dst = (char *) dst + nbytes; |
465 |
src = (char *) src + nbytes; |
466 |
|
467 |
n -= nbytes; |
468 |
} |
469 |
|
470 |
bail_free: |
471 |
free_page((unsigned long) pages); |
472 |
bail: |
473 |
return ret; |
474 |
} |
475 |
|
476 |
static int kcopy_do_get(struct kcopy_map_entry *kme, pid_t pid, |
477 |
const void __user *src, void __user *dst, |
478 |
unsigned long n) |
479 |
{ |
480 |
struct kcopy_file *kf = kme->file; |
481 |
int ret = 0; |
482 |
|
483 |
if (n == 0) { |
484 |
ret = -EINVAL; |
485 |
goto bail; |
486 |
} |
487 |
|
488 |
ret = kcopy_copy_to_user(dst, kf, pid, (void __user *) src, n); |
489 |
|
490 |
bail: |
491 |
return ret; |
492 |
} |
493 |
|
494 |
static int kcopy_do_put(struct kcopy_map_entry *kme, const void __user *src, |
495 |
pid_t pid, void __user *dst, |
496 |
unsigned long n) |
497 |
{ |
498 |
struct kcopy_file *kf = kme->file; |
499 |
int ret = 0; |
500 |
|
501 |
if (n == 0) { |
502 |
ret = -EINVAL; |
503 |
goto bail; |
504 |
} |
505 |
|
506 |
ret = kcopy_copy_from_user(src, kf, pid, (void __user *) dst, n); |
507 |
|
508 |
bail: |
509 |
return ret; |
510 |
} |
511 |
|
512 |
static int kcopy_do_abi(u32 __user *dst) |
513 |
{ |
514 |
u32 val = KCOPY_ABI; |
515 |
int err; |
516 |
|
517 |
err = put_user(val, dst); |
518 |
if (err) |
519 |
return -EFAULT; |
520 |
|
521 |
return 0; |
522 |
} |
523 |
|
524 |
ssize_t kcopy_write(struct file *filp, const char __user *data, size_t cnt, |
525 |
loff_t *o) |
526 |
{ |
527 |
struct kcopy_map_entry *kme = filp->private_data; |
528 |
struct kcopy_syscall ks; |
529 |
int err = 0; |
530 |
const void __user *src; |
531 |
void __user *dst; |
532 |
unsigned long n; |
533 |
|
534 |
if (cnt != sizeof(struct kcopy_syscall)) { |
535 |
err = -EINVAL; |
536 |
goto bail; |
537 |
} |
538 |
|
539 |
err = copy_from_user(&ks, data, cnt); |
540 |
if (unlikely(err)) |
541 |
goto bail; |
542 |
|
543 |
src = kcopy_syscall_src(&ks); |
544 |
dst = kcopy_syscall_dst(&ks); |
545 |
n = kcopy_syscall_n(&ks); |
546 |
if (ks.tag == KCOPY_GET_SYSCALL) |
547 |
err = kcopy_do_get(kme, ks.pid, src, dst, n); |
548 |
else if (ks.tag == KCOPY_PUT_SYSCALL) |
549 |
err = kcopy_do_put(kme, src, ks.pid, dst, n); |
550 |
else if (ks.tag == KCOPY_ABI_SYSCALL) |
551 |
err = kcopy_do_abi(dst); |
552 |
else |
553 |
err = -EINVAL; |
554 |
|
555 |
bail: |
556 |
return err ? err : cnt; |
557 |
} |
558 |
|
559 |
static const struct file_operations kcopy_fops = { |
560 |
.owner = THIS_MODULE, |
561 |
.open = kcopy_open, |
562 |
.release = kcopy_release, |
563 |
.flush = kcopy_flush, |
564 |
.write = kcopy_write, |
565 |
}; |
566 |
|
567 |
static int __init kcopy_init(void) |
568 |
{ |
569 |
int ret; |
570 |
const char *name = "kcopy"; |
571 |
int i; |
572 |
int ninit = 0; |
573 |
|
574 |
mutex_init(&kcopy_dev.open_lock); |
575 |
|
576 |
ret = alloc_chrdev_region(&kcopy_dev.dev, 0, KCOPY_MAX_MINORS, name); |
577 |
if (ret) |
578 |
goto bail; |
579 |
|
580 |
kcopy_dev.class = class_create(THIS_MODULE, (char *) name); |
581 |
|
582 |
if (IS_ERR(kcopy_dev.class)) { |
583 |
ret = PTR_ERR(kcopy_dev.class); |
584 |
printk(KERN_ERR "kcopy: Could not create " |
585 |
"device class (err %d)\n", -ret); |
586 |
goto bail_chrdev; |
587 |
} |
588 |
|
589 |
cdev_init(&kcopy_dev.cdev, &kcopy_fops); |
590 |
ret = cdev_add(&kcopy_dev.cdev, kcopy_dev.dev, KCOPY_MAX_MINORS); |
591 |
if (ret < 0) { |
592 |
printk(KERN_ERR "kcopy: Could not add cdev (err %d)\n", |
593 |
-ret); |
594 |
goto bail_class; |
595 |
} |
596 |
|
597 |
for (i = 0; i < KCOPY_MAX_MINORS; i++) { |
598 |
char devname[8]; |
599 |
const int minor = MINOR(kcopy_dev.dev) + i; |
600 |
const dev_t dev = MKDEV(MAJOR(kcopy_dev.dev), minor); |
601 |
|
602 |
snprintf(devname, sizeof(devname), "kcopy%02d", i); |
603 |
kcopy_dev.devp[i] = |
604 |
device_create(kcopy_dev.class, NULL, |
605 |
dev, NULL, devname); |
606 |
|
607 |
if (IS_ERR(kcopy_dev.devp[i])) { |
608 |
ret = PTR_ERR(kcopy_dev.devp[i]); |
609 |
printk(KERN_ERR "kcopy: Could not create " |
610 |
"devp %d (err %d)\n", i, -ret); |
611 |
goto bail_cdev_add; |
612 |
} |
613 |
|
614 |
ninit++; |
615 |
} |
616 |
|
617 |
ret = 0; |
618 |
goto bail; |
619 |
|
620 |
bail_cdev_add: |
621 |
for (i = 0; i < ninit; i++) |
622 |
device_unregister(kcopy_dev.devp[i]); |
623 |
|
624 |
cdev_del(&kcopy_dev.cdev); |
625 |
bail_class: |
626 |
class_destroy(kcopy_dev.class); |
627 |
bail_chrdev: |
628 |
unregister_chrdev_region(kcopy_dev.dev, KCOPY_MAX_MINORS); |
629 |
bail: |
630 |
return ret; |
631 |
} |
632 |
|
633 |
static void __exit kcopy_fini(void) |
634 |
{ |
635 |
int i; |
636 |
|
637 |
for (i = 0; i < KCOPY_MAX_MINORS; i++) |
638 |
device_unregister(kcopy_dev.devp[i]); |
639 |
|
640 |
cdev_del(&kcopy_dev.cdev); |
641 |
class_destroy(kcopy_dev.class); |
642 |
unregister_chrdev_region(kcopy_dev.dev, KCOPY_MAX_MINORS); |
643 |
} |
644 |
|
645 |
module_init(kcopy_init); |
646 |
module_exit(kcopy_fini); |