All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] Two patches for fs/super.c
@ 2010-03-24  5:12 NeilBrown
  2010-03-24  5:12 ` [PATCH 2/2] fs/super: remove S_BIAS NeilBrown
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: NeilBrown @ 2010-03-24  5:12 UTC (permalink / raw)
  To: Al Viro; +Cc: Christoph Hellwig, linux-kernel


Two patches.

 The second removes S_BIAS which is unnecessary and so potentially
 confusing.

 The first I found the need for while preparing the second patch.  It
 seems that get_active_super doesn't follow the pattern of all other
 code that walks the super_blocks list.

NeilBrown

---

NeilBrown (2):
      VFS: use __put_super_and_need_restart in get_active_super.
      fs/super: remove S_BIAS


 fs/notify/inotify/inotify.c |   33 ++++++++++++++-------------------
 fs/super.c                  |   20 ++++++++------------
 include/linux/fs.h          |    1 -
 3 files changed, 22 insertions(+), 32 deletions(-)

-- 


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/2] VFS: use __put_super_and_need_restart in get_active_super.
  2010-03-24  5:12 [PATCH 0/2] Two patches for fs/super.c NeilBrown
  2010-03-24  5:12 ` [PATCH 2/2] fs/super: remove S_BIAS NeilBrown
@ 2010-03-24  5:12 ` NeilBrown
  2010-03-24  9:20 ` [PATCH 0/2] Two patches for fs/super.c Al Viro
  2 siblings, 0 replies; 5+ messages in thread
From: NeilBrown @ 2010-03-24  5:12 UTC (permalink / raw)
  To: Al Viro; +Cc: Christoph Hellwig, linux-kernel

The recently-added get_active_super() walks the super_blocks
list but unlike all other code that walks this list it does
not use __put_super_and_need_restart().
I believe this is an omission that should be fixed.

cc: Christoph Hellwig <hch@lst.de>
Signed-off-by: NeilBrown <neilb@suse.de>
---
 fs/super.c |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)

diff --git a/fs/super.c b/fs/super.c
index f35ac60..34c8391 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -482,6 +482,7 @@ struct super_block *get_active_super(struct block_device *bdev)
 		return NULL;
 
 	spin_lock(&sb_lock);
+restart:
 	list_for_each_entry(sb, &super_blocks, s_list) {
 		if (sb->s_bdev != bdev)
 			continue;
@@ -500,9 +501,10 @@ struct super_block *get_active_super(struct block_device *bdev)
 			spin_unlock(&sb_lock);
 		}
 		up_write(&sb->s_umount);
-		put_super(sb);
 		yield();
 		spin_lock(&sb_lock);
+		if (__put_super_and_need_restart(sb))
+			goto restart;
 	}
 	spin_unlock(&sb_lock);
 	return NULL;



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/2] fs/super: remove S_BIAS
  2010-03-24  5:12 [PATCH 0/2] Two patches for fs/super.c NeilBrown
