Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Copyright (C) 2017 Oracle.  All Rights Reserved.
0004  *
0005  * Author: Darrick J. Wong <darrick.wong@oracle.com>
0006  */
0007 #include "ext4.h"
0008 #include <linux/fsmap.h>
0009 #include "fsmap.h"
0010 #include "mballoc.h"
0011 #include <linux/sort.h>
0012 #include <linux/list_sort.h>
0013 #include <trace/events/ext4.h>
0014 
0015 /* Convert an ext4_fsmap to an fsmap. */
0016 void ext4_fsmap_from_internal(struct super_block *sb, struct fsmap *dest,
0017                   struct ext4_fsmap *src)
0018 {
0019     dest->fmr_device = src->fmr_device;
0020     dest->fmr_flags = src->fmr_flags;
0021     dest->fmr_physical = src->fmr_physical << sb->s_blocksize_bits;
0022     dest->fmr_owner = src->fmr_owner;
0023     dest->fmr_offset = 0;
0024     dest->fmr_length = src->fmr_length << sb->s_blocksize_bits;
0025     dest->fmr_reserved[0] = 0;
0026     dest->fmr_reserved[1] = 0;
0027     dest->fmr_reserved[2] = 0;
0028 }
0029 
0030 /* Convert an fsmap to an ext4_fsmap. */
0031 void ext4_fsmap_to_internal(struct super_block *sb, struct ext4_fsmap *dest,
0032                 struct fsmap *src)
0033 {
0034     dest->fmr_device = src->fmr_device;
0035     dest->fmr_flags = src->fmr_flags;
0036     dest->fmr_physical = src->fmr_physical >> sb->s_blocksize_bits;
0037     dest->fmr_owner = src->fmr_owner;
0038     dest->fmr_length = src->fmr_length >> sb->s_blocksize_bits;
0039 }
0040 
0041 /* getfsmap query state */
0042 struct ext4_getfsmap_info {
0043     struct ext4_fsmap_head  *gfi_head;
0044     ext4_fsmap_format_t gfi_formatter;  /* formatting fn */
0045     void            *gfi_format_arg;/* format buffer */
0046     ext4_fsblk_t        gfi_next_fsblk; /* next fsblock we expect */
0047     u32         gfi_dev;    /* device id */
0048     ext4_group_t        gfi_agno;   /* bg number, if applicable */
0049     struct ext4_fsmap   gfi_low;    /* low rmap key */
0050     struct ext4_fsmap   gfi_high;   /* high rmap key */
0051     struct ext4_fsmap   gfi_lastfree;   /* free ext at end of last bg */
0052     struct list_head    gfi_meta_list;  /* fixed metadata list */
0053     bool            gfi_last;   /* last extent? */
0054 };
0055 
0056 /* Associate a device with a getfsmap handler. */
0057 struct ext4_getfsmap_dev {
0058     int         (*gfd_fn)(struct super_block *sb,
0059                       struct ext4_fsmap *keys,
0060                       struct ext4_getfsmap_info *info);
0061     u32         gfd_dev;
0062 };
0063 
0064 /* Compare two getfsmap device handlers. */
0065 static int ext4_getfsmap_dev_compare(const void *p1, const void *p2)
0066 {
0067     const struct ext4_getfsmap_dev *d1 = p1;
0068     const struct ext4_getfsmap_dev *d2 = p2;
0069 
0070     return d1->gfd_dev - d2->gfd_dev;
0071 }
0072 
0073 /* Compare a record against our starting point */
0074 static bool ext4_getfsmap_rec_before_low_key(struct ext4_getfsmap_info *info,
0075                          struct ext4_fsmap *rec)
0076 {
0077     return rec->fmr_physical < info->gfi_low.fmr_physical;
0078 }
0079 
0080 /*
0081  * Format a reverse mapping for getfsmap, having translated rm_startblock
0082  * into the appropriate daddr units.
0083  */
0084 static int ext4_getfsmap_helper(struct super_block *sb,
0085                 struct ext4_getfsmap_info *info,
0086                 struct ext4_fsmap *rec)
0087 {
0088     struct ext4_fsmap fmr;
0089     struct ext4_sb_info *sbi = EXT4_SB(sb);
0090     ext4_fsblk_t rec_fsblk = rec->fmr_physical;
0091     ext4_group_t agno;
0092     ext4_grpblk_t cno;
0093     int error;
0094 
0095     if (fatal_signal_pending(current))
0096         return -EINTR;
0097 
0098     /*
0099      * Filter out records that start before our startpoint, if the
0100      * caller requested that.
0101      */
0102     if (ext4_getfsmap_rec_before_low_key(info, rec)) {
0103         rec_fsblk += rec->fmr_length;
0104         if (info->gfi_next_fsblk < rec_fsblk)
0105             info->gfi_next_fsblk = rec_fsblk;
0106         return EXT4_QUERY_RANGE_CONTINUE;
0107     }
0108 
0109     /* Are we just counting mappings? */
0110     if (info->gfi_head->fmh_count == 0) {
0111         if (info->gfi_head->fmh_entries == UINT_MAX)
0112             return EXT4_QUERY_RANGE_ABORT;
0113 
0114         if (rec_fsblk > info->gfi_next_fsblk)
0115             info->gfi_head->fmh_entries++;
0116 
0117         if (info->gfi_last)
0118             return EXT4_QUERY_RANGE_CONTINUE;
0119 
0120         info->gfi_head->fmh_entries++;
0121 
0122         rec_fsblk += rec->fmr_length;
0123         if (info->gfi_next_fsblk < rec_fsblk)
0124             info->gfi_next_fsblk = rec_fsblk;
0125         return EXT4_QUERY_RANGE_CONTINUE;
0126     }
0127 
0128     /*
0129      * If the record starts past the last physical block we saw,
0130      * then we've found a gap.  Report the gap as being owned by
0131      * whatever the caller specified is the missing owner.
0132      */
0133     if (rec_fsblk > info->gfi_next_fsblk) {
0134         if (info->gfi_head->fmh_entries >= info->gfi_head->fmh_count)
0135             return EXT4_QUERY_RANGE_ABORT;
0136 
0137         ext4_get_group_no_and_offset(sb, info->gfi_next_fsblk,
0138                 &agno, &cno);
0139         trace_ext4_fsmap_mapping(sb, info->gfi_dev, agno,
0140                 EXT4_C2B(sbi, cno),
0141                 rec_fsblk - info->gfi_next_fsblk,
0142                 EXT4_FMR_OWN_UNKNOWN);
0143 
0144         fmr.fmr_device = info->gfi_dev;
0145         fmr.fmr_physical = info->gfi_next_fsblk;
0146         fmr.fmr_owner = EXT4_FMR_OWN_UNKNOWN;
0147         fmr.fmr_length = rec_fsblk - info->gfi_next_fsblk;
0148         fmr.fmr_flags = FMR_OF_SPECIAL_OWNER;
0149         error = info->gfi_formatter(&fmr, info->gfi_format_arg);
0150         if (error)
0151             return error;
0152         info->gfi_head->fmh_entries++;
0153     }
0154 
0155     if (info->gfi_last)
0156         goto out;
0157 
0158     /* Fill out the extent we found */
0159     if (info->gfi_head->fmh_entries >= info->gfi_head->fmh_count)
0160         return EXT4_QUERY_RANGE_ABORT;
0161 
0162     ext4_get_group_no_and_offset(sb, rec_fsblk, &agno, &cno);
0163     trace_ext4_fsmap_mapping(sb, info->gfi_dev, agno, EXT4_C2B(sbi, cno),
0164             rec->fmr_length, rec->fmr_owner);
0165 
0166     fmr.fmr_device = info->gfi_dev;
0167     fmr.fmr_physical = rec_fsblk;
0168     fmr.fmr_owner = rec->fmr_owner;
0169     fmr.fmr_flags = FMR_OF_SPECIAL_OWNER;
0170     fmr.fmr_length = rec->fmr_length;
0171     error = info->gfi_formatter(&fmr, info->gfi_format_arg);
0172     if (error)
0173         return error;
0174     info->gfi_head->fmh_entries++;
0175 
0176 out:
0177     rec_fsblk += rec->fmr_length;
0178     if (info->gfi_next_fsblk < rec_fsblk)
0179         info->gfi_next_fsblk = rec_fsblk;
0180     return EXT4_QUERY_RANGE_CONTINUE;
0181 }
0182 
0183 static inline ext4_fsblk_t ext4_fsmap_next_pblk(struct ext4_fsmap *fmr)
0184 {
0185     return fmr->fmr_physical + fmr->fmr_length;
0186 }
0187 
0188 /* Transform a blockgroup's free record into a fsmap */
0189 static int ext4_getfsmap_datadev_helper(struct super_block *sb,
0190                     ext4_group_t agno, ext4_grpblk_t start,
0191                     ext4_grpblk_t len, void *priv)
0192 {
0193     struct ext4_fsmap irec;
0194     struct ext4_getfsmap_info *info = priv;
0195     struct ext4_fsmap *p;
0196     struct ext4_fsmap *tmp;
0197     struct ext4_sb_info *sbi = EXT4_SB(sb);
0198     ext4_fsblk_t fsb;
0199     ext4_fsblk_t fslen;
0200     int error;
0201 
0202     fsb = (EXT4_C2B(sbi, start) + ext4_group_first_block_no(sb, agno));
0203     fslen = EXT4_C2B(sbi, len);
0204 
0205     /* If the retained free extent record is set... */
0206     if (info->gfi_lastfree.fmr_owner) {
0207         /* ...and abuts this one, lengthen it and return. */
0208         if (ext4_fsmap_next_pblk(&info->gfi_lastfree) == fsb) {
0209             info->gfi_lastfree.fmr_length += fslen;
0210             return 0;
0211         }
0212 
0213         /*
0214          * There's a gap between the two free extents; emit the
0215          * retained extent prior to merging the meta_list.
0216          */
0217         error = ext4_getfsmap_helper(sb, info, &info->gfi_lastfree);
0218         if (error)
0219             return error;
0220         info->gfi_lastfree.fmr_owner = 0;
0221     }
0222 
0223     /* Merge in any relevant extents from the meta_list */
0224     list_for_each_entry_safe(p, tmp, &info->gfi_meta_list, fmr_list) {
0225         if (p->fmr_physical + p->fmr_length <= info->gfi_next_fsblk) {
0226             list_del(&p->fmr_list);
0227             kfree(p);
0228         } else if (p->fmr_physical < fsb) {
0229             error = ext4_getfsmap_helper(sb, info, p);
0230             if (error)
0231                 return error;
0232 
0233             list_del(&p->fmr_list);
0234             kfree(p);
0235         }
0236     }
0237 
0238     irec.fmr_device = 0;
0239     irec.fmr_physical = fsb;
0240     irec.fmr_length = fslen;
0241     irec.fmr_owner = EXT4_FMR_OWN_FREE;
0242     irec.fmr_flags = 0;
0243 
0244     /* If this is a free extent at the end of a bg, buffer it. */
0245     if (ext4_fsmap_next_pblk(&irec) ==
0246             ext4_group_first_block_no(sb, agno + 1)) {
0247         info->gfi_lastfree = irec;
0248         return 0;
0249     }
0250 
0251     /* Otherwise, emit it */
0252     return ext4_getfsmap_helper(sb, info, &irec);
0253 }
0254 
0255 /* Execute a getfsmap query against the log device. */
0256 static int ext4_getfsmap_logdev(struct super_block *sb, struct ext4_fsmap *keys,
0257                 struct ext4_getfsmap_info *info)
0258 {
0259     journal_t *journal = EXT4_SB(sb)->s_journal;
0260     struct ext4_fsmap irec;
0261 
0262     /* Set up search keys */
0263     info->gfi_low = keys[0];
0264     info->gfi_low.fmr_length = 0;
0265 
0266     memset(&info->gfi_high, 0xFF, sizeof(info->gfi_high));
0267 
0268     trace_ext4_fsmap_low_key(sb, info->gfi_dev, 0,
0269             info->gfi_low.fmr_physical,
0270             info->gfi_low.fmr_length,
0271             info->gfi_low.fmr_owner);
0272 
0273     trace_ext4_fsmap_high_key(sb, info->gfi_dev, 0,
0274             info->gfi_high.fmr_physical,
0275             info->gfi_high.fmr_length,
0276             info->gfi_high.fmr_owner);
0277 
0278     if (keys[0].fmr_physical > 0)
0279         return 0;
0280 
0281     /* Fabricate an rmap entry for the external log device. */
0282     irec.fmr_physical = journal->j_blk_offset;
0283     irec.fmr_length = journal->j_total_len;
0284     irec.fmr_owner = EXT4_FMR_OWN_LOG;
0285     irec.fmr_flags = 0;
0286 
0287     return ext4_getfsmap_helper(sb, info, &irec);
0288 }
0289 
0290 /* Helper to fill out an ext4_fsmap. */
0291 static inline int ext4_getfsmap_fill(struct list_head *meta_list,
0292                      ext4_fsblk_t fsb, ext4_fsblk_t len,
0293                      uint64_t owner)
0294 {
0295     struct ext4_fsmap *fsm;
0296 
0297     fsm = kmalloc(sizeof(*fsm), GFP_NOFS);
0298     if (!fsm)
0299         return -ENOMEM;
0300     fsm->fmr_device = 0;
0301     fsm->fmr_flags = 0;
0302     fsm->fmr_physical = fsb;
0303     fsm->fmr_owner = owner;
0304     fsm->fmr_length = len;
0305     list_add_tail(&fsm->fmr_list, meta_list);
0306 
0307     return 0;
0308 }
0309 
0310 /*
0311  * This function returns the number of file system metadata blocks at
0312  * the beginning of a block group, including the reserved gdt blocks.
0313  */
0314 static unsigned int ext4_getfsmap_find_sb(struct super_block *sb,
0315                       ext4_group_t agno,
0316                       struct list_head *meta_list)
0317 {
0318     struct ext4_sb_info *sbi = EXT4_SB(sb);
0319     ext4_fsblk_t fsb = ext4_group_first_block_no(sb, agno);
0320     ext4_fsblk_t len;
0321     unsigned long first_meta_bg = le32_to_cpu(sbi->s_es->s_first_meta_bg);
0322     unsigned long metagroup = agno / EXT4_DESC_PER_BLOCK(sb);
0323     int error;
0324 
0325     /* Record the superblock. */
0326     if (ext4_bg_has_super(sb, agno)) {
0327         error = ext4_getfsmap_fill(meta_list, fsb, 1, EXT4_FMR_OWN_FS);
0328         if (error)
0329             return error;
0330         fsb++;
0331     }
0332 
0333     /* Record the group descriptors. */
0334     len = ext4_bg_num_gdb(sb, agno);
0335     if (!len)
0336         return 0;
0337     error = ext4_getfsmap_fill(meta_list, fsb, len,
0338                    EXT4_FMR_OWN_GDT);
0339     if (error)
0340         return error;
0341     fsb += len;
0342 
0343     /* Reserved GDT blocks */
0344     if (!ext4_has_feature_meta_bg(sb) || metagroup < first_meta_bg) {
0345         len = le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks);
0346         error = ext4_getfsmap_fill(meta_list, fsb, len,
0347                        EXT4_FMR_OWN_RESV_GDT);
0348         if (error)
0349             return error;
0350     }
0351 
0352     return 0;
0353 }
0354 
0355 /* Compare two fsmap items. */
0356 static int ext4_getfsmap_compare(void *priv,
0357                  const struct list_head *a,
0358                  const struct list_head *b)
0359 {
0360     struct ext4_fsmap *fa;
0361     struct ext4_fsmap *fb;
0362 
0363     fa = container_of(a, struct ext4_fsmap, fmr_list);
0364     fb = container_of(b, struct ext4_fsmap, fmr_list);
0365     if (fa->fmr_physical < fb->fmr_physical)
0366         return -1;
0367     else if (fa->fmr_physical > fb->fmr_physical)
0368         return 1;
0369     return 0;
0370 }
0371 
0372 /* Merge adjacent extents of fixed metadata. */
0373 static void ext4_getfsmap_merge_fixed_metadata(struct list_head *meta_list)
0374 {
0375     struct ext4_fsmap *p;
0376     struct ext4_fsmap *prev = NULL;
0377     struct ext4_fsmap *tmp;
0378 
0379     list_for_each_entry_safe(p, tmp, meta_list, fmr_list) {
0380         if (!prev) {
0381             prev = p;
0382             continue;
0383         }
0384 
0385         if (prev->fmr_owner == p->fmr_owner &&
0386             prev->fmr_physical + prev->fmr_length == p->fmr_physical) {
0387             prev->fmr_length += p->fmr_length;
0388             list_del(&p->fmr_list);
0389             kfree(p);
0390         } else
0391             prev = p;
0392     }
0393 }
0394 
0395 /* Free a list of fixed metadata. */
0396 static void ext4_getfsmap_free_fixed_metadata(struct list_head *meta_list)
0397 {
0398     struct ext4_fsmap *p;
0399     struct ext4_fsmap *tmp;
0400 
0401     list_for_each_entry_safe(p, tmp, meta_list, fmr_list) {
0402         list_del(&p->fmr_list);
0403         kfree(p);
0404     }
0405 }
0406 
0407 /* Find all the fixed metadata in the filesystem. */
0408 static int ext4_getfsmap_find_fixed_metadata(struct super_block *sb,
0409                          struct list_head *meta_list)
0410 {
0411     struct ext4_group_desc *gdp;
0412     ext4_group_t agno;
0413     int error;
0414 
0415     INIT_LIST_HEAD(meta_list);
0416 
0417     /* Collect everything. */
0418     for (agno = 0; agno < EXT4_SB(sb)->s_groups_count; agno++) {
0419         gdp = ext4_get_group_desc(sb, agno, NULL);
0420         if (!gdp) {
0421             error = -EFSCORRUPTED;
0422             goto err;
0423         }
0424 
0425         /* Superblock & GDT */
0426         error = ext4_getfsmap_find_sb(sb, agno, meta_list);
0427         if (error)
0428             goto err;
0429 
0430         /* Block bitmap */
0431         error = ext4_getfsmap_fill(meta_list,
0432                        ext4_block_bitmap(sb, gdp), 1,
0433                        EXT4_FMR_OWN_BLKBM);
0434         if (error)
0435             goto err;
0436 
0437         /* Inode bitmap */
0438         error = ext4_getfsmap_fill(meta_list,
0439                        ext4_inode_bitmap(sb, gdp), 1,
0440                        EXT4_FMR_OWN_INOBM);
0441         if (error)
0442             goto err;
0443 
0444         /* Inodes */
0445         error = ext4_getfsmap_fill(meta_list,
0446                        ext4_inode_table(sb, gdp),
0447                        EXT4_SB(sb)->s_itb_per_group,
0448                        EXT4_FMR_OWN_INODES);
0449         if (error)
0450             goto err;
0451     }
0452 
0453     /* Sort the list */
0454     list_sort(NULL, meta_list, ext4_getfsmap_compare);
0455 
0456     /* Merge adjacent extents */
0457     ext4_getfsmap_merge_fixed_metadata(meta_list);
0458 
0459     return 0;
0460 err:
0461     ext4_getfsmap_free_fixed_metadata(meta_list);
0462     return error;
0463 }
0464 
0465 /* Execute a getfsmap query against the buddy bitmaps */
0466 static int ext4_getfsmap_datadev(struct super_block *sb,
0467                  struct ext4_fsmap *keys,
0468                  struct ext4_getfsmap_info *info)
0469 {
0470     struct ext4_sb_info *sbi = EXT4_SB(sb);
0471     ext4_fsblk_t start_fsb;
0472     ext4_fsblk_t end_fsb;
0473     ext4_fsblk_t bofs;
0474     ext4_fsblk_t eofs;
0475     ext4_group_t start_ag;
0476     ext4_group_t end_ag;
0477     ext4_grpblk_t first_cluster;
0478     ext4_grpblk_t last_cluster;
0479     int error = 0;
0480 
0481     bofs = le32_to_cpu(sbi->s_es->s_first_data_block);
0482     eofs = ext4_blocks_count(sbi->s_es);
0483     if (keys[0].fmr_physical >= eofs)
0484         return 0;
0485     else if (keys[0].fmr_physical < bofs)
0486         keys[0].fmr_physical = bofs;
0487     if (keys[1].fmr_physical >= eofs)
0488         keys[1].fmr_physical = eofs - 1;
0489     start_fsb = keys[0].fmr_physical;
0490     end_fsb = keys[1].fmr_physical;
0491 
0492     /* Determine first and last group to examine based on start and end */
0493     ext4_get_group_no_and_offset(sb, start_fsb, &start_ag, &first_cluster);
0494     ext4_get_group_no_and_offset(sb, end_fsb, &end_ag, &last_cluster);
0495 
0496     /*
0497      * Convert the fsmap low/high keys to bg based keys.  Initialize
0498      * low to the fsmap low key and max out the high key to the end
0499      * of the bg.
0500      */
0501     info->gfi_low = keys[0];
0502     info->gfi_low.fmr_physical = EXT4_C2B(sbi, first_cluster);
0503     info->gfi_low.fmr_length = 0;
0504 
0505     memset(&info->gfi_high, 0xFF, sizeof(info->gfi_high));
0506 
0507     /* Assemble a list of all the fixed-location metadata. */
0508     error = ext4_getfsmap_find_fixed_metadata(sb, &info->gfi_meta_list);
0509     if (error)
0510         goto err;
0511 
0512     /* Query each bg */
0513     for (info->gfi_agno = start_ag;
0514          info->gfi_agno <= end_ag;
0515          info->gfi_agno++) {
0516         /*
0517          * Set the bg high key from the fsmap high key if this
0518          * is the last bg that we're querying.
0519          */
0520         if (info->gfi_agno == end_ag) {
0521             info->gfi_high = keys[1];
0522             info->gfi_high.fmr_physical = EXT4_C2B(sbi,
0523                     last_cluster);
0524             info->gfi_high.fmr_length = 0;
0525         }
0526 
0527         trace_ext4_fsmap_low_key(sb, info->gfi_dev, info->gfi_agno,
0528                 info->gfi_low.fmr_physical,
0529                 info->gfi_low.fmr_length,
0530                 info->gfi_low.fmr_owner);
0531 
0532         trace_ext4_fsmap_high_key(sb, info->gfi_dev, info->gfi_agno,
0533                 info->gfi_high.fmr_physical,
0534                 info->gfi_high.fmr_length,
0535                 info->gfi_high.fmr_owner);
0536 
0537         error = ext4_mballoc_query_range(sb, info->gfi_agno,
0538                 EXT4_B2C(sbi, info->gfi_low.fmr_physical),
0539                 EXT4_B2C(sbi, info->gfi_high.fmr_physical),
0540                 ext4_getfsmap_datadev_helper, info);
0541         if (error)
0542             goto err;
0543 
0544         /*
0545          * Set the bg low key to the start of the bg prior to
0546          * moving on to the next bg.
0547          */
0548         if (info->gfi_agno == start_ag)
0549             memset(&info->gfi_low, 0, sizeof(info->gfi_low));
0550     }
0551 
0552     /* Do we have a retained free extent? */
0553     if (info->gfi_lastfree.fmr_owner) {
0554         error = ext4_getfsmap_helper(sb, info, &info->gfi_lastfree);
0555         if (error)
0556             goto err;
0557     }
0558 
0559     /* Report any gaps at the end of the bg */
0560     info->gfi_last = true;
0561     error = ext4_getfsmap_datadev_helper(sb, end_ag, last_cluster, 0, info);
0562     if (error)
0563         goto err;
0564 
0565 err:
0566     ext4_getfsmap_free_fixed_metadata(&info->gfi_meta_list);
0567     return error;
0568 }
0569 
0570 /* Do we recognize the device? */
0571 static bool ext4_getfsmap_is_valid_device(struct super_block *sb,
0572                       struct ext4_fsmap *fm)
0573 {
0574     if (fm->fmr_device == 0 || fm->fmr_device == UINT_MAX ||
0575         fm->fmr_device == new_encode_dev(sb->s_bdev->bd_dev))
0576         return true;
0577     if (EXT4_SB(sb)->s_journal_bdev &&
0578         fm->fmr_device == new_encode_dev(EXT4_SB(sb)->s_journal_bdev->bd_dev))
0579         return true;
0580     return false;
0581 }
0582 
0583 /* Ensure that the low key is less than the high key. */
0584 static bool ext4_getfsmap_check_keys(struct ext4_fsmap *low_key,
0585                      struct ext4_fsmap *high_key)
0586 {
0587     if (low_key->fmr_device > high_key->fmr_device)
0588         return false;
0589     if (low_key->fmr_device < high_key->fmr_device)
0590         return true;
0591 
0592     if (low_key->fmr_physical > high_key->fmr_physical)
0593         return false;
0594     if (low_key->fmr_physical < high_key->fmr_physical)
0595         return true;
0596 
0597     if (low_key->fmr_owner > high_key->fmr_owner)
0598         return false;
0599     if (low_key->fmr_owner < high_key->fmr_owner)
0600         return true;
0601 
0602     return false;
0603 }
0604 
0605 #define EXT4_GETFSMAP_DEVS  2
0606 /*
0607  * Get filesystem's extents as described in head, and format for
0608  * output.  Calls formatter to fill the user's buffer until all
0609  * extents are mapped, until the passed-in head->fmh_count slots have
0610  * been filled, or until the formatter short-circuits the loop, if it
0611  * is tracking filled-in extents on its own.
0612  *
0613  * Key to Confusion
0614  * ----------------
0615  * There are multiple levels of keys and counters at work here:
0616  * _fsmap_head.fmh_keys     -- low and high fsmap keys passed in;
0617  *                 these reflect fs-wide block addrs.
0618  * dkeys            -- fmh_keys used to query each device;
0619  *                 these are fmh_keys but w/ the low key
0620  *                 bumped up by fmr_length.
0621  * _getfsmap_info.gfi_next_fsblk-- next fs block we expect to see; this
0622  *                 is how we detect gaps in the fsmap
0623  *                 records and report them.
0624  * _getfsmap_info.gfi_low/high  -- per-bg low/high keys computed from
0625  *                 dkeys; used to query the free space.
0626  */
0627 int ext4_getfsmap(struct super_block *sb, struct ext4_fsmap_head *head,
0628           ext4_fsmap_format_t formatter, void *arg)
0629 {
0630     struct ext4_fsmap dkeys[2]; /* per-dev keys */
0631     struct ext4_getfsmap_dev handlers[EXT4_GETFSMAP_DEVS];
0632     struct ext4_getfsmap_info info = { NULL };
0633     int i;
0634     int error = 0;
0635 
0636     if (head->fmh_iflags & ~FMH_IF_VALID)
0637         return -EINVAL;
0638     if (!ext4_getfsmap_is_valid_device(sb, &head->fmh_keys[0]) ||
0639         !ext4_getfsmap_is_valid_device(sb, &head->fmh_keys[1]))
0640         return -EINVAL;
0641 
0642     head->fmh_entries = 0;
0643 
0644     /* Set up our device handlers. */
0645     memset(handlers, 0, sizeof(handlers));
0646     handlers[0].gfd_dev = new_encode_dev(sb->s_bdev->bd_dev);
0647     handlers[0].gfd_fn = ext4_getfsmap_datadev;
0648     if (EXT4_SB(sb)->s_journal_bdev) {
0649         handlers[1].gfd_dev = new_encode_dev(
0650                 EXT4_SB(sb)->s_journal_bdev->bd_dev);
0651         handlers[1].gfd_fn = ext4_getfsmap_logdev;
0652     }
0653 
0654     sort(handlers, EXT4_GETFSMAP_DEVS, sizeof(struct ext4_getfsmap_dev),
0655             ext4_getfsmap_dev_compare, NULL);
0656 
0657     /*
0658      * To continue where we left off, we allow userspace to use the
0659      * last mapping from a previous call as the low key of the next.
0660      * This is identified by a non-zero length in the low key. We
0661      * have to increment the low key in this scenario to ensure we
0662      * don't return the same mapping again, and instead return the
0663      * very next mapping.
0664      *
0665      * Bump the physical offset as there can be no other mapping for
0666      * the same physical block range.
0667      */
0668     dkeys[0] = head->fmh_keys[0];
0669     dkeys[0].fmr_physical += dkeys[0].fmr_length;
0670     dkeys[0].fmr_owner = 0;
0671     dkeys[0].fmr_length = 0;
0672     memset(&dkeys[1], 0xFF, sizeof(struct ext4_fsmap));
0673 
0674     if (!ext4_getfsmap_check_keys(dkeys, &head->fmh_keys[1]))
0675         return -EINVAL;
0676 
0677     info.gfi_next_fsblk = head->fmh_keys[0].fmr_physical +
0678               head->fmh_keys[0].fmr_length;
0679     info.gfi_formatter = formatter;
0680     info.gfi_format_arg = arg;
0681     info.gfi_head = head;
0682 
0683     /* For each device we support... */
0684     for (i = 0; i < EXT4_GETFSMAP_DEVS; i++) {
0685         /* Is this device within the range the user asked for? */
0686         if (!handlers[i].gfd_fn)
0687             continue;
0688         if (head->fmh_keys[0].fmr_device > handlers[i].gfd_dev)
0689             continue;
0690         if (head->fmh_keys[1].fmr_device < handlers[i].gfd_dev)
0691             break;
0692 
0693         /*
0694          * If this device number matches the high key, we have
0695          * to pass the high key to the handler to limit the
0696          * query results.  If the device number exceeds the
0697          * low key, zero out the low key so that we get
0698          * everything from the beginning.
0699          */
0700         if (handlers[i].gfd_dev == head->fmh_keys[1].fmr_device)
0701             dkeys[1] = head->fmh_keys[1];
0702         if (handlers[i].gfd_dev > head->fmh_keys[0].fmr_device)
0703             memset(&dkeys[0], 0, sizeof(struct ext4_fsmap));
0704 
0705         info.gfi_dev = handlers[i].gfd_dev;
0706         info.gfi_last = false;
0707         info.gfi_agno = -1;
0708         error = handlers[i].gfd_fn(sb, dkeys, &info);
0709         if (error)
0710             break;
0711         info.gfi_next_fsblk = 0;
0712     }
0713 
0714     head->fmh_oflags = FMH_OF_DEV_T;
0715     return error;
0716 }