btrfs: Fix bug for misused dev_t when lookup in dev state hash table.
diff mbox

Message ID 1508240087-36513-1-git-send-email-gujx@cn.fujitsu.com
State New
Headers show

Commit Message

Gu Jinxiang Oct. 17, 2017, 11:34 a.m. UTC
From: Gu JinXiang <gujx@cn.fujitsu.com>

Fix bug of commit 74d46992e0d9
("block: replace bi_bdev with a gendisk pointer and partitions index").

In this modify, use bio_dev(bio) to find dev state in function
__btrfsic_submit_bio. But when dev_state added to hashtable, it is using
dev_t of block_device.

Reproduce of this bug:
Use MOUNT_OPTIONS="-o check_int" when run btrfs/001 in xfstest.
Then there will be WARNING like below.
WARNING:
btrfs: attempt to write superblock which references block M @29523968 (sda7     /1111654400/2) which is never written!

Signed-off-by: Gu JinXiang <gujx@cn.fujitsu.com>
---
 fs/btrfs/check-integrity.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Comments

Nikolay Borisov Oct. 17, 2017, 1:35 p.m. UTC | #1
On 17.10.2017 14:34, Gu Jinxiang wrote:
> From: Gu JinXiang <gujx@cn.fujitsu.com>
> 
> Fix bug of commit 74d46992e0d9
> ("block: replace bi_bdev with a gendisk pointer and partitions index").
> 
> In this modify, use bio_dev(bio) to find dev state in function
> __btrfsic_submit_bio. But when dev_state added to hashtable, it is using
> dev_t of block_device.

This is rather incomprehensible. So bio_dev(bio) actually returns the
dev_t of the device to which this bio is submitted and the same dev_t
should be used when btrfsic_dev_state_hashtable_add is called? What am I
missing in here?


> 
> Reproduce of this bug:
> Use MOUNT_OPTIONS="-o check_int" when run btrfs/001 in xfstest.
> Then there will be WARNING like below.
> WARNING:
> btrfs: attempt to write superblock which references block M @29523968 (sda7     /1111654400/2) which is never written!
> 
> Signed-off-by: Gu JinXiang <gujx@cn.fujitsu.com>
> ---
>  fs/btrfs/check-integrity.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/fs/btrfs/check-integrity.c b/fs/btrfs/check-integrity.c
> index fb07e3c22b9a..02f9eb83173f 100644
> --- a/fs/btrfs/check-integrity.c
> +++ b/fs/btrfs/check-integrity.c
> @@ -2803,7 +2803,7 @@ static void __btrfsic_submit_bio(struct bio *bio)
>  	mutex_lock(&btrfsic_mutex);
>  	/* since btrfsic_submit_bio() is also called before
>  	 * btrfsic_mount(), this might return NULL */
> -	dev_state = btrfsic_dev_state_lookup(bio_dev(bio));
> +	dev_state = btrfsic_dev_state_lookup(bio_dev(bio) + bio->bi_partno);

So this function looks up in btrfsic_dev_state_hashtable. And stuff in
this hashtable ias added via btrfsic_dev_state_hashtable_add function
which seems to be only using the dev_t (after your other patch is applied):

static void btrfsic_dev_state_hashtable_add(

                struct btrfsic_dev_state *ds,

                struct btrfsic_dev_state_hashtable *h)

{

        const unsigned int hashval =

            (((unsigned int)((uintptr_t)ds->bdev->bd_dev)) &

             (BTRFSIC_DEV2STATE_HASHTABLE_SIZE - 1));



        list_add(&ds->collision_resolving_node, h->table + hashval);

}


So how come your change is correct since you are passing the dev_t +
partition number?

>  	if (NULL != dev_state &&
>  	    (bio_op(bio) == REQ_OP_WRITE) && bio_has_data(bio)) {
>  		unsigned int i = 0;
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Gu Jinxiang Oct. 18, 2017, 3:43 a.m. UTC | #2
Hi,

> -----Original Message-----

> From: Nikolay Borisov [mailto:nborisov@suse.com]

> Sent: Tuesday, October 17, 2017 9:36 PM

> To: Gu, Jinxiang/顾 金香 <gujx@cn.fujitsu.com>; linux-btrfs@vger.kernel.org; hch@lst.de

> Subject: Re: [PATCH] btrfs: Fix bug for misused dev_t when lookup in dev state hash table.

> 

> 

> 

> On 17.10.2017 14:34, Gu Jinxiang wrote:

> > From: Gu JinXiang <gujx@cn.fujitsu.com>

> >

> > Fix bug of commit 74d46992e0d9

> > ("block: replace bi_bdev with a gendisk pointer and partitions index").

> >

> > In this modify, use bio_dev(bio) to find dev state in function

> > __btrfsic_submit_bio. But when dev_state added to hashtable, it is

> > using dev_t of block_device.

> 

> This is rather incomprehensible. So bio_dev(bio) actually returns the dev_t of the device to which this bio is submitted

> and the same dev_t should be used when btrfsic_dev_state_hashtable_add is called? What am I missing in here?

> 


bio_dev(bio) returns a dev_t of part0 which is different from dev_t in block_device(bd_dev).
bd_dev in block_device represents the exact partition.
block_device.bd_dev = bio->bi_partno (same as block_device.bd_partno) + bio_dev(bio).

When add a dev_state into hashtable it is using the exact partition's dev_t.
So when lookup it, it should also use the exact partition's dev_t.

> 

> >

> > Reproduce of this bug:

> > Use MOUNT_OPTIONS="-o check_int" when run btrfs/001 in xfstest.

> > Then there will be WARNING like below.

> > WARNING:

> > btrfs: attempt to write superblock which references block M @29523968 (sda7     /1111654400/2) which is never written!

> >

> > Signed-off-by: Gu JinXiang <gujx@cn.fujitsu.com>

> > ---

> >  fs/btrfs/check-integrity.c | 2 +-

> >  1 file changed, 1 insertion(+), 1 deletion(-)

> >

> > diff --git a/fs/btrfs/check-integrity.c b/fs/btrfs/check-integrity.c

> > index fb07e3c22b9a..02f9eb83173f 100644

> > --- a/fs/btrfs/check-integrity.c

> > +++ b/fs/btrfs/check-integrity.c

> > @@ -2803,7 +2803,7 @@ static void __btrfsic_submit_bio(struct bio *bio)

> >  	mutex_lock(&btrfsic_mutex);

> >  	/* since btrfsic_submit_bio() is also called before

> >  	 * btrfsic_mount(), this might return NULL */

> > -	dev_state = btrfsic_dev_state_lookup(bio_dev(bio));

> > +	dev_state = btrfsic_dev_state_lookup(bio_dev(bio) + bio->bi_partno);

> 

> So this function looks up in btrfsic_dev_state_hashtable. And stuff in this hashtable ias added via

> btrfsic_dev_state_hashtable_add function which seems to be only using the dev_t (after your other patch is applied):

> 

> static void btrfsic_dev_state_hashtable_add(

> 

>                 struct btrfsic_dev_state *ds,

> 

>                 struct btrfsic_dev_state_hashtable *h)

> 

> {

> 

>         const unsigned int hashval =

> 

>             (((unsigned int)((uintptr_t)ds->bdev->bd_dev)) &

> 

>              (BTRFSIC_DEV2STATE_HASHTABLE_SIZE - 1));

> 

> 

> 

>         list_add(&ds->collision_resolving_node, h->table + hashval);

> 

> }

> 

> 

> So how come your change is correct since you are passing the dev_t + partition number?

> 

> >  	if (NULL != dev_state &&

> >  	    (bio_op(bio) == REQ_OP_WRITE) && bio_has_data(bio)) {

> >  		unsigned int i = 0;

> >

>
Nikolay Borisov Oct. 18, 2017, 6:15 a.m. UTC | #3
On 18.10.2017 06:43, Gu, Jinxiang wrote:
> Hi,
> 
>> -----Original Message-----
>> From: Nikolay Borisov [mailto:nborisov@suse.com]
>> Sent: Tuesday, October 17, 2017 9:36 PM
>> To: Gu, Jinxiang/顾 金香 <gujx@cn.fujitsu.com>; linux-btrfs@vger.kernel.org; hch@lst.de
>> Subject: Re: [PATCH] btrfs: Fix bug for misused dev_t when lookup in dev state hash table.
>>
>>
>>
>> On 17.10.2017 14:34, Gu Jinxiang wrote:
>>> From: Gu JinXiang <gujx@cn.fujitsu.com>
>>>
>>> Fix bug of commit 74d46992e0d9
>>> ("block: replace bi_bdev with a gendisk pointer and partitions index").
>>>
>>> In this modify, use bio_dev(bio) to find dev state in function
>>> __btrfsic_submit_bio. But when dev_state added to hashtable, it is
>>> using dev_t of block_device.
>>
>> This is rather incomprehensible. So bio_dev(bio) actually returns the dev_t of the device to which this bio is submitted
>> and the same dev_t should be used when btrfsic_dev_state_hashtable_add is called? What am I missing in here?
>>
> 
> bio_dev(bio) returns a dev_t of part0 which is different from dev_t in block_device(bd_dev).
> bd_dev in block_device represents the exact partition.
> block_device.bd_dev = bio->bi_partno (same as block_device.bd_partno) + bio_dev(bio).
> 
> When add a dev_state into hashtable it is using the exact partition's dev_t.
> So when lookup it, it should also use the exact partition's dev_t.

Right, ok. Can you please put this explanation into the changelog of the
patch and resend

> 
>>
>>>
>>> Reproduce of this bug:
>>> Use MOUNT_OPTIONS="-o check_int" when run btrfs/001 in xfstest.
>>> Then there will be WARNING like below.
>>> WARNING:
>>> btrfs: attempt to write superblock which references block M @29523968 (sda7     /1111654400/2) which is never written!
>>>
>>> Signed-off-by: Gu JinXiang <gujx@cn.fujitsu.com>
>>> ---
>>>  fs/btrfs/check-integrity.c | 2 +-
>>>  1 file changed, 1 insertion(+), 1 deletion(-)
>>>
>>> diff --git a/fs/btrfs/check-integrity.c b/fs/btrfs/check-integrity.c
>>> index fb07e3c22b9a..02f9eb83173f 100644
>>> --- a/fs/btrfs/check-integrity.c
>>> +++ b/fs/btrfs/check-integrity.c
>>> @@ -2803,7 +2803,7 @@ static void __btrfsic_submit_bio(struct bio *bio)
>>>  	mutex_lock(&btrfsic_mutex);
>>>  	/* since btrfsic_submit_bio() is also called before
>>>  	 * btrfsic_mount(), this might return NULL */
>>> -	dev_state = btrfsic_dev_state_lookup(bio_dev(bio));
>>> +	dev_state = btrfsic_dev_state_lookup(bio_dev(bio) + bio->bi_partno);
>>
>> So this function looks up in btrfsic_dev_state_hashtable. And stuff in this hashtable ias added via
>> btrfsic_dev_state_hashtable_add function which seems to be only using the dev_t (after your other patch is applied):
>>
>> static void btrfsic_dev_state_hashtable_add(
>>
>>                 struct btrfsic_dev_state *ds,
>>
>>                 struct btrfsic_dev_state_hashtable *h)
>>
>> {
>>
>>         const unsigned int hashval =
>>
>>             (((unsigned int)((uintptr_t)ds->bdev->bd_dev)) &
>>
>>              (BTRFSIC_DEV2STATE_HASHTABLE_SIZE - 1));
>>
>>
>>
>>         list_add(&ds->collision_resolving_node, h->table + hashval);
>>
>> }
>>
>>
>> So how come your change is correct since you are passing the dev_t + partition number?
>>
>>>  	if (NULL != dev_state &&
>>>  	    (bio_op(bio) == REQ_OP_WRITE) && bio_has_data(bio)) {
>>>  		unsigned int i = 0;
>>>
>>
> 
> 
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch
diff mbox

diff --git a/fs/btrfs/check-integrity.c b/fs/btrfs/check-integrity.c
index fb07e3c22b9a..02f9eb83173f 100644
--- a/fs/btrfs/check-integrity.c
+++ b/fs/btrfs/check-integrity.c
@@ -2803,7 +2803,7 @@  static void __btrfsic_submit_bio(struct bio *bio)
 	mutex_lock(&btrfsic_mutex);
 	/* since btrfsic_submit_bio() is also called before
 	 * btrfsic_mount(), this might return NULL */
-	dev_state = btrfsic_dev_state_lookup(bio_dev(bio));
+	dev_state = btrfsic_dev_state_lookup(bio_dev(bio) + bio->bi_partno);
 	if (NULL != dev_state &&
 	    (bio_op(bio) == REQ_OP_WRITE) && bio_has_data(bio)) {
 		unsigned int i = 0;