diff -upN reference/fs/fcntl.c current/fs/fcntl.c --- reference/fs/fcntl.c 2004-04-07 14:54:28.000000000 -0700 +++ current/fs/fcntl.c 2004-04-09 11:53:04.000000000 -0700 @@ -537,9 +537,19 @@ int send_sigurg(struct fown_struct *fown return ret; } -static rwlock_t fasync_lock = RW_LOCK_UNLOCKED; +static spinlock_t fasync_lock = SPIN_LOCK_UNLOCKED; static kmem_cache_t *fasync_cache; +struct fasync_rcu_struct { + struct fasync_struct data; + struct rcu_head rcu; +}; + +static void fasync_free(void *data) +{ + kmem_cache_free(fasync_cache, data); +} + /* * fasync_helper() is used by some character device drivers (mainly mice) * to set up the fasync queue. It returns negative on error, 0 if it did @@ -548,7 +558,7 @@ static kmem_cache_t *fasync_cache; int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp) { struct fasync_struct *fa, **fp; - struct fasync_struct *new = NULL; + struct fasync_rcu_struct *new = NULL; int result = 0; if (on) { @@ -556,15 +566,23 @@ int fasync_helper(int fd, struct file * if (!new) return -ENOMEM; } - write_lock_irq(&fasync_lock); + spin_lock(&fasync_lock); for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) { if (fa->fa_file == filp) { if(on) { + /* RCU violation: + * We are modifying a struct that's visible by + * readers. If there is a fasync notification + * right now, then it could go to either the + * old or the new fd. Shouldn't matter. + * Manfred + */ fa->fa_fd = fd; kmem_cache_free(fasync_cache, new); } else { *fp = fa->fa_next; - kmem_cache_free(fasync_cache, fa); + new = container_of(fa, struct fasync_rcu_struct, data); + call_rcu(&new->rcu, fasync_free, new); result = 1; } goto out; @@ -572,15 +590,16 @@ int fasync_helper(int fd, struct file * } if (on) { - new->magic = FASYNC_MAGIC; - new->fa_file = filp; - new->fa_fd = fd; - new->fa_next = *fapp; - *fapp = new; + new->data.magic = FASYNC_MAGIC; + new->data.fa_file = filp; + new->data.fa_fd = fd; + new->data.fa_next = *fapp; + smp_wmb(); + *fapp = &new->data; result = 1; } out: - write_unlock_irq(&fasync_lock); + spin_unlock(&fasync_lock); return result; } @@ -590,7 +609,8 @@ void __kill_fasync(struct fasync_struct { while (fa) { struct fown_struct * fown; - if (fa->magic != FASYNC_MAGIC) { + read_barrier_depends(); + if (unlikely(fa->magic != FASYNC_MAGIC)) { printk(KERN_ERR "kill_fasync: bad magic number in " "fasync_struct!\n"); return; @@ -613,10 +633,10 @@ void kill_fasync(struct fasync_struct ** * the list is empty. */ if (*fp) { - read_lock(&fasync_lock); + rcu_read_lock(); /* reread *fp after obtaining the lock */ __kill_fasync(*fp, sig, band); - read_unlock(&fasync_lock); + rcu_read_unlock(); } } @@ -625,7 +645,7 @@ EXPORT_SYMBOL(kill_fasync); static int __init fasync_init(void) { fasync_cache = kmem_cache_create("fasync_cache", - sizeof(struct fasync_struct), 0, 0, NULL, NULL); + sizeof(struct fasync_rcu_struct), 0, 0, NULL, NULL); if (!fasync_cache) panic("cannot create fasync slab cache"); return 0;