LCOV - code coverage report
Current view: top level - fs/ext4 - fsmap.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 0 332 0.0 %
Date: 2022-04-01 13:59:58 Functions: 0 14 0.0 %
Branches: 0 152 0.0 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0+
       2                 :            : /*
       3                 :            :  * Copyright (C) 2017 Oracle.  All Rights Reserved.
       4                 :            :  *
       5                 :            :  * Author: Darrick J. Wong <darrick.wong@oracle.com>
       6                 :            :  */
       7                 :            : #include "ext4.h"
       8                 :            : #include <linux/fsmap.h>
       9                 :            : #include "fsmap.h"
      10                 :            : #include "mballoc.h"
      11                 :            : #include <linux/sort.h>
      12                 :            : #include <linux/list_sort.h>
      13                 :            : #include <trace/events/ext4.h>
      14                 :            : 
      15                 :            : /* Convert an ext4_fsmap to an fsmap. */
      16                 :          0 : void ext4_fsmap_from_internal(struct super_block *sb, struct fsmap *dest,
      17                 :            :                               struct ext4_fsmap *src)
      18                 :            : {
      19                 :          0 :         dest->fmr_device = src->fmr_device;
      20                 :          0 :         dest->fmr_flags = src->fmr_flags;
      21                 :          0 :         dest->fmr_physical = src->fmr_physical << sb->s_blocksize_bits;
      22                 :          0 :         dest->fmr_owner = src->fmr_owner;
      23                 :          0 :         dest->fmr_offset = 0;
      24                 :          0 :         dest->fmr_length = src->fmr_length << sb->s_blocksize_bits;
      25                 :          0 :         dest->fmr_reserved[0] = 0;
      26                 :          0 :         dest->fmr_reserved[1] = 0;
      27                 :          0 :         dest->fmr_reserved[2] = 0;
      28                 :          0 : }
      29                 :            : 
      30                 :            : /* Convert an fsmap to an ext4_fsmap. */
      31                 :          0 : void ext4_fsmap_to_internal(struct super_block *sb, struct ext4_fsmap *dest,
      32                 :            :                             struct fsmap *src)
      33                 :            : {
      34                 :          0 :         dest->fmr_device = src->fmr_device;
      35                 :          0 :         dest->fmr_flags = src->fmr_flags;
      36                 :          0 :         dest->fmr_physical = src->fmr_physical >> sb->s_blocksize_bits;
      37                 :          0 :         dest->fmr_owner = src->fmr_owner;
      38                 :          0 :         dest->fmr_length = src->fmr_length >> sb->s_blocksize_bits;
      39                 :          0 : }
      40                 :            : 
      41                 :            : /* getfsmap query state */
      42                 :            : struct ext4_getfsmap_info {
      43                 :            :         struct ext4_fsmap_head  *gfi_head;
      44                 :            :         ext4_fsmap_format_t     gfi_formatter;  /* formatting fn */
      45                 :            :         void                    *gfi_format_arg;/* format buffer */
      46                 :            :         ext4_fsblk_t            gfi_next_fsblk; /* next fsblock we expect */
      47                 :            :         u32                     gfi_dev;        /* device id */
      48                 :            :         ext4_group_t            gfi_agno;       /* bg number, if applicable */
      49                 :            :         struct ext4_fsmap       gfi_low;        /* low rmap key */
      50                 :            :         struct ext4_fsmap       gfi_high;       /* high rmap key */
      51                 :            :         struct ext4_fsmap       gfi_lastfree;   /* free ext at end of last bg */
      52                 :            :         struct list_head        gfi_meta_list;  /* fixed metadata list */
      53                 :            :         bool                    gfi_last;       /* last extent? */
      54                 :            : };
      55                 :            : 
      56                 :            : /* Associate a device with a getfsmap handler. */
      57                 :            : struct ext4_getfsmap_dev {
      58                 :            :         int                     (*gfd_fn)(struct super_block *sb,
      59                 :            :                                       struct ext4_fsmap *keys,
      60                 :            :                                       struct ext4_getfsmap_info *info);
      61                 :            :         u32                     gfd_dev;
      62                 :            : };
      63                 :            : 
      64                 :            : /* Compare two getfsmap device handlers. */
      65                 :          0 : static int ext4_getfsmap_dev_compare(const void *p1, const void *p2)
      66                 :            : {
      67                 :          0 :         const struct ext4_getfsmap_dev *d1 = p1;
      68                 :          0 :         const struct ext4_getfsmap_dev *d2 = p2;
      69                 :            : 
      70                 :          0 :         return d1->gfd_dev - d2->gfd_dev;
      71                 :            : }
      72                 :            : 
      73                 :            : /* Compare a record against our starting point */
      74                 :          0 : static bool ext4_getfsmap_rec_before_low_key(struct ext4_getfsmap_info *info,
      75                 :            :                                              struct ext4_fsmap *rec)
      76                 :            : {
      77                 :          0 :         return rec->fmr_physical < info->gfi_low.fmr_physical;
      78                 :            : }
      79                 :            : 
      80                 :            : /*
      81                 :            :  * Format a reverse mapping for getfsmap, having translated rm_startblock
      82                 :            :  * into the appropriate daddr units.
      83                 :            :  */
      84                 :          0 : static int ext4_getfsmap_helper(struct super_block *sb,
      85                 :            :                                 struct ext4_getfsmap_info *info,
      86                 :            :                                 struct ext4_fsmap *rec)
      87                 :            : {
      88                 :          0 :         struct ext4_fsmap fmr;
      89                 :          0 :         struct ext4_sb_info *sbi = EXT4_SB(sb);
      90                 :          0 :         ext4_fsblk_t rec_fsblk = rec->fmr_physical;
      91                 :          0 :         ext4_group_t agno;
      92                 :          0 :         ext4_grpblk_t cno;
      93                 :          0 :         int error;
      94                 :            : 
      95         [ #  # ]:          0 :         if (fatal_signal_pending(current))
      96                 :            :                 return -EINTR;
      97                 :            : 
      98                 :            :         /*
      99                 :            :          * Filter out records that start before our startpoint, if the
     100                 :            :          * caller requested that.
     101                 :            :          */
     102         [ #  # ]:          0 :         if (ext4_getfsmap_rec_before_low_key(info, rec)) {
     103                 :          0 :                 rec_fsblk += rec->fmr_length;
     104         [ #  # ]:          0 :                 if (info->gfi_next_fsblk < rec_fsblk)
     105                 :          0 :                         info->gfi_next_fsblk = rec_fsblk;
     106                 :          0 :                 return EXT4_QUERY_RANGE_CONTINUE;
     107                 :            :         }
     108                 :            : 
     109                 :            :         /* Are we just counting mappings? */
     110         [ #  # ]:          0 :         if (info->gfi_head->fmh_count == 0) {
     111         [ #  # ]:          0 :                 if (rec_fsblk > info->gfi_next_fsblk)
     112                 :          0 :                         info->gfi_head->fmh_entries++;
     113                 :            : 
     114         [ #  # ]:          0 :                 if (info->gfi_last)
     115                 :            :                         return EXT4_QUERY_RANGE_CONTINUE;
     116                 :            : 
     117                 :          0 :                 info->gfi_head->fmh_entries++;
     118                 :            : 
     119                 :          0 :                 rec_fsblk += rec->fmr_length;
     120         [ #  # ]:          0 :                 if (info->gfi_next_fsblk < rec_fsblk)
     121                 :          0 :                         info->gfi_next_fsblk = rec_fsblk;
     122                 :          0 :                 return EXT4_QUERY_RANGE_CONTINUE;
     123                 :            :         }
     124                 :            : 
     125                 :            :         /*
     126                 :            :          * If the record starts past the last physical block we saw,
     127                 :            :          * then we've found a gap.  Report the gap as being owned by
     128                 :            :          * whatever the caller specified is the missing owner.
     129                 :            :          */
     130         [ #  # ]:          0 :         if (rec_fsblk > info->gfi_next_fsblk) {
     131         [ #  # ]:          0 :                 if (info->gfi_head->fmh_entries >= info->gfi_head->fmh_count)
     132                 :            :                         return EXT4_QUERY_RANGE_ABORT;
     133                 :            : 
     134                 :          0 :                 ext4_get_group_no_and_offset(sb, info->gfi_next_fsblk,
     135                 :            :                                 &agno, &cno);
     136                 :          0 :                 trace_ext4_fsmap_mapping(sb, info->gfi_dev, agno,
     137                 :          0 :                                 EXT4_C2B(sbi, cno),
     138                 :          0 :                                 rec_fsblk - info->gfi_next_fsblk,
     139                 :            :                                 EXT4_FMR_OWN_UNKNOWN);
     140                 :            : 
     141                 :          0 :                 fmr.fmr_device = info->gfi_dev;
     142                 :          0 :                 fmr.fmr_physical = info->gfi_next_fsblk;
     143                 :          0 :                 fmr.fmr_owner = EXT4_FMR_OWN_UNKNOWN;
     144                 :          0 :                 fmr.fmr_length = rec_fsblk - info->gfi_next_fsblk;
     145                 :          0 :                 fmr.fmr_flags = FMR_OF_SPECIAL_OWNER;
     146                 :          0 :                 error = info->gfi_formatter(&fmr, info->gfi_format_arg);
     147         [ #  # ]:          0 :                 if (error)
     148                 :            :                         return error;
     149                 :          0 :                 info->gfi_head->fmh_entries++;
     150                 :            :         }
     151                 :            : 
     152         [ #  # ]:          0 :         if (info->gfi_last)
     153                 :          0 :                 goto out;
     154                 :            : 
     155                 :            :         /* Fill out the extent we found */
     156         [ #  # ]:          0 :         if (info->gfi_head->fmh_entries >= info->gfi_head->fmh_count)
     157                 :            :                 return EXT4_QUERY_RANGE_ABORT;
     158                 :            : 
     159                 :          0 :         ext4_get_group_no_and_offset(sb, rec_fsblk, &agno, &cno);
     160                 :          0 :         trace_ext4_fsmap_mapping(sb, info->gfi_dev, agno, EXT4_C2B(sbi, cno),
     161                 :            :                         rec->fmr_length, rec->fmr_owner);
     162                 :            : 
     163                 :          0 :         fmr.fmr_device = info->gfi_dev;
     164                 :          0 :         fmr.fmr_physical = rec_fsblk;
     165                 :          0 :         fmr.fmr_owner = rec->fmr_owner;
     166                 :          0 :         fmr.fmr_flags = FMR_OF_SPECIAL_OWNER;
     167                 :          0 :         fmr.fmr_length = rec->fmr_length;
     168                 :          0 :         error = info->gfi_formatter(&fmr, info->gfi_format_arg);
     169         [ #  # ]:          0 :         if (error)
     170                 :            :                 return error;
     171                 :          0 :         info->gfi_head->fmh_entries++;
     172                 :            : 
     173                 :          0 : out:
     174                 :          0 :         rec_fsblk += rec->fmr_length;
     175         [ #  # ]:          0 :         if (info->gfi_next_fsblk < rec_fsblk)
     176                 :          0 :                 info->gfi_next_fsblk = rec_fsblk;
     177                 :            :         return EXT4_QUERY_RANGE_CONTINUE;
     178                 :            : }
     179                 :            : 
     180                 :          0 : static inline ext4_fsblk_t ext4_fsmap_next_pblk(struct ext4_fsmap *fmr)
     181                 :            : {
     182                 :          0 :         return fmr->fmr_physical + fmr->fmr_length;
     183                 :            : }
     184                 :            : 
     185                 :            : /* Transform a blockgroup's free record into a fsmap */
     186                 :          0 : static int ext4_getfsmap_datadev_helper(struct super_block *sb,
     187                 :            :                                         ext4_group_t agno, ext4_grpblk_t start,
     188                 :            :                                         ext4_grpblk_t len, void *priv)
     189                 :            : {
     190                 :          0 :         struct ext4_fsmap irec;
     191                 :          0 :         struct ext4_getfsmap_info *info = priv;
     192                 :          0 :         struct ext4_fsmap *p;
     193                 :          0 :         struct ext4_fsmap *tmp;
     194         [ #  # ]:          0 :         struct ext4_sb_info *sbi = EXT4_SB(sb);
     195                 :          0 :         ext4_fsblk_t fsb;
     196                 :          0 :         ext4_fsblk_t fslen;
     197                 :          0 :         int error;
     198                 :            : 
     199         [ #  # ]:          0 :         fsb = (EXT4_C2B(sbi, start) + ext4_group_first_block_no(sb, agno));
     200                 :          0 :         fslen = EXT4_C2B(sbi, len);
     201                 :            : 
     202                 :            :         /* If the retained free extent record is set... */
     203         [ #  # ]:          0 :         if (info->gfi_lastfree.fmr_owner) {
     204                 :            :                 /* ...and abuts this one, lengthen it and return. */
     205         [ #  # ]:          0 :                 if (ext4_fsmap_next_pblk(&info->gfi_lastfree) == fsb) {
     206                 :          0 :                         info->gfi_lastfree.fmr_length += fslen;
     207                 :          0 :                         return 0;
     208                 :            :                 }
     209                 :            : 
     210                 :            :                 /*
     211                 :            :                  * There's a gap between the two free extents; emit the
     212                 :            :                  * retained extent prior to merging the meta_list.
     213                 :            :                  */
     214                 :          0 :                 error = ext4_getfsmap_helper(sb, info, &info->gfi_lastfree);
     215         [ #  # ]:          0 :                 if (error)
     216                 :            :                         return error;
     217                 :          0 :                 info->gfi_lastfree.fmr_owner = 0;
     218                 :            :         }
     219                 :            : 
     220                 :            :         /* Merge in any relevant extents from the meta_list */
     221         [ #  # ]:          0 :         list_for_each_entry_safe(p, tmp, &info->gfi_meta_list, fmr_list) {
     222         [ #  # ]:          0 :                 if (p->fmr_physical + p->fmr_length <= info->gfi_next_fsblk) {
     223                 :          0 :                         list_del(&p->fmr_list);
     224                 :          0 :                         kfree(p);
     225         [ #  # ]:          0 :                 } else if (p->fmr_physical < fsb) {
     226                 :          0 :                         error = ext4_getfsmap_helper(sb, info, p);
     227         [ #  # ]:          0 :                         if (error)
     228                 :          0 :                                 return error;
     229                 :            : 
     230                 :          0 :                         list_del(&p->fmr_list);
     231                 :          0 :                         kfree(p);
     232                 :            :                 }
     233                 :            :         }
     234                 :            : 
     235                 :          0 :         irec.fmr_device = 0;
     236                 :          0 :         irec.fmr_physical = fsb;
     237                 :          0 :         irec.fmr_length = fslen;
     238                 :          0 :         irec.fmr_owner = EXT4_FMR_OWN_FREE;
     239                 :          0 :         irec.fmr_flags = 0;
     240                 :            : 
     241                 :            :         /* If this is a free extent at the end of a bg, buffer it. */
     242                 :          0 :         if (ext4_fsmap_next_pblk(&irec) ==
     243         [ #  # ]:          0 :                         ext4_group_first_block_no(sb, agno + 1)) {
     244                 :          0 :                 info->gfi_lastfree = irec;
     245                 :          0 :                 return 0;
     246                 :            :         }
     247                 :            : 
     248                 :            :         /* Otherwise, emit it */
     249                 :          0 :         return ext4_getfsmap_helper(sb, info, &irec);
     250                 :            : }
     251                 :            : 
     252                 :            : /* Execute a getfsmap query against the log device. */
     253                 :          0 : static int ext4_getfsmap_logdev(struct super_block *sb, struct ext4_fsmap *keys,
     254                 :            :                                 struct ext4_getfsmap_info *info)
     255                 :            : {
     256                 :          0 :         journal_t *journal = EXT4_SB(sb)->s_journal;
     257                 :          0 :         struct ext4_fsmap irec;
     258                 :            : 
     259                 :            :         /* Set up search keys */
     260                 :          0 :         info->gfi_low = keys[0];
     261                 :          0 :         info->gfi_low.fmr_length = 0;
     262                 :            : 
     263                 :          0 :         memset(&info->gfi_high, 0xFF, sizeof(info->gfi_high));
     264                 :            : 
     265                 :          0 :         trace_ext4_fsmap_low_key(sb, info->gfi_dev, 0,
     266                 :            :                         info->gfi_low.fmr_physical,
     267                 :            :                         info->gfi_low.fmr_length,
     268                 :            :                         info->gfi_low.fmr_owner);
     269                 :            : 
     270                 :          0 :         trace_ext4_fsmap_high_key(sb, info->gfi_dev, 0,
     271                 :            :                         info->gfi_high.fmr_physical,
     272                 :            :                         info->gfi_high.fmr_length,
     273                 :            :                         info->gfi_high.fmr_owner);
     274                 :            : 
     275         [ #  # ]:          0 :         if (keys[0].fmr_physical > 0)
     276                 :            :                 return 0;
     277                 :            : 
     278                 :            :         /* Fabricate an rmap entry for the external log device. */
     279                 :          0 :         irec.fmr_physical = journal->j_blk_offset;
     280                 :          0 :         irec.fmr_length = journal->j_maxlen;
     281                 :          0 :         irec.fmr_owner = EXT4_FMR_OWN_LOG;
     282                 :          0 :         irec.fmr_flags = 0;
     283                 :            : 
     284                 :          0 :         return ext4_getfsmap_helper(sb, info, &irec);
     285                 :            : }
     286                 :            : 
     287                 :            : /* Helper to fill out an ext4_fsmap. */
     288                 :          0 : static inline int ext4_getfsmap_fill(struct list_head *meta_list,
     289                 :            :                                      ext4_fsblk_t fsb, ext4_fsblk_t len,
     290                 :            :                                      uint64_t owner)
     291                 :            : {
     292                 :          0 :         struct ext4_fsmap *fsm;
     293                 :            : 
     294                 :          0 :         fsm = kmalloc(sizeof(*fsm), GFP_NOFS);
     295         [ #  # ]:          0 :         if (!fsm)
     296                 :            :                 return -ENOMEM;
     297                 :          0 :         fsm->fmr_device = 0;
     298                 :          0 :         fsm->fmr_flags = 0;
     299                 :          0 :         fsm->fmr_physical = fsb;
     300                 :          0 :         fsm->fmr_owner = owner;
     301                 :          0 :         fsm->fmr_length = len;
     302                 :          0 :         list_add_tail(&fsm->fmr_list, meta_list);
     303                 :            : 
     304                 :          0 :         return 0;
     305                 :            : }
     306                 :            : 
     307                 :            : /*
     308                 :            :  * This function returns the number of file system metadata blocks at
     309                 :            :  * the beginning of a block group, including the reserved gdt blocks.
     310                 :            :  */
     311                 :          0 : static unsigned int ext4_getfsmap_find_sb(struct super_block *sb,
     312                 :            :                                           ext4_group_t agno,
     313                 :            :                                           struct list_head *meta_list)
     314                 :            : {
     315                 :          0 :         struct ext4_sb_info *sbi = EXT4_SB(sb);
     316                 :          0 :         ext4_fsblk_t fsb = ext4_group_first_block_no(sb, agno);
     317                 :          0 :         ext4_fsblk_t len;
     318                 :          0 :         unsigned long first_meta_bg = le32_to_cpu(sbi->s_es->s_first_meta_bg);
     319                 :          0 :         unsigned long metagroup = agno / EXT4_DESC_PER_BLOCK(sb);
     320                 :          0 :         int error;
     321                 :            : 
     322                 :            :         /* Record the superblock. */
     323         [ #  # ]:          0 :         if (ext4_bg_has_super(sb, agno)) {
     324                 :          0 :                 error = ext4_getfsmap_fill(meta_list, fsb, 1, EXT4_FMR_OWN_FS);
     325         [ #  # ]:          0 :                 if (error)
     326                 :          0 :                         return error;
     327                 :          0 :                 fsb++;
     328                 :            :         }
     329                 :            : 
     330                 :            :         /* Record the group descriptors. */
     331                 :          0 :         len = ext4_bg_num_gdb(sb, agno);
     332         [ #  # ]:          0 :         if (!len)
     333                 :            :                 return 0;
     334                 :          0 :         error = ext4_getfsmap_fill(meta_list, fsb, len,
     335                 :            :                                    EXT4_FMR_OWN_GDT);
     336         [ #  # ]:          0 :         if (error)
     337                 :          0 :                 return error;
     338                 :          0 :         fsb += len;
     339                 :            : 
     340                 :            :         /* Reserved GDT blocks */
     341   [ #  #  #  # ]:          0 :         if (!ext4_has_feature_meta_bg(sb) || metagroup < first_meta_bg) {
     342                 :          0 :                 len = le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks);
     343                 :          0 :                 error = ext4_getfsmap_fill(meta_list, fsb, len,
     344                 :            :                                            EXT4_FMR_OWN_RESV_GDT);
     345         [ #  # ]:          0 :                 if (error)
     346                 :          0 :                         return error;
     347                 :            :         }
     348                 :            : 
     349                 :            :         return 0;
     350                 :            : }
     351                 :            : 
     352                 :            : /* Compare two fsmap items. */
     353                 :          0 : static int ext4_getfsmap_compare(void *priv,
     354                 :            :                                  struct list_head *a,
     355                 :            :                                  struct list_head *b)
     356                 :            : {
     357                 :          0 :         struct ext4_fsmap *fa;
     358                 :          0 :         struct ext4_fsmap *fb;
     359                 :            : 
     360                 :          0 :         fa = container_of(a, struct ext4_fsmap, fmr_list);
     361                 :          0 :         fb = container_of(b, struct ext4_fsmap, fmr_list);
     362         [ #  # ]:          0 :         if (fa->fmr_physical < fb->fmr_physical)
     363                 :            :                 return -1;
     364         [ #  # ]:          0 :         else if (fa->fmr_physical > fb->fmr_physical)
     365                 :          0 :                 return 1;
     366                 :            :         return 0;
     367                 :            : }
     368                 :            : 
     369                 :            : /* Merge adjacent extents of fixed metadata. */
     370                 :          0 : static void ext4_getfsmap_merge_fixed_metadata(struct list_head *meta_list)
     371                 :            : {
     372                 :          0 :         struct ext4_fsmap *p;
     373                 :          0 :         struct ext4_fsmap *prev = NULL;
     374                 :          0 :         struct ext4_fsmap *tmp;
     375                 :            : 
     376         [ #  # ]:          0 :         list_for_each_entry_safe(p, tmp, meta_list, fmr_list) {
     377         [ #  # ]:          0 :                 if (!prev) {
     378                 :          0 :                         prev = p;
     379                 :          0 :                         continue;
     380                 :            :                 }
     381                 :            : 
     382         [ #  # ]:          0 :                 if (prev->fmr_owner == p->fmr_owner &&
     383         [ #  # ]:          0 :                     prev->fmr_physical + prev->fmr_length == p->fmr_physical) {
     384                 :          0 :                         prev->fmr_length += p->fmr_length;
     385                 :          0 :                         list_del(&p->fmr_list);
     386                 :          0 :                         kfree(p);
     387                 :            :                 } else
     388                 :            :                         prev = p;
     389                 :            :         }
     390                 :          0 : }
     391                 :            : 
     392                 :            : /* Free a list of fixed metadata. */
     393                 :          0 : static void ext4_getfsmap_free_fixed_metadata(struct list_head *meta_list)
     394                 :            : {
     395                 :          0 :         struct ext4_fsmap *p;
     396                 :          0 :         struct ext4_fsmap *tmp;
     397                 :            : 
     398         [ #  # ]:          0 :         list_for_each_entry_safe(p, tmp, meta_list, fmr_list) {
     399                 :          0 :                 list_del(&p->fmr_list);
     400                 :          0 :                 kfree(p);
     401                 :            :         }
     402                 :          0 : }
     403                 :            : 
     404                 :            : /* Find all the fixed metadata in the filesystem. */
     405                 :          0 : static int ext4_getfsmap_find_fixed_metadata(struct super_block *sb,
     406                 :            :                                              struct list_head *meta_list)
     407                 :            : {
     408                 :          0 :         struct ext4_group_desc *gdp;
     409                 :          0 :         ext4_group_t agno;
     410                 :          0 :         int error;
     411                 :            : 
     412                 :          0 :         INIT_LIST_HEAD(meta_list);
     413                 :            : 
     414                 :            :         /* Collect everything. */
     415         [ #  # ]:          0 :         for (agno = 0; agno < EXT4_SB(sb)->s_groups_count; agno++) {
     416                 :          0 :                 gdp = ext4_get_group_desc(sb, agno, NULL);
     417         [ #  # ]:          0 :                 if (!gdp) {
     418                 :          0 :                         error = -EFSCORRUPTED;
     419                 :          0 :                         goto err;
     420                 :            :                 }
     421                 :            : 
     422                 :            :                 /* Superblock & GDT */
     423                 :          0 :                 error = ext4_getfsmap_find_sb(sb, agno, meta_list);
     424         [ #  # ]:          0 :                 if (error)
     425                 :          0 :                         goto err;
     426                 :            : 
     427                 :            :                 /* Block bitmap */
     428                 :          0 :                 error = ext4_getfsmap_fill(meta_list,
     429                 :            :                                            ext4_block_bitmap(sb, gdp), 1,
     430                 :            :                                            EXT4_FMR_OWN_BLKBM);
     431         [ #  # ]:          0 :                 if (error)
     432                 :          0 :                         goto err;
     433                 :            : 
     434                 :            :                 /* Inode bitmap */
     435                 :          0 :                 error = ext4_getfsmap_fill(meta_list,
     436                 :            :                                            ext4_inode_bitmap(sb, gdp), 1,
     437                 :            :                                            EXT4_FMR_OWN_INOBM);
     438         [ #  # ]:          0 :                 if (error)
     439                 :          0 :                         goto err;
     440                 :            : 
     441                 :            :                 /* Inodes */
     442                 :          0 :                 error = ext4_getfsmap_fill(meta_list,
     443                 :            :                                            ext4_inode_table(sb, gdp),
     444                 :          0 :                                            EXT4_SB(sb)->s_itb_per_group,
     445                 :            :                                            EXT4_FMR_OWN_INODES);
     446         [ #  # ]:          0 :                 if (error)
     447                 :          0 :                         goto err;
     448                 :            :         }
     449                 :            : 
     450                 :            :         /* Sort the list */
     451                 :          0 :         list_sort(NULL, meta_list, ext4_getfsmap_compare);
     452                 :            : 
     453                 :            :         /* Merge adjacent extents */
     454                 :          0 :         ext4_getfsmap_merge_fixed_metadata(meta_list);
     455                 :            : 
     456                 :          0 :         return 0;
     457                 :          0 : err:
     458                 :          0 :         ext4_getfsmap_free_fixed_metadata(meta_list);
     459                 :          0 :         return error;
     460                 :            : }
     461                 :            : 
     462                 :            : /* Execute a getfsmap query against the buddy bitmaps */
     463                 :          0 : static int ext4_getfsmap_datadev(struct super_block *sb,
     464                 :            :                                  struct ext4_fsmap *keys,
     465                 :            :                                  struct ext4_getfsmap_info *info)
     466                 :            : {
     467         [ #  # ]:          0 :         struct ext4_sb_info *sbi = EXT4_SB(sb);
     468                 :          0 :         ext4_fsblk_t start_fsb;
     469                 :          0 :         ext4_fsblk_t end_fsb;
     470                 :          0 :         ext4_fsblk_t bofs;
     471                 :          0 :         ext4_fsblk_t eofs;
     472                 :          0 :         ext4_group_t start_ag;
     473                 :          0 :         ext4_group_t end_ag;
     474                 :          0 :         ext4_grpblk_t first_cluster;
     475                 :          0 :         ext4_grpblk_t last_cluster;
     476                 :          0 :         int error = 0;
     477                 :            : 
     478                 :          0 :         bofs = le32_to_cpu(sbi->s_es->s_first_data_block);
     479         [ #  # ]:          0 :         eofs = ext4_blocks_count(sbi->s_es);
     480         [ #  # ]:          0 :         if (keys[0].fmr_physical >= eofs)
     481                 :            :                 return 0;
     482         [ #  # ]:          0 :         else if (keys[0].fmr_physical < bofs)
     483                 :          0 :                 keys[0].fmr_physical = bofs;
     484         [ #  # ]:          0 :         if (keys[1].fmr_physical >= eofs)
     485                 :          0 :                 keys[1].fmr_physical = eofs - 1;
     486                 :          0 :         start_fsb = keys[0].fmr_physical;
     487                 :          0 :         end_fsb = keys[1].fmr_physical;
     488                 :            : 
     489                 :            :         /* Determine first and last group to examine based on start and end */
     490                 :          0 :         ext4_get_group_no_and_offset(sb, start_fsb, &start_ag, &first_cluster);
     491                 :          0 :         ext4_get_group_no_and_offset(sb, end_fsb, &end_ag, &last_cluster);
     492                 :            : 
     493                 :            :         /*
     494                 :            :          * Convert the fsmap low/high keys to bg based keys.  Initialize
     495                 :            :          * low to the fsmap low key and max out the high key to the end
     496                 :            :          * of the bg.
     497                 :            :          */
     498                 :          0 :         info->gfi_low = keys[0];
     499                 :          0 :         info->gfi_low.fmr_physical = EXT4_C2B(sbi, first_cluster);
     500                 :          0 :         info->gfi_low.fmr_length = 0;
     501                 :            : 
     502                 :          0 :         memset(&info->gfi_high, 0xFF, sizeof(info->gfi_high));
     503                 :            : 
     504                 :            :         /* Assemble a list of all the fixed-location metadata. */
     505                 :          0 :         error = ext4_getfsmap_find_fixed_metadata(sb, &info->gfi_meta_list);
     506         [ #  # ]:          0 :         if (error)
     507                 :          0 :                 goto err;
     508                 :            : 
     509                 :            :         /* Query each bg */
     510                 :          0 :         for (info->gfi_agno = start_ag;
     511         [ #  # ]:          0 :              info->gfi_agno <= end_ag;
     512                 :          0 :              info->gfi_agno++) {
     513                 :            :                 /*
     514                 :            :                  * Set the bg high key from the fsmap high key if this
     515                 :            :                  * is the last bg that we're querying.
     516                 :            :                  */
     517         [ #  # ]:          0 :                 if (info->gfi_agno == end_ag) {
     518                 :          0 :                         info->gfi_high = keys[1];
     519                 :          0 :                         info->gfi_high.fmr_physical = EXT4_C2B(sbi,
     520                 :            :                                         last_cluster);
     521                 :          0 :                         info->gfi_high.fmr_length = 0;
     522                 :            :                 }
     523                 :            : 
     524                 :          0 :                 trace_ext4_fsmap_low_key(sb, info->gfi_dev, info->gfi_agno,
     525                 :            :                                 info->gfi_low.fmr_physical,
     526                 :            :                                 info->gfi_low.fmr_length,
     527                 :            :                                 info->gfi_low.fmr_owner);
     528                 :            : 
     529                 :          0 :                 trace_ext4_fsmap_high_key(sb, info->gfi_dev, info->gfi_agno,
     530                 :            :                                 info->gfi_high.fmr_physical,
     531                 :            :                                 info->gfi_high.fmr_length,
     532                 :            :                                 info->gfi_high.fmr_owner);
     533                 :            : 
     534                 :          0 :                 error = ext4_mballoc_query_range(sb, info->gfi_agno,
     535                 :          0 :                                 EXT4_B2C(sbi, info->gfi_low.fmr_physical),
     536                 :          0 :                                 EXT4_B2C(sbi, info->gfi_high.fmr_physical),
     537                 :            :                                 ext4_getfsmap_datadev_helper, info);
     538         [ #  # ]:          0 :                 if (error)
     539                 :          0 :                         goto err;
     540                 :            : 
     541                 :            :                 /*
     542                 :            :                  * Set the bg low key to the start of the bg prior to
     543                 :            :                  * moving on to the next bg.
     544                 :            :                  */
     545         [ #  # ]:          0 :                 if (info->gfi_agno == start_ag)
     546                 :          0 :                         memset(&info->gfi_low, 0, sizeof(info->gfi_low));
     547                 :            :         }
     548                 :            : 
     549                 :            :         /* Do we have a retained free extent? */
     550         [ #  # ]:          0 :         if (info->gfi_lastfree.fmr_owner) {
     551                 :          0 :                 error = ext4_getfsmap_helper(sb, info, &info->gfi_lastfree);
     552         [ #  # ]:          0 :                 if (error)
     553                 :          0 :                         goto err;
     554                 :            :         }
     555                 :            : 
     556                 :            :         /* Report any gaps at the end of the bg */
     557                 :          0 :         info->gfi_last = true;
     558                 :          0 :         error = ext4_getfsmap_datadev_helper(sb, end_ag, last_cluster, 0, info);
     559         [ #  # ]:          0 :         if (error)
     560                 :          0 :                 goto err;
     561                 :            : 
     562                 :          0 : err:
     563                 :          0 :         ext4_getfsmap_free_fixed_metadata(&info->gfi_meta_list);
     564                 :          0 :         return error;
     565                 :            : }
     566                 :            : 
     567                 :            : /* Do we recognize the device? */
     568                 :            : static bool ext4_getfsmap_is_valid_device(struct super_block *sb,
     569                 :            :                                           struct ext4_fsmap *fm)
     570                 :            : {
     571                 :            :         if (fm->fmr_device == 0 || fm->fmr_device == UINT_MAX ||
     572                 :            :             fm->fmr_device == new_encode_dev(sb->s_bdev->bd_dev))
     573                 :            :                 return true;
     574                 :            :         if (EXT4_SB(sb)->journal_bdev &&
     575                 :            :             fm->fmr_device == new_encode_dev(EXT4_SB(sb)->journal_bdev->bd_dev))
     576                 :            :                 return true;
     577                 :            :         return false;
     578                 :            : }
     579                 :            : 
     580                 :            : /* Ensure that the low key is less than the high key. */
     581                 :          0 : static bool ext4_getfsmap_check_keys(struct ext4_fsmap *low_key,
     582                 :            :                                      struct ext4_fsmap *high_key)
     583                 :            : {
     584                 :          0 :         if (low_key->fmr_device > high_key->fmr_device)
     585                 :            :                 return false;
     586         [ #  # ]:          0 :         if (low_key->fmr_device < high_key->fmr_device)
     587                 :            :                 return true;
     588                 :            : 
     589         [ #  # ]:          0 :         if (low_key->fmr_physical > high_key->fmr_physical)
     590                 :            :                 return false;
     591         [ #  # ]:          0 :         if (low_key->fmr_physical < high_key->fmr_physical)
     592                 :            :                 return true;
     593                 :            : 
     594                 :          0 :         if (low_key->fmr_owner > high_key->fmr_owner)
     595                 :            :                 return false;
     596         [ #  # ]:          0 :         if (low_key->fmr_owner < high_key->fmr_owner)
     597                 :            :                 return true;
     598                 :            : 
     599                 :            :         return false;
     600                 :            : }
     601                 :            : 
     602                 :            : #define EXT4_GETFSMAP_DEVS      2
     603                 :            : /*
     604                 :            :  * Get filesystem's extents as described in head, and format for
     605                 :            :  * output.  Calls formatter to fill the user's buffer until all
     606                 :            :  * extents are mapped, until the passed-in head->fmh_count slots have
     607                 :            :  * been filled, or until the formatter short-circuits the loop, if it
     608                 :            :  * is tracking filled-in extents on its own.
     609                 :            :  *
     610                 :            :  * Key to Confusion
     611                 :            :  * ----------------
     612                 :            :  * There are multiple levels of keys and counters at work here:
     613                 :            :  * _fsmap_head.fmh_keys         -- low and high fsmap keys passed in;
     614                 :            :  *                                 these reflect fs-wide block addrs.
     615                 :            :  * dkeys                        -- fmh_keys used to query each device;
     616                 :            :  *                                 these are fmh_keys but w/ the low key
     617                 :            :  *                                 bumped up by fmr_length.
     618                 :            :  * _getfsmap_info.gfi_next_fsblk-- next fs block we expect to see; this
     619                 :            :  *                                 is how we detect gaps in the fsmap
     620                 :            :  *                                 records and report them.
     621                 :            :  * _getfsmap_info.gfi_low/high  -- per-bg low/high keys computed from
     622                 :            :  *                                 dkeys; used to query the free space.
     623                 :            :  */
     624                 :          0 : int ext4_getfsmap(struct super_block *sb, struct ext4_fsmap_head *head,
     625                 :            :                   ext4_fsmap_format_t formatter, void *arg)
     626                 :            : {
     627                 :          0 :         struct ext4_fsmap dkeys[2];     /* per-dev keys */
     628                 :          0 :         struct ext4_getfsmap_dev handlers[EXT4_GETFSMAP_DEVS];
     629                 :          0 :         struct ext4_getfsmap_info info = { NULL };
     630                 :          0 :         int i;
     631                 :          0 :         int error = 0;
     632                 :            : 
     633         [ #  # ]:          0 :         if (head->fmh_iflags & ~FMH_IF_VALID)
     634                 :            :                 return -EINVAL;
     635         [ #  # ]:          0 :         if (!ext4_getfsmap_is_valid_device(sb, &head->fmh_keys[0]) ||
     636         [ #  # ]:          0 :             !ext4_getfsmap_is_valid_device(sb, &head->fmh_keys[1]))
     637                 :            :                 return -EINVAL;
     638                 :            : 
     639                 :          0 :         head->fmh_entries = 0;
     640                 :            : 
     641                 :            :         /* Set up our device handlers. */
     642                 :          0 :         memset(handlers, 0, sizeof(handlers));
     643         [ #  # ]:          0 :         handlers[0].gfd_dev = new_encode_dev(sb->s_bdev->bd_dev);
     644                 :          0 :         handlers[0].gfd_fn = ext4_getfsmap_datadev;
     645         [ #  # ]:          0 :         if (EXT4_SB(sb)->journal_bdev) {
     646                 :          0 :                 handlers[1].gfd_dev = new_encode_dev(
     647                 :            :                                 EXT4_SB(sb)->journal_bdev->bd_dev);
     648                 :          0 :                 handlers[1].gfd_fn = ext4_getfsmap_logdev;
     649                 :            :         }
     650                 :            : 
     651                 :          0 :         sort(handlers, EXT4_GETFSMAP_DEVS, sizeof(struct ext4_getfsmap_dev),
     652                 :            :                         ext4_getfsmap_dev_compare, NULL);
     653                 :            : 
     654                 :            :         /*
     655                 :            :          * To continue where we left off, we allow userspace to use the
     656                 :            :          * last mapping from a previous call as the low key of the next.
     657                 :            :          * This is identified by a non-zero length in the low key. We
     658                 :            :          * have to increment the low key in this scenario to ensure we
     659                 :            :          * don't return the same mapping again, and instead return the
     660                 :            :          * very next mapping.
     661                 :            :          *
     662                 :            :          * Bump the physical offset as there can be no other mapping for
     663                 :            :          * the same physical block range.
     664                 :            :          */
     665                 :          0 :         dkeys[0] = head->fmh_keys[0];
     666                 :          0 :         dkeys[0].fmr_physical += dkeys[0].fmr_length;
     667                 :          0 :         dkeys[0].fmr_owner = 0;
     668                 :          0 :         dkeys[0].fmr_length = 0;
     669                 :          0 :         memset(&dkeys[1], 0xFF, sizeof(struct ext4_fsmap));
     670                 :            : 
     671         [ #  # ]:          0 :         if (!ext4_getfsmap_check_keys(dkeys, &head->fmh_keys[1]))
     672                 :            :                 return -EINVAL;
     673                 :            : 
     674                 :          0 :         info.gfi_next_fsblk = head->fmh_keys[0].fmr_physical +
     675                 :          0 :                           head->fmh_keys[0].fmr_length;
     676                 :          0 :         info.gfi_formatter = formatter;
     677                 :          0 :         info.gfi_format_arg = arg;
     678                 :          0 :         info.gfi_head = head;
     679                 :            : 
     680                 :            :         /* For each device we support... */
     681         [ #  # ]:          0 :         for (i = 0; i < EXT4_GETFSMAP_DEVS; i++) {
     682                 :            :                 /* Is this device within the range the user asked for? */
     683         [ #  # ]:          0 :                 if (!handlers[i].gfd_fn)
     684                 :          0 :                         continue;
     685         [ #  # ]:          0 :                 if (head->fmh_keys[0].fmr_device > handlers[i].gfd_dev)
     686                 :          0 :                         continue;
     687         [ #  # ]:          0 :                 if (head->fmh_keys[1].fmr_device < handlers[i].gfd_dev)
     688                 :            :                         break;
     689                 :            : 
     690                 :            :                 /*
     691                 :            :                  * If this device number matches the high key, we have
     692                 :            :                  * to pass the high key to the handler to limit the
     693                 :            :                  * query results.  If the device number exceeds the
     694                 :            :                  * low key, zero out the low key so that we get
     695                 :            :                  * everything from the beginning.
     696                 :            :                  */
     697         [ #  # ]:          0 :                 if (handlers[i].gfd_dev == head->fmh_keys[1].fmr_device)
     698                 :          0 :                         dkeys[1] = head->fmh_keys[1];
     699         [ #  # ]:          0 :                 if (handlers[i].gfd_dev > head->fmh_keys[0].fmr_device)
     700                 :          0 :                         memset(&dkeys[0], 0, sizeof(struct ext4_fsmap));
     701                 :            : 
     702                 :          0 :                 info.gfi_dev = handlers[i].gfd_dev;
     703                 :          0 :                 info.gfi_last = false;
     704                 :          0 :                 info.gfi_agno = -1;
     705                 :          0 :                 error = handlers[i].gfd_fn(sb, dkeys, &info);
     706         [ #  # ]:          0 :                 if (error)
     707                 :            :                         break;
     708                 :          0 :                 info.gfi_next_fsblk = 0;
     709                 :            :         }
     710                 :            : 
     711                 :          0 :         head->fmh_oflags = FMH_OF_DEV_T;
     712                 :          0 :         return error;
     713                 :            : }

Generated by: LCOV version 1.14