Skip to contentSkip to conversation

Fix AFFS race condition.

PATCH In review Vladimir 'φ-coder/phcoder' Serbinenko 3 participants 6 messages May 13, 2012 – May 14, 2012

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH v2 1/1 Fix AFFS race condition.

AFFS code preallocates several blocks as an optimisation. Unfortunately it's not protected by lock so the same blocks may end up allocated twice. Here is a fix.

fs/affs/affs.h +2 -0
index 45a0ce4..fc1d4ca 100644
@@ -66,6 +66,8 @@ struct affs_inode_info {
 	u32	 i_protect;			/* unused attribute bits */
 	u32	 i_lastalloc;			/* last allocated block */
 	int	 i_pa_cnt;			/* number of preallocated blocks */
+	spinlock_t i_alloc;		        /* Protects last 2 fields. */
+
 	struct inode vfs_inode;
 };
 
fs/affs/bitmap.c +23 -11
index 3e26271..3341bde 100644
@@ -151,12 +151,18 @@ affs_alloc_block(struct inode *inode, u32 goal)
 
 	pr_debug("AFFS: balloc(inode=%lu,goal=%u): ", inode->i_ino, goal);
 
+	spin_lock(&AFFS_I(inode)->i_alloc);
+
 	if (AFFS_I(inode)->i_pa_cnt) {
-		pr_debug("%d\n", AFFS_I(inode)->i_lastalloc+1);
+		u32 ret;
 		AFFS_I(inode)->i_pa_cnt--;
-		return ++AFFS_I(inode)->i_lastalloc;
+		ret = ++AFFS_I(inode)->i_lastalloc;
+		spin_unlock(&AFFS_I(inode)->i_alloc);
+		return ret;
 	}
 
