diff options
Diffstat (limited to 'fs/fcntl.c')
| -rw-r--r-- | fs/fcntl.c | 102 |
1 files changed, 66 insertions, 36 deletions
diff --git a/fs/fcntl.c b/fs/fcntl.c index 2cf93ec40a67..97e01dc0d95f 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c | |||
| @@ -618,60 +618,90 @@ static DEFINE_RWLOCK(fasync_lock); | |||
| 618 | static struct kmem_cache *fasync_cache __read_mostly; | 618 | static struct kmem_cache *fasync_cache __read_mostly; |
| 619 | 619 | ||
| 620 | /* | 620 | /* |
| 621 | * fasync_helper() is used by almost all character device drivers | 621 | * Remove a fasync entry. If successfully removed, return |
| 622 | * to set up the fasync queue. It returns negative on error, 0 if it did | 622 | * positive and clear the FASYNC flag. If no entry exists, |
| 623 | * no changes and positive if it added/deleted the entry. | 623 | * do nothing and return 0. |
| 624 | * | ||
| 625 | * NOTE! It is very important that the FASYNC flag always | ||
| 626 | * match the state "is the filp on a fasync list". | ||
| 627 | * | ||
| 628 | * We always take the 'filp->f_lock', in since fasync_lock | ||
| 629 | * needs to be irq-safe. | ||
| 624 | */ | 630 | */ |
| 625 | int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp) | 631 | static int fasync_remove_entry(struct file *filp, struct fasync_struct **fapp) |
| 626 | { | 632 | { |
| 627 | struct fasync_struct *fa, **fp; | 633 | struct fasync_struct *fa, **fp; |
| 628 | struct fasync_struct *new = NULL; | ||
| 629 | int result = 0; | 634 | int result = 0; |
| 630 | 635 | ||
| 631 | if (on) { | 636 | spin_lock(&filp->f_lock); |
| 632 | new = kmem_cache_alloc(fasync_cache, GFP_KERNEL); | 637 | write_lock_irq(&fasync_lock); |
| 633 | if (!new) | 638 | for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) { |
| 634 | return -ENOMEM; | 639 | if (fa->fa_file != filp) |
| 640 | continue; | ||
| 641 | *fp = fa->fa_next; | ||
| 642 | kmem_cache_free(fasync_cache, fa); | ||
| 643 | filp->f_flags &= ~FASYNC; | ||
| 644 | result = 1; | ||
| 645 | break; | ||
| 635 | } | 646 | } |
| 647 | write_unlock_irq(&fasync_lock); | ||
| 648 | spin_unlock(&filp->f_lock); | ||
| 649 | return result; | ||
| 650 | } | ||
| 651 | |||
| 652 | /* | ||
| 653 | * Add a fasync entry. Return negative on error, positive if | ||
| 654 | * added, and zero if did nothing but change an existing one. | ||
| 655 | * | ||
| 656 | * NOTE! It is very important that the FASYNC flag always | ||
| 657 | * match the state "is the filp on a fasync list". | ||
| 658 | */ | ||
| 659 | static int fasync_add_entry(int fd, struct file *filp, struct fasync_struct **fapp) | ||
| 660 | { | ||
| 661 | struct fasync_struct *new, *fa, **fp; | ||
| 662 | int result = 0; | ||
| 663 | |||
| 664 | new = kmem_cache_alloc(fasync_cache, GFP_KERNEL); | ||
| 665 | if (!new) | ||
| 666 | return -ENOMEM; | ||
| 636 | 667 | ||
| 637 | /* | ||
| 638 | * We need to take f_lock first since it's not an IRQ-safe | ||
| 639 | * lock. | ||
| 640 | */ | ||
| 641 | spin_lock(&filp->f_lock); | 668 | spin_lock(&filp->f_lock); |
| 642 | write_lock_irq(&fasync_lock); | 669 | write_lock_irq(&fasync_lock); |
| 643 | for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) { | 670 | for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) { |
| 644 | if (fa->fa_file == filp) { | 671 | if (fa->fa_file != filp) |
| 645 | if(on) { | 672 | continue; |
| 646 | fa->fa_fd = fd; | 673 | fa->fa_fd = fd; |
| 647 | kmem_cache_free(fasync_cache, new); | 674 | kmem_cache_free(fasync_cache, new); |
| 648 | } else { | 675 | goto out; |
| 649 | *fp = fa->fa_next; | ||
| 650 | kmem_cache_free(fasync_cache, fa); | ||
| 651 | result = 1; | ||
| 652 | } | ||
| 653 | goto out; | ||
| 654 | } | ||
| 655 | } | 676 | } |
| 656 | 677 | ||
| 657 | if (on) { | 678 | new->magic = FASYNC_MAGIC; |
| 658 | new->magic = FASYNC_MAGIC; | 679 | new->fa_file = filp; |
| 659 | new->fa_file = filp; | 680 | new->fa_fd = fd; |
| 660 | new->fa_fd = fd; | 681 | new->fa_next = *fapp; |
| 661 | new->fa_next = *fapp; | 682 | *fapp = new; |
| 662 | *fapp = new; | 683 | result = 1; |
| 663 | result = 1; | 684 | filp->f_flags |= FASYNC; |
| 664 | } | 685 | |
| 665 | out: | 686 | out: |
| 666 | if (on) | ||
| 667 | filp->f_flags |= FASYNC; | ||
| 668 | else | ||
| 669 | filp->f_flags &= ~FASYNC; | ||
| 670 | write_unlock_irq(&fasync_lock); | 687 | write_unlock_irq(&fasync_lock); |
| 671 | spin_unlock(&filp->f_lock); | 688 | spin_unlock(&filp->f_lock); |
| 672 | return result; | 689 | return result; |
| 673 | } | 690 | } |
| 674 | 691 | ||
| 692 | /* | ||
| 693 | * fasync_helper() is used by almost all character device drivers | ||
| 694 | * to set up the fasync queue, and for regular files by the file | ||
| 695 | * lease code. It returns negative on error, 0 if it did no changes | ||
| 696 | * and positive if it added/deleted the entry. | ||
| 697 | */ | ||
| 698 | int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp) | ||
| 699 | { | ||
| 700 | if (!on) | ||
| 701 | return fasync_remove_entry(filp, fapp); | ||
| 702 | return fasync_add_entry(fd, filp, fapp); | ||
| 703 | } | ||
| 704 | |||
| 675 | EXPORT_SYMBOL(fasync_helper); | 705 | EXPORT_SYMBOL(fasync_helper); |
| 676 | 706 | ||
| 677 | void __kill_fasync(struct fasync_struct *fa, int sig, int band) | 707 | void __kill_fasync(struct fasync_struct *fa, int sig, int band) |
