LCOV - code coverage report
Current view: top level - fs/xfs - xfs_discard.c (source / functions) Hit Total Coverage
Test: fstests of 6.5.0-rc3-acha @ Mon Jul 31 20:08:06 PDT 2023 Lines: 164 177 92.7 %
Date: 2023-07-31 20:08:07 Functions: 5 5 100.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0
       2             : /*
       3             :  * Copyright (C) 2010 Red Hat, Inc.
       4             :  * All Rights Reserved.
       5             :  */
       6             : #include "xfs.h"
       7             : #include "xfs_shared.h"
       8             : #include "xfs_format.h"
       9             : #include "xfs_log_format.h"
      10             : #include "xfs_trans_resv.h"
      11             : #include "xfs_mount.h"
      12             : #include "xfs_btree.h"
      13             : #include "xfs_alloc_btree.h"
      14             : #include "xfs_alloc.h"
      15             : #include "xfs_discard.h"
      16             : #include "xfs_error.h"
      17             : #include "xfs_extent_busy.h"
      18             : #include "xfs_trace.h"
      19             : #include "xfs_log.h"
      20             : #include "xfs_ag.h"
      21             : #include "xfs_health.h"
      22             : 
      23             : /*
      24             :  * For trim functions that support it, cycle the metadata locks periodically
      25             :  * to prevent other parts of the filesystem from starving.
      26             :  */
      27             : #define XFS_TRIM_RELAX_INTERVAL (HZ)
      28             : 
      29             : /* Trim the free space in this AG by block number. */
      30             : static inline int
      31       57689 : xfs_trim_ag_bybno(
      32             :         struct xfs_perag        *pag,
      33             :         struct xfs_buf          **agbpp,
      34             :         xfs_daddr_t             start,
      35             :         xfs_daddr_t             end,
      36             :         xfs_daddr_t             minlen,
      37             :         uint64_t                *blocks_trimmed)
      38             : {
      39       57689 :         struct xfs_mount        *mp = pag->pag_mount;
      40       57689 :         struct block_device     *bdev = xfs_buftarg_bdev(mp->m_ddev_targp);
      41       57689 :         struct xfs_btree_cur    *cur;
      42       57689 :         struct xfs_agf          *agf = (*agbpp)->b_addr;
      43       57689 :         xfs_daddr_t             end_daddr;
      44       57689 :         xfs_agnumber_t          agno = pag->pag_agno;
      45       57689 :         xfs_agblock_t           start_agbno;
      46       57689 :         xfs_agblock_t           end_agbno;
      47       57689 :         xfs_extlen_t            minlen_fsb = XFS_BB_TO_FSB(mp, minlen);
      48       57689 :         unsigned long           last_relax = jiffies;
      49       57689 :         int                     i;
      50       57689 :         int                     error;
      51             : 
      52       57689 :         start = max(start, XFS_AGB_TO_DADDR(mp, agno, 0));
      53       57689 :         start_agbno = xfs_daddr_to_agbno(mp, start);
      54             : 
      55       57689 :         end_daddr = XFS_AGB_TO_DADDR(mp, agno, be32_to_cpu(agf->agf_length));
      56       57689 :         end = min(end, end_daddr - 1);
      57       57689 :         end_agbno = xfs_daddr_to_agbno(mp, end);
      58             : 
      59       57689 :         cur = xfs_allocbt_init_cursor(mp, NULL, *agbpp, pag, XFS_BTNUM_BNO);
      60             : 
      61       57689 :         error = xfs_alloc_lookup_le(cur, start_agbno, 0, &i);
      62       57689 :         if (error)
      63           0 :                 goto out_del_cursor;
      64             : 
      65             :         /*
      66             :          * If we didn't find anything at or below start_agbno, increment the
      67             :          * cursor to see if there's another record above it.
      68             :          */
      69       57689 :         if (!i) {
      70       47999 :                 error = xfs_btree_increment(cur, 0, &i);
      71       47999 :                 if (error)
      72           0 :                         goto out_del_cursor;
      73             :         }
      74             : 
      75             :         /* Loop the entire range that was asked for. */
      76     9180735 :         while (i) {
      77     9147293 :                 xfs_agblock_t   fbno;
      78     9147293 :                 xfs_extlen_t    flen;
      79     9147293 :                 xfs_daddr_t     dbno;
      80     9147293 :                 xfs_extlen_t    dlen;
      81             : 
      82     9147293 :                 error = xfs_alloc_get_rec(cur, &fbno, &flen, &i);
      83     9147293 :                 if (error)
      84           7 :                         goto out_del_cursor;
      85     9147293 :                 if (XFS_IS_CORRUPT(mp, i != 1)) {
      86           0 :                         xfs_btree_mark_sick(cur);
      87           0 :                         error = -EFSCORRUPTED;
      88           0 :                         goto out_del_cursor;
      89             :                 }
      90             : 
      91             :                 /* Skip extents entirely outside of the range. */
      92     9147293 :                 if (fbno >= end_agbno)
      93             :                         break;
      94     9123053 :                 if (fbno + flen < start_agbno)
      95        1658 :                         goto next_extent;
      96             : 
      97             :                 /* Trim the extent returned to the range we want. */
      98     9121395 :                 if (fbno < start_agbno) {
      99        7986 :                         flen -= start_agbno - fbno;
     100        7986 :                         fbno = start_agbno;
     101             :                 }
     102     9121395 :                 if (fbno + flen > end_agbno + 1)
     103        7728 :                         flen = end_agbno - fbno + 1;
     104             : 
     105             :                 /* Ignore too small. */
     106     9121395 :                 if (flen < minlen_fsb) {
     107     8772265 :                         trace_xfs_discard_toosmall(mp, agno, fbno, flen);
     108     8772265 :                         goto next_extent;
     109             :                 }
     110             : 
     111             :                 /*
     112             :                  * If any blocks in the range are still busy, skip the
     113             :                  * discard and try again the next time.
     114             :                  */
     115      349130 :                 if (xfs_extent_busy_search(mp, pag, fbno, flen)) {
     116         106 :                         trace_xfs_discard_busy(mp, agno, fbno, flen);
     117         106 :                         goto next_extent;
     118             :                 }
     119             : 
     120      349024 :                 trace_xfs_discard_extent(mp, agno, fbno, flen);
     121             : 
     122      349024 :                 dbno = XFS_AGB_TO_DADDR(mp, agno, fbno);
     123      349024 :                 dlen = XFS_FSB_TO_BB(mp, flen);
     124      349024 :                 error = blkdev_issue_discard(bdev, dbno, dlen, GFP_NOFS);
     125      349024 :                 if (error)
     126           0 :                         goto out_del_cursor;
     127      349024 :                 *blocks_trimmed += flen;
     128             : 
     129      349024 :                 if (time_after(jiffies, last_relax + XFS_TRIM_RELAX_INTERVAL)) {
     130             :                         /*
     131             :                          * Cycle the AGF lock since we know how to pick up
     132             :                          * where we left off.
     133             :                          */
     134         403 :                         trace_xfs_discard_relax(mp, agno, fbno, flen);
     135         403 :                         xfs_btree_del_cursor(cur, error);
     136         403 :                         xfs_buf_relse(*agbpp);
     137             : 
     138         403 :                         error = xfs_alloc_read_agf(pag, NULL, 0, agbpp);
     139         403 :                         if (error)
     140           0 :                                 return error;
     141             : 
     142         403 :                         cur = xfs_allocbt_init_cursor(mp, NULL, *agbpp, pag,
     143             :                                         XFS_BTNUM_BNO);
     144         403 :                         error = xfs_alloc_lookup_ge(cur, fbno + flen, 0, &i);
     145         403 :                         last_relax = jiffies;
     146             :                 } else {
     147      348621 : next_extent:
     148     9122650 :                         error = xfs_btree_increment(cur, 0, &i);
     149             :                 }
     150     9123053 :                 if (error)
     151           0 :                         goto out_del_cursor;
     152             : 
     153     9123053 :                 if (fatal_signal_pending(current)) {
     154           7 :                         error = -ERESTARTSYS;
     155           7 :                         goto out_del_cursor;
     156             :                 }
     157             :         }
     158             : 
     159       33442 : out_del_cursor:
     160       57689 :         xfs_btree_del_cursor(cur, error);
     161       57689 :         return error;
     162             : }
     163             : 
     164             : /* Trim the free space in this AG by length. */
     165             : static inline int
     166        7231 : xfs_trim_ag_bylen(
     167             :         struct xfs_perag        *pag,
     168             :         struct xfs_buf          *agbp,
     169             :         xfs_daddr_t             start,
     170             :         xfs_daddr_t             end,
     171             :         xfs_daddr_t             minlen,
     172             :         uint64_t                *blocks_trimmed)
     173             : {
     174        7231 :         struct xfs_mount        *mp = pag->pag_mount;
     175        7231 :         struct block_device     *bdev = xfs_buftarg_bdev(mp->m_ddev_targp);
     176        7231 :         struct xfs_btree_cur    *cur;
     177        7231 :         struct xfs_agf          *agf = agbp->b_addr;
     178        7231 :         int                     error;
     179        7231 :         int                     i;
     180             : 
     181        7231 :         cur = xfs_allocbt_init_cursor(mp, NULL, agbp, pag, XFS_BTNUM_CNT);
     182             : 
     183             :         /*
     184             :          * Look up the longest btree in the AGF and start with it.
     185             :          */
     186       14462 :         error = xfs_alloc_lookup_ge(cur, 0, be32_to_cpu(agf->agf_longest), &i);
     187        7231 :         if (error)
     188           0 :                 goto out_del_cursor;
     189             : 
     190             :         /*
     191             :          * Loop until we are done with all extents that are large
     192             :          * enough to be worth discarding.
     193             :          */
     194      267689 :         while (i) {
     195      267013 :                 xfs_agblock_t   fbno;
     196      267013 :                 xfs_extlen_t    flen;
     197      267013 :                 xfs_daddr_t     dbno;
     198      267013 :                 xfs_extlen_t    dlen;
     199             : 
     200      267013 :                 error = xfs_alloc_get_rec(cur, &fbno, &flen, &i);
     201      267013 :                 if (error)
     202             :                         break;
     203      267013 :                 if (XFS_IS_CORRUPT(mp, i != 1)) {
     204           0 :                         xfs_btree_mark_sick(cur);
     205           0 :                         error = -EFSCORRUPTED;
     206           0 :                         break;
     207             :                 }
     208      534026 :                 ASSERT(flen <= be32_to_cpu(agf->agf_longest));
     209             : 
     210             :                 /*
     211             :                  * use daddr format for all range/len calculations as that is
     212             :                  * the format the range/len variables are supplied in by
     213             :                  * userspace.
     214             :                  */
     215      267013 :                 dbno = XFS_AGB_TO_DADDR(mp, pag->pag_agno, fbno);
     216      267013 :                 dlen = XFS_FSB_TO_BB(mp, flen);
     217             : 
     218             :                 /*
     219             :                  * Too small?  Give up.
     220             :                  */
     221      267013 :                 if (dlen < minlen) {
     222        6555 :                         trace_xfs_discard_toosmall(mp, pag->pag_agno, fbno,
     223             :                                         flen);
     224        6555 :                         break;
     225             :                 }
     226             : 
     227             :                 /*
     228             :                  * If any blocks in the range are still busy, skip the
     229             :                  * discard and try again the next time.
     230             :                  */
     231      260458 :                 if (xfs_extent_busy_search(mp, pag, fbno, flen)) {
     232        2965 :                         trace_xfs_discard_busy(mp, pag->pag_agno, fbno, flen);
     233        2965 :                         goto next_extent;
     234             :                 }
     235             : 
     236      257493 :                 trace_xfs_discard_extent(mp, pag->pag_agno, fbno, flen);
     237      257493 :                 error = blkdev_issue_discard(bdev, dbno, dlen, GFP_NOFS);
     238      257493 :                 if (error)
     239             :                         break;
     240      257493 :                 *blocks_trimmed += flen;
     241             : 
     242      260458 : next_extent:
     243      260458 :                 error = xfs_btree_decrement(cur, 0, &i);
     244      260458 :                 if (error)
     245             :                         break;
     246             : 
     247      260458 :                 if (fatal_signal_pending(current)) {
     248             :                         error = -ERESTARTSYS;
     249             :                         break;
     250             :                 }
     251             :         }
     252             : 
     253         676 : out_del_cursor:
     254        7231 :         xfs_btree_del_cursor(cur, error);
     255        7231 :         return error;
     256             : }
     257             : 
     258             : STATIC int
     259       64920 : xfs_trim_ag_extents(
     260             :         struct xfs_perag        *pag,
     261             :         xfs_daddr_t             start,
     262             :         xfs_daddr_t             end,
     263             :         xfs_daddr_t             minlen,
     264             :         uint64_t                *blocks_trimmed)
     265             : {
     266       64920 :         struct xfs_mount        *mp = pag->pag_mount;
     267       64920 :         struct xfs_buf          *agbp;
     268       64920 :         struct xfs_agf          *agf;
     269       64920 :         int                     error;
     270             : 
     271             :         /*
     272             :          * Force out the log.  This means any transactions that might have freed
     273             :          * space before we take the AGF buffer lock are now on disk, and the
     274             :          * volatile disk cache is flushed.
     275             :          */
     276       64920 :         xfs_log_force(mp, XFS_LOG_SYNC);
     277             : 
     278       64919 :         error = xfs_alloc_read_agf(pag, NULL, 0, &agbp);
     279       64920 :         if (error)
     280             :                 return error;
     281       64920 :         agf = agbp->b_addr;
     282             : 
     283       64920 :         if (start > XFS_AGB_TO_DADDR(mp, pag->pag_agno, 0) ||
     284       27261 :             end < XFS_AGB_TO_DADDR(mp, pag->pag_agno,
     285       27261 :                                    be32_to_cpu(agf->agf_length)) - 1) {
     286             :                 /* Only trimming part of this AG */
     287       57689 :                 error = xfs_trim_ag_bybno(pag, &agbp, start, end, minlen,
     288             :                                 blocks_trimmed);
     289             :         } else {
     290             :                 /* Trim this entire AG */
     291        7231 :                 error = xfs_trim_ag_bylen(pag, agbp, start, end, minlen,
     292             :                                 blocks_trimmed);
     293             :         }
     294             : 
     295       64920 :         xfs_buf_relse(agbp);
     296       64920 :         return error;
     297             : }
     298             : 
     299             : static int
     300       38101 : xfs_trim_ddev_extents(
     301             :         struct xfs_mount        *mp,
     302             :         xfs_daddr_t             start,
     303             :         xfs_daddr_t             end,
     304             :         xfs_daddr_t             minlen,
     305             :         uint64_t                *blocks_trimmed)
     306             : {
     307       38101 :         struct xfs_perag        *pag;
     308       38101 :         xfs_agnumber_t          agno;
     309       38101 :         int                     error, last_error = 0;
     310             : 
     311       38101 :         if (end > XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks) - 1)
     312        6180 :                 end = XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks) - 1;
     313             : 
     314       38101 :         agno = xfs_daddr_to_agno(mp, start);
     315      103014 :         for_each_perag_range(mp, agno, xfs_daddr_to_agno(mp, end), pag) {
     316       64920 :                 error = xfs_trim_ag_extents(pag, start, end, minlen,
     317             :                                 blocks_trimmed);
     318       64920 :                 if (error) {
     319           7 :                         last_error = error;
     320           7 :                         if (error == -ERESTARTSYS) {
     321           7 :                                 xfs_perag_rele(pag);
     322           7 :                                 break;
     323             :                         }
     324             :                 }
     325             :         }
     326             : 
     327       38101 :         return last_error;
     328             : }
     329             : 
     330             : /*
     331             :  * trim a range of the filesystem.
     332             :  *
     333             :  * Note: the parameters passed from userspace are byte ranges into the
     334             :  * filesystem which does not match to the format we use for filesystem block
     335             :  * addressing. FSB addressing is sparse (AGNO|AGBNO), while the incoming format
     336             :  * is a linear address range. Hence we need to use DADDR based conversions and
     337             :  * comparisons for determining the correct offset and regions to trim.
     338             :  */
     339             : int
     340       38117 : xfs_ioc_trim(
     341             :         struct xfs_mount                *mp,
     342             :         struct fstrim_range __user      *urange)
     343             : {
     344       38117 :         struct block_device     *bdev = xfs_buftarg_bdev(mp->m_ddev_targp);
     345       38117 :         unsigned int            granularity = bdev_discard_granularity(bdev);
     346       38117 :         struct fstrim_range     range;
     347       38117 :         xfs_daddr_t             start, end, minlen;
     348       38117 :         uint64_t                blocks_trimmed = 0;
     349       38117 :         int                     error, last_error = 0;
     350             : 
     351       38117 :         if (!capable(CAP_SYS_ADMIN))
     352             :                 return -EPERM;
     353       38117 :         if (!bdev_max_discard_sectors(bdev))
     354             :                 return -EOPNOTSUPP;
     355             : 
     356             :         /*
     357             :          * We haven't recovered the log, so we cannot use our bnobt-guided
     358             :          * storage zapping commands.
     359             :          */
     360       38117 :         if (xfs_has_norecovery(mp))
     361             :                 return -EROFS;
     362             : 
     363       38115 :         if (copy_from_user(&range, urange, sizeof(range)))
     364             :                 return -EFAULT;
     365             : 
     366       38115 :         range.minlen = max_t(u64, granularity, range.minlen);
     367       38115 :         minlen = BTOBB(range.minlen);
     368             :         /*
     369             :          * Truncating down the len isn't actually quite correct, but using
     370             :          * BBTOB would mean we trivially get overflows for values
     371             :          * of ULLONG_MAX or slightly lower.  And ULLONG_MAX is the default
     372             :          * used by the fstrim application.  In the end it really doesn't
     373             :          * matter as trimming blocks is an advisory interface.
     374             :          */
     375       38115 :         if (range.start >= XFS_FSB_TO_B(mp, mp->m_sb.sb_dblocks) ||
     376       38105 :             range.minlen > XFS_FSB_TO_B(mp, mp->m_ag_max_usable) ||
     377       38105 :             range.len < mp->m_sb.sb_blocksize)
     378             :                 return -EINVAL;
     379             : 
     380       38101 :         start = BTOBB(range.start);
     381       38101 :         end = start + BTOBBT(range.len) - 1;
     382             : 
     383       38101 :         error = xfs_trim_ddev_extents(mp, start, end, minlen, &blocks_trimmed);
     384       38101 :         if (error == -ERESTARTSYS)
     385             :                 return error;
     386       38094 :         if (error)
     387             :                 last_error = error;
     388             : 
     389       38094 :         if (last_error)
     390             :                 return last_error;
     391             : 
     392       38094 :         range.len = XFS_FSB_TO_B(mp, blocks_trimmed);
     393       38094 :         if (copy_to_user(urange, &range, sizeof(range)))
     394           0 :                 return -EFAULT;
     395             :         return 0;
     396             : }

Generated by: LCOV version 1.14