diff options
Diffstat (limited to 'kernel/futex.c')
-rw-r--r-- | kernel/futex.c | 137 |
1 files changed, 125 insertions, 12 deletions
diff --git a/kernel/futex.c b/kernel/futex.c index c7130f86106c..ca05fe6a70b2 100644 --- a/kernel/futex.c +++ b/kernel/futex.c | |||
@@ -40,6 +40,7 @@ | |||
40 | #include <linux/pagemap.h> | 40 | #include <linux/pagemap.h> |
41 | #include <linux/syscalls.h> | 41 | #include <linux/syscalls.h> |
42 | #include <linux/signal.h> | 42 | #include <linux/signal.h> |
43 | #include <asm/futex.h> | ||
43 | 44 | ||
44 | #define FUTEX_HASHBITS (CONFIG_BASE_SMALL ? 4 : 8) | 45 | #define FUTEX_HASHBITS (CONFIG_BASE_SMALL ? 4 : 8) |
45 | 46 | ||
@@ -327,6 +328,118 @@ out: | |||
327 | } | 328 | } |
328 | 329 | ||
329 | /* | 330 | /* |
331 | * Wake up all waiters hashed on the physical page that is mapped | ||
332 | * to this virtual address: | ||
333 | */ | ||
334 | static int futex_wake_op(unsigned long uaddr1, unsigned long uaddr2, int nr_wake, int nr_wake2, int op) | ||
335 | { | ||
336 | union futex_key key1, key2; | ||
337 | struct futex_hash_bucket *bh1, *bh2; | ||
338 | struct list_head *head; | ||
339 | struct futex_q *this, *next; | ||
340 | int ret, op_ret, attempt = 0; | ||
341 | |||
342 | retryfull: | ||
343 | down_read(¤t->mm->mmap_sem); | ||
344 | |||
345 | ret = get_futex_key(uaddr1, &key1); | ||
346 | if (unlikely(ret != 0)) | ||
347 | goto out; | ||
348 | ret = get_futex_key(uaddr2, &key2); | ||
349 | if (unlikely(ret != 0)) | ||
350 | goto out; | ||
351 | |||
352 | bh1 = hash_futex(&key1); | ||
353 | bh2 = hash_futex(&key2); | ||
354 | |||
355 | retry: | ||
356 | if (bh1 < bh2) | ||
357 | spin_lock(&bh1->lock); | ||
358 | spin_lock(&bh2->lock); | ||
359 | if (bh1 > bh2) | ||
360 | spin_lock(&bh1->lock); | ||
361 | |||
362 | op_ret = futex_atomic_op_inuser(op, (int __user *)uaddr2); | ||
363 | if (unlikely(op_ret < 0)) { | ||
364 | int dummy; | ||
365 | |||
366 | spin_unlock(&bh1->lock); | ||
367 | if (bh1 != bh2) | ||
368 | spin_unlock(&bh2->lock); | ||
369 | |||
370 | /* futex_atomic_op_inuser needs to both read and write | ||
371 | * *(int __user *)uaddr2, but we can't modify it | ||
372 | * non-atomically. Therefore, if get_user below is not | ||
373 | * enough, we need to handle the fault ourselves, while | ||
374 | * still holding the mmap_sem. */ | ||
375 | if (attempt++) { | ||
376 | struct vm_area_struct * vma; | ||
377 | struct mm_struct *mm = current->mm; | ||
378 | |||
379 | ret = -EFAULT; | ||
380 | if (attempt >= 2 || | ||
381 | !(vma = find_vma(mm, uaddr2)) || | ||
382 | vma->vm_start > uaddr2 || | ||
383 | !(vma->vm_flags & VM_WRITE)) | ||
384 | goto out; | ||
385 | |||
386 | switch (handle_mm_fault(mm, vma, uaddr2, 1)) { | ||
387 | case VM_FAULT_MINOR: | ||
388 | current->min_flt++; | ||
389 | break; | ||
390 | case VM_FAULT_MAJOR: | ||
391 | current->maj_flt++; | ||
392 | break; | ||
393 | default: | ||
394 | goto out; | ||
395 | } | ||
396 | goto retry; | ||
397 | } | ||
398 | |||
399 | /* If we would have faulted, release mmap_sem, | ||
400 | * fault it in and start all over again. */ | ||
401 | up_read(¤t->mm->mmap_sem); | ||
402 | |||
403 | ret = get_user(dummy, (int __user *)uaddr2); | ||
404 | if (ret) | ||
405 | return ret; | ||
406 | |||
407 | goto retryfull; | ||
408 | } | ||
409 | |||
410 | head = &bh1->chain; | ||
411 | |||
412 | list_for_each_entry_safe(this, next, head, list) { | ||
413 | if (match_futex (&this->key, &key1)) { | ||
414 | wake_futex(this); | ||
415 | if (++ret >= nr_wake) | ||
416 | break; | ||
417 | } | ||
418 | } | ||
419 | |||
420 | if (op_ret > 0) { | ||
421 | head = &bh2->chain; | ||
422 | |||
423 | op_ret = 0; | ||
424 | list_for_each_entry_safe(this, next, head, list) { | ||
425 | if (match_futex (&this->key, &key2)) { | ||
426 | wake_futex(this); | ||
427 | if (++op_ret >= nr_wake2) | ||
428 | break; | ||
429 | } | ||
430 | } | ||
431 | ret += op_ret; | ||
432 | } | ||
433 | |||
434 | spin_unlock(&bh1->lock); | ||
435 | if (bh1 != bh2) | ||
436 | spin_unlock(&bh2->lock); | ||
437 | out: | ||
438 | up_read(¤t->mm->mmap_sem); | ||
439 | return ret; | ||
440 | } | ||
441 | |||
442 | /* | ||
330 | * Requeue all waiters hashed on one physical page to another | 443 | * Requeue all waiters hashed on one physical page to another |
331 | * physical page. | 444 | * physical page. |
332 | */ | 445 | */ |
@@ -673,23 +786,17 @@ static int futex_fd(unsigned long uaddr, int signal) | |||
673 | filp->f_mapping = filp->f_dentry->d_inode->i_mapping; | 786 | filp->f_mapping = filp->f_dentry->d_inode->i_mapping; |
674 | 787 | ||
675 | if (signal) { | 788 | if (signal) { |
676 | int err; | ||
677 | err = f_setown(filp, current->pid, 1); | 789 | err = f_setown(filp, current->pid, 1); |
678 | if (err < 0) { | 790 | if (err < 0) { |
679 | put_unused_fd(ret); | 791 | goto error; |
680 | put_filp(filp); | ||
681 | ret = err; | ||
682 | goto out; | ||
683 | } | 792 | } |
684 | filp->f_owner.signum = signal; | 793 | filp->f_owner.signum = signal; |
685 | } | 794 | } |
686 | 795 | ||
687 | q = kmalloc(sizeof(*q), GFP_KERNEL); | 796 | q = kmalloc(sizeof(*q), GFP_KERNEL); |
688 | if (!q) { | 797 | if (!q) { |
689 | put_unused_fd(ret); | 798 | err = -ENOMEM; |
690 | put_filp(filp); | 799 | goto error; |
691 | ret = -ENOMEM; | ||
692 | goto out; | ||
693 | } | 800 | } |
694 | 801 | ||
695 | down_read(¤t->mm->mmap_sem); | 802 | down_read(¤t->mm->mmap_sem); |
@@ -697,10 +804,8 @@ static int futex_fd(unsigned long uaddr, int signal) | |||
697 | 804 | ||
698 | if (unlikely(err != 0)) { | 805 | if (unlikely(err != 0)) { |
699 | up_read(¤t->mm->mmap_sem); | 806 | up_read(¤t->mm->mmap_sem); |
700 | put_unused_fd(ret); | ||
701 | put_filp(filp); | ||
702 | kfree(q); | 807 | kfree(q); |
703 | return err; | 808 | goto error; |
704 | } | 809 | } |
705 | 810 | ||
706 | /* | 811 | /* |
@@ -716,6 +821,11 @@ static int futex_fd(unsigned long uaddr, int signal) | |||
716 | fd_install(ret, filp); | 821 | fd_install(ret, filp); |
717 | out: | 822 | out: |
718 | return ret; | 823 | return ret; |
824 | error: | ||
825 | put_unused_fd(ret); | ||
826 | put_filp(filp); | ||
827 | ret = err; | ||
828 | goto out; | ||
719 | } | 829 | } |
720 | 830 | ||
721 | long do_futex(unsigned long uaddr, int op, int val, unsigned long timeout, | 831 | long do_futex(unsigned long uaddr, int op, int val, unsigned long timeout, |
@@ -740,6 +850,9 @@ long do_futex(unsigned long uaddr, int op, int val, unsigned long timeout, | |||
740 | case FUTEX_CMP_REQUEUE: | 850 | case FUTEX_CMP_REQUEUE: |
741 | ret = futex_requeue(uaddr, uaddr2, val, val2, &val3); | 851 | ret = futex_requeue(uaddr, uaddr2, val, val2, &val3); |
742 | break; | 852 | break; |
853 | case FUTEX_WAKE_OP: | ||
854 | ret = futex_wake_op(uaddr, uaddr2, val, val2, val3); | ||
855 | break; | ||
743 | default: | 856 | default: |
744 | ret = -ENOSYS; | 857 | ret = -ENOSYS; |
745 | } | 858 | } |