162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2017 Oracle. All Rights Reserved. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Darrick J. Wong <darrick.wong@oracle.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include "ext4.h" 862306a36Sopenharmony_ci#include <linux/fsmap.h> 962306a36Sopenharmony_ci#include "fsmap.h" 1062306a36Sopenharmony_ci#include "mballoc.h" 1162306a36Sopenharmony_ci#include <linux/sort.h> 1262306a36Sopenharmony_ci#include <linux/list_sort.h> 1362306a36Sopenharmony_ci#include <trace/events/ext4.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* Convert an ext4_fsmap to an fsmap. */ 1662306a36Sopenharmony_civoid ext4_fsmap_from_internal(struct super_block *sb, struct fsmap *dest, 1762306a36Sopenharmony_ci struct ext4_fsmap *src) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci dest->fmr_device = src->fmr_device; 2062306a36Sopenharmony_ci dest->fmr_flags = src->fmr_flags; 2162306a36Sopenharmony_ci dest->fmr_physical = src->fmr_physical << sb->s_blocksize_bits; 2262306a36Sopenharmony_ci dest->fmr_owner = src->fmr_owner; 2362306a36Sopenharmony_ci dest->fmr_offset = 0; 2462306a36Sopenharmony_ci dest->fmr_length = src->fmr_length << sb->s_blocksize_bits; 2562306a36Sopenharmony_ci dest->fmr_reserved[0] = 0; 2662306a36Sopenharmony_ci dest->fmr_reserved[1] = 0; 2762306a36Sopenharmony_ci dest->fmr_reserved[2] = 0; 2862306a36Sopenharmony_ci} 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* Convert an fsmap to an ext4_fsmap. */ 3162306a36Sopenharmony_civoid ext4_fsmap_to_internal(struct super_block *sb, struct ext4_fsmap *dest, 3262306a36Sopenharmony_ci struct fsmap *src) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci dest->fmr_device = src->fmr_device; 3562306a36Sopenharmony_ci dest->fmr_flags = src->fmr_flags; 3662306a36Sopenharmony_ci dest->fmr_physical = src->fmr_physical >> sb->s_blocksize_bits; 3762306a36Sopenharmony_ci dest->fmr_owner = src->fmr_owner; 3862306a36Sopenharmony_ci dest->fmr_length = src->fmr_length >> sb->s_blocksize_bits; 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* getfsmap query state */ 4262306a36Sopenharmony_cistruct ext4_getfsmap_info { 4362306a36Sopenharmony_ci struct ext4_fsmap_head *gfi_head; 4462306a36Sopenharmony_ci ext4_fsmap_format_t gfi_formatter; /* formatting fn */ 4562306a36Sopenharmony_ci void *gfi_format_arg;/* format buffer */ 4662306a36Sopenharmony_ci ext4_fsblk_t gfi_next_fsblk; /* next fsblock we expect */ 4762306a36Sopenharmony_ci u32 gfi_dev; /* device id */ 4862306a36Sopenharmony_ci ext4_group_t gfi_agno; /* bg number, if applicable */ 4962306a36Sopenharmony_ci struct ext4_fsmap gfi_low; /* low rmap key */ 5062306a36Sopenharmony_ci struct ext4_fsmap gfi_high; /* high rmap key */ 5162306a36Sopenharmony_ci struct ext4_fsmap gfi_lastfree; /* free ext at end of last bg */ 5262306a36Sopenharmony_ci struct list_head gfi_meta_list; /* fixed metadata list */ 5362306a36Sopenharmony_ci bool gfi_last; /* last extent? */ 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* Associate a device with a getfsmap handler. */ 5762306a36Sopenharmony_cistruct ext4_getfsmap_dev { 5862306a36Sopenharmony_ci int (*gfd_fn)(struct super_block *sb, 5962306a36Sopenharmony_ci struct ext4_fsmap *keys, 6062306a36Sopenharmony_ci struct ext4_getfsmap_info *info); 6162306a36Sopenharmony_ci u32 gfd_dev; 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* Compare two getfsmap device handlers. */ 6562306a36Sopenharmony_cistatic int ext4_getfsmap_dev_compare(const void *p1, const void *p2) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci const struct ext4_getfsmap_dev *d1 = p1; 6862306a36Sopenharmony_ci const struct ext4_getfsmap_dev *d2 = p2; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return d1->gfd_dev - d2->gfd_dev; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* Compare a record against our starting point */ 7462306a36Sopenharmony_cistatic bool ext4_getfsmap_rec_before_low_key(struct ext4_getfsmap_info *info, 7562306a36Sopenharmony_ci struct ext4_fsmap *rec) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci return rec->fmr_physical < info->gfi_low.fmr_physical; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* 8162306a36Sopenharmony_ci * Format a reverse mapping for getfsmap, having translated rm_startblock 8262306a36Sopenharmony_ci * into the appropriate daddr units. 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_cistatic int ext4_getfsmap_helper(struct super_block *sb, 8562306a36Sopenharmony_ci struct ext4_getfsmap_info *info, 8662306a36Sopenharmony_ci struct ext4_fsmap *rec) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct ext4_fsmap fmr; 8962306a36Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(sb); 9062306a36Sopenharmony_ci ext4_fsblk_t rec_fsblk = rec->fmr_physical; 9162306a36Sopenharmony_ci ext4_group_t agno; 9262306a36Sopenharmony_ci ext4_grpblk_t cno; 9362306a36Sopenharmony_ci int error; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (fatal_signal_pending(current)) 9662306a36Sopenharmony_ci return -EINTR; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* 9962306a36Sopenharmony_ci * Filter out records that start before our startpoint, if the 10062306a36Sopenharmony_ci * caller requested that. 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_ci if (ext4_getfsmap_rec_before_low_key(info, rec)) { 10362306a36Sopenharmony_ci rec_fsblk += rec->fmr_length; 10462306a36Sopenharmony_ci if (info->gfi_next_fsblk < rec_fsblk) 10562306a36Sopenharmony_ci info->gfi_next_fsblk = rec_fsblk; 10662306a36Sopenharmony_ci return EXT4_QUERY_RANGE_CONTINUE; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* Are we just counting mappings? */ 11062306a36Sopenharmony_ci if (info->gfi_head->fmh_count == 0) { 11162306a36Sopenharmony_ci if (info->gfi_head->fmh_entries == UINT_MAX) 11262306a36Sopenharmony_ci return EXT4_QUERY_RANGE_ABORT; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (rec_fsblk > info->gfi_next_fsblk) 11562306a36Sopenharmony_ci info->gfi_head->fmh_entries++; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (info->gfi_last) 11862306a36Sopenharmony_ci return EXT4_QUERY_RANGE_CONTINUE; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci info->gfi_head->fmh_entries++; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci rec_fsblk += rec->fmr_length; 12362306a36Sopenharmony_ci if (info->gfi_next_fsblk < rec_fsblk) 12462306a36Sopenharmony_ci info->gfi_next_fsblk = rec_fsblk; 12562306a36Sopenharmony_ci return EXT4_QUERY_RANGE_CONTINUE; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* 12962306a36Sopenharmony_ci * If the record starts past the last physical block we saw, 13062306a36Sopenharmony_ci * then we've found a gap. Report the gap as being owned by 13162306a36Sopenharmony_ci * whatever the caller specified is the missing owner. 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_ci if (rec_fsblk > info->gfi_next_fsblk) { 13462306a36Sopenharmony_ci if (info->gfi_head->fmh_entries >= info->gfi_head->fmh_count) 13562306a36Sopenharmony_ci return EXT4_QUERY_RANGE_ABORT; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci ext4_get_group_no_and_offset(sb, info->gfi_next_fsblk, 13862306a36Sopenharmony_ci &agno, &cno); 13962306a36Sopenharmony_ci trace_ext4_fsmap_mapping(sb, info->gfi_dev, agno, 14062306a36Sopenharmony_ci EXT4_C2B(sbi, cno), 14162306a36Sopenharmony_ci rec_fsblk - info->gfi_next_fsblk, 14262306a36Sopenharmony_ci EXT4_FMR_OWN_UNKNOWN); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci fmr.fmr_device = info->gfi_dev; 14562306a36Sopenharmony_ci fmr.fmr_physical = info->gfi_next_fsblk; 14662306a36Sopenharmony_ci fmr.fmr_owner = EXT4_FMR_OWN_UNKNOWN; 14762306a36Sopenharmony_ci fmr.fmr_length = rec_fsblk - info->gfi_next_fsblk; 14862306a36Sopenharmony_ci fmr.fmr_flags = FMR_OF_SPECIAL_OWNER; 14962306a36Sopenharmony_ci error = info->gfi_formatter(&fmr, info->gfi_format_arg); 15062306a36Sopenharmony_ci if (error) 15162306a36Sopenharmony_ci return error; 15262306a36Sopenharmony_ci info->gfi_head->fmh_entries++; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (info->gfi_last) 15662306a36Sopenharmony_ci goto out; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* Fill out the extent we found */ 15962306a36Sopenharmony_ci if (info->gfi_head->fmh_entries >= info->gfi_head->fmh_count) 16062306a36Sopenharmony_ci return EXT4_QUERY_RANGE_ABORT; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci ext4_get_group_no_and_offset(sb, rec_fsblk, &agno, &cno); 16362306a36Sopenharmony_ci trace_ext4_fsmap_mapping(sb, info->gfi_dev, agno, EXT4_C2B(sbi, cno), 16462306a36Sopenharmony_ci rec->fmr_length, rec->fmr_owner); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci fmr.fmr_device = info->gfi_dev; 16762306a36Sopenharmony_ci fmr.fmr_physical = rec_fsblk; 16862306a36Sopenharmony_ci fmr.fmr_owner = rec->fmr_owner; 16962306a36Sopenharmony_ci fmr.fmr_flags = FMR_OF_SPECIAL_OWNER; 17062306a36Sopenharmony_ci fmr.fmr_length = rec->fmr_length; 17162306a36Sopenharmony_ci error = info->gfi_formatter(&fmr, info->gfi_format_arg); 17262306a36Sopenharmony_ci if (error) 17362306a36Sopenharmony_ci return error; 17462306a36Sopenharmony_ci info->gfi_head->fmh_entries++; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ciout: 17762306a36Sopenharmony_ci rec_fsblk += rec->fmr_length; 17862306a36Sopenharmony_ci if (info->gfi_next_fsblk < rec_fsblk) 17962306a36Sopenharmony_ci info->gfi_next_fsblk = rec_fsblk; 18062306a36Sopenharmony_ci return EXT4_QUERY_RANGE_CONTINUE; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic inline ext4_fsblk_t ext4_fsmap_next_pblk(struct ext4_fsmap *fmr) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci return fmr->fmr_physical + fmr->fmr_length; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/* Transform a blockgroup's free record into a fsmap */ 18962306a36Sopenharmony_cistatic int ext4_getfsmap_datadev_helper(struct super_block *sb, 19062306a36Sopenharmony_ci ext4_group_t agno, ext4_grpblk_t start, 19162306a36Sopenharmony_ci ext4_grpblk_t len, void *priv) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct ext4_fsmap irec; 19462306a36Sopenharmony_ci struct ext4_getfsmap_info *info = priv; 19562306a36Sopenharmony_ci struct ext4_fsmap *p; 19662306a36Sopenharmony_ci struct ext4_fsmap *tmp; 19762306a36Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(sb); 19862306a36Sopenharmony_ci ext4_fsblk_t fsb; 19962306a36Sopenharmony_ci ext4_fsblk_t fslen; 20062306a36Sopenharmony_ci int error; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci fsb = (EXT4_C2B(sbi, start) + ext4_group_first_block_no(sb, agno)); 20362306a36Sopenharmony_ci fslen = EXT4_C2B(sbi, len); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* If the retained free extent record is set... */ 20662306a36Sopenharmony_ci if (info->gfi_lastfree.fmr_owner) { 20762306a36Sopenharmony_ci /* ...and abuts this one, lengthen it and return. */ 20862306a36Sopenharmony_ci if (ext4_fsmap_next_pblk(&info->gfi_lastfree) == fsb) { 20962306a36Sopenharmony_ci info->gfi_lastfree.fmr_length += fslen; 21062306a36Sopenharmony_ci return 0; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* 21462306a36Sopenharmony_ci * There's a gap between the two free extents; emit the 21562306a36Sopenharmony_ci * retained extent prior to merging the meta_list. 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_ci error = ext4_getfsmap_helper(sb, info, &info->gfi_lastfree); 21862306a36Sopenharmony_ci if (error) 21962306a36Sopenharmony_ci return error; 22062306a36Sopenharmony_ci info->gfi_lastfree.fmr_owner = 0; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* Merge in any relevant extents from the meta_list */ 22462306a36Sopenharmony_ci list_for_each_entry_safe(p, tmp, &info->gfi_meta_list, fmr_list) { 22562306a36Sopenharmony_ci if (p->fmr_physical + p->fmr_length <= info->gfi_next_fsblk) { 22662306a36Sopenharmony_ci list_del(&p->fmr_list); 22762306a36Sopenharmony_ci kfree(p); 22862306a36Sopenharmony_ci } else if (p->fmr_physical < fsb) { 22962306a36Sopenharmony_ci error = ext4_getfsmap_helper(sb, info, p); 23062306a36Sopenharmony_ci if (error) 23162306a36Sopenharmony_ci return error; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci list_del(&p->fmr_list); 23462306a36Sopenharmony_ci kfree(p); 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci irec.fmr_device = 0; 23962306a36Sopenharmony_ci irec.fmr_physical = fsb; 24062306a36Sopenharmony_ci irec.fmr_length = fslen; 24162306a36Sopenharmony_ci irec.fmr_owner = EXT4_FMR_OWN_FREE; 24262306a36Sopenharmony_ci irec.fmr_flags = 0; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* If this is a free extent at the end of a bg, buffer it. */ 24562306a36Sopenharmony_ci if (ext4_fsmap_next_pblk(&irec) == 24662306a36Sopenharmony_ci ext4_group_first_block_no(sb, agno + 1)) { 24762306a36Sopenharmony_ci info->gfi_lastfree = irec; 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* Otherwise, emit it */ 25262306a36Sopenharmony_ci return ext4_getfsmap_helper(sb, info, &irec); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci/* Execute a getfsmap query against the log device. */ 25662306a36Sopenharmony_cistatic int ext4_getfsmap_logdev(struct super_block *sb, struct ext4_fsmap *keys, 25762306a36Sopenharmony_ci struct ext4_getfsmap_info *info) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci journal_t *journal = EXT4_SB(sb)->s_journal; 26062306a36Sopenharmony_ci struct ext4_fsmap irec; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* Set up search keys */ 26362306a36Sopenharmony_ci info->gfi_low = keys[0]; 26462306a36Sopenharmony_ci info->gfi_low.fmr_length = 0; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci memset(&info->gfi_high, 0xFF, sizeof(info->gfi_high)); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci trace_ext4_fsmap_low_key(sb, info->gfi_dev, 0, 26962306a36Sopenharmony_ci info->gfi_low.fmr_physical, 27062306a36Sopenharmony_ci info->gfi_low.fmr_length, 27162306a36Sopenharmony_ci info->gfi_low.fmr_owner); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci trace_ext4_fsmap_high_key(sb, info->gfi_dev, 0, 27462306a36Sopenharmony_ci info->gfi_high.fmr_physical, 27562306a36Sopenharmony_ci info->gfi_high.fmr_length, 27662306a36Sopenharmony_ci info->gfi_high.fmr_owner); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (keys[0].fmr_physical > 0) 27962306a36Sopenharmony_ci return 0; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* Fabricate an rmap entry for the external log device. */ 28262306a36Sopenharmony_ci irec.fmr_physical = journal->j_blk_offset; 28362306a36Sopenharmony_ci irec.fmr_length = journal->j_total_len; 28462306a36Sopenharmony_ci irec.fmr_owner = EXT4_FMR_OWN_LOG; 28562306a36Sopenharmony_ci irec.fmr_flags = 0; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci return ext4_getfsmap_helper(sb, info, &irec); 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci/* Helper to fill out an ext4_fsmap. */ 29162306a36Sopenharmony_cistatic inline int ext4_getfsmap_fill(struct list_head *meta_list, 29262306a36Sopenharmony_ci ext4_fsblk_t fsb, ext4_fsblk_t len, 29362306a36Sopenharmony_ci uint64_t owner) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct ext4_fsmap *fsm; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci fsm = kmalloc(sizeof(*fsm), GFP_NOFS); 29862306a36Sopenharmony_ci if (!fsm) 29962306a36Sopenharmony_ci return -ENOMEM; 30062306a36Sopenharmony_ci fsm->fmr_device = 0; 30162306a36Sopenharmony_ci fsm->fmr_flags = 0; 30262306a36Sopenharmony_ci fsm->fmr_physical = fsb; 30362306a36Sopenharmony_ci fsm->fmr_owner = owner; 30462306a36Sopenharmony_ci fsm->fmr_length = len; 30562306a36Sopenharmony_ci list_add_tail(&fsm->fmr_list, meta_list); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return 0; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci/* 31162306a36Sopenharmony_ci * This function returns the number of file system metadata blocks at 31262306a36Sopenharmony_ci * the beginning of a block group, including the reserved gdt blocks. 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_cistatic unsigned int ext4_getfsmap_find_sb(struct super_block *sb, 31562306a36Sopenharmony_ci ext4_group_t agno, 31662306a36Sopenharmony_ci struct list_head *meta_list) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(sb); 31962306a36Sopenharmony_ci ext4_fsblk_t fsb = ext4_group_first_block_no(sb, agno); 32062306a36Sopenharmony_ci ext4_fsblk_t len; 32162306a36Sopenharmony_ci unsigned long first_meta_bg = le32_to_cpu(sbi->s_es->s_first_meta_bg); 32262306a36Sopenharmony_ci unsigned long metagroup = agno / EXT4_DESC_PER_BLOCK(sb); 32362306a36Sopenharmony_ci int error; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* Record the superblock. */ 32662306a36Sopenharmony_ci if (ext4_bg_has_super(sb, agno)) { 32762306a36Sopenharmony_ci error = ext4_getfsmap_fill(meta_list, fsb, 1, EXT4_FMR_OWN_FS); 32862306a36Sopenharmony_ci if (error) 32962306a36Sopenharmony_ci return error; 33062306a36Sopenharmony_ci fsb++; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* Record the group descriptors. */ 33462306a36Sopenharmony_ci len = ext4_bg_num_gdb(sb, agno); 33562306a36Sopenharmony_ci if (!len) 33662306a36Sopenharmony_ci return 0; 33762306a36Sopenharmony_ci error = ext4_getfsmap_fill(meta_list, fsb, len, 33862306a36Sopenharmony_ci EXT4_FMR_OWN_GDT); 33962306a36Sopenharmony_ci if (error) 34062306a36Sopenharmony_ci return error; 34162306a36Sopenharmony_ci fsb += len; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* Reserved GDT blocks */ 34462306a36Sopenharmony_ci if (!ext4_has_feature_meta_bg(sb) || metagroup < first_meta_bg) { 34562306a36Sopenharmony_ci len = le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks); 34662306a36Sopenharmony_ci error = ext4_getfsmap_fill(meta_list, fsb, len, 34762306a36Sopenharmony_ci EXT4_FMR_OWN_RESV_GDT); 34862306a36Sopenharmony_ci if (error) 34962306a36Sopenharmony_ci return error; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci return 0; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci/* Compare two fsmap items. */ 35662306a36Sopenharmony_cistatic int ext4_getfsmap_compare(void *priv, 35762306a36Sopenharmony_ci const struct list_head *a, 35862306a36Sopenharmony_ci const struct list_head *b) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct ext4_fsmap *fa; 36162306a36Sopenharmony_ci struct ext4_fsmap *fb; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci fa = container_of(a, struct ext4_fsmap, fmr_list); 36462306a36Sopenharmony_ci fb = container_of(b, struct ext4_fsmap, fmr_list); 36562306a36Sopenharmony_ci if (fa->fmr_physical < fb->fmr_physical) 36662306a36Sopenharmony_ci return -1; 36762306a36Sopenharmony_ci else if (fa->fmr_physical > fb->fmr_physical) 36862306a36Sopenharmony_ci return 1; 36962306a36Sopenharmony_ci return 0; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci/* Merge adjacent extents of fixed metadata. */ 37362306a36Sopenharmony_cistatic void ext4_getfsmap_merge_fixed_metadata(struct list_head *meta_list) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct ext4_fsmap *p; 37662306a36Sopenharmony_ci struct ext4_fsmap *prev = NULL; 37762306a36Sopenharmony_ci struct ext4_fsmap *tmp; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci list_for_each_entry_safe(p, tmp, meta_list, fmr_list) { 38062306a36Sopenharmony_ci if (!prev) { 38162306a36Sopenharmony_ci prev = p; 38262306a36Sopenharmony_ci continue; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (prev->fmr_owner == p->fmr_owner && 38662306a36Sopenharmony_ci prev->fmr_physical + prev->fmr_length == p->fmr_physical) { 38762306a36Sopenharmony_ci prev->fmr_length += p->fmr_length; 38862306a36Sopenharmony_ci list_del(&p->fmr_list); 38962306a36Sopenharmony_ci kfree(p); 39062306a36Sopenharmony_ci } else 39162306a36Sopenharmony_ci prev = p; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci/* Free a list of fixed metadata. */ 39662306a36Sopenharmony_cistatic void ext4_getfsmap_free_fixed_metadata(struct list_head *meta_list) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci struct ext4_fsmap *p; 39962306a36Sopenharmony_ci struct ext4_fsmap *tmp; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci list_for_each_entry_safe(p, tmp, meta_list, fmr_list) { 40262306a36Sopenharmony_ci list_del(&p->fmr_list); 40362306a36Sopenharmony_ci kfree(p); 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci/* Find all the fixed metadata in the filesystem. */ 40862306a36Sopenharmony_cistatic int ext4_getfsmap_find_fixed_metadata(struct super_block *sb, 40962306a36Sopenharmony_ci struct list_head *meta_list) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct ext4_group_desc *gdp; 41262306a36Sopenharmony_ci ext4_group_t agno; 41362306a36Sopenharmony_ci int error; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci INIT_LIST_HEAD(meta_list); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* Collect everything. */ 41862306a36Sopenharmony_ci for (agno = 0; agno < EXT4_SB(sb)->s_groups_count; agno++) { 41962306a36Sopenharmony_ci gdp = ext4_get_group_desc(sb, agno, NULL); 42062306a36Sopenharmony_ci if (!gdp) { 42162306a36Sopenharmony_ci error = -EFSCORRUPTED; 42262306a36Sopenharmony_ci goto err; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci /* Superblock & GDT */ 42662306a36Sopenharmony_ci error = ext4_getfsmap_find_sb(sb, agno, meta_list); 42762306a36Sopenharmony_ci if (error) 42862306a36Sopenharmony_ci goto err; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* Block bitmap */ 43162306a36Sopenharmony_ci error = ext4_getfsmap_fill(meta_list, 43262306a36Sopenharmony_ci ext4_block_bitmap(sb, gdp), 1, 43362306a36Sopenharmony_ci EXT4_FMR_OWN_BLKBM); 43462306a36Sopenharmony_ci if (error) 43562306a36Sopenharmony_ci goto err; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* Inode bitmap */ 43862306a36Sopenharmony_ci error = ext4_getfsmap_fill(meta_list, 43962306a36Sopenharmony_ci ext4_inode_bitmap(sb, gdp), 1, 44062306a36Sopenharmony_ci EXT4_FMR_OWN_INOBM); 44162306a36Sopenharmony_ci if (error) 44262306a36Sopenharmony_ci goto err; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci /* Inodes */ 44562306a36Sopenharmony_ci error = ext4_getfsmap_fill(meta_list, 44662306a36Sopenharmony_ci ext4_inode_table(sb, gdp), 44762306a36Sopenharmony_ci EXT4_SB(sb)->s_itb_per_group, 44862306a36Sopenharmony_ci EXT4_FMR_OWN_INODES); 44962306a36Sopenharmony_ci if (error) 45062306a36Sopenharmony_ci goto err; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* Sort the list */ 45462306a36Sopenharmony_ci list_sort(NULL, meta_list, ext4_getfsmap_compare); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* Merge adjacent extents */ 45762306a36Sopenharmony_ci ext4_getfsmap_merge_fixed_metadata(meta_list); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci return 0; 46062306a36Sopenharmony_cierr: 46162306a36Sopenharmony_ci ext4_getfsmap_free_fixed_metadata(meta_list); 46262306a36Sopenharmony_ci return error; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci/* Execute a getfsmap query against the buddy bitmaps */ 46662306a36Sopenharmony_cistatic int ext4_getfsmap_datadev(struct super_block *sb, 46762306a36Sopenharmony_ci struct ext4_fsmap *keys, 46862306a36Sopenharmony_ci struct ext4_getfsmap_info *info) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(sb); 47162306a36Sopenharmony_ci ext4_fsblk_t start_fsb; 47262306a36Sopenharmony_ci ext4_fsblk_t end_fsb; 47362306a36Sopenharmony_ci ext4_fsblk_t bofs; 47462306a36Sopenharmony_ci ext4_fsblk_t eofs; 47562306a36Sopenharmony_ci ext4_group_t start_ag; 47662306a36Sopenharmony_ci ext4_group_t end_ag; 47762306a36Sopenharmony_ci ext4_grpblk_t first_cluster; 47862306a36Sopenharmony_ci ext4_grpblk_t last_cluster; 47962306a36Sopenharmony_ci int error = 0; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci bofs = le32_to_cpu(sbi->s_es->s_first_data_block); 48262306a36Sopenharmony_ci eofs = ext4_blocks_count(sbi->s_es); 48362306a36Sopenharmony_ci if (keys[0].fmr_physical >= eofs) 48462306a36Sopenharmony_ci return 0; 48562306a36Sopenharmony_ci else if (keys[0].fmr_physical < bofs) 48662306a36Sopenharmony_ci keys[0].fmr_physical = bofs; 48762306a36Sopenharmony_ci if (keys[1].fmr_physical >= eofs) 48862306a36Sopenharmony_ci keys[1].fmr_physical = eofs - 1; 48962306a36Sopenharmony_ci if (keys[1].fmr_physical < keys[0].fmr_physical) 49062306a36Sopenharmony_ci return 0; 49162306a36Sopenharmony_ci start_fsb = keys[0].fmr_physical; 49262306a36Sopenharmony_ci end_fsb = keys[1].fmr_physical; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* Determine first and last group to examine based on start and end */ 49562306a36Sopenharmony_ci ext4_get_group_no_and_offset(sb, start_fsb, &start_ag, &first_cluster); 49662306a36Sopenharmony_ci ext4_get_group_no_and_offset(sb, end_fsb, &end_ag, &last_cluster); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci /* 49962306a36Sopenharmony_ci * Convert the fsmap low/high keys to bg based keys. Initialize 50062306a36Sopenharmony_ci * low to the fsmap low key and max out the high key to the end 50162306a36Sopenharmony_ci * of the bg. 50262306a36Sopenharmony_ci */ 50362306a36Sopenharmony_ci info->gfi_low = keys[0]; 50462306a36Sopenharmony_ci info->gfi_low.fmr_physical = EXT4_C2B(sbi, first_cluster); 50562306a36Sopenharmony_ci info->gfi_low.fmr_length = 0; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci memset(&info->gfi_high, 0xFF, sizeof(info->gfi_high)); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* Assemble a list of all the fixed-location metadata. */ 51062306a36Sopenharmony_ci error = ext4_getfsmap_find_fixed_metadata(sb, &info->gfi_meta_list); 51162306a36Sopenharmony_ci if (error) 51262306a36Sopenharmony_ci goto err; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* Query each bg */ 51562306a36Sopenharmony_ci for (info->gfi_agno = start_ag; 51662306a36Sopenharmony_ci info->gfi_agno <= end_ag; 51762306a36Sopenharmony_ci info->gfi_agno++) { 51862306a36Sopenharmony_ci /* 51962306a36Sopenharmony_ci * Set the bg high key from the fsmap high key if this 52062306a36Sopenharmony_ci * is the last bg that we're querying. 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_ci if (info->gfi_agno == end_ag) { 52362306a36Sopenharmony_ci info->gfi_high = keys[1]; 52462306a36Sopenharmony_ci info->gfi_high.fmr_physical = EXT4_C2B(sbi, 52562306a36Sopenharmony_ci last_cluster); 52662306a36Sopenharmony_ci info->gfi_high.fmr_length = 0; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci trace_ext4_fsmap_low_key(sb, info->gfi_dev, info->gfi_agno, 53062306a36Sopenharmony_ci info->gfi_low.fmr_physical, 53162306a36Sopenharmony_ci info->gfi_low.fmr_length, 53262306a36Sopenharmony_ci info->gfi_low.fmr_owner); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci trace_ext4_fsmap_high_key(sb, info->gfi_dev, info->gfi_agno, 53562306a36Sopenharmony_ci info->gfi_high.fmr_physical, 53662306a36Sopenharmony_ci info->gfi_high.fmr_length, 53762306a36Sopenharmony_ci info->gfi_high.fmr_owner); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci error = ext4_mballoc_query_range(sb, info->gfi_agno, 54062306a36Sopenharmony_ci EXT4_B2C(sbi, info->gfi_low.fmr_physical), 54162306a36Sopenharmony_ci EXT4_B2C(sbi, info->gfi_high.fmr_physical), 54262306a36Sopenharmony_ci ext4_getfsmap_datadev_helper, info); 54362306a36Sopenharmony_ci if (error) 54462306a36Sopenharmony_ci goto err; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci /* 54762306a36Sopenharmony_ci * Set the bg low key to the start of the bg prior to 54862306a36Sopenharmony_ci * moving on to the next bg. 54962306a36Sopenharmony_ci */ 55062306a36Sopenharmony_ci if (info->gfi_agno == start_ag) 55162306a36Sopenharmony_ci memset(&info->gfi_low, 0, sizeof(info->gfi_low)); 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci /* Do we have a retained free extent? */ 55562306a36Sopenharmony_ci if (info->gfi_lastfree.fmr_owner) { 55662306a36Sopenharmony_ci error = ext4_getfsmap_helper(sb, info, &info->gfi_lastfree); 55762306a36Sopenharmony_ci if (error) 55862306a36Sopenharmony_ci goto err; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* Report any gaps at the end of the bg */ 56262306a36Sopenharmony_ci info->gfi_last = true; 56362306a36Sopenharmony_ci error = ext4_getfsmap_datadev_helper(sb, end_ag, last_cluster, 0, info); 56462306a36Sopenharmony_ci if (error) 56562306a36Sopenharmony_ci goto err; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cierr: 56862306a36Sopenharmony_ci ext4_getfsmap_free_fixed_metadata(&info->gfi_meta_list); 56962306a36Sopenharmony_ci return error; 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci/* Do we recognize the device? */ 57362306a36Sopenharmony_cistatic bool ext4_getfsmap_is_valid_device(struct super_block *sb, 57462306a36Sopenharmony_ci struct ext4_fsmap *fm) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci if (fm->fmr_device == 0 || fm->fmr_device == UINT_MAX || 57762306a36Sopenharmony_ci fm->fmr_device == new_encode_dev(sb->s_bdev->bd_dev)) 57862306a36Sopenharmony_ci return true; 57962306a36Sopenharmony_ci if (EXT4_SB(sb)->s_journal_bdev && 58062306a36Sopenharmony_ci fm->fmr_device == new_encode_dev(EXT4_SB(sb)->s_journal_bdev->bd_dev)) 58162306a36Sopenharmony_ci return true; 58262306a36Sopenharmony_ci return false; 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci/* Ensure that the low key is less than the high key. */ 58662306a36Sopenharmony_cistatic bool ext4_getfsmap_check_keys(struct ext4_fsmap *low_key, 58762306a36Sopenharmony_ci struct ext4_fsmap *high_key) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci if (low_key->fmr_device > high_key->fmr_device) 59062306a36Sopenharmony_ci return false; 59162306a36Sopenharmony_ci if (low_key->fmr_device < high_key->fmr_device) 59262306a36Sopenharmony_ci return true; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (low_key->fmr_physical > high_key->fmr_physical) 59562306a36Sopenharmony_ci return false; 59662306a36Sopenharmony_ci if (low_key->fmr_physical < high_key->fmr_physical) 59762306a36Sopenharmony_ci return true; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (low_key->fmr_owner > high_key->fmr_owner) 60062306a36Sopenharmony_ci return false; 60162306a36Sopenharmony_ci if (low_key->fmr_owner < high_key->fmr_owner) 60262306a36Sopenharmony_ci return true; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci return false; 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci#define EXT4_GETFSMAP_DEVS 2 60862306a36Sopenharmony_ci/* 60962306a36Sopenharmony_ci * Get filesystem's extents as described in head, and format for 61062306a36Sopenharmony_ci * output. Calls formatter to fill the user's buffer until all 61162306a36Sopenharmony_ci * extents are mapped, until the passed-in head->fmh_count slots have 61262306a36Sopenharmony_ci * been filled, or until the formatter short-circuits the loop, if it 61362306a36Sopenharmony_ci * is tracking filled-in extents on its own. 61462306a36Sopenharmony_ci * 61562306a36Sopenharmony_ci * Key to Confusion 61662306a36Sopenharmony_ci * ---------------- 61762306a36Sopenharmony_ci * There are multiple levels of keys and counters at work here: 61862306a36Sopenharmony_ci * _fsmap_head.fmh_keys -- low and high fsmap keys passed in; 61962306a36Sopenharmony_ci * these reflect fs-wide block addrs. 62062306a36Sopenharmony_ci * dkeys -- fmh_keys used to query each device; 62162306a36Sopenharmony_ci * these are fmh_keys but w/ the low key 62262306a36Sopenharmony_ci * bumped up by fmr_length. 62362306a36Sopenharmony_ci * _getfsmap_info.gfi_next_fsblk-- next fs block we expect to see; this 62462306a36Sopenharmony_ci * is how we detect gaps in the fsmap 62562306a36Sopenharmony_ci * records and report them. 62662306a36Sopenharmony_ci * _getfsmap_info.gfi_low/high -- per-bg low/high keys computed from 62762306a36Sopenharmony_ci * dkeys; used to query the free space. 62862306a36Sopenharmony_ci */ 62962306a36Sopenharmony_ciint ext4_getfsmap(struct super_block *sb, struct ext4_fsmap_head *head, 63062306a36Sopenharmony_ci ext4_fsmap_format_t formatter, void *arg) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci struct ext4_fsmap dkeys[2]; /* per-dev keys */ 63362306a36Sopenharmony_ci struct ext4_getfsmap_dev handlers[EXT4_GETFSMAP_DEVS]; 63462306a36Sopenharmony_ci struct ext4_getfsmap_info info = { NULL }; 63562306a36Sopenharmony_ci int i; 63662306a36Sopenharmony_ci int error = 0; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci if (head->fmh_iflags & ~FMH_IF_VALID) 63962306a36Sopenharmony_ci return -EINVAL; 64062306a36Sopenharmony_ci if (!ext4_getfsmap_is_valid_device(sb, &head->fmh_keys[0]) || 64162306a36Sopenharmony_ci !ext4_getfsmap_is_valid_device(sb, &head->fmh_keys[1])) 64262306a36Sopenharmony_ci return -EINVAL; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci head->fmh_entries = 0; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* Set up our device handlers. */ 64762306a36Sopenharmony_ci memset(handlers, 0, sizeof(handlers)); 64862306a36Sopenharmony_ci handlers[0].gfd_dev = new_encode_dev(sb->s_bdev->bd_dev); 64962306a36Sopenharmony_ci handlers[0].gfd_fn = ext4_getfsmap_datadev; 65062306a36Sopenharmony_ci if (EXT4_SB(sb)->s_journal_bdev) { 65162306a36Sopenharmony_ci handlers[1].gfd_dev = new_encode_dev( 65262306a36Sopenharmony_ci EXT4_SB(sb)->s_journal_bdev->bd_dev); 65362306a36Sopenharmony_ci handlers[1].gfd_fn = ext4_getfsmap_logdev; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci sort(handlers, EXT4_GETFSMAP_DEVS, sizeof(struct ext4_getfsmap_dev), 65762306a36Sopenharmony_ci ext4_getfsmap_dev_compare, NULL); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* 66062306a36Sopenharmony_ci * To continue where we left off, we allow userspace to use the 66162306a36Sopenharmony_ci * last mapping from a previous call as the low key of the next. 66262306a36Sopenharmony_ci * This is identified by a non-zero length in the low key. We 66362306a36Sopenharmony_ci * have to increment the low key in this scenario to ensure we 66462306a36Sopenharmony_ci * don't return the same mapping again, and instead return the 66562306a36Sopenharmony_ci * very next mapping. 66662306a36Sopenharmony_ci * 66762306a36Sopenharmony_ci * Bump the physical offset as there can be no other mapping for 66862306a36Sopenharmony_ci * the same physical block range. 66962306a36Sopenharmony_ci */ 67062306a36Sopenharmony_ci dkeys[0] = head->fmh_keys[0]; 67162306a36Sopenharmony_ci dkeys[0].fmr_physical += dkeys[0].fmr_length; 67262306a36Sopenharmony_ci dkeys[0].fmr_owner = 0; 67362306a36Sopenharmony_ci dkeys[0].fmr_length = 0; 67462306a36Sopenharmony_ci memset(&dkeys[1], 0xFF, sizeof(struct ext4_fsmap)); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if (!ext4_getfsmap_check_keys(dkeys, &head->fmh_keys[1])) 67762306a36Sopenharmony_ci return -EINVAL; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci info.gfi_next_fsblk = head->fmh_keys[0].fmr_physical + 68062306a36Sopenharmony_ci head->fmh_keys[0].fmr_length; 68162306a36Sopenharmony_ci info.gfi_formatter = formatter; 68262306a36Sopenharmony_ci info.gfi_format_arg = arg; 68362306a36Sopenharmony_ci info.gfi_head = head; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci /* For each device we support... */ 68662306a36Sopenharmony_ci for (i = 0; i < EXT4_GETFSMAP_DEVS; i++) { 68762306a36Sopenharmony_ci /* Is this device within the range the user asked for? */ 68862306a36Sopenharmony_ci if (!handlers[i].gfd_fn) 68962306a36Sopenharmony_ci continue; 69062306a36Sopenharmony_ci if (head->fmh_keys[0].fmr_device > handlers[i].gfd_dev) 69162306a36Sopenharmony_ci continue; 69262306a36Sopenharmony_ci if (head->fmh_keys[1].fmr_device < handlers[i].gfd_dev) 69362306a36Sopenharmony_ci break; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci /* 69662306a36Sopenharmony_ci * If this device number matches the high key, we have 69762306a36Sopenharmony_ci * to pass the high key to the handler to limit the 69862306a36Sopenharmony_ci * query results. If the device number exceeds the 69962306a36Sopenharmony_ci * low key, zero out the low key so that we get 70062306a36Sopenharmony_ci * everything from the beginning. 70162306a36Sopenharmony_ci */ 70262306a36Sopenharmony_ci if (handlers[i].gfd_dev == head->fmh_keys[1].fmr_device) 70362306a36Sopenharmony_ci dkeys[1] = head->fmh_keys[1]; 70462306a36Sopenharmony_ci if (handlers[i].gfd_dev > head->fmh_keys[0].fmr_device) 70562306a36Sopenharmony_ci memset(&dkeys[0], 0, sizeof(struct ext4_fsmap)); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci info.gfi_dev = handlers[i].gfd_dev; 70862306a36Sopenharmony_ci info.gfi_last = false; 70962306a36Sopenharmony_ci info.gfi_agno = -1; 71062306a36Sopenharmony_ci error = handlers[i].gfd_fn(sb, dkeys, &info); 71162306a36Sopenharmony_ci if (error) 71262306a36Sopenharmony_ci break; 71362306a36Sopenharmony_ci info.gfi_next_fsblk = 0; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci head->fmh_oflags = FMH_OF_DEV_T; 71762306a36Sopenharmony_ci return error; 71862306a36Sopenharmony_ci} 719