@ 2010-03-24  5:12 ` NeilBrown
  2010-03-24  5:12 ` [PATCH 1/2] VFS: use __put_super_and_need_restart in get_active_super NeilBrown
  2010-03-24  9:20 ` [PATCH 0/2] Two patches for fs/super.c Al Viro
  2 siblings, 0 replies; 5+ messages in thread
From: NeilBrown @ 2010-03-24  5:12 UTC (permalink / raw)
  To: Al Viro; +Cc: Christoph Hellwig, linux-kernel

The fact that S_BIAS is very large is only used four times,
all(*) with:
		if (s->s_count > S_BIAS) {
			atomic_inc(&s->s_active);

The statement "s->s_count > S_BIAS" is exactly equivalent
to "atomic_read(&s->s_active) != 0", as the bias is subtracted
as soon as s_active becomes zero.
So the above test can more simply become:

		if (atomic_inc_not_zero(&s->s_active)) {

With this in place, S_BIAS does not need to be large, and a value of
'1' will suit.  This simplifies code in a number of places and
removes the need to take a spinlock in several cases.

(*) full disclosure:  in two cases (inotify) it is
           if (s->s_count >= S_BIAS {
however the logic still holds - that can only be true if
s_active is not zero.

Signed-off-by: NeilBrown <neilb@suse.de>
---
 fs/notify/inotify/inotify.c |   33 ++++++++++++++-------------------
 fs/super.c                  |   16 +++++-----------
 include/linux/fs.h          |    1 -
 3 files changed, 19 insertions(+), 31 deletions(-)

diff --git a/fs/notify/inotify/inotify.c b/fs/notify/inotify/inotify.c
index 40b1cf9..44256e5 100644
--- a/fs/notify/inotify/inotify.c
+++ b/fs/notify/inotify/inotify.c
@@ -110,14 +110,10 @@ EXPORT_SYMBOL_GPL(get_inotify_watch);
 int pin_inotify_watch(struct inotify_watch *watch)
 {
 	struct super_block *sb = watch->inode->i_sb;
-	spin_lock(&sb_lock);
-	if (sb->s_count >= S_BIAS) {
-		atomic_inc(&sb->s_active);
-		spin_unlock(&sb_lock);
+	if (atomic_inc_not_zero(&sb->s_active)) {
 		atomic_inc(&watch->count);
 		return 1;
 	}
-	spin_unlock(&sb_lock);
 	return 0;
 }
 
@@ -518,16 +514,17 @@ EXPORT_SYMBOL_GPL(inotify_init_watch);
  * ->s_umount, which will almost certainly wait until the superblock is shut
  * down and the watch in question is pining for fjords.  That's fine, but
  * there is a problem - we might have hit the window between ->s_active
- * getting to 0 / ->s_count - below S_BIAS (i.e. the moment when superblock
- * is past the point of no return and is heading for shutdown) and the
- * moment when deactivate_super() acquires ->s_umount.  We could just do
- * drop_super() yield() and retry, but that's rather antisocial and this
- * stuff is luser-triggerable.  OTOH, having grabbed ->s_umount and having
- * found that we'd got there first (i.e. that ->s_root is non-NULL) we know
- * that we won't race with inotify_umount_inodes().  So we could grab a
- * reference to watch and do the rest as above, just with drop_super() instead
- * of deactivate_super(), right?  Wrong.  We had to drop ih->mutex before we
- * could grab ->s_umount.  So the watch could've been gone already.
+ * getting to 0 (i.e. the moment when superblock is past the point of no
+ * return and is heading for shutdown) and the moment when
+ * deactivate_super() acquires ->s_umount.  We could just do drop_super()
+ * yield() and retry, but that's rather antisocial and this stuff is
+ * luser-triggerable.  OTOH, having grabbed ->s_umount and having found
+ * that we'd got there first (i.e. that ->s_root is non-NULL) we know that
+ * we won't race with inotify_umount_inodes().  So we could grab a
+ * reference to watch and do the rest as above, just with drop_super()
+ * instead of deactivate_super(), right?  Wrong.  We had to drop ih->mutex
+ * before we could grab ->s_umount.  So the watch could've been gone
+ * already.
  *
  * That still can be dealt with - we need to save watch->wd, do idr_find()
  * and compare its result with our pointer.  If they match, we either have
@@ -565,14 +562,12 @@ static int pin_to_kill(struct inotify_handle *ih, struct inotify_watch *watch)
 	struct super_block *sb = watch->inode->i_sb;
 	s32 wd = watch->wd;
 
-	spin_lock(&sb_lock);
-	if (sb->s_count >= S_BIAS) {
-		atomic_inc(&sb->s_active);
-		spin_unlock(&sb_lock);
+	if (atomic_inc_not_zero(&sb->s_active)) {
 		get_inotify_watch(watch);
 		mutex_unlock(&ih->mutex);
 		return 1;	/* the best outcome */
 	}
+	spin_lock(&sb_lock);
 	sb->s_count++;
 	spin_unlock(&sb_lock);
 	mutex_unlock(&ih->mutex); /* can't grab ->s_umount under it */
diff --git a/fs/super.c b/fs/super.c
index 34c8391..9806711 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -92,7 +92,7 @@ static struct super_block *alloc_super(struct file_system_type *type)
 		 * subclass.
 		 */
 		down_write_nested(&s->s_umount, SINGLE_DEPTH_NESTING);
-		s->s_count = S_BIAS;
+		s->s_count = 1;
 		atomic_set(&s->s_active, 1);
 		mutex_init(&s->s_vfs_rename_mutex);
 		mutex_init(&s->s_dquot.dqio_mutex);
@@ -188,9 +188,7 @@ void put_super(struct super_block *sb)
 void deactivate_super(struct super_block *s)
 {
 	struct file_system_type *fs = s->s_type;
-	if (atomic_dec_and_lock(&s->s_active, &sb_lock)) {
-		s->s_count -= S_BIAS-1;
-		spin_unlock(&sb_lock);
+	if (atomic_dec_and_test(&s->s_active)) {
 		vfs_dq_off(s, 0);
 		down_write(&s->s_umount);
 		fs->kill_sb(s);
@@ -215,9 +213,7 @@ EXPORT_SYMBOL(deactivate_super);
 void deactivate_locked_super(struct super_block *s)
 {
 	struct file_system_type *fs = s->s_type;
-	if (atomic_dec_and_lock(&s->s_active, &sb_lock)) {
-		s->s_count -= S_BIAS-1;
-		spin_unlock(&sb_lock);
+	if (atomic_dec_and_test(&s->s_active)) {
 		vfs_dq_off(s, 0);
 		fs->kill_sb(s);
 		put_filesystem(fs);
@@ -247,8 +243,7 @@ static int grab_super(struct super_block *s) __releases(sb_lock)
 	down_write(&s->s_umount);
 	if (s->s_root) {
 		spin_lock(&sb_lock);
-		if (s->s_count > S_BIAS) {
-			atomic_inc(&s->s_active);
+		if (atomic_inc_not_zero(&s->s_active)) {
 			s->s_count--;
 			spin_unlock(&sb_lock);
 			return 1;
@@ -492,8 +487,7 @@ restart:
 		down_write(&sb->s_umount);
 		if (sb->s_root) {
 			spin_lock(&sb_lock);
-			if (sb->s_count > S_BIAS) {
-				atomic_inc(&sb->s_active);
+			if (atomic_inc_not_zero(&sb->s_active)) {
 				sb->s_count--;
 				spin_unlock(&sb_lock);
 				return sb;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 10b8ded..0e31ba8 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1315,7 +1315,6 @@ extern struct list_head super_blocks;
 extern spinlock_t sb_lock;
 
 #define sb_entry(list)  list_entry((list), struct super_block, s_list)
-#define S_BIAS (1<<30)
 struct super_block {
 	struct list_head	s_list;		/* Keep this first */
 	dev_t			s_dev;		/* search index; _not_ kdev_t */



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 0/2] Two patches for fs/super.c
  2010-03-24  5:12 [PATCH 0/2] Two patches for fs/super.c NeilBrown
  2010-03-24  5:12 ` [PATCH 2/2] fs/super: remove S_BIAS NeilBrown
  2010-03-24  5:12 ` [PATCH 1/2] VFS: use __put_super_and_need_restart in get_active_super NeilBrown
@ 2010-03-24  9:20 ` Al Viro
  2010-03-24  9:50   ` Neil Brown
  2 siblings, 1 reply; 5+ messages in thread
From: Al Viro @ 2010-03-24  9:20 UTC (permalink / raw)
  To: NeilBrown; +Cc: Christoph Hellwig, linux-kernel

On Wed, Mar 24, 2010 at 04:12:23PM +1100, NeilBrown wrote:
> 
> Two patches.
> 
>  The second removes S_BIAS which is unnecessary and so potentially
>  confusing.
> 
>  The first I found the need for while preparing the second patch.  It
>  seems that get_active_super doesn't follow the pattern of all other
>  code that walks the super_blocks list.

See #untested in vfs tree...

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 0/2] Two patches for fs/super.c
  2010-03-24  9:20 ` [PATCH 0/2] Two patches for fs/super.c Al Viro
