All of lore.kernel.org
 help / color / mirror / Atom feed
From: Andi Kleen <andi@firstfloor.org>
To: viro@zeniv.linux.org.uk
Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org,
	Andi Kleen <ak@linux.intel.com>
Subject: [PATCH 2/7] VFS: Do (nearly) lockless generic_file_llseek
Date: Thu, 15 Sep 2011 16:06:48 -0700	[thread overview]
Message-ID: <1316128013-21980-3-git-send-email-andi@firstfloor.org> (raw)
In-Reply-To: <1316128013-21980-1-git-send-email-andi@firstfloor.org>

From: Andi Kleen <ak@linux.intel.com>

The i_mutex lock use of generic _file_llseek hurts.  Independent processes
accessing the same file synchronize over a single lock, even though
they have no need for synchronization at all.

Under high utilization this can cause llseek to scale very poorly on larger
systems.

This patch does some rethinking of the llseek locking model:

First the 64bit f_pos is not necessarily atomic without locks
on 32bit systems. This can already cause races with read() today.
This was discussed on linux-kernel in the past and deemed acceptable.
The patch does not change that.

Let's look at the different seek variants:

SEEK_SET: Doesn't really need any locking.
If there's a race one writer wins, the other loses.

For 32bit the non atomic update races against read()
stay the same. Without a lock they can also happen
against write() now.  The read() race was deemed
acceptable in past discussions, and I think if it's
ok for read it's ok for write too.

=> Don't need a lock.

SEEK_END: This behaves like SEEK_SET plus it reads
the maximum size too. Reading the maximum size would have the
32bit atomic problem. But luckily we already have a way to read
the maximum size without locking (i_size_read), so we
can just use that instead.

Without i_mutex there is no synchronization with write() anymore,
however since the write() update is atomic on 64bit it just behaves
like another racy SEEK_SET.  On non atomic 32bit it's the same
as SEEK_SET.

=> Don't need a lock, but need to use i_size_read()

SEEK_CUR: This has a read-modify-write race window
on the same file. One could argue that any application
doing unsynchronized seeks on the same file is already broken.
But for the sake of not adding a regression here I'm
using the file->f_lock to synchronize this. Using this
lock is much better than the inode mutex because it doesn't
synchronize between processes.

=> So still need a lock, but can use a cheap local one.

This patch implements this new scheme in generic_file_llseek.
I dropped generic_file_llseek_unlocked and changed all callers.

Signed-off-by: Andi Kleen <ak@linux.intel.com>
---
 fs/btrfs/file.c    |    2 +-
 fs/cifs/cifsfs.c   |    2 +-
 fs/gfs2/file.c     |    4 ++--
 fs/nfs/file.c      |    5 +++--
 fs/read_write.c    |   33 ++++++---------------------------
 include/linux/fs.h |    2 --
 6 files changed, 13 insertions(+), 35 deletions(-)

diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 7ec0a24..62f56d4 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1813,7 +1813,7 @@ static loff_t btrfs_file_llseek(struct file *file, loff_t offset, int origin)
 	switch (origin) {
 	case SEEK_END:
 	case SEEK_CUR:
-		offset = generic_file_llseek_unlocked(file, offset, origin);
+		offset = generic_file_llseek(file, offset, origin);
 		goto out;
 	case SEEK_DATA:
 	case SEEK_HOLE:
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index f93eb94..e43641b 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -721,7 +721,7 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int origin)
 		if (rc < 0)
 			return (loff_t)rc;
 	}
-	return generic_file_llseek_unlocked(file, offset, origin);
+	return generic_file_llseek(file, offset, origin);
 }
 
 static int cifs_setlease(struct file *file, long arg, struct file_lock **lease)
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index edeb9e8..fe6bc02 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -63,11 +63,11 @@ static loff_t gfs2_llseek(struct file *file, loff_t offset, int origin)
 		error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY,
 					   &i_gh);
 		if (!error) {
-			error = generic_file_llseek_unlocked(file, offset, origin);
+			error = generic_file_llseek(file, offset, origin);
 			gfs2_glock_dq_uninit(&i_gh);
 		}
 	} else
-		error = generic_file_llseek_unlocked(file, offset, origin);
+		error = generic_file_llseek(file, offset, origin);
 
 	return error;
 }
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 28b8c3f..12623ab 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -198,11 +198,12 @@ static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin)
 		if (retval < 0)
 			return (loff_t)retval;
 
+		/* AK: should drop this lock. Unlikely to be needed. */
 		spin_lock(&inode->i_lock);
-		loff = generic_file_llseek_unlocked(filp, offset, origin);
+		loff = generic_file_llseek(filp, offset, origin);
 		spin_unlock(&inode->i_lock);
 	} else
-		loff = generic_file_llseek_unlocked(filp, offset, origin);
+		loff = generic_file_llseek(filp, offset, origin);
 	return loff;
 }
 
diff --git a/fs/read_write.c b/fs/read_write.c
index 179f1c3..24f0001 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -36,22 +36,23 @@ static inline int unsigned_offsets(struct file *file)
 }
 
 /**
- * generic_file_llseek_unlocked - lockless generic llseek implementation
+ * generic_file_llseek - generic llseek implementation for regular files
  * @file:	file structure to seek on
  * @offset:	file offset to seek to
  * @origin:	type of seek
  *
- * Updates the file offset to the value specified by @offset and @origin.
- * Locking must be provided by the caller.
+ * This is a generic implemenation of ->llseek useable for all normal local
+ * filesystems.  It just updates the file offset to the value specified by
+ * @offset and @origin under i_mutex.
  */
 loff_t
