Browse Source

Merge tag 'locks-v4.18-1' of git://git.kernel.org/pub/scm/linux/kernel/git/jlayton/linux

Pull fasync fix from Jeff Layton:
 "Just a single fix for a deadlock in the fasync handling code that
  Kirill observed while testing.

  The fix is to change the fa_lock to be rwlock_t, and use a read lock
  in kill_fasync_rcu"

* tag 'locks-v4.18-1' of git://git.kernel.org/pub/scm/linux/kernel/git/jlayton/linux:
  fasync: Fix deadlock between task-context and interrupt-context kill_fasync()
Linus Torvalds 7 years ago
parent
commit
9214407d12
2 changed files with 8 additions and 9 deletions
  1. 7 8
      fs/fcntl.c
  2. 1 1
      include/linux/fs.h

+ 7 - 8
fs/fcntl.c

@@ -871,9 +871,9 @@ int fasync_remove_entry(struct file *filp, struct fasync_struct **fapp)
 		if (fa->fa_file != filp)
 		if (fa->fa_file != filp)
 			continue;
 			continue;
 
 
-		spin_lock_irq(&fa->fa_lock);
+		write_lock_irq(&fa->fa_lock);
 		fa->fa_file = NULL;
 		fa->fa_file = NULL;
-		spin_unlock_irq(&fa->fa_lock);
+		write_unlock_irq(&fa->fa_lock);
 
 
 		*fp = fa->fa_next;
 		*fp = fa->fa_next;
 		call_rcu(&fa->fa_rcu, fasync_free_rcu);
 		call_rcu(&fa->fa_rcu, fasync_free_rcu);
@@ -918,13 +918,13 @@ struct fasync_struct *fasync_insert_entry(int fd, struct file *filp, struct fasy
 		if (fa->fa_file != filp)
 		if (fa->fa_file != filp)
 			continue;
 			continue;
 
 
-		spin_lock_irq(&fa->fa_lock);
+		write_lock_irq(&fa->fa_lock);
 		fa->fa_fd = fd;
 		fa->fa_fd = fd;
-		spin_unlock_irq(&fa->fa_lock);
+		write_unlock_irq(&fa->fa_lock);
 		goto out;
 		goto out;
 	}
 	}
 
 
-	spin_lock_init(&new->fa_lock);
+	rwlock_init(&new->fa_lock);
 	new->magic = FASYNC_MAGIC;
 	new->magic = FASYNC_MAGIC;
 	new->fa_file = filp;
 	new->fa_file = filp;
 	new->fa_fd = fd;
 	new->fa_fd = fd;
@@ -987,14 +987,13 @@ static void kill_fasync_rcu(struct fasync_struct *fa, int sig, int band)
 {
 {
 	while (fa) {
 	while (fa) {
 		struct fown_struct *fown;
 		struct fown_struct *fown;
-		unsigned long flags;
 
 
 		if (fa->magic != FASYNC_MAGIC) {
 		if (fa->magic != FASYNC_MAGIC) {
 			printk(KERN_ERR "kill_fasync: bad magic number in "
 			printk(KERN_ERR "kill_fasync: bad magic number in "
 			       "fasync_struct!\n");
 			       "fasync_struct!\n");
 			return;
 			return;
 		}
 		}
-		spin_lock_irqsave(&fa->fa_lock, flags);
+		read_lock(&fa->fa_lock);
 		if (fa->fa_file) {
 		if (fa->fa_file) {
 			fown = &fa->fa_file->f_owner;
 			fown = &fa->fa_file->f_owner;
 			/* Don't send SIGURG to processes which have not set a
 			/* Don't send SIGURG to processes which have not set a
@@ -1003,7 +1002,7 @@ static void kill_fasync_rcu(struct fasync_struct *fa, int sig, int band)
 			if (!(sig == SIGURG && fown->signum == 0))
 			if (!(sig == SIGURG && fown->signum == 0))
 				send_sigio(fown, fa->fa_fd, band);
 				send_sigio(fown, fa->fa_fd, band);
 		}
 		}
-		spin_unlock_irqrestore(&fa->fa_lock, flags);
+		read_unlock(&fa->fa_lock);
 		fa = rcu_dereference(fa->fa_next);
 		fa = rcu_dereference(fa->fa_next);
 	}
 	}
 }
 }

+ 1 - 1
include/linux/fs.h

@@ -1250,7 +1250,7 @@ static inline int locks_lock_file_wait(struct file *filp, struct file_lock *fl)
 }
 }
 
 
 struct fasync_struct {
 struct fasync_struct {
-	spinlock_t		fa_lock;
+	rwlock_t		fa_lock;
 	int			magic;
 	int			magic;
 	int			fa_fd;
 	int			fa_fd;
 	struct fasync_struct	*fa_next; /* singly linked list */
 	struct fasync_struct	*fa_next; /* singly linked list */