Commit e80c14e1 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'fasync-helper'

* fasync-helper:
  fasync: split 'fasync_helper()' into separate add/remove functions
parents 7284ce6c 53281b6d
...@@ -618,60 +618,90 @@ static DEFINE_RWLOCK(fasync_lock); ...@@ -618,60 +618,90 @@ static DEFINE_RWLOCK(fasync_lock);
static struct kmem_cache *fasync_cache __read_mostly; static struct kmem_cache *fasync_cache __read_mostly;
/* /*
* fasync_helper() is used by almost all character device drivers * Remove a fasync entry. If successfully removed, return
* to set up the fasync queue. It returns negative on error, 0 if it did * positive and clear the FASYNC flag. If no entry exists,
* no changes and positive if it added/deleted the entry. * do nothing and return 0.
*
* NOTE! It is very important that the FASYNC flag always
* match the state "is the filp on a fasync list".
*
* We always take the 'filp->f_lock', in since fasync_lock
* needs to be irq-safe.
*/ */
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp) static int fasync_remove_entry(struct file *filp, struct fasync_struct **fapp)
{ {
struct fasync_struct *fa, **fp; struct fasync_struct *fa, **fp;
struct fasync_struct *new = NULL;
int result = 0; int result = 0;
if (on) { spin_lock(&filp->f_lock);
write_lock_irq(&fasync_lock);
for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {
if (fa->fa_file != filp)
continue;
*fp = fa->fa_next;
kmem_cache_free(fasync_cache, fa);
filp->f_flags &= ~FASYNC;
result = 1;
break;
}
write_unlock_irq(&fasync_lock);
spin_unlock(&filp->f_lock);
return result;
}
/*
* Add a fasync entry. Return negative on error, positive if
* added, and zero if did nothing but change an existing one.
*
* NOTE! It is very important that the FASYNC flag always
* match the state "is the filp on a fasync list".
*/
static int fasync_add_entry(int fd, struct file *filp, struct fasync_struct **fapp)
{
struct fasync_struct *new, *fa, **fp;
int result = 0;
new = kmem_cache_alloc(fasync_cache, GFP_KERNEL); new = kmem_cache_alloc(fasync_cache, GFP_KERNEL);
if (!new) if (!new)
return -ENOMEM; return -ENOMEM;
}
/*
* We need to take f_lock first since it's not an IRQ-safe
* lock.
*/
spin_lock(&filp->f_lock); spin_lock(&filp->f_lock);
write_lock_irq(&fasync_lock); write_lock_irq(&fasync_lock);
for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) { for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {
if (fa->fa_file == filp) { if (fa->fa_file != filp)
if(on) { continue;
fa->fa_fd = fd; fa->fa_fd = fd;
kmem_cache_free(fasync_cache, new); kmem_cache_free(fasync_cache, new);
} else {
*fp = fa->fa_next;
kmem_cache_free(fasync_cache, fa);
result = 1;
}
goto out; goto out;
} }
}
if (on) {
new->magic = FASYNC_MAGIC; new->magic = FASYNC_MAGIC;
new->fa_file = filp; new->fa_file = filp;
new->fa_fd = fd; new->fa_fd = fd;
new->fa_next = *fapp; new->fa_next = *fapp;
*fapp = new; *fapp = new;
result = 1; result = 1;
}
out:
if (on)
filp->f_flags |= FASYNC; filp->f_flags |= FASYNC;
else
filp->f_flags &= ~FASYNC; out:
write_unlock_irq(&fasync_lock); write_unlock_irq(&fasync_lock);
spin_unlock(&filp->f_lock); spin_unlock(&filp->f_lock);
return result; return result;
} }
/*
* fasync_helper() is used by almost all character device drivers
* to set up the fasync queue, and for regular files by the file
* lease code. It returns negative on error, 0 if it did no changes
* and positive if it added/deleted the entry.
*/
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
{
if (!on)
return fasync_remove_entry(filp, fapp);
return fasync_add_entry(fd, filp, fapp);
}
EXPORT_SYMBOL(fasync_helper); EXPORT_SYMBOL(fasync_helper);
void __kill_fasync(struct fasync_struct *fa, int sig, int band) void __kill_fasync(struct fasync_struct *fa, int sig, int band)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment