diff mbox

[V2] btrfs: close any open devices if btrfs_mount fails

Message ID 515DAB1F.2050308@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Eric Sandeen April 4, 2013, 4:32 p.m. UTC
This:

   # mkfs.btrfs /dev/sdb{1,2} ; wipefs -a /dev/sdb1; mount /dev/sdb2 /mnt/test

would lead to a blkdev open/close mismatch when the mount fails, and
a permanently busy (opened O_EXCL) sdb2:

   # wipefs -a /dev/sdb2
   wipefs: error: /dev/sdb2: probing initialization failed: Device or resource busy

It's because btrfs_open_devices() may open some devices, and still
return failure.  So the error unwinding needs to close any open
devices in fs_devices before returning.

Reported-by: Jan Safranek <jsafrane@redhat.com>
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
---

V2: use open_devices not opened, that seems like the right test...
if a test is necessary I'm not certain, tbh.  Seems prudently
defensive.

Note, __btrfs_open_devices is weird; it seems to return success or
failure based on the outcome of the result of the last call
to btrfs_get_bdev_and_sb().  But that's a different bug...


--
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


--
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

Comments

Zach Brown April 4, 2013, 6:46 p.m. UTC | #1
> It's because btrfs_open_devices() may open some devices, and still
> return failure.  So the error unwinding needs to close any open
> devices in fs_devices before returning.

Yeah, looks like.

> Note, __btrfs_open_devices is weird; it seems to return success or
> failure based on the outcome of the result of the last call
> to btrfs_get_bdev_and_sb().  But that's a different bug...

I disagree that this is a different bug, I think it's the root cause of
this bug.

> @@ -1125,7 +1125,7 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
>  
>  	error = btrfs_open_devices(fs_devices, mode, fs_type);
>  	if (error)
> -		goto error_fs_info;
> +		goto error_close_devices;

Wouldn't open_seed_devices() also need a change like this?

I'd just rework __btrfs_open_devices to clean up after itself when it
returns an error.

>  error_close_devices:
> -	btrfs_close_devices(fs_devices);
> +	if (fs_devices->open_devices)
> +		btrfs_close_devices(fs_devices);

I guess that ->open_devices is supposed to be protected by the
uuid_mutex so it shouldn't be tested out here.  In any case, it wouldn't
be needed if btrfs_open_devices() cleaned up as it failed.

- z
--
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
Eric Sandeen April 4, 2013, 7:45 p.m. UTC | #2
On 4/4/13 1:46 PM, Zach Brown wrote:
>> It's because btrfs_open_devices() may open some devices, and still
>> return failure.  So the error unwinding needs to close any open
>> devices in fs_devices before returning.
> 
> Yeah, looks like.
> 
>> Note, __btrfs_open_devices is weird; it seems to return success or
>> failure based on the outcome of the result of the last call
>> to btrfs_get_bdev_and_sb().  But that's a different bug...
> 
> I disagree that this is a different bug, I think it's the root cause of
> this bug.
> 
>> @@ -1125,7 +1125,7 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
>>  
>>  	error = btrfs_open_devices(fs_devices, mode, fs_type);
>>  	if (error)
>> -		goto error_fs_info;
>> +		goto error_close_devices;
> 
> Wouldn't open_seed_devices() also need a change like this?
> 
> I'd just rework __btrfs_open_devices to clean up after itself when it
> returns an error.
> 
>>  error_close_devices:
>> -	btrfs_close_devices(fs_devices);
>> +	if (fs_devices->open_devices)
>> +		btrfs_close_devices(fs_devices);
> 
> I guess that ->open_devices is supposed to be protected by the
> uuid_mutex so it shouldn't be tested out here.  In any case, it wouldn't
> be needed if btrfs_open_devices() cleaned up as it failed.

I guess I had a feeling that in something like a degraded mount scenario
you might live with failures.  But I guess that is initiated on the mount
commandline, i.e. "mount this subset; it's degraded" not "mount these devices,
and if some fail that's cool."

Right?

Thanks,
-Eric

> - z
> 

--
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
Zach Brown April 4, 2013, 7:50 p.m. UTC | #3
> I guess I had a feeling that in something like a degraded mount scenario
> you might live with failures.  But I guess that is initiated on the mount
> commandline, i.e. "mount this subset; it's degraded" not "mount these devices,
> and if some fail that's cool."
> 
> Right?

Maybe?  Who knows.

- z
--
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
Chris Mason April 4, 2013, 8:05 p.m. UTC | #4
Quoting Eric Sandeen (2013-04-04 15:45:28)
> On 4/4/13 1:46 PM, Zach Brown wrote:
> >> It's because btrfs_open_devices() may open some devices, and still
> >> return failure.  So the error unwinding needs to close any open
> >> devices in fs_devices before returning.
> > 
> > Yeah, looks like.
> > 
> >> Note, __btrfs_open_devices is weird; it seems to return success or
> >> failure based on the outcome of the result of the last call
> >> to btrfs_get_bdev_and_sb().  But that's a different bug...
> > 
> > I disagree that this is a different bug, I think it's the root cause of
> > this bug.
> > 
> >> @@ -1125,7 +1125,7 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
> >>  
> >>      error = btrfs_open_devices(fs_devices, mode, fs_type);
> >>      if (error)
> >> -            goto error_fs_info;
> >> +            goto error_close_devices;
> > 
> > Wouldn't open_seed_devices() also need a change like this?
> > 
> > I'd just rework __btrfs_open_devices to clean up after itself when it
> > returns an error.
> > 
> >>  error_close_devices:
> >> -    btrfs_close_devices(fs_devices);
> >> +    if (fs_devices->open_devices)
> >> +            btrfs_close_devices(fs_devices);
> > 
> > I guess that ->open_devices is supposed to be protected by the
> > uuid_mutex so it shouldn't be tested out here.  In any case, it wouldn't
> > be needed if btrfs_open_devices() cleaned up as it failed.
> 
> I guess I had a feeling that in something like a degraded mount scenario
> you might live with failures.  But I guess that is initiated on the mount
> commandline, i.e. "mount this subset; it's degraded" not "mount these devices,
> and if some fail that's cool."

btrfs_open_devices just means: go off and open every bdev you can from
this uuid.  It should return success if we opened any of them at all.

__btrfs_open_devices() already ignores failures, and this is the
only place it is explicitly setting ret.  It should only happen if there
are no devices to close.

        if (fs_devices->open_devices == 0) {
                ret = -EINVAL;
                goto out;
        }

Unless of course we happen to fail to open the last device in the list:

	ret = btrfs_get_bdev_and_sb(device->name->str, flags, holder, 1, &bdev, &bh);
	if (ret)
		continue;

This is two curlies and a ret = 0 away from correct.

-chris

--
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
diff mbox

Patch

diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index f6b8859..60c67fa 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -1125,7 +1125,7 @@  static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
 
 	error = btrfs_open_devices(fs_devices, mode, fs_type);
 	if (error)
-		goto error_fs_info;
+		goto error_close_devices;
 
 	if (!(flags & MS_RDONLY) && fs_devices->rw_devices == 0) {
 		error = -EACCES;
@@ -1161,7 +1161,8 @@  static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
 	return root;
 
 error_close_devices:
-	btrfs_close_devices(fs_devices);
+	if (fs_devices->open_devices)
+		btrfs_close_devices(fs_devices);
 error_fs_info:
 	free_fs_info(fs_info);
 	return ERR_PTR(error);