-generic_file_llseek_unlocked(struct file *file, loff_t offset, int origin)
+generic_file_llseek(struct file *file, loff_t offset, int origin)
 {
 	struct inode *inode = file->f_mapping->host;
 
 	switch (origin) {
 	case SEEK_END:
-		offset += inode->i_size;
+		offset += i_size_read(inode);
 		break;
 	case SEEK_CUR:
 		/*
@@ -96,28 +97,6 @@ generic_file_llseek_unlocked(struct file *file, loff_t offset, int origin)
 
 	return offset;
 }
-EXPORT_SYMBOL(generic_file_llseek_unlocked);
-
-/**
- * generic_file_llseek - generic llseek implementation for regular files
- * @file:	file structure to seek on
- * @offset:	file offset to seek to
- * @origin:	type of seek
- *
- * This is a generic implemenation of ->llseek useable for all normal local
- * filesystems.  It just updates the file offset to the value specified by
- * @offset and @origin under i_mutex.
- */
-loff_t generic_file_llseek(struct file *file, loff_t offset, int origin)
-{
-	loff_t rval;
-
-	mutex_lock(&file->f_dentry->d_inode->i_mutex);
-	rval = generic_file_llseek_unlocked(file, offset, origin);
-	mutex_unlock(&file->f_dentry->d_inode->i_mutex);
-
-	return rval;
-}
 EXPORT_SYMBOL(generic_file_llseek);
 
 /**
diff --git a/include/linux/fs.h b/include/linux/fs.h
index c2bd68f..d9efdf7 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2399,8 +2399,6 @@ file_ra_state_init(struct file_ra_state *ra, struct address_space *mapping);
 extern loff_t noop_llseek(struct file *file, loff_t offset, int origin);
 extern loff_t no_llseek(struct file *file, loff_t offset, int origin);
 extern loff_t generic_file_llseek(struct file *file, loff_t offset, int origin);
-extern loff_t generic_file_llseek_unlocked(struct file *file, loff_t offset,
-			int origin);
 extern int generic_file_open(struct inode * inode, struct file * filp);
 extern int nonseekable_open(struct inode * inode, struct file * filp);
 
-- 
1.7.4.4


  parent reply	other threads:[~2011-09-15 23:08 UTC|newest]

Thread overview: 55+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-09-15 23:06 Improve lseek scalability v3 Andi Kleen
2011-09-15 23:06 ` [PATCH 1/7] BTRFS: Fix lseek return value for error Andi Kleen
2011-09-15 23:47   ` Thomas Gleixner
2011-09-16 15:48   ` Christoph Hellwig
2011-09-16 16:38     ` Andi Kleen
2011-09-17  6:10     ` Jeff Liu
2011-09-17 23:03       ` Andreas Dilger
2011-09-18  1:46         ` Andi Kleen
2011-09-18  7:29           ` Jeff Liu
2011-09-18  8:42             ` Marco Stornelli
2011-09-18 10:33               ` Jeff liu
2011-09-18 10:33                 ` Jeff liu
2011-09-18 14:55                 ` Chris Mason
2011-09-18 14:55                   ` Chris Mason
2011-09-18 14:55                   ` Chris Mason
2011-09-19 17:52                   ` Andi Kleen
2011-09-19 19:30                     ` Chris Mason
2011-09-19 19:59                       ` Andi Kleen
2011-09-19 22:55                         ` Chris Mason
2011-09-15 23:06 ` Andi Kleen [this message]
2011-09-15 23:06 ` [PATCH 3/7] VFS: Make generic lseek lockless safe Andi Kleen
2011-09-15 23:06 ` [PATCH 4/7] VFS: Add generic_file_llseek_size Andi Kleen
2011-09-16 15:50   ` Christoph Hellwig
2011-09-15 23:06 ` [PATCH 5/7] LSEEK: EXT4: Replace cut'n'pasted llseek code with generic_file_llseek_size Andi Kleen
2011-09-15 23:06 ` [PATCH 6/7] LSEEK: NFS: Drop unnecessary locking in llseek Andi Kleen
2011-09-15 23:06 ` [PATCH 7/7] LSEEK: BTRFS: Avoid i_mutex for SEEK_{CUR,SET,END} Andi Kleen
2011-09-16 13:00 ` Improve lseek scalability v3 Matthew Wilcox
2011-09-16 13:19   ` Josef Bacik
2011-09-16 14:16   ` Andres Freund
2011-09-16 14:23     ` Andi Kleen
2011-09-16 14:41       ` Andres Freund
2011-09-16 15:36     ` Matthew Wilcox
2011-09-16 17:27       ` Andres Freund
2011-09-16 17:39         ` [HACKERS] " Alvaro Herrera
2011-09-16 17:39           ` Alvaro Herrera
2011-09-16 17:50           ` [HACKERS] " Andi Kleen
2011-09-16 20:08         ` Benjamin LaHaise
2011-09-16 21:02           ` Andres Freund
2011-09-16 21:05             ` [HACKERS] " Andres Freund
2011-09-16 22:44           ` Greg Stark
2011-09-19 12:31           ` [HACKERS] " Stephen Frost
2011-09-19 12:31             ` Stephen Frost
2011-09-19 13:25             ` [HACKERS] " Matthew Wilcox
2011-09-20  7:18               ` Marco Stornelli
2011-09-20  7:18                 ` Marco Stornelli
2011-09-19 13:30             ` Robert Haas
2011-09-16 14:26   ` Andres Freund
2011-10-01 20:46 ` Andres Freund
2011-10-01 20:49   ` [PATCH 1/2] LSEEK: BTRFS: Avoid i_mutex for SEEK_{CUR,SET,END} Andres Freund
2011-11-02  8:29     ` Christoph Hellwig
2011-11-05 15:27       ` Chris Mason
2012-03-07 17:16         ` Andres Freund
2011-10-01 20:50   ` [PATCH 2/2] btrfs: Don't have multiple paths to error out in btrfs_file_llseek Andres Freund
2011-10-02  5:28   ` Improve lseek scalability v3 Andi Kleen
  -- strict thread matches above, loose matches on Subject: below --
2011-08-22 20:49 Improve lseek scalability Andi Kleen
2011-08-22 20:49 ` [PATCH 2/7] VFS: Do (nearly) lockless generic_file_llseek Andi Kleen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1316128013-21980-3-git-send-email-andi@firstfloor.org \
    --to=andi@firstfloor.org \
    --cc=ak@linux.intel.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=viro@zeniv.linux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.