Git-commit: a209dfc7b0d94bd6fa94553c097836a2e6d0f0ba
From: Eric Dumazet <eric.dumazet@gmail.com>
Acked-by: Goldwyn Rodrigues <rgoldwyn@suse.com>
Subject: vfs: dont chain pipe/anon/socket on superblock s_inodes list
Patch-mainline: 3.1-rc1
References: bnc#789703
    
Workloads using pipes and sockets hit inode_sb_list_lock contention.
    
superblock s_inodes list is needed for quota, dirty, pagecache and
fsnotify management. pipe/anon/socket fs are clearly not candidates for
these.
    
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>


Index: linux-3.0-SLES11-SP2/fs/anon_inodes.c
===================================================================
--- linux-3.0-SLES11-SP2.orig/fs/anon_inodes.c	2012-11-21 10:39:44.000000000 -0600
+++ linux-3.0-SLES11-SP2/fs/anon_inodes.c	2012-11-21 10:40:31.000000000 -0600
@@ -187,7 +187,7 @@ EXPORT_SYMBOL_GPL(anon_inode_getfd);
  */
 static struct inode *anon_inode_mkinode(void)
 {
-	struct inode *inode = new_inode(anon_inode_mnt->mnt_sb);
+	struct inode *inode = new_inode_pseudo(anon_inode_mnt->mnt_sb);
 
 	if (!inode)
 		return ERR_PTR(-ENOMEM);
Index: linux-3.0-SLES11-SP2/fs/inode.c
===================================================================
--- linux-3.0-SLES11-SP2.orig/fs/inode.c	2012-10-25 08:30:50.000000000 -0500
+++ linux-3.0-SLES11-SP2/fs/inode.c	2012-11-21 10:40:31.000000000 -0600
@@ -368,9 +368,11 @@ EXPORT_SYMBOL_GPL(inode_sb_list_add);
 
 static inline void inode_sb_list_del(struct inode *inode)
 {
-	spin_lock(&inode_sb_list_lock);
-	list_del_init(&inode->i_sb_list);
-	spin_unlock(&inode_sb_list_lock);
+	if (!list_empty(&inode->i_sb_list)) {
+		spin_lock(&inode_sb_list_lock);
+		list_del_init(&inode->i_sb_list);
+		spin_unlock(&inode_sb_list_lock);
+	}
 }
 
 static unsigned long hash(struct super_block *sb, unsigned long hashval)
@@ -842,6 +844,29 @@ unsigned int get_next_ino(void)
 EXPORT_SYMBOL(get_next_ino);
 
 /**
+ *	new_inode_pseudo 	- obtain an inode
+ *	@sb: superblock
+ *
+ *	Allocates a new inode for given superblock.
+ *	Inode wont be chained in superblock s_inodes list
+ *	This means :
+ *	- fs can't be unmount
+ *	- quotas, fsnotify, writeback can't work
+ */
+struct inode *new_inode_pseudo(struct super_block *sb)
+{
+	struct inode *inode = alloc_inode(sb);
+
+	if (inode) {
+		spin_lock(&inode->i_lock);
+		inode->i_state = 0;
+		spin_unlock(&inode->i_lock);
+		INIT_LIST_HEAD(&inode->i_sb_list);
+	}
+	return inode;
+}
+
+/**
  *	new_inode 	- obtain an inode
  *	@sb: superblock
  *
@@ -859,13 +884,9 @@ struct inode *new_inode(struct super_blo
 
 	spin_lock_prefetch(&inode_sb_list_lock);
 
-	inode = alloc_inode(sb);
-	if (inode) {
-		spin_lock(&inode->i_lock);
-		inode->i_state = 0;
-		spin_unlock(&inode->i_lock);
+	inode = new_inode_pseudo(sb);
+	if (inode)
 		inode_sb_list_add(inode);
-	}
 	return inode;
 }
 EXPORT_SYMBOL(new_inode);
Index: linux-3.0-SLES11-SP2/fs/pipe.c
===================================================================
--- linux-3.0-SLES11-SP2.orig/fs/pipe.c	2012-11-21 10:39:44.000000000 -0600
+++ linux-3.0-SLES11-SP2/fs/pipe.c	2012-11-21 10:40:31.000000000 -0600
@@ -975,7 +975,7 @@ static const struct dentry_operations pi
 
 static struct inode * get_pipe_inode(void)
 {
-	struct inode *inode = new_inode(pipe_mnt->mnt_sb);
+	struct inode *inode = new_inode_pseudo(pipe_mnt->mnt_sb);
 	struct pipe_inode_info *pipe;
 
 	if (!inode)
Index: linux-3.0-SLES11-SP2/include/linux/fs.h
===================================================================
--- linux-3.0-SLES11-SP2.orig/include/linux/fs.h	2012-11-21 10:39:44.000000000 -0600
+++ linux-3.0-SLES11-SP2/include/linux/fs.h	2012-11-21 10:41:11.000000000 -0600
@@ -2317,6 +2317,7 @@ extern void __iget(struct inode * inode)
 extern void iget_failed(struct inode *);
 extern void end_writeback(struct inode *);
 extern void __destroy_inode(struct inode *);
+extern struct inode *new_inode_pseudo(struct super_block *sb);
 extern struct inode *new_inode(struct super_block *);
 extern void free_inode_nonrcu(struct inode *inode);
 extern int should_remove_suid(struct dentry *);
Index: linux-3.0-SLES11-SP2/net/socket.c
===================================================================
--- linux-3.0-SLES11-SP2.orig/net/socket.c	2012-11-21 10:39:44.000000000 -0600
+++ linux-3.0-SLES11-SP2/net/socket.c	2012-11-21 10:40:31.000000000 -0600
@@ -467,7 +467,7 @@ static struct socket *sock_alloc(void)
 	struct inode *inode;
 	struct socket *sock;
 
-	inode = new_inode(sock_mnt->mnt_sb);
+	inode = new_inode_pseudo(sock_mnt->mnt_sb);
 	if (!inode)
 		return NULL;
 
