Chunk allocator unification
v3 cover Chunk allocator unification
This patchset can be fetched from github:
https://github.com/adam900710/btrfs-progs/tree/libbtrfs_prepare
This patchset unified a large part of chunk allocator (free device extent search) between kernel and btrfs-progs.
And reuses kernel function structures like btrfs_finish_chunk_alloc() and btrfs_alloc_dev_extent().
Before the unification:
Kernel | Btrfs-progs
btrfs_alloc_chunk() | btrfs_alloc_chunk()
|- __btrfs_alloc_chunk() | |- Do all the work
|
btrfs_create_pending_block_groups() |
|- btrfs_insert_item() |
|- btrfs_finish_chunk_alloc() |
|- btrfs_alloc_dev_extent() |After the unification:
Kernel | Btrfs-progs
btrfs_alloc_chunk() | btrfs_alloc_chunk()
|- __btrfs_alloc_chunk() | |- __btrfs_alloc_chunk()
| |- btrfs_finish_chunk_alloc()
btrfs_create_pending_block_groups() | |- btrfs_alloc_dev_extent()
|- btrfs_insert_item() |
|- btrfs_finish_chunk_alloc() |And the similiar functions are share the same code base, with minor member/functions change.
Changelog:
v2:
Make error handler in patch 7 better.
New patches to unify more functions used in btrfs_alloc_chunk()
v3:
Use standard library function qsort() to replace the kernel sort(), suggested by David.
Qu Wenruo (9):
btrfs-progs: Refactor parameter of BTRFS_MAX_DEVS() from root to
fs_info
btrfs-progs: Merge btrfs_alloc_data_chunk into btrfs_alloc_chunk
btrfs-progs: Make btrfs_alloc_chunk to handle block group creation
btrfs-progs: Introduce btrfs_raid_array and related infrastructures
btrfs-progs: volumes: Allow find_free_dev_extent() to return maximum
hole size
btrfs-progs: volumes: Unify free dev extent search behavior between
kernel and btrfs-progs.
btrfs-progs: Move chunk stripe size calcution function to volumes.h
btrfs-progs: Use btrfs_device->fs_info to replace
btrfs_device->dev_root
btrfs-progs: Refactor btrfs_alloc_chunk to mimic kernel structure and
behavior
check/main.c | 22 -- convert/main.c | 10 +- ctree.h | 12 +- extent-tree.c | 12 +- kerncompat.h | 5 + mkfs/main.c | 27 +- utils.c | 2 +- volumes.c | 926 ++++++++++++++++++++++++++++++--------------------------- volumes.h | 66 +++- 9 files changed, 572 insertions(+), 510 deletions(-)
Signature · 1 line
2.16.2
PATCH v3 1/9 btrfs-progs: Refactor parameter of BTRFS_MAX_DEVS() from root to fs_info
--- volumes.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
volumes.c +3 -3
@@ -826,7 +826,7 @@ error:return ret; } -#define BTRFS_MAX_DEVS (r) ((BTRFS_LEAF_DATA_SIZE(r->fs_info) \ +#define BTRFS_MAX_DEVS (info) ((BTRFS_LEAF_DATA_SIZE(info) \ - sizeof(struct btrfs_item) \ - sizeof(struct btrfs_chunk)) \ / sizeof(struct btrfs_stripe) + 1)
@@ -882,12 +882,12 @@ int btrfs_alloc_chunk(struct btrfs_trans_handle *trans, calc_size =SZ_1G ; max_chunk_size =10 * calc_size; min_stripe_size =SZ_64M ; - max_stripes =BTRFS_MAX_DEVS (chunk_root); + max_stripes =BTRFS_MAX_DEVS (info); }else if (type &BTRFS_BLOCK_GROUP_METADATA ) { calc_size =SZ_1G ; max_chunk_size =4 * calc_size; min_stripe_size =SZ_32M ; - max_stripes =BTRFS_MAX_DEVS (chunk_root); + max_stripes =BTRFS_MAX_DEVS (info); } }if (type &BTRFS_BLOCK_GROUP_RAID1 ) {
Signature · 1 line
2.16.2
PATCH v3 2/9 btrfs-progs: Merge btrfs_alloc_data_chunk into btrfs_alloc_chunk
In reply to Qu WenruoWe used to have two chunk allocators, btrfs_alloc_chunk() and btrfs_alloc_data_chunk(), the former is the more generic one, while the later is only used in mkfs and convert, to allocate SINGLE data chunk.
Although btrfs_alloc_data_chunk() has some special hacks to cooperate with convert, it's quite simple to integrity it into the generic chunk allocator.
So merge them into one btrfs_alloc_chunk(), with extra @convert parameter and necessary comment, to make code less duplicated and less thing to maintain.
--- convert/main.c | 6 +- extent-tree.c | 2 +- mkfs/main.c | 8 +-- volumes.c | 219 ++++++++++++++++++++++----------------------------------- volumes.h | 5 +- 5 files changed, 94 insertions(+), 146 deletions(-)
convert/main.c +3 -3
@@ -910,9 +910,9 @@ static int make_convert_data_block_groups(struct btrfs_trans_handle *trans, len =min (max_chunk_size, cache->start + cache->size - cur); - ret =btrfs_alloc_data_chunk (trans, fs_info, - &cur_backup, len, -BTRFS_BLOCK_GROUP_DATA ,1 ); + ret =btrfs_alloc_chunk (trans, fs_info, + &cur_backup, &len, +BTRFS_BLOCK_GROUP_DATA , true);if (ret <0 )break ; ret = btrfs_make_block_group(trans, fs_info, 0,
extent-tree.c +1 -1
@@ -1906,7 +1906,7 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans,return 0 ; ret =btrfs_alloc_chunk (trans, fs_info, &start, &num_bytes, - space_info->flags); + space_info->flags, false);if (ret == -ENOSPC ) { space_info->full =1 ;return 0 ;
mkfs/main.c +4 -4
@@ -83,7 +83,7 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed, ret =btrfs_alloc_chunk (trans, fs_info, &chunk_start, &chunk_size,BTRFS_BLOCK_GROUP_METADATA | -BTRFS_BLOCK_GROUP_DATA ); +BTRFS_BLOCK_GROUP_DATA , false);if (ret == -ENOSPC ) {error ("no space to allocate data/metadata chunk" ); goto err;
@@ -100,7 +100,7 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed, }else { ret =btrfs_alloc_chunk (trans, fs_info, &chunk_start, &chunk_size, -BTRFS_BLOCK_GROUP_METADATA ); +BTRFS_BLOCK_GROUP_METADATA , false);if (ret == -ENOSPC ) {error ("no space to allocate metadata chunk" ); goto err;
@@ -134,7 +134,7 @@ static int create_data_block_groups(struct btrfs_trans_handle *trans,if (!mixed) { ret =btrfs_alloc_chunk (trans, fs_info, &chunk_start, &chunk_size, -BTRFS_BLOCK_GROUP_DATA ); +BTRFS_BLOCK_GROUP_DATA , false);if (ret == -ENOSPC ) {error ("no space to allocate data chunk" ); goto err;
@@ -242,7 +242,7 @@ static int create_one_raid_group(struct btrfs_trans_handle *trans,int ret; ret =btrfs_alloc_chunk (trans, fs_info, - &chunk_start, &chunk_size, type); + &chunk_start, &chunk_size, type, false);if (ret == -ENOSPC ) {error ("not enough free space to allocate chunk" );exit (1 );
volumes.c +85 -134
281 lines not on this page · raw message
volumes.h +1 -4
@@ -208,10 +208,7 @@ int btrfs_read_sys_array(struct btrfs_fs_info *fs_info);int btrfs_read_chunk_tree (struct btrfs_fs_info *fs_info);int btrfs_alloc_chunk (struct btrfs_trans_handle *trans,struct btrfs_fs_info *fs_info,u64 *start, -u64 *num_bytes,u64 type); -int btrfs_alloc_data_chunk (struct btrfs_trans_handle *trans, -struct btrfs_fs_info *fs_info,u64 *start, -u64 num_bytes,u64 type,int convert); +u64 *num_bytes,u64 type,bool convert);int btrfs_open_devices (struct btrfs_fs_devices *fs_devices,int flags);int btrfs_close_devices (struct btrfs_fs_devices *fs_devices);
Signature · 1 line
2.16.2
PATCH v3 3/9 btrfs-progs: Make btrfs_alloc_chunk to handle block group creation
In reply to Qu WenruoBefore this patch, chunk allocation is split into 2 parts:
1) Chunk allocation
Handled by btrfs_alloc_chunk(), which will insert chunk and device extent items.
2) Block group allocation
Handled by btrfs_make_block_group(), which will insert block group item and update space info.
However for chunk allocation, we don't really need to split these operations as all btrfs_alloc_chunk() has btrfs_make_block_group() followed.
So it's reasonable to merge btrfs_make_block_group() call into btrfs_alloc_chunk() to save several lines, and provides the basis for later btrfs_alloc_chunk() rework.
--- convert/main.c | 4 ---- extent-tree.c | 10 ++-------- mkfs/main.c | 19 ------------------- volumes.c | 10 ++++++---- 4 files changed, 8 insertions(+), 35 deletions(-)
convert/main.c +0 -4
@@ -915,10 +915,6 @@ static int make_convert_data_block_groups(struct btrfs_trans_handle *trans, BTRFS_BLOCK_GROUP_DATA, true);if (ret <0 )break ; - ret =btrfs_make_block_group (trans, fs_info,0 , -BTRFS_BLOCK_GROUP_DATA , cur, len); -if (ret <0 ) -break ; cur += len; } }
extent-tree.c +2 -8
@@ -1909,15 +1909,9 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans, space_info->flags, false);if (ret == -ENOSPC ) { space_info->full =1 ; -return 0 ; +return ret; } - -BUG_ON (ret); - - ret =btrfs_make_block_group (trans, fs_info,0 , space_info->flags, - start, num_bytes); -BUG_ON (ret); -return 0 ; +return ret; } static int update_block_group(struct btrfs_root *root,
mkfs/main.c +0 -19
@@ -88,12 +88,6 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed,error ("no space to allocate data/metadata chunk" ); goto err; } -if (ret) -return ret; - ret =btrfs_make_block_group (trans, fs_info,0 , -BTRFS_BLOCK_GROUP_METADATA | -BTRFS_BLOCK_GROUP_DATA , - chunk_start, chunk_size);if (ret)return ret; allocation->mixed += chunk_size;
@@ -107,12 +101,7 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed, }if (ret)return ret; - ret =btrfs_make_block_group (trans, fs_info,0 , -BTRFS_BLOCK_GROUP_METADATA , - chunk_start, chunk_size); allocation->metadata += chunk_size; -if (ret) -return ret; } root->fs_info->system_allocs =0 ;
@@ -141,12 +130,7 @@ static int create_data_block_groups(struct btrfs_trans_handle *trans, }if (ret)return ret; - ret =btrfs_make_block_group (trans, fs_info,0 , -BTRFS_BLOCK_GROUP_DATA , - chunk_start, chunk_size); allocation->data += chunk_size; -if (ret) -return ret; } err:
@@ -250,9 +234,6 @@ static int create_one_raid_group(struct btrfs_trans_handle *trans,if (ret)return ret; - ret =btrfs_make_block_group (trans, fs_info,0 , - type, chunk_start, chunk_size); - type &=BTRFS_BLOCK_GROUP_TYPE_MASK ;if (type ==BTRFS_BLOCK_GROUP_DATA ) { allocation->data += chunk_size;
volumes.c +6 -4
@@ -837,10 +837,9 @@ error: / sizeof(struct btrfs_stripe) + 1)/* -* Alloc a chunk, will insert dev extents, chunk item. -* NOTE: This function will not insert block group item nor mark newly -* allocated chunk available for later allocation. -* Block group item and free space update is handled by btrfs_make_block_group() +* Alloc a chunk, will insert dev extents, chunk item, and insert new +* block group and update space info (so that extent allocator can use +* newly allocated chunk). * * @start: return value of allocated chunk start bytenr. * @num_bytes: return value of allocated chunk size
@@ -1159,6 +1158,9 @@ alloc_chunk: }kfree (chunk); + + ret =btrfs_make_block_group (trans, info,0 , type, map->ce.start, + map->ce.size);return ret; out_chunk_map:
Signature · 1 line
2.16.2
PATCH v3 4/9 btrfs-progs: Introduce btrfs_raid_array and related infrastructures
In reply to Qu WenruoAs part of the effort to unify code and behavior between btrfs-progs and kernel, copy the btrfs_raid_array from kernel to btrfs-progs.
So later we can use the btrfs_raid_array[] to get needed raid info other than manually do if-else branches.
--- ctree.h | 12 +++++++++++- volumes.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ volumes.h | 30 +++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 1 deletion(-)
ctree.h +11 -1
@@ -958,7 +958,17 @@ struct btrfs_csum_item {#define BTRFS_BLOCK_GROUP_RAID5 (1ULL << 7)#define BTRFS_BLOCK_GROUP_RAID6 (1ULL << 8)#define BTRFS_BLOCK_GROUP_RESERVED BTRFS_AVAIL_ALLOC_BIT_SINGLE -#define BTRFS_NR_RAID_TYPES 7 + +enum btrfs_raid_types { +BTRFS_RAID_RAID10 , +BTRFS_RAID_RAID1 , +BTRFS_RAID_DUP , +BTRFS_RAID_RAID0 , +BTRFS_RAID_SINGLE , +BTRFS_RAID_RAID5 , +BTRFS_RAID_RAID6 , +BTRFS_NR_RAID_TYPES +};#define BTRFS_BLOCK_GROUP_TYPE_MASK (BTRFS_BLOCK_GROUP_DATA | \ BTRFS_BLOCK_GROUP_SYSTEM | \
volumes.c +66 -0
72 lines not on this page · raw message
volumes.h +30 -0
@@ -108,6 +108,36 @@ struct map_lookup {struct btrfs_bio_stripe stripes[]; }; +struct btrfs_raid_attr { +int sub_stripes;/* sub_stripes info for map */ +int dev_stripes;/* stripes per dev */ +int devs_max;/* max devs to use */ +int devs_min;/* min devs needed */ +int tolerated_failures;/* max tolerated fail devs */ +int devs_increment;/* ndevs has to be a multiple of this */ +int ncopies;/* how many copies to data has */ +}; + +extern const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES ]; + +static inline enum btrfs_raid_types btrfs_bg_flags_to_raid_index (u64 flags) +{ +if (flags &BTRFS_BLOCK_GROUP_RAID10 ) +return BTRFS_RAID_RAID10 ; +else if (flags &BTRFS_BLOCK_GROUP_RAID1 ) +return BTRFS_RAID_RAID1 ; +else if (flags &BTRFS_BLOCK_GROUP_DUP ) +return BTRFS_RAID_DUP ; +else if (flags &BTRFS_BLOCK_GROUP_RAID0 ) +return BTRFS_RAID_RAID0 ; +else if (flags &BTRFS_BLOCK_GROUP_RAID5 ) +return BTRFS_RAID_RAID5 ; +else if (flags &BTRFS_BLOCK_GROUP_RAID6 ) +return BTRFS_RAID_RAID6 ; + +return BTRFS_RAID_SINGLE ;/* BTRFS_BLOCK_GROUP_SINGLE */ +} +#define btrfs_multi_bio_size (n) (sizeof(struct btrfs_multi_bio) + \ (sizeof(struct btrfs_bio_stripe) * (n)))#define btrfs_map_lookup_size (n) (sizeof(struct map_lookup) + \
Signature · 1 line
2.16.2
PATCH v3 5/9 btrfs-progs: volumes: Allow find_free_dev_extent() to return maximum hole size
In reply to Qu WenruoJust as kernel find_free_dev_extent(), allow it to return maximum hole size for us to build device list for later chunk allocator rework.
--- volumes.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
volumes.c +3 -3
@@ -516,10 +516,10 @@ out: }static int find_free_dev_extent (struct btrfs_device *device,u64 num_bytes, -u64 *start) +u64 *start,u64 *len) {/* FIXME use last free of some kind */ -return find_free_dev_extent_start (device, num_bytes,0 , start,NULL ); +return find_free_dev_extent_start (device, num_bytes,0 , start, len); } static int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans,
@@ -543,7 +543,7 @@ static int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans,* is responsible to make sure it's free. */ if (!convert) { - ret =find_free_dev_extent (device, num_bytes, start); + ret =find_free_dev_extent (device, num_bytes, start,NULL );if (ret) goto err; }
Signature · 1 line
2.16.2
PATCH v3 6/9 btrfs-progs: volumes: Unify free dev extent search behavior between kernel and btrfs-progs.
In reply to Qu WenruoAs preparation to create libbtrfs which shares code between kernel and btrfs, this patch mainly unifies the search for free device extents.
The main modifications are:
1) Search for free device extent
Use the kernel method, by sorting the devices by its max hole capability, and use that sorted result to determine stripe size and chunk size.
The previous method, which uses available bytes of each device to search, can't handle scattered small holes in each device.
2) Chunk/stripe minimal size
Remove the minimal size requirement.
Now the real minimal stripe size is 64K (BTRFS_STRIPE_LEN), the same as kernel one.
While up limit is still kept as is, and minimal device size is still kept for a while.
But since we no longer have strange minimal stripe size limit, existing minimal device size calculation won't cause any problem.
3) How to insert device extents
Although not the same as kernel, here we follow kernel behavior to delay dev extents insert.
There is plan to follow kernel __btrfs_alloc_chunk() to make it only handle chunk mapping allocation, while do nothing with tree operation.
4) Usage of btrfs_raid_array[]
Which makes a lot of old if-else branches disappear.
There are still a lot of work to do (both kernel and btrfs-progs) before we could starting extracting code into libbtrfs, but this should make libbtrfs inside our reach.
--- kerncompat.h | 5 + volumes.c | 620 ++++++++++++++++++++++++++--------------------------------- volumes.h | 7 + 3 files changed, 284 insertions(+), 348 deletions(-)
kerncompat.h +5 -0
@@ -285,6 +285,7 @@ static inline int IS_ERR_OR_NULL(const void *ptr)*/ #define kmalloc (x, y) malloc(x)#define kzalloc (x, y) calloc(1, x) +#define kcalloc (x, y) calloc(x, y)#define kstrdup (x, y) strdup(x)#define kfree (x) free(x)#define vmalloc (x) malloc(x)
@@ -394,4 +395,8 @@ struct __una_u64 { __le64 x; } __attribute__((__packed__));#define noinline#endif +static inline u64 div_u64 (u64 dividend,u32 divisor) +{ +return dividend / ((u64 ) divisor); +}#endif
volumes.c +272 -348
740 lines not on this page · raw message
volumes.h +7 -0
@@ -108,6 +108,13 @@ struct map_lookup {struct btrfs_bio_stripe stripes[]; }; +struct btrfs_device_info { +struct btrfs_device *dev; +u64 dev_offset; +u64 max_avail; +u64 total_avail; +}; +struct btrfs_raid_attr {int sub_stripes;/* sub_stripes info for map */ int dev_stripes;/* stripes per dev */
Signature · 1 line
2.16.2
PATCH v3 7/9 btrfs-progs: Move chunk stripe size calcution function to volumes.h
In reply to Qu Wenruo--- check/main.c | 22 ---------------------- volumes.h | 22 ++++++++++++++++++++++ 2 files changed, 22 insertions(+), 22 deletions(-)
check/main.c +0 -22
@@ -7638,28 +7638,6 @@ repair_abort:return err; } -u64 calc_stripe_length (u64 type,u64 length,int num_stripes) -{ -u64 stripe_size; - -if (type &BTRFS_BLOCK_GROUP_RAID0 ) { - stripe_size = length; - stripe_size /= num_stripes; - }else if (type &BTRFS_BLOCK_GROUP_RAID10 ) { - stripe_size = length *2 ; - stripe_size /= num_stripes; - }else if (type &BTRFS_BLOCK_GROUP_RAID5 ) { - stripe_size = length; - stripe_size /= (num_stripes -1 ); - }else if (type &BTRFS_BLOCK_GROUP_RAID6 ) { - stripe_size = length; - stripe_size /= (num_stripes -2 ); - }else { - stripe_size = length; - } -return stripe_size; -} -/* * Check the chunk with its block group/dev list ref: * Return 0 if all refs seems valid.
volumes.h +22 -0
@@ -216,6 +216,28 @@ static inline int check_crossing_stripes(struct btrfs_fs_info *fs_info, (bg_offset + len - 1) / BTRFS_STRIPE_LEN); } +static inline u64 calc_stripe_length (u64 type,u64 length,int num_stripes) +{ +u64 stripe_size; + +if (type &BTRFS_BLOCK_GROUP_RAID0 ) { + stripe_size = length; + stripe_size /= num_stripes; + }else if (type &BTRFS_BLOCK_GROUP_RAID10 ) { + stripe_size = length *2 ; + stripe_size /= num_stripes; + }else if (type &BTRFS_BLOCK_GROUP_RAID5 ) { + stripe_size = length; + stripe_size /= (num_stripes -1 ); + }else if (type &BTRFS_BLOCK_GROUP_RAID6 ) { + stripe_size = length; + stripe_size /= (num_stripes -2 ); + }else { + stripe_size = length; + } +return stripe_size; +} + int __btrfs_map_block(struct btrfs_fs_info *fs_info, int rw, u64 logical, u64 *length, u64 *type, struct btrfs_multi_bio **multi_ret, int mirror_num,
Signature · 1 line
2.16.2
PATCH v3 8/9 btrfs-progs: Use btrfs_device->fs_info to replace btrfs_device->dev_root
In reply to Qu WenruoSame as kernel declaration.
--- utils.c | 2 +- volumes.c | 6 +++--- volumes.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-)
utils.c +1 -1
@@ -216,7 +216,7 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans, device->total_bytes = device_total_bytes; device->bytes_used =0 ; device->total_ios =0 ; - device->dev_root = fs_info->dev_root; + device->fs_info = fs_info; device->name =strdup (path);if (!device->name) { ret = -ENOMEM ;
volumes.c +3 -3
@@ -379,7 +379,7 @@ static int find_free_dev_extent_start(struct btrfs_device *device, u64 *start, u64 *len) {struct btrfs_key key; -struct btrfs_root *root = device->dev_root; +struct btrfs_root *root = device->fs_info->dev_root;struct btrfs_dev_extent *dev_extent;struct btrfs_path *path;u64 hole_size;
@@ -723,7 +723,7 @@ int btrfs_update_device(struct btrfs_trans_handle *trans,struct extent_buffer *leaf;struct btrfs_key key; - root = device->dev_root->fs_info->chunk_root; + root = device->fs_info->chunk_root; path =btrfs_alloc_path ();if (!path)
@@ -1894,7 +1894,7 @@ static int read_one_dev(struct btrfs_fs_info *fs_info, }fill_device_from_item (leaf, dev_item, device); - device->dev_root = fs_info->dev_root; + device->fs_info = fs_info;return ret; }
volumes.h +1 -1
@@ -26,7 +26,7 @@struct btrfs_device {struct list_head dev_list; -struct btrfs_root *dev_root; +struct btrfs_fs_info *fs_info;struct btrfs_fs_devices *fs_devices;u64 total_ios;
Signature · 1 line
2.16.2
PATCH v3 9/9 btrfs-progs: Refactor btrfs_alloc_chunk to mimic kernel structure and behavior
In reply to Qu WenruoKernel uses a delayed chunk allocation behavior for metadata chunks
KERNEL:btrfs_alloc_chunk()
|- __btrfs_alloc_chunk(): Only allocate chunk mapping
|- btrfs_make_block_group(): Add corresponding bg to fs_info->new_bgs
Then at transaction commit time, it finishes the remaining work:btrfs_start_dirty_block_groups():
|- btrfs_create_pending_block_groups()
|- btrfs_insert_item(): To insert block group item
|- btrfs_finish_chunk_alloc(): Insert chunk items/dev extents
Although btrfs-progs btrfs_alloc_chunk() does all the work in one function, it can still benefit from function refactor like:
btrfs-progs:btrfs_alloc_chunk(): Wrapper for both normal and convert chunks
|- __btrfs_alloc_chunk(): Only alloc chunk mapping | |- btrfs_make_block_group(): <<Insert bg items into extent tree>> |- btrfs_finish_chunk_alloc(): Insert chunk items/dev extents
With such refactor, the following functions can share most of its code with kernel now:__btrfs_alloc_chunk()btrfs_finish_chunk_alloc()btrfs_alloc_dev_extent()
--- volumes.c | 421 ++++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 260 insertions(+), 161 deletions(-)
volumes.c +260 -161
@@ -522,55 +522,40 @@ static int find_free_dev_extent(struct btrfs_device *device, u64 num_bytes,return find_free_dev_extent_start (device, num_bytes,0 , start, len); } -static int btrfs_insert_dev_extents (struct btrfs_trans_handle *trans, -struct btrfs_fs_info *fs_info, -struct map_lookup *map,u64 stripe_size) +static int btrfs_alloc_dev_extent (struct btrfs_trans_handle *trans, +struct btrfs_device *device, +u64 chunk_offset,u64 physical, +u64 stripe_size) { -int ret =0 ; -struct btrfs_path path; +int ret; +struct btrfs_path *path; +struct btrfs_fs_info *fs_info = device->fs_info;struct btrfs_root *root = fs_info->dev_root;struct btrfs_dev_extent *extent;struct extent_buffer *leaf;struct btrfs_key key; -int i; -btrfs_init_path (&path); - -for (i =0 ; i < map->num_stripes; i++) { -struct btrfs_device *device = map->stripes[i].dev; - - key.objectid = device->devid; - key.offset = map->stripes[i].physical; - key.type =BTRFS_DEV_EXTENT_KEY ; + path =btrfs_alloc_path (); +if (!path) +return -ENOMEM ; - ret =btrfs_insert_empty_item (trans, root, &path, &key, -sizeof (*extent)); -if (ret <0 ) - goto out; - leaf = path.nodes[0 ]; - extent =btrfs_item_ptr (leaf, path.slots[0 ], - struct btrfs_dev_extent); -btrfs_set_dev_extent_chunk_tree (leaf, extent, + key.objectid = device->devid; + key.offset = physical; + key.type =BTRFS_DEV_EXTENT_KEY ; + ret =btrfs_insert_empty_item (trans, root, path, &key,sizeof (*extent)); +if (ret) + goto out; + leaf = path->nodes[0 ]; + extent = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dev_extent); +btrfs_set_dev_extent_chunk_tree (leaf, extent,BTRFS_CHUNK_TREE_OBJECTID ); -btrfs_set_dev_extent_chunk_objectid (leaf, extent, -BTRFS_FIRST_CHUNK_TREE_OBJECTID ); -btrfs_set_dev_extent_chunk_offset (leaf, extent, map->ce.start); - -write_extent_buffer (leaf, fs_info->chunk_tree_uuid, - (unsigned long )btrfs_dev_extent_chunk_tree_uuid (extent), -BTRFS_UUID_SIZE ); - -btrfs_set_dev_extent_length (leaf, extent, stripe_size); -btrfs_mark_buffer_dirty (leaf); -btrfs_release_path (&path); - - device->bytes_used += stripe_size; - ret =btrfs_update_device (trans, device); -if (ret <0 ) - goto out; - } +btrfs_set_dev_extent_chunk_objectid (leaf, extent, +BTRFS_FIRST_CHUNK_TREE_OBJECTID ); +btrfs_set_dev_extent_chunk_offset (leaf, extent, chunk_offset); +btrfs_set_dev_extent_length (leaf, extent, stripe_size); +btrfs_mark_buffer_dirty (leaf); out: -btrfs_release_path (&path); +btrfs_free_path (path);return ret; }
@@ -812,28 +797,28 @@ static int btrfs_cmp_device_info(const void *a, const void *b) / sizeof(struct btrfs_stripe) + 1)/* -* Alloc a chunk, will insert dev extents, chunk item, and insert new -* block group and update space info (so that extent allocator can use -* newly allocated chunk). +* Alloc a chunk mapping. +* Will do chunk size calculation and free dev extent search, and insert +* chunk mapping into chunk mapping tree. +* +* NOTE: This function doesn't handle any chunk item/dev extent insert. +* chunk item/dev extent insert is handled by later btrfs_finish_chunk_alloc(). +* And for convert chunk (1:1 mapped, more flex chunk location), it's +* handled by __btrfs_alloc_convert_chunk(). +* +* Qu: Block group item is still inserted in this function by +* btrfs_make_block_group(), this still differs from kernel. * * @start: return value of allocated chunk start bytenr. * @num_bytes: return value of allocated chunk size * @type: chunk type (including both profile and type) -* @convert: if the chunk is allocated for convert case. -* If @convert is true, chunk allocator will skip device extent -* search, but use *start and *num_bytes as chunk start/num_bytes -* and devive offset, to build a 1:1 chunk mapping for convert. */ -int btrfs_alloc_chunk (struct btrfs_trans_handle *trans, -struct btrfs_fs_info *info,u64 *start, -u64 *num_bytes,u64 type,bool convert) +static int __btrfs_alloc_chunk (struct btrfs_trans_handle *trans, +struct btrfs_fs_info *info,u64 *start, +u64 *num_bytes,u64 type) { -struct btrfs_root *extent_root = info->extent_root; -struct btrfs_root *chunk_root = info->chunk_root;struct btrfs_device *device =NULL ; -struct btrfs_chunk *chunk;struct list_head *dev_list = &info->fs_devices->devices; -struct btrfs_stripe *stripe;struct map_lookup *map;struct btrfs_device_info *devices_info =NULL ;u64 percent_max;
@@ -844,8 +829,6 @@ int btrfs_alloc_chunk(struct btrfs_trans_handle *trans,int index;int ndevs =0 ;int rw_devs =0 ; -int stripe_len =BTRFS_STRIPE_LEN ; -struct btrfs_key key;u64 offset;int data_stripes;/* number of stripes that counts for bg size */ int sub_stripes;/* sub_stripes info for map */
@@ -873,34 +856,6 @@ int btrfs_alloc_chunk(struct btrfs_trans_handle *trans, devs_increment = btrfs_raid_array[index].devs_increment; ncopies = btrfs_raid_array[index].ncopies; -if (convert) { -/* For convert, profile must be SINGLE */ -if (type &BTRFS_BLOCK_GROUP_PROFILE_MASK ) { -error ("convert only suports SINGLE profile" ); -return -EINVAL ; - } -if (!IS_ALIGNED (*start, info->sectorsize)) { -error ("chunk start not aligned, start=%llu sectorsize=%u" , - *start, info->sectorsize); -return -EINVAL ; - } -if (!IS_ALIGNED (*num_bytes, info->sectorsize)) { -error ("chunk size not aligned, size=%llu sectorsize=%u" , - *num_bytes, info->sectorsize); -return -EINVAL ; - } - num_stripes =1 ; - data_stripes =1 ; - offset = *start; - stripe_size = *num_bytes; -/* -* For convert, we use 1:1 chunk mapping specified by @start and -* @num_bytes, so there is no need to go through dev_extent -* searching. -*/ - goto alloc_chunk; - } -/* * Chunk size calculation part. */
@@ -1046,55 +1001,23 @@ int btrfs_alloc_chunk(struct btrfs_trans_handle *trans,/* * Fill chunk mapping and chunk stripes */ -alloc_chunk: -if (!convert) { - ret =find_next_chunk (info, &offset); -if (ret) - goto out_devices_info; - } - key.objectid =BTRFS_FIRST_CHUNK_TREE_OBJECTID ; - key.type =BTRFS_CHUNK_ITEM_KEY ; - key.offset = offset; - *num_bytes = stripe_size * data_stripes; - - chunk =kmalloc (btrfs_chunk_item_size (num_stripes),GFP_NOFS ); -if (!chunk) + ret =find_next_chunk (info, &offset); +if (ret) goto out_devices_info; + *num_bytes = stripe_size * data_stripes; map =kmalloc (btrfs_map_lookup_size (num_stripes),GFP_NOFS );if (!map) goto out_chunk_map; map->num_stripes = num_stripes; -if (convert) { - device = list_entry(dev_list->next, struct btrfs_device, - dev_list); +for (i =0 ; i < ndevs; i++) { +for (j =0 ; j < dev_stripes; ++j) { +int s = i * dev_stripes + j; - map->stripes[0 ].dev = device; - map->stripes[0 ].physical = *start; - stripe = &chunk->stripe; -btrfs_set_stack_stripe_devid (stripe, device->devid); -btrfs_set_stack_stripe_offset (stripe, *start); -memcpy (stripe->dev_uuid, device->uuid,BTRFS_UUID_SIZE ); - }else { -for (i =0 ; i < ndevs; i++) { -for (j =0 ; j < dev_stripes; ++j) { -int s = i * dev_stripes + j; - - device = devices_info[i].dev; - map->stripes[s].dev = device; - map->stripes[s].physical = - devices_info[i].dev_offset + - j * stripe_size; - stripe = &chunk->stripe + s; -btrfs_set_stack_stripe_devid (stripe, - device->devid); -btrfs_set_stack_stripe_offset (stripe, - devices_info[i].dev_offset + - j * stripe_size); -memcpy (stripe->dev_uuid, device->uuid, -BTRFS_UUID_SIZE ); - } + map->stripes[s].dev = devices_info[i].dev; + map->stripes[s].physical = devices_info[i].dev_offset + + j * stripe_size; } } map->stripe_len =BTRFS_STRIPE_LEN ;
@@ -1103,60 +1026,236 @@ alloc_chunk: map->type = type; map->sub_stripes = sub_stripes; map->sector_size = info->sectorsize; - map->ce.start = key.offset; + map->ce.start = offset; map->ce.size = *num_bytes; +kfree (devices_info); +/* +* Insert chunk mapping and block group +*/ ret =insert_cache_extent (&info->mapping_tree.cache_tree, &map->ce);if (ret <0 ) goto out_chunk_map; -/* key was set above */ -btrfs_set_stack_chunk_length (chunk, *num_bytes); -btrfs_set_stack_chunk_owner (chunk, extent_root->root_key.objectid); -btrfs_set_stack_chunk_stripe_len (chunk, stripe_len); -btrfs_set_stack_chunk_type (chunk, type); -btrfs_set_stack_chunk_num_stripes (chunk, num_stripes); -btrfs_set_stack_chunk_io_align (chunk, stripe_len); -btrfs_set_stack_chunk_io_width (chunk, stripe_len); -btrfs_set_stack_chunk_sector_size (chunk, info->sectorsize); -btrfs_set_stack_chunk_sub_stripes (chunk, sub_stripes); + ret =btrfs_make_block_group (trans, info,0 , type, offset, + *num_bytes); + *start = offset; +return ret; + +out_chunk_map: +kfree (map); +out_devices_info: +kfree (devices_info); +return ret; +} + +/* +* Alloc a chunk mapping for convert. +* Convert needs special SINGLE chunk whose logical bytenr is the same as its +* physical bytenr. +* Chunk size and start bytenr are all specified by @start and @num_bytes +* +* And just like __btrfs_alloc_chunk() this only handles chunk mapping and +* block group item. +*/ +static int __btrfs_alloc_convert_chunk (struct btrfs_trans_handle *trans, +struct btrfs_fs_info *fs_info,u64 start, +u64 num_bytes,u64 type) +{ +struct list_head *dev_list = &fs_info->fs_devices->devices; +struct map_lookup *map; +struct btrfs_device *device; +int ret; + +/* For convert, profile must be SINGLE */ +if (type &BTRFS_BLOCK_GROUP_PROFILE_MASK ) { +error ("convert only suports SINGLE profile" ); +return -EINVAL ; + } +if (!IS_ALIGNED (start, fs_info->sectorsize)) { +error ("chunk start not aligned, start=%llu sectorsize=%u" , + start, fs_info->sectorsize); +return -EINVAL ; + } +if (!IS_ALIGNED (num_bytes, fs_info->sectorsize)) { +error ("chunk size not aligned, size=%llu sectorsize=%u" , + num_bytes, fs_info->sectorsize); +return -EINVAL ; + } +if (list_empty (dev_list)) { +error ("no writable device" ); +return -ENODEV ; + } + + device = list_entry(dev_list->next, struct btrfs_device, dev_list); + map =malloc (btrfs_map_lookup_size (1 )); +if (!map) +return -ENOMEM ; + map->num_stripes =1 ; + map->stripes[0 ].dev = device; + map->stripes[0 ].physical = start; + map->stripe_len =BTRFS_STRIPE_LEN ; + map->io_align =BTRFS_STRIPE_LEN ; + map->io_width =BTRFS_STRIPE_LEN ; + map->type = type; + map->sub_stripes =1 ; + map->sector_size = fs_info->sectorsize; + map->ce.start = start; + map->ce.size = num_bytes; + + ret =insert_cache_extent (&fs_info->mapping_tree.cache_tree, &map->ce); +if (ret <0 ) + goto error; + ret =btrfs_make_block_group (trans, fs_info,0 , type, start, num_bytes); +return ret; +error: +free (map); +return ret; +} + +/* +* Finish the chunk allocation by inserting needed chunk item and device +* extents, and update device used bytes +*/ +static int btrfs_finish_chunk_alloc (struct btrfs_trans_handle *trans, +struct btrfs_fs_info *fs_info, +u64 chunk_start,u64 chunk_size) +{ +struct btrfs_root *extent_root = fs_info->extent_root; +struct btrfs_root *chunk_root = fs_info->chunk_root; +struct btrfs_key key; +struct btrfs_device *device; +struct btrfs_chunk *chunk; +struct btrfs_stripe *stripe; +struct cache_extent *ce; +struct map_lookup *map; +size_t item_size; +u64 dev_offset; +u64 stripe_size; +int i =0 ; +int ret =0 ; + + ce =search_cache_extent (&fs_info->mapping_tree.cache_tree, chunk_start); +if (!ce) +return -ENOENT ; + + map = container_of(ce, struct map_lookup, ce); + item_size =btrfs_chunk_item_size (map->num_stripes); + stripe_size =calc_stripe_length (map->type, map->ce.size, + map->num_stripes); + + chunk =kzalloc (item_size,GFP_NOFS ); +if (!chunk) { + ret = -ENOMEM ; + goto out; + }/* -* Insert chunk item and chunk mapping. +* Take the device list mutex to prevent races with the final phase of +* a device replace operation that replaces the device object associated +* with the map's stripes, because the device object's id can change +* at any time during that final phase of the device replace operation +* (dev-replace.c:btrfs_dev_replace_finishing()). */ - ret =btrfs_insert_item (trans, chunk_root, &key, chunk, -btrfs_chunk_item_size (num_stripes)); -BUG_ON (ret); - *start = key.offset; +/* mutex_lock(&fs_info->fs_devices->device_list_mutex); */ +for (i =0 ; i < map->num_stripes; i++) { + device = map->stripes[i].dev; + dev_offset = map->stripes[i].physical; -if (type &BTRFS_BLOCK_GROUP_SYSTEM ) { - ret =btrfs_add_system_chunk (info, &key, - chunk,btrfs_chunk_item_size (num_stripes)); -if (ret <0 ) - goto out_chunk; + device->bytes_used += stripe_size; + ret =btrfs_update_device (trans, device); +if (ret) +break ; + ret =btrfs_alloc_dev_extent (trans, device, chunk_start, + dev_offset, stripe_size); +if (ret) +break ; + } +if (ret) { +/* mutex_unlock(&fs_info->fs_devices->device_list_mutex); */ + goto out; } + stripe = &chunk->stripe; +for (i =0 ; i < map->num_stripes; i++) { + device = map->stripes[i].dev; + dev_offset = map->stripes[i].physical; + +btrfs_set_stack_stripe_devid (stripe, device->devid); +btrfs_set_stack_stripe_offset (stripe, dev_offset); +memcpy (stripe->dev_uuid, device->uuid,BTRFS_UUID_SIZE ); + stripe++; + } +/* mutex_unlock(&fs_info->fs_devices->device_list_mutex); */ + +btrfs_set_stack_chunk_length (chunk, chunk_size); +btrfs_set_stack_chunk_owner (chunk, extent_root->root_key.objectid); +btrfs_set_stack_chunk_stripe_len (chunk, map->stripe_len); +btrfs_set_stack_chunk_type (chunk, map->type); +btrfs_set_stack_chunk_num_stripes (chunk, map->num_stripes); +btrfs_set_stack_chunk_io_align (chunk, map->stripe_len); +btrfs_set_stack_chunk_io_width (chunk, map->stripe_len); +btrfs_set_stack_chunk_sector_size (chunk, fs_info->sectorsize); +btrfs_set_stack_chunk_sub_stripes (chunk, map->sub_stripes); + + key.objectid =BTRFS_FIRST_CHUNK_TREE_OBJECTID ; + key.type =BTRFS_CHUNK_ITEM_KEY ; + key.offset = chunk_start; + + ret =btrfs_insert_item (trans, chunk_root, &key, chunk, item_size); +if (ret ==0 && map->type &BTRFS_BLOCK_GROUP_SYSTEM ) { +/* +* TODO: Cleanup of inserted chunk root in case of +* failure. +*/ + ret =btrfs_add_system_chunk (fs_info, &key, chunk, item_size); + } + +out:kfree (chunk); +return ret; +} + +/* +* Alloc a chunk. +* Will do all the needed work including seaching free device extent, insert +* chunk mapping, chunk item, block group item and device extents. +* +* @start: return value of allocated chunk start bytenr. +* @num_bytes: return value of allocated chunk size +* @type: chunk type (including both profile and type) +* @convert: if the chunk is allocated for convert case. +* If @convert is true, chunk allocator will skip device extent +* search, but use *start and *num_bytes as chunk start/num_bytes +* and devive offset, to build a 1:1 chunk mapping for convert. +*/ +int btrfs_alloc_chunk (struct btrfs_trans_handle *trans, +struct btrfs_fs_info *fs_info,u64 *start,u64 *num_bytes, +u64 type,bool convert) +{ +int ret;/* -* Insert device extents +* Allocate chunk mapping */ - ret =btrfs_insert_dev_extents (trans, info, map, stripe_size); -if (ret <0 ) - goto out_devices_info; - - ret =btrfs_make_block_group (trans, info,0 , type, map->ce.start, - map->ce.size); -kfree (devices_info); -return ret; +if (convert) + ret =__btrfs_alloc_convert_chunk (trans, fs_info, *start, + *num_bytes, type); +else + ret =__btrfs_alloc_chunk (trans, fs_info, start, num_bytes, + type); +if (ret <0 ) { +error ("failed to allocate chunk mapping: %s" ,strerror (-ret)); +return ret; + } -out_chunk_map: -kfree (map); -out_chunk: -kfree (chunk); -out_devices_info: -kfree (devices_info); +/* +* Insert the remaining part (insert variant items) +*/ + ret =btrfs_finish_chunk_alloc (trans, fs_info, *start, *num_bytes); +if (ret <0 ) +error ("failed to finish chunk allocation: %s" ,strerror (-ret));return ret; }
Signature · 1 line
2.16.2