Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
36fac9e9
Commit
36fac9e9
authored
May 16, 2016
by
David Sterba
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'foreign/anand/dev-del-by-id-ext' into for-chris-4.7-20160516
parents
5ef64a3e
88acff64
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
324 additions
and
242 deletions
+324
-242
fs/btrfs/dev-replace.c
fs/btrfs/dev-replace.c
+44
-55
fs/btrfs/dev-replace.h
fs/btrfs/dev-replace.h
+3
-1
fs/btrfs/ioctl.c
fs/btrfs/ioctl.c
+58
-2
fs/btrfs/volumes.c
fs/btrfs/volumes.c
+198
-181
fs/btrfs/volumes.h
fs/btrfs/volumes.h
+7
-2
include/uapi/linux/btrfs.h
include/uapi/linux/btrfs.h
+14
-1
No files found.
fs/btrfs/dev-replace.c
View file @
36fac9e9
...
...
@@ -44,9 +44,6 @@ static void btrfs_dev_replace_update_device_in_mapping_tree(
struct
btrfs_fs_info
*
fs_info
,
struct
btrfs_device
*
srcdev
,
struct
btrfs_device
*
tgtdev
);
static
int
btrfs_dev_replace_find_srcdev
(
struct
btrfs_root
*
root
,
u64
srcdevid
,
char
*
srcdev_name
,
struct
btrfs_device
**
device
);
static
u64
__btrfs_dev_replace_cancel
(
struct
btrfs_fs_info
*
fs_info
);
static
int
btrfs_dev_replace_kthread
(
void
*
data
);
static
int
btrfs_dev_replace_continue_on_mount
(
struct
btrfs_fs_info
*
fs_info
);
...
...
@@ -305,8 +302,8 @@ void btrfs_after_dev_replace_commit(struct btrfs_fs_info *fs_info)
dev_replace
->
cursor_left_last_write_of_item
;
}
int
btrfs_dev_replace_start
(
struct
btrfs_root
*
root
,
struct
btrfs_ioctl_dev_replace_args
*
args
)
int
btrfs_dev_replace_start
(
struct
btrfs_root
*
root
,
char
*
tgtdev_name
,
u64
srcdevid
,
char
*
srcdev_name
,
int
read_src
)
{
struct
btrfs_trans_handle
*
trans
;
struct
btrfs_fs_info
*
fs_info
=
root
->
fs_info
;
...
...
@@ -315,29 +312,16 @@ int btrfs_dev_replace_start(struct btrfs_root *root,
struct
btrfs_device
*
tgt_device
=
NULL
;
struct
btrfs_device
*
src_device
=
NULL
;
switch
(
args
->
start
.
cont_reading_from_srcdev_mode
)
{
case
BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS
:
case
BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID
:
break
;
default:
return
-
EINVAL
;
}
if
((
args
->
start
.
srcdevid
==
0
&&
args
->
start
.
srcdev_name
[
0
]
==
'\0'
)
||
args
->
start
.
tgtdev_name
[
0
]
==
'\0'
)
return
-
EINVAL
;
/* the disk copy procedure reuses the scrub code */
mutex_lock
(
&
fs_info
->
volume_mutex
);
ret
=
btrfs_dev_replace_find_srcdev
(
root
,
args
->
start
.
srcdevid
,
args
->
start
.
srcdev_name
,
&
src_device
);
ret
=
btrfs_find_device_by_devspec
(
root
,
srcdevid
,
srcdev_name
,
&
src_device
);
if
(
ret
)
{
mutex_unlock
(
&
fs_info
->
volume_mutex
);
return
ret
;
}
ret
=
btrfs_init_dev_replace_tgtdev
(
root
,
args
->
start
.
tgtdev_name
,
ret
=
btrfs_init_dev_replace_tgtdev
(
root
,
tgtdev_name
,
src_device
,
&
tgt_device
);
mutex_unlock
(
&
fs_info
->
volume_mutex
);
if
(
ret
)
...
...
@@ -364,18 +348,17 @@ int btrfs_dev_replace_start(struct btrfs_root *root,
break
;
case
BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED
:
case
BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED
:
args
->
resul
t
=
BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED
;
re
t
=
BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED
;
goto
leave
;
}
dev_replace
->
cont_reading_from_srcdev_mode
=
args
->
start
.
cont_reading_from_srcdev_mode
;
dev_replace
->
cont_reading_from_srcdev_mode
=
read_src
;
WARN_ON
(
!
src_device
);
dev_replace
->
srcdev
=
src_device
;
WARN_ON
(
!
tgt_device
);
dev_replace
->
tgtdev
=
tgt_device
;
btrfs_info_in_rcu
(
root
->
fs_info
,
btrfs_info_in_rcu
(
fs_info
,
"dev_replace from %s (devid %llu) to %s started"
,
src_device
->
missing
?
"<missing disk>"
:
rcu_str_deref
(
src_device
->
name
),
...
...
@@ -396,14 +379,13 @@ int btrfs_dev_replace_start(struct btrfs_root *root,
dev_replace
->
item_needs_writeback
=
1
;
atomic64_set
(
&
dev_replace
->
num_write_errors
,
0
);
atomic64_set
(
&
dev_replace
->
num_uncorrectable_read_errors
,
0
);
args
->
result
=
BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR
;
btrfs_dev_replace_unlock
(
dev_replace
,
1
);
ret
=
btrfs_sysfs_add_device_link
(
tgt_device
->
fs_devices
,
tgt_device
);
if
(
ret
)
btrfs_err
(
root
->
fs_info
,
"kobj add dev failed %d
\n
"
,
ret
);
btrfs_err
(
fs_info
,
"kobj add dev failed %d
\n
"
,
ret
);
btrfs_wait_ordered_roots
(
root
->
fs_info
,
-
1
);
btrfs_wait_ordered_roots
(
fs_info
,
-
1
);
/* force writing the updated state information to disk */
trans
=
btrfs_start_transaction
(
root
,
0
);
...
...
@@ -421,11 +403,9 @@ int btrfs_dev_replace_start(struct btrfs_root *root,
btrfs_device_get_total_bytes
(
src_device
),
&
dev_replace
->
scrub_progress
,
0
,
1
);
ret
=
btrfs_dev_replace_finishing
(
root
->
fs_info
,
ret
);
/* don't warn if EINPROGRESS, someone else might be running scrub */
ret
=
btrfs_dev_replace_finishing
(
fs_info
,
ret
);
if
(
ret
==
-
EINPROGRESS
)
{
args
->
result
=
BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS
;
ret
=
0
;
ret
=
BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS
;
}
else
{
WARN_ON
(
ret
);
}
...
...
@@ -440,6 +420,35 @@ int btrfs_dev_replace_start(struct btrfs_root *root,
return
ret
;
}
int
btrfs_dev_replace_by_ioctl
(
struct
btrfs_root
*
root
,
struct
btrfs_ioctl_dev_replace_args
*
args
)
{
int
ret
;
switch
(
args
->
start
.
cont_reading_from_srcdev_mode
)
{
case
BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS
:
case
BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID
:
break
;
default:
return
-
EINVAL
;
}
if
((
args
->
start
.
srcdevid
==
0
&&
args
->
start
.
srcdev_name
[
0
]
==
'\0'
)
||
args
->
start
.
tgtdev_name
[
0
]
==
'\0'
)
return
-
EINVAL
;
ret
=
btrfs_dev_replace_start
(
root
,
args
->
start
.
tgtdev_name
,
args
->
start
.
srcdevid
,
args
->
start
.
srcdev_name
,
args
->
start
.
cont_reading_from_srcdev_mode
);
args
->
result
=
ret
;
/* don't warn if EINPROGRESS, someone else might be running scrub */
if
(
ret
==
BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS
)
ret
=
0
;
return
ret
;
}
/*
* blocked until all flighting bios are finished.
*/
...
...
@@ -560,10 +569,9 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
ASSERT
(
list_empty
(
&
src_device
->
resized_list
));
tgt_device
->
commit_total_bytes
=
src_device
->
commit_total_bytes
;
tgt_device
->
commit_bytes_used
=
src_device
->
bytes_used
;
if
(
fs_info
->
sb
->
s_bdev
==
src_device
->
bdev
)
fs_info
->
sb
->
s_bdev
=
tgt_device
->
bdev
;
if
(
fs_info
->
fs_devices
->
latest_bdev
==
src_device
->
bdev
)
fs_info
->
fs_devices
->
latest_bdev
=
tgt_device
->
bdev
;
btrfs_assign_next_active_device
(
fs_info
,
src_device
,
tgt_device
);
list_add
(
&
tgt_device
->
dev_alloc_list
,
&
fs_info
->
fs_devices
->
alloc_list
);
fs_info
->
fs_devices
->
rw_devices
++
;
...
...
@@ -626,25 +634,6 @@ static void btrfs_dev_replace_update_device_in_mapping_tree(
write_unlock
(
&
em_tree
->
lock
);
}
static
int
btrfs_dev_replace_find_srcdev
(
struct
btrfs_root
*
root
,
u64
srcdevid
,
char
*
srcdev_name
,
struct
btrfs_device
**
device
)
{
int
ret
;
if
(
srcdevid
)
{
ret
=
0
;
*
device
=
btrfs_find_device
(
root
->
fs_info
,
srcdevid
,
NULL
,
NULL
);
if
(
!*
device
)
ret
=
-
ENOENT
;
}
else
{
ret
=
btrfs_find_device_missing_or_by_path
(
root
,
srcdev_name
,
device
);
}
return
ret
;
}
void
btrfs_dev_replace_status
(
struct
btrfs_fs_info
*
fs_info
,
struct
btrfs_ioctl_dev_replace_args
*
args
)
{
...
...
fs/btrfs/dev-replace.h
View file @
36fac9e9
...
...
@@ -25,8 +25,10 @@ int btrfs_init_dev_replace(struct btrfs_fs_info *fs_info);
int
btrfs_run_dev_replace
(
struct
btrfs_trans_handle
*
trans
,
struct
btrfs_fs_info
*
fs_info
);
void
btrfs_after_dev_replace_commit
(
struct
btrfs_fs_info
*
fs_info
);
int
btrfs_dev_replace_
start
(
struct
btrfs_root
*
root
,
int
btrfs_dev_replace_
by_ioctl
(
struct
btrfs_root
*
root
,
struct
btrfs_ioctl_dev_replace_args
*
args
);
int
btrfs_dev_replace_start
(
struct
btrfs_root
*
root
,
char
*
tgtdev_name
,
u64
srcdevid
,
char
*
srcdev_name
,
int
read_src
);
void
btrfs_dev_replace_status
(
struct
btrfs_fs_info
*
fs_info
,
struct
btrfs_ioctl_dev_replace_args
*
args
);
int
btrfs_dev_replace_cancel
(
struct
btrfs_fs_info
*
fs_info
,
...
...
fs/btrfs/ioctl.c
View file @
36fac9e9
...
...
@@ -2676,6 +2676,60 @@ static long btrfs_ioctl_add_dev(struct btrfs_root *root, void __user *arg)
return
ret
;
}
static
long
btrfs_ioctl_rm_dev_v2
(
struct
file
*
file
,
void
__user
*
arg
)
{
struct
btrfs_root
*
root
=
BTRFS_I
(
file_inode
(
file
))
->
root
;
struct
btrfs_ioctl_vol_args_v2
*
vol_args
;
int
ret
;
if
(
!
capable
(
CAP_SYS_ADMIN
))
return
-
EPERM
;
ret
=
mnt_want_write_file
(
file
);
if
(
ret
)
return
ret
;
vol_args
=
memdup_user
(
arg
,
sizeof
(
*
vol_args
));
if
(
IS_ERR
(
vol_args
))
{
ret
=
PTR_ERR
(
vol_args
);
goto
err_drop
;
}
/* Check for compatibility reject unknown flags */
if
(
vol_args
->
flags
&
~
BTRFS_VOL_ARG_V2_FLAGS_SUPPORTED
)
return
-
EOPNOTSUPP
;
if
(
atomic_xchg
(
&
root
->
fs_info
->
mutually_exclusive_operation_running
,
1
))
{
ret
=
BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS
;
goto
out
;
}
mutex_lock
(
&
root
->
fs_info
->
volume_mutex
);
if
(
vol_args
->
flags
&
BTRFS_DEVICE_SPEC_BY_ID
)
{
ret
=
btrfs_rm_device
(
root
,
NULL
,
vol_args
->
devid
);
}
else
{
vol_args
->
name
[
BTRFS_SUBVOL_NAME_MAX
]
=
'\0'
;
ret
=
btrfs_rm_device
(
root
,
vol_args
->
name
,
0
);
}
mutex_unlock
(
&
root
->
fs_info
->
volume_mutex
);
atomic_set
(
&
root
->
fs_info
->
mutually_exclusive_operation_running
,
0
);
if
(
!
ret
)
{
if
(
vol_args
->
flags
&
BTRFS_DEVICE_SPEC_BY_ID
)
btrfs_info
(
root
->
fs_info
,
"device deleted: id %llu"
,
vol_args
->
devid
);
else
btrfs_info
(
root
->
fs_info
,
"device deleted: %s"
,
vol_args
->
name
);
}
out:
kfree
(
vol_args
);
err_drop:
mnt_drop_write_file
(
file
);
return
ret
;
}
static
long
btrfs_ioctl_rm_dev
(
struct
file
*
file
,
void
__user
*
arg
)
{
struct
btrfs_root
*
root
=
BTRFS_I
(
file_inode
(
file
))
->
root
;
...
...
@@ -2703,7 +2757,7 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)
vol_args
->
name
[
BTRFS_PATH_NAME_MAX
]
=
'\0'
;
mutex_lock
(
&
root
->
fs_info
->
volume_mutex
);
ret
=
btrfs_rm_device
(
root
,
vol_args
->
name
);
ret
=
btrfs_rm_device
(
root
,
vol_args
->
name
,
0
);
mutex_unlock
(
&
root
->
fs_info
->
volume_mutex
);
if
(
!
ret
)
...
...
@@ -4387,7 +4441,7 @@ static long btrfs_ioctl_dev_replace(struct btrfs_root *root, void __user *arg)
1
))
{
ret
=
BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS
;
}
else
{
ret
=
btrfs_dev_replace_
start
(
root
,
p
);
ret
=
btrfs_dev_replace_
by_ioctl
(
root
,
p
);
atomic_set
(
&
root
->
fs_info
->
mutually_exclusive_operation_running
,
0
);
...
...
@@ -5480,6 +5534,8 @@ long btrfs_ioctl(struct file *file, unsigned int
return
btrfs_ioctl_add_dev
(
root
,
argp
);
case
BTRFS_IOC_RM_DEV
:
return
btrfs_ioctl_rm_dev
(
file
,
argp
);
case
BTRFS_IOC_RM_DEV_V2
:
return
btrfs_ioctl_rm_dev_v2
(
file
,
argp
);
case
BTRFS_IOC_FS_INFO
:
return
btrfs_ioctl_fs_info
(
root
,
argp
);
case
BTRFS_IOC_DEV_INFO
:
...
...
fs/btrfs/volumes.c
View file @
36fac9e9
...
...
@@ -118,6 +118,21 @@ const u64 btrfs_raid_group[BTRFS_NR_RAID_TYPES] = {
[
BTRFS_RAID_RAID6
]
=
BTRFS_BLOCK_GROUP_RAID6
,
};
/*
* Table to convert BTRFS_RAID_* to the error code if minimum number of devices
* condition is not met. Zero means there's no corresponding
* BTRFS_ERROR_DEV_*_NOT_MET value.
*/
const
int
btrfs_raid_mindev_error
[
BTRFS_NR_RAID_TYPES
]
=
{
[
BTRFS_RAID_RAID10
]
=
BTRFS_ERROR_DEV_RAID10_MIN_NOT_MET
,
[
BTRFS_RAID_RAID1
]
=
BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET
,
[
BTRFS_RAID_DUP
]
=
0
,
[
BTRFS_RAID_RAID0
]
=
0
,
[
BTRFS_RAID_SINGLE
]
=
0
,
[
BTRFS_RAID_RAID5
]
=
BTRFS_ERROR_DEV_RAID5_MIN_NOT_MET
,
[
BTRFS_RAID_RAID6
]
=
BTRFS_ERROR_DEV_RAID6_MIN_NOT_MET
,
};
static
int
init_first_rw_device
(
struct
btrfs_trans_handle
*
trans
,
struct
btrfs_root
*
root
,
struct
btrfs_device
*
device
);
...
...
@@ -699,7 +714,8 @@ static noinline int device_list_add(const char *path,
* if there is new btrfs on an already registered device,
* then remove the stale device entry.
*/
btrfs_free_stale_device
(
device
);
if
(
ret
>
0
)
btrfs_free_stale_device
(
device
);
*
fs_devices_ret
=
fs_devices
;
...
...
@@ -988,6 +1004,56 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
return
ret
;
}
void
btrfs_release_disk_super
(
struct
page
*
page
)
{
kunmap
(
page
);
put_page
(
page
);
}
int
btrfs_read_disk_super
(
struct
block_device
*
bdev
,
u64
bytenr
,
struct
page
**
page
,
struct
btrfs_super_block
**
disk_super
)
{
void
*
p
;
pgoff_t
index
;
/* make sure our super fits in the device */
if
(
bytenr
+
PAGE_SIZE
>=
i_size_read
(
bdev
->
bd_inode
))
return
1
;
/* make sure our super fits in the page */
if
(
sizeof
(
**
disk_super
)
>
PAGE_SIZE
)
return
1
;
/* make sure our super doesn't straddle pages on disk */
index
=
bytenr
>>
PAGE_SHIFT
;
if
((
bytenr
+
sizeof
(
**
disk_super
)
-
1
)
>>
PAGE_SHIFT
!=
index
)
return
1
;
/* pull in the page with our super */
*
page
=
read_cache_page_gfp
(
bdev
->
bd_inode
->
i_mapping
,
index
,
GFP_KERNEL
);
if
(
IS_ERR_OR_NULL
(
*
page
))
return
1
;
p
=
kmap
(
*
page
);
/* align our pointer to the offset of the super block */
*
disk_super
=
p
+
(
bytenr
&
~
PAGE_MASK
);
if
(
btrfs_super_bytenr
(
*
disk_super
)
!=
bytenr
||
btrfs_super_magic
(
*
disk_super
)
!=
BTRFS_MAGIC
)
{
btrfs_release_disk_super
(
*
page
);
return
1
;
}
if
((
*
disk_super
)
->
label
[
0
]
&&
(
*
disk_super
)
->
label
[
BTRFS_LABEL_SIZE
-
1
])
(
*
disk_super
)
->
label
[
BTRFS_LABEL_SIZE
-
1
]
=
'\0'
;
return
0
;
}
/*
* Look for a btrfs signature on a device. This may be called out of the mount path
* and we are not allowed to call set_blocksize during the scan. The superblock
...
...
@@ -999,13 +1065,11 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder,
struct
btrfs_super_block
*
disk_super
;
struct
block_device
*
bdev
;
struct
page
*
page
;
void
*
p
;
int
ret
=
-
EINVAL
;
u64
devid
;
u64
transid
;
u64
total_devices
;
u64
bytenr
;
pgoff_t
index
;
/*
* we would like to check all the supers, but that would make
...
...
@@ -1018,41 +1082,14 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder,
mutex_lock
(
&
uuid_mutex
);
bdev
=
blkdev_get_by_path
(
path
,
flags
,
holder
);
if
(
IS_ERR
(
bdev
))
{
ret
=
PTR_ERR
(
bdev
);
goto
error
;
}
/* make sure our super fits in the device */
if
(
bytenr
+
PAGE_SIZE
>=
i_size_read
(
bdev
->
bd_inode
))
goto
error_bdev_put
;
/* make sure our super fits in the page */
if
(
sizeof
(
*
disk_super
)
>
PAGE_SIZE
)
if
(
btrfs_read_disk_super
(
bdev
,
bytenr
,
&
page
,
&
disk_super
))
goto
error_bdev_put
;
/* make sure our super doesn't straddle pages on disk */
index
=
bytenr
>>
PAGE_SHIFT
;
if
((
bytenr
+
sizeof
(
*
disk_super
)
-
1
)
>>
PAGE_SHIFT
!=
index
)
goto
error_bdev_put
;
/* pull in the page with our super */
page
=
read_cache_page_gfp
(
bdev
->
bd_inode
->
i_mapping
,
index
,
GFP_NOFS
);
if
(
IS_ERR_OR_NULL
(
page
))
goto
error_bdev_put
;
p
=
kmap
(
page
);
/* align our pointer to the offset of the super block */
disk_super
=
p
+
(
bytenr
&
~
PAGE_MASK
);
if
(
btrfs_super_bytenr
(
disk_super
)
!=
bytenr
||
btrfs_super_magic
(
disk_super
)
!=
BTRFS_MAGIC
)
goto
error_unmap
;
devid
=
btrfs_stack_device_id
(
&
disk_super
->
dev_item
);
transid
=
btrfs_super_generation
(
disk_super
);
total_devices
=
btrfs_super_num_devices
(
disk_super
);
...
...
@@ -1060,8 +1097,6 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder,
ret
=
device_list_add
(
path
,
disk_super
,
devid
,
fs_devices_ret
);
if
(
ret
>
0
)
{
if
(
disk_super
->
label
[
0
])
{
if
(
disk_super
->
label
[
BTRFS_LABEL_SIZE
-
1
])
disk_super
->
label
[
BTRFS_LABEL_SIZE
-
1
]
=
'\0'
;
printk
(
KERN_INFO
"BTRFS: device label %s "
,
disk_super
->
label
);
}
else
{
printk
(
KERN_INFO
"BTRFS: device fsid %pU "
,
disk_super
->
fsid
);
...
...
@@ -1073,9 +1108,7 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder,
if
(
!
ret
&&
fs_devices_ret
)
(
*
fs_devices_ret
)
->
total_devices
=
total_devices
;
error_unmap:
kunmap
(
page
);
put_page
(
page
);
btrfs_release_disk_super
(
page
);
error_bdev_put:
blkdev_put
(
bdev
,
flags
);
...
...
@@ -1688,32 +1721,92 @@ static int btrfs_rm_dev_item(struct btrfs_root *root,
return
ret
;
}
int
btrfs_rm_device
(
struct
btrfs_root
*
root
,
char
*
device_path
)
/*
* Verify that @num_devices satisfies the RAID profile constraints in the whole
* filesystem. It's up to the caller to adjust that number regarding eg. device
* replace.
*/
static
int
btrfs_check_raid_min_devices
(
struct
btrfs_fs_info
*
fs_info
,
u64
num_devices
)
{
u64
all_avail
;
unsigned
seq
;
int
i
;
do
{
seq
=
read_seqbegin
(
&
fs_info
->
profiles_lock
);
all_avail
=
fs_info
->
avail_data_alloc_bits
|
fs_info
->
avail_system_alloc_bits
|
fs_info
->
avail_metadata_alloc_bits
;
}
while
(
read_seqretry
(
&
fs_info
->
profiles_lock
,
seq
));
for
(
i
=
0
;
i
<
BTRFS_NR_RAID_TYPES
;
i
++
)
{
if
(
!
(
all_avail
&
btrfs_raid_group
[
i
]))
continue
;
if
(
num_devices
<
btrfs_raid_array
[
i
].
devs_min
)
{
int
ret
=
btrfs_raid_mindev_error
[
i
];
if
(
ret
)
return
ret
;
}
}
return
0
;
}
struct
btrfs_device
*
btrfs_find_next_active_device
(
struct
btrfs_fs_devices
*
fs_devs
,
struct
btrfs_device
*
device
)
{
struct
btrfs_device
*
device
;
struct
btrfs_device
*
next_device
;
struct
block_device
*
bdev
;
struct
buffer_head
*
bh
=
NULL
;
struct
btrfs_super_block
*
disk_super
;
list_for_each_entry
(
next_device
,
&
fs_devs
->
devices
,
dev_list
)
{
if
(
next_device
!=
device
&&
!
next_device
->
missing
&&
next_device
->
bdev
)
return
next_device
;
}
return
NULL
;
}
/*
* Helper function to check if the given device is part of s_bdev / latest_bdev
* and replace it with the provided or the next active device, in the context
* where this function called, there should be always be another device (or
* this_dev) which is active.
*/
void
btrfs_assign_next_active_device
(
struct
btrfs_fs_info
*
fs_info
,
struct
btrfs_device
*
device
,
struct
btrfs_device
*
this_dev
)
{
struct
btrfs_device
*
next_device
;
if
(
this_dev
)
next_device
=
this_dev
;
else
next_device
=
btrfs_find_next_active_device
(
fs_info
->
fs_devices
,
device
);
ASSERT
(
next_device
);
if
(
fs_info
->
sb
->
s_bdev
&&
(
fs_info
->
sb
->
s_bdev
==
device
->
bdev
))
fs_info
->
sb
->
s_bdev
=
next_device
->
bdev
;
if
(
fs_info
->
fs_devices
->
latest_bdev
==
device
->
bdev
)
fs_info
->
fs_devices
->
latest_bdev
=
next_device
->
bdev
;
}
int
btrfs_rm_device
(
struct
btrfs_root
*
root
,
char
*
device_path
,
u64
devid
)
{
struct
btrfs_device
*
device
;
struct
btrfs_fs_devices
*
cur_devices
;
u64
all_avail
;
u64
devid
;
u64
num_devices
;
u8
*
dev_uuid
;
unsigned
seq
;
int
ret
=
0
;
bool
clear_super
=
false
;
char
*
dev_name
=
NULL
;
mutex_lock
(
&
uuid_mutex
);
do
{
seq
=
read_seqbegin
(
&
root
->
fs_info
->
profiles_lock
);
all_avail
=
root
->
fs_info
->
avail_data_alloc_bits
|
root
->
fs_info
->
avail_system_alloc_bits
|
root
->
fs_info
->
avail_metadata_alloc_bits
;
}
while
(
read_seqretry
(
&
root
->
fs_info
->
profiles_lock
,
seq
));
num_devices
=
root
->
fs_info
->
fs_devices
->
num_devices
;
btrfs_dev_replace_lock
(
&
root
->
fs_info
->
dev_replace
,
0
);
if
(
btrfs_dev_replace_is_ongoing
(
&
root
->
fs_info
->
dev_replace
))
{
...
...
@@ -1722,78 +1815,23 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
}
btrfs_dev_replace_unlock
(
&
root
->
fs_info
->
dev_replace
,
0
);
if
((
all_avail
&
BTRFS_BLOCK_GROUP_RAID10
)
&&
num_devices
<=
4
)
{
ret
=
BTRFS_ERROR_DEV_RAID10_MIN_NOT_MET
;
goto
out
;
}
if
((
all_avail
&
BTRFS_BLOCK_GROUP_RAID1
)
&&
num_devices
<=
2
)
{
ret
=
BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET
;
ret
=
btrfs_check_raid_min_devices
(
root
->
fs_info
,
num_devices
-
1
);
if
(
ret
)
goto
out
;
}
if
((
all_avail
&
BTRFS_BLOCK_GROUP_RAID5
)
&&
root
->
fs_info
->
fs_devices
->
rw_devices
<=
2
)
{
ret
=
BTRFS_ERROR_DEV_RAID5_MIN_NOT_MET
;
goto
out
;
}
if
((
all_avail
&
BTRFS_BLOCK_GROUP_RAID6
)
&&
root
->
fs_info
->
fs_devices
->
rw_devices
<=
3
)
{
ret
=
BTRFS_ERROR_DEV_RAID6_MIN_NOT_MET
;
ret
=
btrfs_find_device_by_devspec
(
root
,
devid
,
device_path
,
&
device
);
if
(
ret
)
goto
out
;
}
if
(
strcmp
(
device_path
,
"missing"
)
==
0
)
{
struct
list_head
*
devices
;
struct
btrfs_device
*
tmp
;
device
=
NULL
;
devices
=
&
root
->
fs_info
->
fs_devices
->
devices
;
/*
* It is safe to read the devices since the volume_mutex
* is held.
*/
list_for_each_entry
(
tmp
,
devices
,
dev_list
)
{
if
(
tmp
->
in_fs_metadata
&&
!
tmp
->
is_tgtdev_for_dev_replace
&&
!
tmp
->
bdev
)
{
device
=
tmp
;
break
;
}
}
bdev
=
NULL
;
bh
=
NULL
;
disk_super
=
NULL
;
if
(
!
device
)
{
ret
=
BTRFS_ERROR_DEV_MISSING_NOT_FOUND
;
goto
out
;
}
}
else
{
ret
=
btrfs_get_bdev_and_sb
(
device_path
,
FMODE_WRITE
|
FMODE_EXCL
,
root
->
fs_info
->
bdev_holder
,
0
,
&
bdev
,
&
bh
);
if
(
ret
)
goto
out
;
disk_super
=
(
struct
btrfs_super_block
*
)
bh
->
b_data
;
devid
=
btrfs_stack_device_id
(
&
disk_super
->
dev_item
);
dev_uuid
=
disk_super
->
dev_item
.
uuid
;
device
=
btrfs_find_device
(
root
->
fs_info
,
devid
,
dev_uuid
,
disk_super
->
fsid
);
if
(
!
device
)
{
ret
=
-
ENOENT
;
goto
error_brelse
;
}
}
if
(
device
->
is_tgtdev_for_dev_replace
)
{
ret
=
BTRFS_ERROR_DEV_TGT_REPLACE
;
goto
error_brelse
;
goto
out
;
}
if
(
device
->
writeable
&&
root
->
fs_info
->
fs_devices
->
rw_devices
==
1
)
{
ret
=
BTRFS_ERROR_DEV_ONLY_WRITABLE
;
goto
error_brelse
;
goto
out
;
}
if
(
device
->
writeable
)
{
...
...
@@ -1801,6 +1839,11 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
list_del_init
(
&
device
->
dev_alloc_list
);
device
->
fs_devices
->
rw_devices
--
;
unlock_chunks
(
root
);
dev_name
=
kstrdup
(
device
->
name
->
str
,
GFP_KERNEL
);
if
(
!
dev_name
)
{
ret
=
-
ENOMEM
;
goto
error_undo
;
}
clear_super
=
true
;
}
...
...
@@ -1842,12 +1885,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
if
(
device
->
missing
)
device
->
fs_devices
->
missing_devices
--
;
next_device
=
list_entry
(
root
->
fs_info
->
fs_devices
->
devices
.
next
,
struct
btrfs_device
,
dev_list
);
if
(
device
->
bdev
==
root
->
fs_info
->
sb
->
s_bdev
)
root
->
fs_info
->
sb
->
s_bdev
=
next_device
->
bdev
;
if
(
device
->
bdev
==
root
->
fs_info
->
fs_devices
->
latest_bdev
)
root
->
fs_info
->
fs_devices
->
latest_bdev
=
next_device
->
bdev
;
btrfs_assign_next_active_device
(
root
->
fs_info
,
device
,
NULL
);
if
(
device
->
bdev
)
{
device
->
fs_devices
->
open_devices
--
;
...
...
@@ -1883,63 +1921,23 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
* at this point, the device is zero sized. We want to
* remove it from the devices list and zero out the old super
*/
if
(
clear_super
&&
disk_super
)
{
u64
bytenr
;
int
i
;
/* make sure this device isn't detected as part of
* the FS anymore
*/
memset
(
&
disk_super
->
magic
,
0
,
sizeof
(
disk_super
->
magic
));
set_buffer_dirty
(
bh
);
sync_dirty_buffer
(
bh
);
/* clear the mirror copies of super block on the disk
* being removed, 0th copy is been taken care above and
* the below would take of the rest
*/
for
(
i
=
1
;
i
<
BTRFS_SUPER_MIRROR_MAX
;
i
++
)
{
bytenr
=
btrfs_sb_offset
(
i
);
if
(
bytenr
+
BTRFS_SUPER_INFO_SIZE
>=
i_size_read
(
bdev
->
bd_inode
))
break
;
brelse
(
bh
);
bh
=
__bread
(
bdev
,
bytenr
/
4096
,
BTRFS_SUPER_INFO_SIZE
);
if
(
!
bh
)
continue
;
disk_super
=
(
struct
btrfs_super_block
*
)
bh
->
b_data
;
if
(
btrfs_super_bytenr
(
disk_super
)
!=
bytenr
||
btrfs_super_magic
(
disk_super
)
!=
BTRFS_MAGIC
)
{
continue
;
}
memset
(
&
disk_super
->
magic
,
0
,
sizeof
(
disk_super
->
magic
));
set_buffer_dirty
(
bh
);
sync_dirty_buffer
(
bh
);
if
(
clear_super
)
{
struct
block_device
*
bdev
;
bdev
=
blkdev_get_by_path
(
dev_name
,
FMODE_READ
|
FMODE_EXCL
,
root
->
fs_info
->
bdev_holder
);
if
(
!
IS_ERR
(
bdev
))
{
btrfs_scratch_superblocks
(
bdev
,
dev_name
);
blkdev_put
(
bdev
,
FMODE_READ
|
FMODE_EXCL
);
}
}
ret
=
0
;
if
(
bdev
)
{
/* Notify udev that device has changed */
btrfs_kobject_uevent
(
bdev
,
KOBJ_CHANGE
);
/* Update ctime/mtime for device path for libblkid */
update_dev_time
(
device_path
);
}
error_brelse:
brelse
(
bh
);
if
(
bdev
)
blkdev_put
(
bdev
,
FMODE_READ
|
FMODE_EXCL
);
out:
kfree
(
dev_name
);
mutex_unlock
(
&
uuid_mutex
);
return
ret
;
error_undo:
if
(
device
->
writeable
)
{
lock_chunks
(
root
);
...
...
@@ -1948,7 +1946,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
device
->
fs_devices
->
rw_devices
++
;
unlock_chunks
(
root
);
}
goto
error_brelse
;
goto
out
;
}
void
btrfs_rm_dev_replace_remove_srcdev
(
struct
btrfs_fs_info
*
fs_info
,
...
...
@@ -2017,8 +2015,6 @@ void btrfs_rm_dev_replace_free_srcdev(struct btrfs_fs_info *fs_info,
void
btrfs_destroy_dev_replace_tgtdev
(
struct
btrfs_fs_info
*
fs_info
,
struct
btrfs_device
*
tgtdev
)
{
struct
btrfs_device
*
next_device
;
mutex_lock
(
&
uuid_mutex
);
WARN_ON
(
!
tgtdev
);
mutex_lock
(
&
fs_info
->
fs_devices
->
device_list_mutex
);
...
...
@@ -2030,12 +2026,8 @@ void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info,
fs_info
->
fs_devices
->
num_devices
--
;
next_device
=
list_entry
(
fs_info
->
fs_devices
->
devices
.
next
,
struct
btrfs_device
,
dev_list
);
if
(
tgtdev
->
bdev
==
fs_info
->
sb
->
s_bdev
)
fs_info
->
sb
->
s_bdev
=
next_device
->
bdev
;
if
(
tgtdev
->
bdev
==
fs_info
->
fs_devices
->
latest_bdev
)
fs_info
->
fs_devices
->
latest_bdev
=
next_device
->
bdev
;
btrfs_assign_next_active_device
(
fs_info
,
tgtdev
,
NULL
);
list_del_rcu
(
&
tgtdev
->
dev_list
);
mutex_unlock
(
&
fs_info
->
fs_devices
->
device_list_mutex
);
...
...
@@ -2109,6 +2101,31 @@ int btrfs_find_device_missing_or_by_path(struct btrfs_root *root,
}
}
/*
* Lookup a device given by device id, or the path if the id is 0.
*/
int
btrfs_find_device_by_devspec
(
struct
btrfs_root
*
root
,
u64
devid
,
char
*
devpath
,
struct
btrfs_device
**
device
)
{
int
ret
;
if
(
devid
)
{
ret
=
0
;
*
device
=
btrfs_find_device
(
root
->
fs_info
,
devid
,
NULL
,
NULL
);
if
(
!*
device
)
ret
=
-
ENOENT
;
}
else
{
if
(
!
devpath
||
!
devpath
[
0
])
return
-
EINVAL
;
ret
=
btrfs_find_device_missing_or_by_path
(
root
,
devpath
,
device
);
}
return
ret
;
}
/*
* does all the dirty work required for changing file system's UUID.
*/
...
...
fs/btrfs/volumes.h
View file @
36fac9e9
...
...
@@ -340,7 +340,7 @@ struct btrfs_raid_attr {
};
extern
const
struct
btrfs_raid_attr
btrfs_raid_array
[
BTRFS_NR_RAID_TYPES
];
extern
const
int
btrfs_raid_mindev_error
[
BTRFS_NR_RAID_TYPES
];
extern
const
u64
btrfs_raid_group
[
BTRFS_NR_RAID_TYPES
];
struct
map_lookup
{
...
...
@@ -445,13 +445,18 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder,
struct
btrfs_fs_devices
**
fs_devices_ret
);
int
btrfs_close_devices
(
struct
btrfs_fs_devices
*
fs_devices
);
void
btrfs_close_extra_devices
(
struct
btrfs_fs_devices
*
fs_devices
,
int
step
);
void
btrfs_assign_next_active_device
(
struct
btrfs_fs_info
*
fs_info
,
struct
btrfs_device
*
device
,
struct
btrfs_device
*
this_dev
);
int
btrfs_find_device_missing_or_by_path
(
struct
btrfs_root
*
root
,
char
*
device_path
,
struct
btrfs_device
**
device
);
int
btrfs_find_device_by_devspec
(
struct
btrfs_root
*
root
,
u64
devid
,
char
*
devpath
,
struct
btrfs_device
**
device
);
struct
btrfs_device
*
btrfs_alloc_device
(
struct
btrfs_fs_info
*
fs_info
,
const
u64
*
devid
,
const
u8
*
uuid
);
int
btrfs_rm_device
(
struct
btrfs_root
*
root
,
char
*
device_path
);
int
btrfs_rm_device
(
struct
btrfs_root
*
root
,
char
*
device_path
,
u64
devid
);
void
btrfs_cleanup_fs_uuids
(
void
);
int
btrfs_num_copies
(
struct
btrfs_fs_info
*
fs_info
,
u64
logical
,
u64
len
);
int
btrfs_grow_device
(
struct
btrfs_trans_handle
*
trans
,
...
...
include/uapi/linux/btrfs.h
View file @
36fac9e9
...
...
@@ -36,6 +36,14 @@ struct btrfs_ioctl_vol_args {
#define BTRFS_SUBVOL_CREATE_ASYNC (1ULL << 0)
#define BTRFS_SUBVOL_RDONLY (1ULL << 1)
#define BTRFS_SUBVOL_QGROUP_INHERIT (1ULL << 2)
#define BTRFS_DEVICE_SPEC_BY_ID (1ULL << 3)
#define BTRFS_VOL_ARG_V2_FLAGS_SUPPORTED \
(BTRFS_SUBVOL_CREATE_ASYNC | \
BTRFS_SUBVOL_RDONLY | \
BTRFS_SUBVOL_QGROUP_INHERIT | \
BTRFS_DEVICE_SPEC_BY_ID)
#define BTRFS_FSID_SIZE 16
#define BTRFS_UUID_SIZE 16
#define BTRFS_UUID_UNPARSED_SIZE 37
...
...
@@ -76,7 +84,10 @@ struct btrfs_ioctl_vol_args_v2 {
};
__u64
unused
[
4
];
};
char
name
[
BTRFS_SUBVOL_NAME_MAX
+
1
];
union
{
char
name
[
BTRFS_SUBVOL_NAME_MAX
+
1
];
u64
devid
;
};
};
/*
...
...
@@ -659,5 +670,7 @@ static inline char *btrfs_err_str(enum btrfs_err_code err_code)
struct btrfs_ioctl_feature_flags[2])
#define BTRFS_IOC_GET_SUPPORTED_FEATURES _IOR(BTRFS_IOCTL_MAGIC, 57, \
struct btrfs_ioctl_feature_flags[3])
#define BTRFS_IOC_RM_DEV_V2 _IOW(BTRFS_IOCTL_MAGIC, 58, \
struct btrfs_ioctl_vol_args_v2)
#endif
/* _UAPI_LINUX_BTRFS_H */
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment