/* Maximum number of zones to report per blkdev_report_zones() call */ #define BTRFS_REPORT_NR_ZONES 4096 /* Invalid allocation pointer value for missing devices */ #define WP_MISSING_DEV ((u64)-1) /* Pseudo write pointer value for conventional zone */ #define WP_CONVENTIONAL ((u64)-2)
/* * Location of the first zone of superblock logging zone pairs. * * - primary superblock: 0B (zone 0) * - first copy: 512G (zone starting at that offset) * - second copy: 4T (zone starting at that offset)
*/ #define BTRFS_SB_LOG_PRIMARY_OFFSET (0ULL) #define BTRFS_SB_LOG_FIRST_OFFSET (512ULL * SZ_1G) #define BTRFS_SB_LOG_SECOND_OFFSET (4096ULL * SZ_1G)
/* Number of superblock log zones */ #define BTRFS_NR_SB_LOG_ZONES 2
/* Default number of max active zones when the device has no limits. */ #define BTRFS_DEFAULT_MAX_ACTIVE_ZONES 128
/* * Minimum of active zones we need: * * - BTRFS_SUPER_MIRROR_MAX zones for superblock mirrors * - 3 zones to ensure at least one zone per SYSTEM, META and DATA block group * - 1 zone for tree-log dedicated block group * - 1 zone for relocation
*/ #define BTRFS_MIN_ACTIVE_ZONES (BTRFS_SUPER_MIRROR_MAX + 5)
/* * Minimum / maximum supported zone size. Currently, SMR disks have a zone * size of 256MiB, and we are expecting ZNS drives to be in the 1-4GiB range. * We do not expect the zone size to become larger than 8GiB or smaller than * 4MiB in the near future.
*/ #define BTRFS_MAX_ZONE_SIZE SZ_8G #define BTRFS_MIN_ZONE_SIZE SZ_4M
for (int i = 0; i < BTRFS_NR_SB_LOG_ZONES; i++) {
ASSERT(zones[i].type != BLK_ZONE_TYPE_CONVENTIONAL);
empty[i] = (zones[i].cond == BLK_ZONE_COND_EMPTY);
full[i] = sb_zone_is_full(&zones[i]);
}
/* * Possible states of log buffer zones * * Empty[0] In use[0] Full[0] * Empty[1] * 0 1 * In use[1] x x 1 * Full[1] 0 0 C * * Log position: * *: Special case, no superblock is written * 0: Use write pointer of zones[0] * 1: Use write pointer of zones[1] * C: Compare super blocks from zones[0] and zones[1], use the latest * one determined by generation * x: Invalid state
*/
if (empty[0] && empty[1]) { /* Special case to distinguish no superblock to read */
*wp_ret = zones[0].start << SECTOR_SHIFT; return -ENOENT;
} elseif (full[0] && full[1]) { /* Compare two super blocks */ struct address_space *mapping = bdev->bd_mapping; struct page *page[BTRFS_NR_SB_LOG_ZONES]; struct btrfs_super_block *super[BTRFS_NR_SB_LOG_ZONES];
for (int i = 0; i < BTRFS_NR_SB_LOG_ZONES; i++) {
u64 zone_end = (zones[i].start + zones[i].capacity) << SECTOR_SHIFT;
u64 bytenr = ALIGN_DOWN(zone_end, BTRFS_SUPER_INFO_SIZE) -
BTRFS_SUPER_INFO_SIZE;
page[i] = read_cache_page_gfp(mapping,
bytenr >> PAGE_SHIFT, GFP_NOFS); if (IS_ERR(page[i])) { if (i == 1)
btrfs_release_disk_super(super[0]); return PTR_ERR(page[i]);
}
super[i] = page_address(page[i]);
}
/* * Emulate blkdev_report_zones() for a non-zoned device. It slices up the block * device into static sized chunks and fake a conventional zone on each of * them.
*/ staticint emulate_report_zones(struct btrfs_device *device, u64 pos, struct blk_zone *zones, unsignedint nr_zones)
{ const sector_t zone_sectors = device->fs_info->zone_size >> SECTOR_SHIFT;
sector_t bdev_size = bdev_nr_sectors(device->bdev); unsignedint i;
pos >>= SECTOR_SHIFT; for (i = 0; i < nr_zones; i++) {
zones[i].start = i * zone_sectors + pos;
zones[i].len = zone_sectors;
zones[i].capacity = zone_sectors;
zones[i].wp = zones[i].start + zone_sectors;
zones[i].type = BLK_ZONE_TYPE_CONVENTIONAL;
zones[i].cond = BLK_ZONE_COND_NOT_WP;
ASSERT(IS_ALIGNED(pos, zinfo->zone_size));
zno = pos >> zinfo->zone_size_shift; /* * We cannot report zones beyond the zone end. So, it is OK to * cap *nr_zones to at the end.
*/
*nr_zones = min_t(u32, *nr_zones, zinfo->nr_zones - zno);
for (i = 0; i < *nr_zones; i++) { struct blk_zone *zone_info;
zone_info = &zinfo->zone_cache[zno + i]; if (!zone_info->len) break;
}
if (i == *nr_zones) { /* Cache hit on all the zones */
memcpy(zones, zinfo->zone_cache + zno, sizeof(*zinfo->zone_cache) * *nr_zones); return 0;
}
}
ret = blkdev_report_zones(device->bdev, pos >> SECTOR_SHIFT, *nr_zones,
copy_zone_info_cb, zones); if (ret < 0) {
btrfs_err(device->fs_info, "zoned: failed to read zone %llu on %s (devid %llu)",
pos, rcu_dereference(device->name),
device->devid); return ret;
}
*nr_zones = ret; if (!ret) return -EIO;
/* The emulated zone size is determined from the size of device extent */ staticint calculate_emulated_zone_size(struct btrfs_fs_info *fs_info)
{
BTRFS_PATH_AUTO_FREE(path); struct btrfs_root *root = fs_info->dev_root; struct btrfs_key key; struct extent_buffer *leaf; struct btrfs_dev_extent *dext; int ret = 0;
path = btrfs_alloc_path(); if (!path) return -ENOMEM;
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); if (ret < 0) return ret;
if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) {
ret = btrfs_next_leaf(root, path); if (ret < 0) return ret; /* No dev extents at all? Not good */ if (ret > 0) return -EUCLEAN;
}
int btrfs_get_dev_zone_info_all_devices(struct btrfs_fs_info *fs_info)
{ struct btrfs_fs_devices *fs_devices = fs_info->fs_devices; struct btrfs_device *device; int ret = 0;
/* fs_info->zone_size might not set yet. Use the incomapt flag here. */ if (!btrfs_fs_incompat(fs_info, ZONED)) return 0;
mutex_lock(&fs_devices->device_list_mutex);
list_for_each_entry(device, &fs_devices->devices, dev_list) { /* We can skip reading of zone info for missing devices */ if (!device->bdev) continue;
ret = btrfs_get_dev_zone_info(device, true); if (ret) break;
}
mutex_unlock(&fs_devices->device_list_mutex);
/* We reject devices with a zone size larger than 8GB */ if (zone_info->zone_size > BTRFS_MAX_ZONE_SIZE) {
btrfs_err(fs_info, "zoned: %s: zone size %llu larger than supported maximum %llu",
rcu_dereference(device->name),
zone_info->zone_size, BTRFS_MAX_ZONE_SIZE);
ret = -EINVAL; goto out;
} elseif (zone_info->zone_size < BTRFS_MIN_ZONE_SIZE) {
btrfs_err(fs_info, "zoned: %s: zone size %llu smaller than supported minimum %u",
rcu_dereference(device->name),
zone_info->zone_size, BTRFS_MIN_ZONE_SIZE);
ret = -EINVAL; goto out;
}
max_active_zones = min_not_zero(bdev_max_active_zones(bdev),
bdev_max_open_zones(bdev)); if (!max_active_zones && zone_info->nr_zones > BTRFS_DEFAULT_MAX_ACTIVE_ZONES)
max_active_zones = BTRFS_DEFAULT_MAX_ACTIVE_ZONES; if (max_active_zones && max_active_zones < BTRFS_MIN_ACTIVE_ZONES) {
btrfs_err(fs_info, "zoned: %s: max active zones %u is too small, need at least %u active zones",
rcu_dereference(device->name), max_active_zones,
BTRFS_MIN_ACTIVE_ZONES);
ret = -EINVAL; goto out;
}
zone_info->max_active_zones = max_active_zones;
zone_info->seq_zones = bitmap_zalloc(zone_info->nr_zones, GFP_KERNEL); if (!zone_info->seq_zones) {
ret = -ENOMEM; goto out;
}
zone_info->empty_zones = bitmap_zalloc(zone_info->nr_zones, GFP_KERNEL); if (!zone_info->empty_zones) {
ret = -ENOMEM; goto out;
}
zone_info->active_zones = bitmap_zalloc(zone_info->nr_zones, GFP_KERNEL); if (!zone_info->active_zones) {
ret = -ENOMEM; goto out;
}
zones = kvcalloc(BTRFS_REPORT_NR_ZONES, sizeof(struct blk_zone), GFP_KERNEL); if (!zones) {
ret = -ENOMEM; goto out;
}
/* * Enable zone cache only for a zoned device. On a non-zoned device, we * fill the zone info with emulated CONVENTIONAL zones, so no need to * use the cache.
*/ if (populate_cache && bdev_is_zoned(device->bdev)) {
zone_info->zone_cache = vcalloc(zone_info->nr_zones, sizeof(struct blk_zone)); if (!zone_info->zone_cache) {
btrfs_err(device->fs_info, "zoned: failed to allocate zone cache for %s",
rcu_dereference(device->name));
ret = -ENOMEM; goto out;
}
}
/* Get zones type */
nactive = 0; while (sector < nr_sectors) {
nr_zones = BTRFS_REPORT_NR_ZONES;
ret = btrfs_get_dev_zones(device, sector << SECTOR_SHIFT, zones,
&nr_zones); if (ret) goto out;
for (i = 0; i < nr_zones; i++) { if (zones[i].type == BLK_ZONE_TYPE_SEQWRITE_REQ)
__set_bit(nreported, zone_info->seq_zones); switch (zones[i].cond) { case BLK_ZONE_COND_EMPTY:
__set_bit(nreported, zone_info->empty_zones); break; case BLK_ZONE_COND_IMP_OPEN: case BLK_ZONE_COND_EXP_OPEN: case BLK_ZONE_COND_CLOSED:
__set_bit(nreported, zone_info->active_zones);
nactive++; break;
}
nreported++;
}
sector = zones[nr_zones - 1].start + zones[nr_zones - 1].len;
}
if (nreported != zone_info->nr_zones) {
btrfs_err(device->fs_info, "inconsistent number of zones on %s (%u/%u)",
rcu_dereference(device->name), nreported,
zone_info->nr_zones);
ret = -EIO; goto out;
}
if (max_active_zones) { if (nactive > max_active_zones) { if (bdev_max_active_zones(bdev) == 0) {
max_active_zones = 0;
zone_info->max_active_zones = 0; goto validate;
}
btrfs_err(device->fs_info, "zoned: %u active zones on %s exceeds max_active_zones %u",
nactive, rcu_dereference(device->name),
max_active_zones);
ret = -EIO; goto out;
}
atomic_set(&zone_info->active_zones_left,
max_active_zones - nactive);
set_bit(BTRFS_FS_ACTIVE_ZONE_TRACKING, &fs_info->flags);
}
validate: /* Validate superblock log */
nr_zones = BTRFS_NR_SB_LOG_ZONES; for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
u32 sb_zone;
u64 sb_wp; int sb_pos = BTRFS_NR_SB_LOG_ZONES * i;
ret = btrfs_get_dev_zones(device,
zone_start_physical(sb_zone, zone_info),
&zone_info->sb_zones[sb_pos],
&nr_zones); if (ret) goto out;
if (nr_zones != BTRFS_NR_SB_LOG_ZONES) {
btrfs_err(device->fs_info, "zoned: failed to read super block log zone info at devid %llu zone %u",
device->devid, sb_zone);
ret = -EUCLEAN; goto out;
}
/* * If zones[0] is conventional, always use the beginning of the * zone to record superblock. No need to validate in that case.
*/ if (zone_info->sb_zones[BTRFS_NR_SB_LOG_ZONES * i].type ==
BLK_ZONE_TYPE_CONVENTIONAL) continue;
ret = sb_write_pointer(device->bdev,
&zone_info->sb_zones[sb_pos], &sb_wp); if (ret != -ENOENT && ret) {
btrfs_err(device->fs_info, "zoned: super block log zone corrupted devid %llu zone %u",
device->devid, sb_zone);
ret = -EUCLEAN; goto out;
}
}
kvfree(zones);
if (bdev_is_zoned(bdev)) {
model = "host-managed zoned";
emulated = "";
} else {
model = "regular";
emulated = "emulated ";
}
list_for_each_entry(device, &fs_info->fs_devices->devices, dev_list) { if (device->bdev && bdev_is_zoned(device->bdev)) {
btrfs_err(fs_info, "zoned: mode not enabled but zoned device found: %pg",
device->bdev); return -EINVAL;
}
}
return 0;
}
int btrfs_check_zoned_mode(struct btrfs_fs_info *fs_info)
{ struct queue_limits *lim = &fs_info->limits; struct btrfs_device *device;
u64 zone_size = 0; int ret;
/* * Host-Managed devices can't be used without the ZONED flag. With the * ZONED all devices can be used, using zone emulation if required.
*/ if (!btrfs_fs_incompat(fs_info, ZONED)) return btrfs_check_for_zoned_device(fs_info);
if (!zone_size) {
zone_size = zone_info->zone_size;
} elseif (zone_info->zone_size != zone_size) {
btrfs_err(fs_info, "zoned: unequal block device zone sizes: have %llu found %llu",
zone_info->zone_size, zone_size); return -EINVAL;
}
/* * With the zoned emulation, we can have non-zoned device on the * zoned mode. In this case, we don't have a valid max zone * append size.
*/ if (bdev_is_zoned(device->bdev))
blk_stack_limits(lim, bdev_limits(device->bdev), 0);
}
ret = blk_validate_limits(lim); if (ret) {
btrfs_err(fs_info, "zoned: failed to validate queue limits"); return ret;
}
/* * stripe_size is always aligned to BTRFS_STRIPE_LEN in * btrfs_create_chunk(). Since we want stripe_len == zone_size, * check the alignment here.
*/ if (!IS_ALIGNED(zone_size, BTRFS_STRIPE_LEN)) {
btrfs_err(fs_info, "zoned: zone size %llu not aligned to stripe %u",
zone_size, BTRFS_STRIPE_LEN); return -EINVAL;
}
if (btrfs_fs_incompat(fs_info, MIXED_GROUPS)) {
btrfs_err(fs_info, "zoned: mixed block groups not supported"); return -EINVAL;
}
fs_info->zone_size = zone_size; /* * Also limit max_zone_append_size by max_segments * PAGE_SIZE. * Technically, we can have multiple pages per segment. But, since * we add the pages one by one to a bio, and cannot increase the * metadata reservation even if it increases the number of extents, it * is safe to stick with the limit.
*/
fs_info->max_zone_append_size = ALIGN_DOWN(
min3((u64)lim->max_zone_append_sectors << SECTOR_SHIFT,
(u64)lim->max_sectors << SECTOR_SHIFT,
(u64)lim->max_segments << PAGE_SHIFT),
fs_info->sectorsize);
fs_info->fs_devices->chunk_alloc_policy = BTRFS_CHUNK_ALLOC_ZONED;
/* * Check mount options here, because we might change fs_info->zoned * from fs_info->zone_size.
*/
ret = btrfs_check_mountopts_zoned(fs_info, &fs_info->mount_opt); if (ret) return ret;
btrfs_info(fs_info, "zoned mode enabled with zone size %llu", zone_size); return 0;
}
int btrfs_check_mountopts_zoned(conststruct btrfs_fs_info *info, unsignedlonglong *mount_opt)
{ if (!btrfs_is_zoned(info)) return 0;
/* * Space cache writing is not COWed. Disable that to avoid write errors * in sequential zones.
*/ if (btrfs_raw_test_opt(*mount_opt, SPACE_CACHE)) {
btrfs_err(info, "zoned: space cache v1 is not supported"); return -EINVAL;
}
if (btrfs_raw_test_opt(*mount_opt, NODATACOW)) {
btrfs_err(info, "zoned: NODATACOW not supported"); return -EINVAL;
}
if (btrfs_raw_test_opt(*mount_opt, DISCARD_ASYNC)) {
btrfs_info(info, "zoned: async discard ignored and disabled for zoned mode");
btrfs_clear_opt(*mount_opt, DISCARD_ASYNC);
}
return 0;
}
staticint sb_log_location(struct block_device *bdev, struct blk_zone *zones, int rw, u64 *bytenr_ret)
{
u64 wp; int ret;
if (reset && reset->cond != BLK_ZONE_COND_EMPTY) { unsignedint nofs_flags;
ASSERT(sb_zone_is_full(reset));
nofs_flags = memalloc_nofs_save();
ret = blkdev_zone_mgmt(bdev, REQ_OP_ZONE_RESET,
reset->start, reset->len);
memalloc_nofs_restore(nofs_flags); if (ret) return ret;
reset->cond = BLK_ZONE_COND_EMPTY;
reset->wp = reset->start;
}
} elseif (ret != -ENOENT) { /* * For READ, we want the previous one. Move write pointer to * the end of a zone, if it is at the head of a zone.
*/
u64 zone_end = 0;
int btrfs_sb_log_location(struct btrfs_device *device, int mirror, int rw,
u64 *bytenr_ret)
{ struct btrfs_zoned_device_info *zinfo = device->zone_info;
u32 zone_num;
/* * For a zoned filesystem on a non-zoned block device, use the same * super block locations as regular filesystem. Doing so, the super * block can always be retrieved and the zoned flag of the volume * detected from the super block information.
*/ if (!bdev_is_zoned(device->bdev)) {
*bytenr_ret = btrfs_sb_offset(mirror); return 0;
}
if (!test_bit(zone_num, zinfo->seq_zones)) returnfalse;
returntrue;
}
int btrfs_advance_sb_log(struct btrfs_device *device, int mirror)
{ struct btrfs_zoned_device_info *zinfo = device->zone_info; struct blk_zone *zone; int i;
if (!is_sb_log_zone(zinfo, mirror)) return 0;
zone = &zinfo->sb_zones[BTRFS_NR_SB_LOG_ZONES * mirror]; for (i = 0; i < BTRFS_NR_SB_LOG_ZONES; i++) { /* Advance the next zone */ if (zone->cond == BLK_ZONE_COND_FULL) {
zone++; continue;
}
if (zone->cond == BLK_ZONE_COND_EMPTY)
zone->cond = BLK_ZONE_COND_IMP_OPEN;
zone->wp += SUPER_INFO_SECTORS;
if (sb_zone_is_full(zone)) { /* * No room left to write new superblock. Since * superblock is written with REQ_SYNC, it is safe to * finish the zone now. * * If the write pointer is exactly at the capacity, * explicit ZONE_FINISH is not necessary.
*/ if (zone->wp != zone->start + zone->capacity) { unsignedint nofs_flags; int ret;
nofs_flags = memalloc_nofs_save();
ret = blkdev_zone_mgmt(device->bdev,
REQ_OP_ZONE_FINISH, zone->start,
zone->len);
memalloc_nofs_restore(nofs_flags); if (ret) return ret;
}
/* * Find allocatable zones within a given region. * * @device: the device to allocate a region on * @hole_start: the position of the hole to allocate the region * @num_bytes: size of wanted region * @hole_end: the end of the hole * @return: position of allocatable zones * * Allocatable region should not contain any superblock locations.
*/
u64 btrfs_find_allocatable_zones(struct btrfs_device *device, u64 hole_start,
u64 hole_end, u64 num_bytes)
{ struct btrfs_zoned_device_info *zinfo = device->zone_info; const u8 shift = zinfo->zone_size_shift;
u64 nzones = num_bytes >> shift;
u64 pos = hole_start;
u64 begin, end; bool have_sb; int i;
while (pos < hole_end) {
begin = pos >> shift;
end = begin + nzones;
if (end > zinfo->nr_zones) return hole_end;
/* Check if zones in the region are all empty */ if (btrfs_dev_is_sequential(device, pos) &&
!bitmap_test_range_all_set(zinfo->empty_zones, begin, nzones)) {
pos += zinfo->zone_size; continue;
}
have_sb = false; for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
u32 sb_zone;
u64 sb_pos;
/* We can use any number of zones */ if (zone_info->max_active_zones == 0) returntrue;
if (!test_bit(zno, zone_info->active_zones)) { /* Active zone left? */ if (atomic_dec_if_positive(&zone_info->active_zones_left) < 0) returnfalse; if (test_and_set_bit(zno, zone_info->active_zones)) { /* Someone already set the bit */
atomic_inc(&zone_info->active_zones_left);
}
}
if (begin + nbits > zinfo->nr_zones) return -ERANGE;
/* All the zones are conventional */ if (bitmap_test_range_all_zero(zinfo->seq_zones, begin, nbits)) return 0;
/* All the zones are sequential and empty */ if (bitmap_test_range_all_set(zinfo->seq_zones, begin, nbits) &&
bitmap_test_range_all_set(zinfo->empty_zones, begin, nbits)) return 0;
if (!btrfs_dev_is_sequential(device, pos) ||
btrfs_dev_is_empty_zone(device, pos)) continue;
/* Free regions should be empty */
btrfs_warn(
device->fs_info, "zoned: resetting device %s (devid %llu) zone %llu for allocation",
rcu_dereference(device->name), device->devid, pos >> shift);
WARN_ON_ONCE(1);
ret = btrfs_reset_device_zone(device, pos, zinfo->zone_size,
&reset_bytes); if (ret) return ret;
}
return 0;
}
/* * Calculate an allocation pointer from the extent allocation information * for a block group consist of conventional zones. It is pointed to the * end of the highest addressed extent in the block group as an allocation * offset.
*/ staticint calculate_alloc_pointer(struct btrfs_block_group *cache,
u64 *offset_ret, boolnew)
{ struct btrfs_fs_info *fs_info = cache->fs_info; struct btrfs_root *root;
BTRFS_PATH_AUTO_FREE(path); struct btrfs_key key; struct btrfs_key found_key; int ret;
u64 length;
/* * Avoid tree lookups for a new block group, there's no use for it. * It must always be 0. * * Also, we have a lock chain of extent buffer lock -> chunk mutex. * For new a block group, this function is called from * btrfs_make_block_group() which is already taking the chunk mutex. * Thus, we cannot call calculate_alloc_pointer() which takes extent * buffer locks to avoid deadlock.
*/ if (new) {
*offset_ret = 0; return 0;
}
path = btrfs_alloc_path(); if (!path) return -ENOMEM;
root = btrfs_extent_root(fs_info, key.objectid);
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); /* We should not find the exact match */ if (!ret)
ret = -EUCLEAN; if (ret < 0) return ret;
ret = btrfs_previous_extent_item(root, path, cache->start); if (ret) { if (ret == 1) {
ret = 0;
*offset_ret = 0;
} return ret;
}
/* * The group is mapped to a sequential zone. Get the zone write pointer * to determine the allocation offset within the zone.
*/
WARN_ON(!IS_ALIGNED(info->physical, fs_info->zone_size));
if (zone_info[0].alloc_offset == WP_MISSING_DEV) {
btrfs_err(bg->fs_info, "zoned: cannot recover write pointer for zone %llu",
zone_info[0].physical); return -EIO;
} if (zone_info[1].alloc_offset == WP_MISSING_DEV) {
btrfs_err(bg->fs_info, "zoned: cannot recover write pointer for zone %llu",
zone_info[1].physical); return -EIO;
}
if (zone_info[0].alloc_offset == WP_CONVENTIONAL)
zone_info[0].alloc_offset = last_alloc;
if (zone_info[1].alloc_offset == WP_CONVENTIONAL)
zone_info[1].alloc_offset = last_alloc;
if (zone_info[0].alloc_offset != zone_info[1].alloc_offset) {
btrfs_err(bg->fs_info, "zoned: write pointer offset mismatch of zones in DUP profile"); return -EIO;
}
if (test_bit(0, active) != test_bit(1, active)) { if (!btrfs_zone_activate(bg)) return -EIO;
} elseif (test_bit(0, active)) {
set_bit(BLOCK_GROUP_FLAG_ZONE_IS_ACTIVE, &bg->runtime_flags);
}
/* Sanity check */ if (!IS_ALIGNED(length, fs_info->zone_size)) {
btrfs_err(fs_info, "zoned: block group %llu len %llu unaligned to zone size %llu",
logical, length, fs_info->zone_size); return -EIO;
}
map = btrfs_find_chunk_map(fs_info, logical, length); if (!map) return -EINVAL;
cache->physical_map = map;
zone_info = kcalloc(map->num_stripes, sizeof(*zone_info), GFP_NOFS); if (!zone_info) {
ret = -ENOMEM; goto out;
}
active = bitmap_zalloc(map->num_stripes, GFP_NOFS); if (!active) {
ret = -ENOMEM; goto out;
}
for (i = 0; i < map->num_stripes; i++) {
ret = btrfs_load_zone_info(fs_info, i, &zone_info[i], active, map, new); if (ret) goto out;
if (zone_info[i].alloc_offset == WP_CONVENTIONAL)
num_conventional++; else
num_sequential++;
}
if (num_sequential > 0)
set_bit(BLOCK_GROUP_FLAG_SEQUENTIAL_ZONE, &cache->runtime_flags);
if (num_conventional > 0) {
ret = calculate_alloc_pointer(cache, &last_alloc, new); if (ret) {
btrfs_err(fs_info, "zoned: failed to determine allocation offset of bg %llu",
cache->start); goto out;
} elseif (map->num_stripes == num_conventional) {
cache->alloc_offset = last_alloc;
cache->zone_capacity = cache->length;
set_bit(BLOCK_GROUP_FLAG_ZONE_IS_ACTIVE, &cache->runtime_flags); goto out;
}
}
profile = map->type & BTRFS_BLOCK_GROUP_PROFILE_MASK; switch (profile) { case 0: /* single */
ret = btrfs_load_block_group_single(cache, &zone_info[0], active); break; case BTRFS_BLOCK_GROUP_DUP:
ret = btrfs_load_block_group_dup(cache, map, zone_info, active,
last_alloc); break; case BTRFS_BLOCK_GROUP_RAID1: case BTRFS_BLOCK_GROUP_RAID1C3: case BTRFS_BLOCK_GROUP_RAID1C4:
ret = btrfs_load_block_group_raid1(cache, map, zone_info,
active, last_alloc); break; case BTRFS_BLOCK_GROUP_RAID0:
ret = btrfs_load_block_group_raid0(cache, map, zone_info,
active, last_alloc); break; case BTRFS_BLOCK_GROUP_RAID10:
ret = btrfs_load_block_group_raid10(cache, map, zone_info,
active, last_alloc); break; case BTRFS_BLOCK_GROUP_RAID5: case BTRFS_BLOCK_GROUP_RAID6: default:
btrfs_err(fs_info, "zoned: profile %s not yet supported",
btrfs_bg_type_to_raid_name(map->type));
ret = -EINVAL; goto out;
}
if (ret == -EIO && profile != 0 && profile != BTRFS_BLOCK_GROUP_RAID0 &&
profile != BTRFS_BLOCK_GROUP_RAID10) { /* * Detected broken write pointer. Make this block group * unallocatable by setting the allocation pointer at the end of * allocatable region. Relocating this block group will fix the * mismatch. * * Currently, we cannot handle RAID0 or RAID10 case like this * because we don't have a proper zone_capacity value. But, * reading from this block group won't work anyway by a missing * stripe.
*/
cache->alloc_offset = cache->zone_capacity;
}
out: /* Reject non SINGLE data profiles without RST */ if ((map->type & BTRFS_BLOCK_GROUP_DATA) &&
(map->type & BTRFS_BLOCK_GROUP_PROFILE_MASK) &&
!fs_info->stripe_root) {
btrfs_err(fs_info, "zoned: data %s needs raid-stripe-tree",
btrfs_bg_type_to_raid_name(map->type));
ret = -EINVAL;
}
if (cache->alloc_offset > cache->zone_capacity) {
btrfs_err(fs_info, "zoned: invalid write pointer %llu (larger than zone capacity %llu) in block group %llu",
cache->alloc_offset, cache->zone_capacity,
cache->start);
ret = -EIO;
}
/* An extent is allocated after the write pointer */ if (!ret && num_conventional && last_alloc > cache->alloc_offset) {
btrfs_err(fs_info, "zoned: got wrong write pointer in BG %llu: %llu > %llu",
logical, last_alloc, cache->alloc_offset);
ret = -EIO;
}
/* We only need ->free_space in ALLOC_SEQ block groups */
cache->cached = BTRFS_CACHE_FINISHED;
cache->free_space_ctl->free_space = free;
cache->zone_unusable = unusable;
}
if (btrfs_op(&bbio->bio) != BTRFS_MAP_WRITE) returnfalse;
/* * Using REQ_OP_ZONE_APPEND for relocation can break assumptions on the * extent layout the relocation code has. * Furthermore we have set aside own block-group from which only the * relocation "process" can allocate and make sure only one process at a * time can add pages to an extent that gets relocated, so it's safe to * use regular REQ_OP_WRITE for this special case.
*/ if (btrfs_is_data_reloc_root(inode->root)) returnfalse;
cache = btrfs_lookup_block_group(fs_info, start);
ASSERT(cache); if (!cache) returnfalse;
ret = !!test_bit(BLOCK_GROUP_FLAG_SEQUENTIAL_ZONE, &cache->runtime_flags);
btrfs_put_block_group(cache);
write_lock(&em_tree->lock);
em = btrfs_search_extent_mapping(em_tree, ordered->file_offset,
ordered->num_bytes); /* The em should be a new COW extent, thus it should not have an offset. */
ASSERT(em->offset == 0);
em->disk_bytenr = logical;
btrfs_free_extent_map(em);
write_unlock(&em_tree->lock);
}
/* * Write to pre-allocated region is for the data relocation, and so * it should use WRITE operation. No split/rewrite are necessary.
*/ if (test_bit(BTRFS_ORDERED_PREALLOC, &ordered->flags)) return;
ASSERT(!list_empty(&ordered->list)); /* The ordered->list can be empty in the above pre-alloc case. */
sum = list_first_entry(&ordered->list, struct btrfs_ordered_sum, list);
logical = sum->logical;
len = sum->len;
while (len < ordered->disk_num_bytes) {
sum = list_next_entry(sum, list); if (sum->logical == logical + len) {
len += sum->len; continue;
} if (!btrfs_zoned_split_ordered(ordered, logical, len)) {
set_bit(BTRFS_ORDERED_IOERR, &ordered->flags);
btrfs_err(fs_info, "failed to split ordered extent"); goto out;
}
logical = sum->logical;
len = sum->len;
}
if (ordered->disk_bytenr != logical)
btrfs_rewrite_logical_zoned(ordered, logical);
out: /* * If we end up here for nodatasum I/O, the btrfs_ordered_sum structures * were allocated by btrfs_alloc_dummy_sum only to record the logical * addresses and don't contain actual checksums. We thus must free them * here so that we don't attempt to log the csums later.
*/ if ((inode->flags & BTRFS_INODE_NODATASUM) ||
test_bit(BTRFS_FS_STATE_NO_DATA_CSUMS, &fs_info->fs_state)) { while ((sum = list_first_entry_or_null(&ordered->list,
typeof(*sum), list))) {
list_del(&sum->list);
kfree(sum);
}
}
}
if (tgt) { /* * If there is an unsent IO left in the allocated area, * we cannot wait for them as it may cause a deadlock.
*/ if (tgt->meta_write_pointer < tgt->start + tgt->alloc_offset) { if (wbc->sync_mode == WB_SYNC_NONE ||
(wbc->sync_mode == WB_SYNC_ALL && !wbc->for_sync)) returnfalse;
}
/* Pivot active metadata/system block group. */
btrfs_zoned_meta_io_unlock(fs_info);
wait_eb_writebacks(tgt);
do_zone_finish(tgt, true);
btrfs_zoned_meta_io_lock(fs_info); if (*active_bg == tgt) {
btrfs_put_block_group(tgt);
*active_bg = NULL;
}
} if (!btrfs_zone_activate(block_group)) returnfalse; if (*active_bg != block_group) {
ASSERT(*active_bg == NULL);
*active_bg = block_group;
btrfs_get_block_group(block_group);
}
}
returntrue;
}
/* * Check if @ctx->eb is aligned to the write pointer. * * Return: * 0: @ctx->eb is at the write pointer. You can write it. * -EAGAIN: There is a hole. The caller should handle the case. * -EBUSY: There is a hole, but the caller can just bail out.
*/ int btrfs_check_meta_write_pointer(struct btrfs_fs_info *fs_info, struct btrfs_eb_write_context *ctx)
{ conststruct writeback_control *wbc = ctx->wbc; conststruct extent_buffer *eb = ctx->eb; struct btrfs_block_group *block_group = ctx->zoned_bg;
if (!block_group) {
block_group = btrfs_lookup_block_group(fs_info, eb->start); if (!block_group) return 0;
ctx->zoned_bg = block_group;
}
if (block_group->meta_write_pointer == eb->start) { struct btrfs_block_group **tgt;
if (!test_bit(BTRFS_FS_ACTIVE_ZONE_TRACKING, &fs_info->flags)) return 0;
if (block_group->flags & BTRFS_BLOCK_GROUP_SYSTEM)
tgt = &fs_info->active_system_bg; else
tgt = &fs_info->active_meta_bg; if (check_bg_is_active(ctx, tgt)) return 0;
}
/* * Since we may release fs_info->zoned_meta_io_lock, someone can already * start writing this eb. In that case, we can just bail out.
*/ if (block_group->meta_write_pointer > eb->start) return -EBUSY;
/* If for_sync, this hole will be filled with transaction commit. */ if (wbc->sync_mode == WB_SYNC_ALL && !wbc->for_sync) return -EAGAIN; return -EBUSY;
}
int btrfs_zoned_issue_zeroout(struct btrfs_device *device, u64 physical, u64 length)
{ if (!btrfs_dev_is_sequential(device, physical)) return -EOPNOTSUPP;
staticint read_zone_info(struct btrfs_fs_info *fs_info, u64 logical, struct blk_zone *zone)
{ struct btrfs_io_context *bioc = NULL;
u64 mapped_length = PAGE_SIZE; unsignedint nofs_flag; int nmirrors; int i, ret;
ret = btrfs_map_block(fs_info, BTRFS_MAP_GET_READ_MIRRORS, logical,
&mapped_length, &bioc, NULL, NULL); if (ret || !bioc || mapped_length < PAGE_SIZE) {
ret = -EIO; goto out_put_bioc;
}
if (bioc->map_type & BTRFS_BLOCK_GROUP_RAID56_MASK) {
ret = -EINVAL; goto out_put_bioc;
}
nofs_flag = memalloc_nofs_save();
nmirrors = (int)bioc->num_stripes; for (i = 0; i < nmirrors; i++) {
u64 physical = bioc->stripes[i].physical; struct btrfs_device *dev = bioc->stripes[i].dev;
/* Missing device */ if (!dev->bdev) continue;
ret = btrfs_get_dev_zone(dev, physical, zone); /* Failing device */ if (ret == -EIO || ret == -EOPNOTSUPP) continue; break;
}
memalloc_nofs_restore(nofs_flag);
out_put_bioc:
btrfs_put_bioc(bioc); return ret;
}
/* * Synchronize write pointer in a zone at @physical_start on @tgt_dev, by * filling zeros between @physical_pos to a write pointer of dev-replace * source device.
*/ int btrfs_sync_zone_write_pointer(struct btrfs_device *tgt_dev, u64 logical,
u64 physical_start, u64 physical_pos)
{ struct btrfs_fs_info *fs_info = tgt_dev->fs_info; struct blk_zone zone;
u64 length;
u64 wp; int ret;
if (!btrfs_dev_is_sequential(tgt_dev, physical_pos)) return 0;
ret = read_zone_info(fs_info, logical, &zone); if (ret) return ret;
/* * Activate block group and underlying device zones * * @block_group: the block group to activate * * Return: true on success, false otherwise
*/ bool btrfs_zone_activate(struct btrfs_block_group *block_group)
{ struct btrfs_fs_info *fs_info = block_group->fs_info; struct btrfs_chunk_map *map; struct btrfs_device *device;
u64 physical; constbool is_data = (block_group->flags & BTRFS_BLOCK_GROUP_DATA); bool ret; int i;
if (!btrfs_is_zoned(block_group->fs_info)) returntrue;
map = block_group->physical_map;
spin_lock(&fs_info->zone_active_bgs_lock);
spin_lock(&block_group->lock); if (test_bit(BLOCK_GROUP_FLAG_ZONE_IS_ACTIVE, &block_group->runtime_flags)) {
ret = true; goto out_unlock;
}
if (block_group->flags & BTRFS_BLOCK_GROUP_DATA) { /* The caller should check if the block group is full. */ if (WARN_ON_ONCE(btrfs_zoned_bg_is_full(block_group))) {
ret = false; goto out_unlock;
}
} else { /* Since it is already written, it should have been active. */
WARN_ON_ONCE(block_group->meta_write_pointer != block_group->start);
}
for (i = 0; i < map->num_stripes; i++) { struct btrfs_zoned_device_info *zinfo; int reserved = 0;
if (is_data)
reserved = zinfo->reserved_active_zones; /* * For the data block group, leave active zones for one * metadata block group and one system block group.
*/ if (atomic_read(&zinfo->active_zones_left) <= reserved) {
ret = false; goto out_unlock;
}
if (!btrfs_dev_set_active_zone(device, physical)) { /* Cannot activate the zone */
ret = false; goto out_unlock;
} if (!is_data)
zinfo->reserved_active_zones--;
}
/* Successfully activated all the zones */
set_bit(BLOCK_GROUP_FLAG_ZONE_IS_ACTIVE, &block_group->runtime_flags);
spin_unlock(&block_group->lock);
/* For the active block group list */
btrfs_get_block_group(block_group);
list_add_tail(&block_group->active_bg_list, &fs_info->zone_active_bgs);
spin_unlock(&fs_info->zone_active_bgs_lock);
spin_lock(&block_group->lock); if (!test_bit(BLOCK_GROUP_FLAG_ZONE_IS_ACTIVE, &block_group->runtime_flags)) {
spin_unlock(&block_group->lock); return 0;
}
/* Check if we have unwritten allocated space */ if (is_metadata &&
block_group->start + block_group->alloc_offset > block_group->meta_write_pointer) {
spin_unlock(&block_group->lock); return -EAGAIN;
}
/* * If we are sure that the block group is full (= no more room left for * new allocation) and the IO for the last usable block is completed, we * don't need to wait for the other IOs. This holds because we ensure * the sequential IO submissions using the ZONE_APPEND command for data * and block_group->meta_write_pointer for metadata.
*/ if (!fully_written) { if (test_bit(BLOCK_GROUP_FLAG_ZONED_DATA_RELOC, &block_group->runtime_flags)) {
spin_unlock(&block_group->lock); return -EAGAIN;
}
spin_unlock(&block_group->lock);
ret = btrfs_inc_block_group_ro(block_group, false); if (ret) return ret;
/* Ensure all writes in this block group finish */
btrfs_wait_block_group_reservations(block_group); /* No need to wait for NOCOW writers. Zoned mode does not allow that */
btrfs_wait_ordered_roots(fs_info, U64_MAX, block_group); /* Wait for extent buffers to be written. */ if (is_metadata)
wait_eb_writebacks(block_group);
spin_lock(&block_group->lock);
/* * Bail out if someone already deactivated the block group, or * allocated space is left in the block group.
*/ if (!test_bit(BLOCK_GROUP_FLAG_ZONE_IS_ACTIVE,
&block_group->runtime_flags)) {
spin_unlock(&block_group->lock);
btrfs_dec_block_group_ro(block_group); return 0;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.