@ 2010-03-24  9:50   ` Neil Brown
  0 siblings, 0 replies; 5+ messages in thread
From: Neil Brown @ 2010-03-24  9:50 UTC (permalink / raw)
  To: Al Viro; +Cc: Christoph Hellwig, linux-kernel

On Wed, 24 Mar 2010 09:20:11 +0000
Al Viro <viro@ZenIV.linux.org.uk> wrote:

> On Wed, Mar 24, 2010 at 04:12:23PM +1100, NeilBrown wrote:
> > 
> > Two patches.
> > 
> >  The second removes S_BIAS which is unnecessary and so potentially
> >  confusing.
> > 
> >  The first I found the need for while preparing the second patch.  It
> >  seems that get_active_super doesn't follow the pattern of all other
> >  code that walks the super_blocks list.
> 
> See #untested in vfs tree...
>

Ahhh, I see I've been beaten to it by 2 days... I should have posted that
patch months ago :-)

Thanks anyway.

NeilBrown

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2010-03-24  9:50 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-03-24  5:12 [PATCH 0/2] Two patches for fs/super.c NeilBrown
2010-03-24  5:12 ` [PATCH 2/2] fs/super: remove S_BIAS NeilBrown
2010-03-24  5:12 ` [PATCH 1/2] VFS: use __put_super_and_need_restart in get_active_super NeilBrown
2010-03-24  9:20 ` [PATCH 0/2] Two patches for fs/super.c Al Viro
2010-03-24  9:50   ` Neil Brown

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.