+	spin_unlock(&AFFS_I(inode)->i_alloc);
+
 	if (!goal || goal > sbi->s_partition_size) {
 		if (goal)
 			affs_warning(sb, "affs_balloc", "invalid goal %d", goal);
@@ -230,16 +236,22 @@ find_bit:
 	bit = ffs(tmp & mask) - 1;
 	blk += bit + sbi->s_reserved;
 	mask2 = mask = 1 << (bit & 31);
-	AFFS_I(inode)->i_lastalloc = blk;
-
-	/* prealloc as much as possible within this word */
-	while ((mask2 <<= 1)) {
-		if (!(tmp & mask2))
-			break;
-		AFFS_I(inode)->i_pa_cnt++;
-		mask |= mask2;
+
+	spin_lock(&AFFS_I(inode)->i_alloc);
+	if (!AFFS_I(inode)->i_pa_cnt) {
+		AFFS_I(inode)->i_lastalloc = blk;
+
+		/* prealloc as much as possible within this word */
+		while ((mask2 <<= 1)) {
+			if (!(tmp & mask2))
+				break;
+			AFFS_I(inode)->i_pa_cnt++;
+			mask |= mask2;
+		}
+		bm->bm_free -= AFFS_I(inode)->i_pa_cnt + 1;
 	}
-	bm->bm_free -= AFFS_I(inode)->i_pa_cnt + 1;
+
+	spin_unlock(&AFFS_I(inode)->i_alloc);
 
 	*data = cpu_to_be32(tmp & ~mask);
 
fs/affs/file.c +12 -3
index 2f4c935..829e976 100644
@@ -795,12 +795,21 @@ void
 affs_free_prealloc(struct inode *inode)
 {
 	struct super_block *sb = inode->i_sb;
+	u32 first, cnt;
 
 	pr_debug("AFFS: free_prealloc(ino=%lu)\n", inode->i_ino);
 
-	while (AFFS_I(inode)->i_pa_cnt) {
-		AFFS_I(inode)->i_pa_cnt--;
-		affs_free_block(sb, ++AFFS_I(inode)->i_lastalloc);
+	spin_lock(&AFFS_I(inode)->i_alloc);
+	first = AFFS_I(inode)->i_lastalloc;
+	cnt = AFFS_I(inode)->i_pa_cnt;
+	AFFS_I(inode)->i_lastalloc += cnt;
+	AFFS_I(inode)->i_pa_cnt = 0;
+
+	spin_unlock(&AFFS_I(inode)->i_alloc);
+
+	while (cnt) {
+		cnt--;
+		affs_free_block(sb, ++first);
 	}
 }
 
fs/affs/super.c +1 -0
index 0782653..1df3c95 100644
@@ -91,6 +91,7 @@ static struct inode *affs_alloc_inode(struct super_block *sb)
 	i->i_lc = NULL;
 	i->i_ext_bh = NULL;
 	i->i_pa_cnt = 0;
+	spin_lock_init(&i->i_alloc);
 
 	return &i->vfs_inode;
 }
reviewer Reviewed-by on v2 1/1
#1 lore raw
Quoted 5 lines · Vladimir 'φ-coder/phcoder' Serbinenko

AFFS code preallocates several blocks as an optimisation. Unfortunately it's not protected by lock so the same blocks may end up allocated twice. Here is a fix.

The patch looks good to me now. Thanks! You can add:

Al, will you merge this patch through your tree? AFFS does not seem to
have a maintainer so you are a default fallback...

Honza

Quoted 112 lines
fs/affs/affs.h +2 -0
index 45a0ce4..fc1d4ca 100644
@@ -66,6 +66,8 @@ struct affs_inode_info {
 	u32	 i_protect;			/* unused attribute bits */
 	u32	 i_lastalloc;			/* last allocated block */
 	int	 i_pa_cnt;			/* number of preallocated blocks */
+	spinlock_t i_alloc;		        /* Protects last 2 fields. */
+
 	struct inode vfs_inode;
 };
 
fs/affs/bitmap.c +23 -11
index 3e26271..3341bde 100644
@@ -151,12 +151,18 @@ affs_alloc_block(struct inode *inode, u32 goal)
 
 	pr_debug("AFFS: balloc(inode=%lu,goal=%u): ", inode->i_ino, goal);
 
+	spin_lock(&AFFS_I(inode)->i_alloc);
+
 	if (AFFS_I(inode)->i_pa_cnt) {
-		pr_debug("%d\n", AFFS_I(inode)->i_lastalloc+1);
+		u32 ret;
 		AFFS_I(inode)->i_pa_cnt--;
-		return ++AFFS_I(inode)->i_lastalloc;
+		ret = ++AFFS_I(inode)->i_lastalloc;
+		spin_unlock(&AFFS_I(inode)->i_alloc);
+		return ret;
 	}
 
+	spin_unlock(&AFFS_I(inode)->i_alloc);
+
 	if (!goal || goal > sbi->s_partition_size) {
 		if (goal)
 			affs_warning(sb, "affs_balloc", "invalid goal %d", goal);
@@ -230,16 +236,22 @@ find_bit:
 	bit = ffs(tmp & mask) - 1;
 	blk += bit + sbi->s_reserved;
 	mask2 = mask = 1 << (bit & 31);
-	AFFS_I(inode)->i_lastalloc = blk;
-
-	/* prealloc as much as possible within this word */
-	while ((mask2 <<= 1)) {
-		if (!(tmp & mask2))
-			break;
-		AFFS_I(inode)->i_pa_cnt++;
-		mask |= mask2;
+
+	spin_lock(&AFFS_I(inode)->i_alloc);
+	if (!AFFS_I(inode)->i_pa_cnt) {
+		AFFS_I(inode)->i_lastalloc = blk;
+
+		/* prealloc as much as possible within this word */
+		while ((mask2 <<= 1)) {
+			if (!(tmp & mask2))
+				break;
+			AFFS_I(inode)->i_pa_cnt++;
+			mask |= mask2;
+		}
+		bm->bm_free -= AFFS_I(inode)->i_pa_cnt + 1;
 	}
-	bm->bm_free -= AFFS_I(inode)->i_pa_cnt + 1;
+
+	spin_unlock(&AFFS_I(inode)->i_alloc);
 
 	*data = cpu_to_be32(tmp & ~mask);
 
fs/affs/file.c +12 -3
index 2f4c935..829e976 100644
@@ -795,12 +795,21 @@ void
 affs_free_prealloc(struct inode *inode)
 {
 	struct super_block *sb = inode->i_sb;
+	u32 first, cnt;
 
 	pr_debug("AFFS: free_prealloc(ino=%lu)\n", inode->i_ino);
 
-	while (AFFS_I(inode)->i_pa_cnt) {
-		AFFS_I(inode)->i_pa_cnt--;
-		affs_free_block(sb, ++AFFS_I(inode)->i_lastalloc);
+	spin_lock(&AFFS_I(inode)->i_alloc);
+	first = AFFS_I(inode)->i_lastalloc;
+	cnt = AFFS_I(inode)->i_pa_cnt;
+	AFFS_I(inode)->i_lastalloc += cnt;
+	AFFS_I(inode)->i_pa_cnt = 0;
+
+	spin_unlock(&AFFS_I(inode)->i_alloc);
+
+	while (cnt) {
+		cnt--;
+		affs_free_block(sb, ++first);
 	}
 }
 
fs/affs/super.c +1 -0
index 0782653..1df3c95 100644
@@ -91,6 +91,7 @@ static struct inode *affs_alloc_inode(struct super_block *sb)
 	i->i_lc = NULL;
 	i->i_ext_bh = NULL;
 	i->i_pa_cnt = 0;
+	spin_lock_init(&i->i_alloc);
 
 	return &i->vfs_inode;
 }
Signature · 2 lines
Jan Kara <[email protected]>
SUSE Labs, CR
#2 lore raw

2012/5/14 Jan Kara <[email protected]>:

Quoted 5 lines · Vladimir 'φ-coder/phcoder' Serbinenko
Quoted 4 lines

AFFS code preallocates several blocks as an optimisation. Unfortunately it's not protected by lock so the same blocks may end up allocated twice. Here is a fix.

Quoted 9 lines
Quoted 1 line

 The patch looks good to me now. Thanks! You can add:

 Al, will you merge this patch through your tree? AFFS does not seem to have a maintainer so you are a default fallback...

                                                               Honza

I don't know the AFFS code, so only a question. Instead to use a spin lock, I think we can use a simple mutex. Or is the spin lock mandatory?

Marco
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

reviewer
#3 lore raw
Quoted 13 lines · Marco Stornelli

2012/5/14 Jan Kara <[email protected]>:

Quoted 12 lines · Vladimir 'φ-coder/phcoder' Serbinenko
Quoted 5 lines

AFFS code preallocates several blocks as an optimisation. Unfortunately it's not protected by lock so the same blocks may end up allocated twice. Here is a fix.

 The patch looks good to me now. Thanks! You can add:

 Al, will you merge this patch through your tree? AFFS does not seem to have a maintainer so you are a default fallback...

Quoted 6 lines
Quoted 2 lines

                                                               Honza

I don't know the AFFS code, so only a question. Instead to use a spin lock, I think we can use a simple mutex. Or is the spin lock mandatory?

So what would be an advantage of a mutex? Spinlock *is* the simple locking
variant...

Honza

Signature · 6 lines
Jan Kara <[email protected]>
SUSE Labs, CR
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#5 lore raw
In reply to Marco Stornelli
Quoted 13 lines · Marco Stornelli

2012/5/14 Jan Kara <[email protected]>:

Quoted 12 lines · Vladimir 'φ-coder/phcoder' Serbinenko
Quoted 5 lines

AFFS code preallocates several blocks as an optimisation. Unfortunately it's not protected by lock so the same blocks may end up allocated twice. Here is a fix.

The patch looks good to me now. Thanks! You can add:

Al, will you merge this patch through your tree? AFFS does not seem to
have a maintainer so you are a default fallback...

Quoted 6 lines
Quoted 2 lines

Honza

I don't know the AFFS code, so only a question. Instead to use a spin lock, I think we can use a simple mutex. Or is the spin lock mandatory?

My first version used mutex. But then Jan suggested that since the critical section is very short and doesn't contain any instructions which might sleep, it's better for performance to use a spin lock.

Signature · 2 lines
Regards
Vladimir 'φ-coder/phcoder' Serbinenko
#4 lore raw
In reply to Jan Kara

2012/5/14 Jan Kara <[email protected]>:

Quoted 17 lines · Marco Stornelli
Quoted 16 lines

2012/5/14 Jan Kara <[email protected]>:

Quoted 14 lines · Vladimir 'φ-coder/phcoder' Serbinenko
Quoted 5 lines

AFFS code preallocates several blocks as an optimisation. Unfortunately it's not protected by lock so the same blocks may end up allocated twice. Here is a fix.

 The patch looks good to me now. Thanks! You can add:

 Al, will you merge this patch through your tree? AFFS does not seem to have a maintainer so you are a default fallback...

                                                               Honza

Quoted 8 lines
Quoted 3 lines

I don't know the AFFS code, so only a question. Instead to use a spin lock, I think we can use a simple mutex. Or is the spin lock mandatory?

 So what would be an advantage of a mutex? Spinlock *is* the simple locking variant...

                                                               Honza --

None actually, only style, but if there are performance consideration already done, ok it was only a question. :)

Marco
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html