// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2000-2005 Silicon Graphics, Inc. * All Rights Reserved. */ #include "xfs.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_log_format.h" #include "xfs_trans_resv.h" #include "xfs_bit.h" #include "xfs_mount.h" #include "xfs_inode.h" #include "xfs_bmap.h" #include "xfs_bmap_btree.h" #include "xfs_trans_space.h" #include "xfs_trans.h" #include "xfs_rtalloc.h" #include "xfs_error.h" #include "xfs_rtbitmap.h" #include "xfs_health.h" #include "xfs_sb.h" #include "xfs_errortag.h" #include "xfs_log.h" #include "xfs_buf_item.h" #include "xfs_extent_busy.h" /* * Realtime allocator bitmap functions shared with userspace. */ static xfs_failaddr_t xfs_rtbuf_verify( struct xfs_buf *bp) { struct xfs_mount *mp = bp->b_mount; struct xfs_rtbuf_blkinfo *hdr = bp->b_addr; if (!xfs_verify_magic(bp, hdr->rt_magic)) return __this_address; if (!xfs_has_rtgroups(mp)) return __this_address; if (!xfs_has_crc(mp)) return __this_address; if (!uuid_equal(&hdr->rt_uuid, &mp->m_sb.sb_meta_uuid)) return __this_address; if (hdr->rt_blkno != cpu_to_be64(xfs_buf_daddr(bp))) return __this_address; return NULL; } static void xfs_rtbuf_verify_read( struct xfs_buf *bp) { struct xfs_mount *mp = bp->b_mount; struct xfs_rtbuf_blkinfo *hdr = bp->b_addr; xfs_failaddr_t fa; if (!xfs_has_rtgroups(mp)) return; if (!xfs_log_check_lsn(mp, be64_to_cpu(hdr->rt_lsn))) { fa = __this_address; goto fail; } if (!xfs_buf_verify_cksum(bp, XFS_RTBUF_CRC_OFF)) { fa = __this_address; goto fail; } fa = xfs_rtbuf_verify(bp); if (fa) goto fail; return; fail: xfs_verifier_error(bp, -EFSCORRUPTED, fa); } static void xfs_rtbuf_verify_write( struct xfs_buf *bp) { struct xfs_mount *mp = bp->b_mount; struct xfs_rtbuf_blkinfo *hdr = bp->b_addr; struct xfs_buf_log_item *bip = bp->b_log_item; xfs_failaddr_t fa; if (!xfs_has_rtgroups(mp)) return; fa = xfs_rtbuf_verify(bp); if (fa) { xfs_verifier_error(bp, -EFSCORRUPTED, fa); return; } if (bip) hdr->rt_lsn = cpu_to_be64(bip->bli_item.li_lsn); xfs_buf_update_cksum(bp, XFS_RTBUF_CRC_OFF); } const struct xfs_buf_ops xfs_rtbuf_ops = { .name = "rtbuf", .verify_read = xfs_rtbuf_verify_read, .verify_write = xfs_rtbuf_verify_write, }; const struct xfs_buf_ops xfs_rtbitmap_buf_ops = { .name = "xfs_rtbitmap", .magic = { 0, cpu_to_be32(XFS_RTBITMAP_MAGIC) }, .verify_read = xfs_rtbuf_verify_read, .verify_write = xfs_rtbuf_verify_write, .verify_struct = xfs_rtbuf_verify, }; const struct xfs_buf_ops xfs_rtsummary_buf_ops = { .name = "xfs_rtsummary", .magic = { 0, cpu_to_be32(XFS_RTSUMMARY_MAGIC) }, .verify_read = xfs_rtbuf_verify_read, .verify_write = xfs_rtbuf_verify_write, .verify_struct = xfs_rtbuf_verify, }; /* Release cached rt bitmap and summary buffers. */ void xfs_rtbuf_cache_relse( struct xfs_rtalloc_args *args) { if (args->rbmbp) { xfs_trans_brelse(args->tp, args->rbmbp); args->rbmbp = NULL; args->rbmoff = NULLFILEOFF; } if (args->sumbp) { xfs_trans_brelse(args->tp, args->sumbp); args->sumbp = NULL; args->sumoff = NULLFILEOFF; } } /* * Get a buffer for the bitmap or summary file block specified. * The buffer is returned read and locked. */ static int xfs_rtbuf_get( struct xfs_rtalloc_args *args, xfs_fileoff_t block, /* block number in bitmap or summary */ enum xfs_rtg_inodes type) { struct xfs_inode *ip = args->rtg->rtg_inodes[type]; struct xfs_mount *mp = args->mp; struct xfs_buf **cbpp; /* cached block buffer */ xfs_fileoff_t *coffp; /* cached block number */ struct xfs_buf *bp; /* block buffer, result */ struct xfs_bmbt_irec map; enum xfs_blft buf_type; int nmap = 1; int error; switch (type) { case XFS_RTGI_SUMMARY: cbpp = &args->sumbp; coffp = &args->sumoff; buf_type = XFS_BLFT_RTSUMMARY_BUF; break; case XFS_RTGI_BITMAP: cbpp = &args->rbmbp; coffp = &args->rbmoff; buf_type = XFS_BLFT_RTBITMAP_BUF; break; default: return -EINVAL; } /* * If we have a cached buffer, and the block number matches, use that. */ if (*cbpp && *coffp == block) return 0; /* * Otherwise we have to have to get the buffer. If there was an old * one, get rid of it first. */ if (*cbpp) { xfs_trans_brelse(args->tp, *cbpp); *cbpp = NULL; } error = xfs_bmapi_read(ip, block, 1, &map, &nmap, 0); if (error) return error; if (XFS_IS_CORRUPT(mp, nmap == 0 || !xfs_bmap_is_written_extent(&map))) { xfs_rtginode_mark_sick(args->rtg, type); return -EFSCORRUPTED; } ASSERT(map.br_startblock != NULLFSBLOCK); error = xfs_trans_read_buf(mp, args->tp, mp->m_ddev_targp, XFS_FSB_TO_DADDR(mp, map.br_startblock), mp->m_bsize, 0, &bp, xfs_rtblock_ops(mp, type)); if (xfs_metadata_is_sick(error)) xfs_rtginode_mark_sick(args->rtg, type); if (error) return error; if (xfs_has_rtgroups(mp)) { struct xfs_rtbuf_blkinfo *hdr = bp->b_addr; if (hdr->rt_owner != cpu_to_be64(ip->i_ino)) { xfs_buf_mark_corrupt(bp); xfs_trans_brelse(args->tp, bp); xfs_rtginode_mark_sick(args->rtg, type); return -EFSCORRUPTED; } } xfs_trans_buf_set_type(args->tp, bp, buf_type); *cbpp = bp; *coffp = block; return 0; } int xfs_rtbitmap_read_buf( struct xfs_rtalloc_args *args, xfs_fileoff_t block) { struct xfs_mount *mp = args->mp; if (XFS_IS_CORRUPT(mp, block >= mp->m_sb.sb_rbmblocks)) { xfs_rtginode_mark_sick(args->rtg, XFS_RTGI_BITMAP); return -EFSCORRUPTED; } return xfs_rtbuf_get(args, block, XFS_RTGI_BITMAP); } int xfs_rtsummary_read_buf( struct xfs_rtalloc_args *args, xfs_fileoff_t block) { struct xfs_mount *mp = args->mp; if (XFS_IS_CORRUPT(mp, block >= mp->m_rsumblocks)) { xfs_rtginode_mark_sick(args->rtg, XFS_RTGI_SUMMARY); return -EFSCORRUPTED; } return xfs_rtbuf_get(args, block, XFS_RTGI_SUMMARY); } /* * Searching backward from start find the first block whose allocated/free state * is different from start's. */ int xfs_rtfind_back( struct xfs_rtalloc_args *args, xfs_rtxnum_t start, /* starting rtext to look at */ xfs_rtxnum_t *rtx) /* out: start rtext found */ { struct xfs_mount *mp = args->mp; int bit; /* bit number in the word */ xfs_fileoff_t block; /* bitmap block number */ int error; /* error value */ xfs_rtxnum_t firstbit; /* first useful bit in the word */ xfs_rtxnum_t i; /* current bit number rel. to start */ xfs_rtxnum_t len; /* length of inspected area */ xfs_rtword_t mask; /* mask of relevant bits for value */ xfs_rtword_t want; /* mask for "good" values */ xfs_rtword_t wdiff; /* difference from wanted value */ xfs_rtword_t incore; unsigned int word; /* word number in the buffer */ /* * Compute and read in starting bitmap block for starting block. */ block = xfs_rtx_to_rbmblock(mp, start); error = xfs_rtbitmap_read_buf(args, block); if (error) return error; /* * Get the first word's index & point to it. */ word = xfs_rtx_to_rbmword(mp, start); bit = (int)(start & (XFS_NBWORD - 1)); len = start + 1; /* * Compute match value, based on the bit at start: if 1 (free) * then all-ones, else all-zeroes. */ incore = xfs_rtbitmap_getword(args, word); want = (incore & ((xfs_rtword_t)1 << bit)) ? -1 : 0; /* * If the starting position is not word-aligned, deal with the * partial word. */ if (bit < XFS_NBWORD - 1) { /* * Calculate first (leftmost) bit number to look at, * and mask for all the relevant bits in this word. */ firstbit = max_t(xfs_srtblock_t, bit - len + 1, 0); mask = (((xfs_rtword_t)1 << (bit - firstbit + 1)) - 1) << firstbit; /* * Calculate the difference between the value there * and what we're looking for. */ if ((wdiff = (incore ^ want) & mask)) { /* * Different. Mark where we are and return. */ i = bit - xfs_highbit32(wdiff); *rtx = start - i + 1; return 0; } i = bit - firstbit + 1; /* * Go on to previous block if that's where the previous word is * and we need the previous word. */ if (--word == -1 && i < len) { /* * If done with this block, get the previous one. */ error = xfs_rtbitmap_read_buf(args, --block); if (error) return error; word = mp->m_blockwsize - 1; } } else { /* * Starting on a word boundary, no partial word. */ i = 0; } /* * Loop over whole words in buffers. When we use up one buffer * we move on to the previous one. */ while (len - i >= XFS_NBWORD) { /* * Compute difference between actual and desired value. */ incore = xfs_rtbitmap_getword(args, word); if ((wdiff = incore ^ want)) { /* * Different, mark where we are and return. */ i += XFS_NBWORD - 1 - xfs_highbit32(wdiff); *rtx = start - i + 1; return 0; } i += XFS_NBWORD; /* * Go on to previous block if that's where the previous word is * and we need the previous word. */ if (--word == -1 && i < len) { /* * If done with this block, get the previous one. */ error = xfs_rtbitmap_read_buf(args, --block); if (error) return error; word = mp->m_blockwsize - 1; } } /* * If not ending on a word boundary, deal with the last * (partial) word. */ if (len - i) { /* * Calculate first (leftmost) bit number to look at, * and mask for all the relevant bits in this word. */ firstbit = XFS_NBWORD - (len - i); mask = (((xfs_rtword_t)1 << (len - i)) - 1) << firstbit; /* * Compute difference between actual and desired value. */ incore = xfs_rtbitmap_getword(args, word); if ((wdiff = (incore ^ want) & mask)) { /* * Different, mark where we are and return. */ i += XFS_NBWORD - 1 - xfs_highbit32(wdiff); *rtx = start - i + 1; return 0; } else i = len; } /* * No match, return that we scanned the whole area. */ *rtx = start - i + 1; return 0; } /* * Searching forward from start to limit, find the first block whose * allocated/free state is different from start's. */ int xfs_rtfind_forw( struct xfs_rtalloc_args *args, xfs_rtxnum_t start, /* starting rtext to look at */ xfs_rtxnum_t limit, /* last rtext to look at */ xfs_rtxnum_t *rtx) /* out: start rtext found */ { struct xfs_mount *mp = args->mp; int bit; /* bit number in the word */ xfs_fileoff_t block; /* bitmap block number */ int error; xfs_rtxnum_t i; /* current bit number rel. to start */ xfs_rtxnum_t lastbit;/* last useful bit in the word */ xfs_rtxnum_t len; /* length of inspected area */ xfs_rtword_t mask; /* mask of relevant bits for value */ xfs_rtword_t want; /* mask for "good" values */ xfs_rtword_t wdiff; /* difference from wanted value */ xfs_rtword_t incore; unsigned int word; /* word number in the buffer */ ASSERT(start <= limit); /* * Compute and read in starting bitmap block for starting block. */ block = xfs_rtx_to_rbmblock(mp, start); error = xfs_rtbitmap_read_buf(args, block); if (error) return error; /* * Get the first word's index & point to it. */ word = xfs_rtx_to_rbmword(mp, start); bit = (int)(start & (XFS_NBWORD - 1)); len = limit - start + 1; /* * Compute match value, based on the bit at start: if 1 (free) * then all-ones, else all-zeroes. */ incore = xfs_rtbitmap_getword(args, word); want = (incore & ((xfs_rtword_t)1 << bit)) ? -1 : 0; /* * If the starting position is not word-aligned, deal with the * partial word. */ if (bit) { /* * Calculate last (rightmost) bit number to look at, * and mask for all the relevant bits in this word. */ lastbit = min(bit + len, XFS_NBWORD); mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit; /* * Calculate the difference between the value there * and what we're looking for. */ if ((wdiff = (incore ^ want) & mask)) { /* * Different. Mark where we are and return. */ i = xfs_lowbit32(wdiff) - bit; *rtx = start + i - 1; return 0; } i = lastbit - bit; /* * Go on to next block if that's where the next word is * and we need the next word. */ if (++word == mp->m_blockwsize && i < len) { /* * If done with this block, get the previous one. */ error = xfs_rtbitmap_read_buf(args, ++block); if (error) return error; word = 0; } } else { /* * Starting on a word boundary, no partial word. */ i = 0; } /* * Loop over whole words in buffers. When we use up one buffer * we move on to the next one. */ while (len - i >= XFS_NBWORD) { /* * Compute difference between actual and desired value. */ incore = xfs_rtbitmap_getword(args, word); if ((wdiff = incore ^ want)) { /* * Different, mark where we are and return. */ i += xfs_lowbit32(wdiff); *rtx = start + i - 1; return 0; } i += XFS_NBWORD; /* * Go on to next block if that's where the next word is * and we need the next word. */ if (++word == mp->m_blockwsize && i < len) { /* * If done with this block, get the next one. */ error = xfs_rtbitmap_read_buf(args, ++block); if (error) return error; word = 0; } } /* * If not ending on a word boundary, deal with the last * (partial) word. */ if ((lastbit = len - i)) { /* * Calculate mask for all the relevant bits in this word. */ mask = ((xfs_rtword_t)1 << lastbit) - 1; /* * Compute difference between actual and desired value. */ incore = xfs_rtbitmap_getword(args, word); if ((wdiff = (incore ^ want) & mask)) { /* * Different, mark where we are and return. */ i += xfs_lowbit32(wdiff); *rtx = start + i - 1; return 0; } else i = len; } /* * No match, return that we scanned the whole area. */ *rtx = start + i - 1; return 0; } /* Log rtsummary counter at @infoword. */ static inline void xfs_trans_log_rtsummary( struct xfs_rtalloc_args *args, unsigned int infoword) { struct xfs_buf *bp = args->sumbp; size_t first, last; first = (void *)xfs_rsumblock_infoptr(args, infoword) - bp->b_addr; last = first + sizeof(xfs_suminfo_t) - 1; xfs_trans_log_buf(args->tp, bp, first, last); } /* * Modify the summary information for a given extent size, bitmap block * combination. */ int xfs_rtmodify_summary( struct xfs_rtalloc_args *args, int log, /* log2 of extent size */ xfs_fileoff_t bbno, /* bitmap block number */ int delta) /* in/out: summary block number */ { struct xfs_mount *mp = args->mp; xfs_rtsumoff_t so = xfs_rtsumoffs(mp, log, bbno); uint8_t *rsum_cache = args->rtg->rtg_rsum_cache; unsigned int infoword; xfs_suminfo_t val; int error; error = xfs_rtsummary_read_buf(args, xfs_rtsumoffs_to_block(mp, so)); if (error) return error; infoword = xfs_rtsumoffs_to_infoword(mp, so); val = xfs_suminfo_add(args, infoword, delta); if (rsum_cache) { if (val == 0 && log + 1 == rsum_cache[bbno]) rsum_cache[bbno] = log; if (val != 0 && log >= rsum_cache[bbno]) rsum_cache[bbno] = log + 1; } xfs_trans_log_rtsummary(args, infoword); return 0; } /* * Read and return the summary information for a given extent size, bitmap block * combination. */ int xfs_rtget_summary( struct xfs_rtalloc_args *args, int log, /* log2 of extent size */ xfs_fileoff_t bbno, /* bitmap block number */ xfs_suminfo_t *sum) /* out: summary info for this block */ { struct xfs_mount *mp = args->mp; xfs_rtsumoff_t so = xfs_rtsumoffs(mp, log, bbno); int error; error = xfs_rtsummary_read_buf(args, xfs_rtsumoffs_to_block(mp, so)); if (!error) *sum = xfs_suminfo_get(args, xfs_rtsumoffs_to_infoword(mp, so)); return error; } /* Log rtbitmap block from the word @from to the byte before @next. */ static inline void xfs_trans_log_rtbitmap( struct xfs_rtalloc_args *args, unsigned int from, unsigned int next) { struct xfs_buf *bp = args->rbmbp; size_t first, last; first = (void *)xfs_rbmblock_wordptr(args, from) - bp->b_addr; last = ((void *)xfs_rbmblock_wordptr(args, next) - 1) - bp->b_addr; xfs_trans_log_buf(args->tp, bp, first, last); } /* * Set the given range of bitmap bits to the given value. * Do whatever I/O and logging is required. */ int xfs_rtmodify_range( struct xfs_rtalloc_args *args, xfs_rtxnum_t start, /* starting rtext to modify */ xfs_rtxlen_t len, /* length of extent to modify */ int val) /* 1 for free, 0 for allocated */ { struct xfs_mount *mp = args->mp; int bit; /* bit number in the word */ xfs_fileoff_t block; /* bitmap block number */ int error; int i; /* current bit number rel. to start */ int lastbit; /* last useful bit in word */ xfs_rtword_t mask; /* mask of relevant bits for value */ xfs_rtword_t incore; unsigned int firstword; /* first word used in the buffer */ unsigned int word; /* word number in the buffer */ /* * Compute starting bitmap block number. */ block = xfs_rtx_to_rbmblock(mp, start); /* * Read the bitmap block, and point to its data. */ error = xfs_rtbitmap_read_buf(args, block); if (error) return error; /* * Compute the starting word's address, and starting bit. */ firstword = word = xfs_rtx_to_rbmword(mp, start); bit = (int)(start & (XFS_NBWORD - 1)); /* * 0 (allocated) => all zeroes; 1 (free) => all ones. */ val = -val; /* * If not starting on a word boundary, deal with the first * (partial) word. */ if (bit) { /* * Compute first bit not changed and mask of relevant bits. */ lastbit = min(bit + len, XFS_NBWORD); mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit; /* * Set/clear the active bits. */ incore = xfs_rtbitmap_getword(args, word); if (val) incore |= mask; else incore &= ~mask; xfs_rtbitmap_setword(args, word, incore); i = lastbit - bit; /* * Go on to the next block if that's where the next word is * and we need the next word. */ if (++word == mp->m_blockwsize && i < len) { /* * Log the changed part of this block. * Get the next one. */ xfs_trans_log_rtbitmap(args, firstword, word); error = xfs_rtbitmap_read_buf(args, ++block); if (error) return error; firstword = word = 0; } } else { /* * Starting on a word boundary, no partial word. */ i = 0; } /* * Loop over whole words in buffers. When we use up one buffer * we move on to the next one. */ while (len - i >= XFS_NBWORD) { /* * Set the word value correctly. */ xfs_rtbitmap_setword(args, word, val); i += XFS_NBWORD; /* * Go on to the next block if that's where the next word is * and we need the next word. */ if (++word == mp->m_blockwsize && i < len) { /* * Log the changed part of this block. * Get the next one. */ xfs_trans_log_rtbitmap(args, firstword, word); error = xfs_rtbitmap_read_buf(args, ++block); if (error) return error; firstword = word = 0; } } /* * If not ending on a word boundary, deal with the last * (partial) word. */ if ((lastbit = len - i)) { /* * Compute a mask of relevant bits. */ mask = ((xfs_rtword_t)1 << lastbit) - 1; /* * Set/clear the active bits. */ incore = xfs_rtbitmap_getword(args, word); if (val) incore |= mask; else incore &= ~mask; xfs_rtbitmap_setword(args, word, incore); word++; } /* * Log any remaining changed bytes. */ if (word > firstword) xfs_trans_log_rtbitmap(args, firstword, word); return 0; } /* * Mark an extent specified by start and len freed. * Updates all the summary information as well as the bitmap. */ int xfs_rtfree_range( struct xfs_rtalloc_args *args, xfs_rtxnum_t start, /* starting rtext to free */ xfs_rtxlen_t len) /* in/out: summary block number */ { struct xfs_mount *mp = args->mp; xfs_rtxnum_t end; /* end of the freed extent */ int error; /* error value */ xfs_rtxnum_t postblock; /* first rtext freed > end */ xfs_rtxnum_t preblock; /* first rtext freed < start */ end = start + len - 1; /* * Modify the bitmap to mark this extent freed. */ error = xfs_rtmodify_range(args, start, len, 1); if (error) { return error; } /* * Assume we're freeing out of the middle of an allocated extent. * We need to find the beginning and end of the extent so we can * properly update the summary. */ error = xfs_rtfind_back(args, start, &preblock); if (error) { return error; } /* * Find the next allocated block (end of allocated extent). */ error = xfs_rtfind_forw(args, end, args->rtg->rtg_extents - 1, &postblock); if (error) return error; /* * If there are blocks not being freed at the front of the * old extent, add summary data for them to be allocated. */ if (preblock < start) { error = xfs_rtmodify_summary(args, xfs_highbit64(start - preblock), xfs_rtx_to_rbmblock(mp, preblock), -1); if (error) { return error; } } /* * If there are blocks not being freed at the end of the * old extent, add summary data for them to be allocated. */ if (postblock > end) { error = xfs_rtmodify_summary(args, xfs_highbit64(postblock - end), xfs_rtx_to_rbmblock(mp, end + 1), -1); if (error) { return error; } } /* * Increment the summary information corresponding to the entire * (new) free extent. */ return xfs_rtmodify_summary(args, xfs_highbit64(postblock + 1 - preblock), xfs_rtx_to_rbmblock(mp, preblock), 1); } /* * Check that the given range is either all allocated (val = 0) or * all free (val = 1). */ int xfs_rtcheck_range( struct xfs_rtalloc_args *args, xfs_rtxnum_t start, /* starting rtext number of extent */ xfs_rtxlen_t len, /* length of extent */ int val, /* 1 for free, 0 for allocated */ xfs_rtxnum_t *new, /* out: first rtext not matching */ int *stat) /* out: 1 for matches, 0 for not */ { struct xfs_mount *mp = args->mp; int bit; /* bit number in the word */ xfs_fileoff_t block; /* bitmap block number */ int error; xfs_rtxnum_t i; /* current bit number rel. to start */ xfs_rtxnum_t lastbit; /* last useful bit in word */ xfs_rtword_t mask; /* mask of relevant bits for value */ xfs_rtword_t wdiff; /* difference from wanted value */ xfs_rtword_t incore; unsigned int word; /* word number in the buffer */ /* * Compute starting bitmap block number */ block = xfs_rtx_to_rbmblock(mp, start); /* * Read the bitmap block. */ error = xfs_rtbitmap_read_buf(args, block); if (error) return error; /* * Compute the starting word's address, and starting bit. */ word = xfs_rtx_to_rbmword(mp, start); bit = (int)(start & (XFS_NBWORD - 1)); /* * 0 (allocated) => all zero's; 1 (free) => all one's. */ val = -val; /* * If not starting on a word boundary, deal with the first * (partial) word. */ if (bit) { /* * Compute first bit not examined. */ lastbit = min(bit + len, XFS_NBWORD); /* * Mask of relevant bits. */ mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit; /* * Compute difference between actual and desired value. */ incore = xfs_rtbitmap_getword(args, word); if ((wdiff = (incore ^ val) & mask)) { /* * Different, compute first wrong bit and return. */ i = xfs_lowbit32(wdiff) - bit; *new = start + i; *stat = 0; return 0; } i = lastbit - bit; /* * Go on to next block if that's where the next word is * and we need the next word. */ if (++word == mp->m_blockwsize && i < len) { /* * If done with this block, get the next one. */ error = xfs_rtbitmap_read_buf(args, ++block); if (error) return error; word = 0; } } else { /* * Starting on a word boundary, no partial word. */ i = 0; } /* * Loop over whole words in buffers. When we use up one buffer * we move on to the next one. */ while (len - i >= XFS_NBWORD) { /* * Compute difference between actual and desired value. */ incore = xfs_rtbitmap_getword(args, word); if ((wdiff = incore ^ val)) { /* * Different, compute first wrong bit and return. */ i += xfs_lowbit32(wdiff); *new = start + i; *stat = 0; return 0; } i += XFS_NBWORD; /* * Go on to next block if that's where the next word is * and we need the next word. */ if (++word == mp->m_blockwsize && i < len) { /* * If done with this block, get the next one. */ error = xfs_rtbitmap_read_buf(args, ++block); if (error) return error; word = 0; } } /* * If not ending on a word boundary, deal with the last * (partial) word. */ if ((lastbit = len - i)) { /* * Mask of relevant bits. */ mask = ((xfs_rtword_t)1 << lastbit) - 1; /* * Compute difference between actual and desired value. */ incore = xfs_rtbitmap_getword(args, word); if ((wdiff = (incore ^ val) & mask)) { /* * Different, compute first wrong bit and return. */ i += xfs_lowbit32(wdiff); *new = start + i; *stat = 0; return 0; } else i = len; } /* * Successful, return. */ *new = start + i; *stat = 1; return 0; } #ifdef DEBUG /* * Check that the given extent (block range) is allocated already. */ STATIC int xfs_rtcheck_alloc_range( struct xfs_rtalloc_args *args, xfs_rtxnum_t start, /* starting rtext number of extent */ xfs_rtxlen_t len) /* length of extent */ { xfs_rtxnum_t new; /* dummy for xfs_rtcheck_range */ int stat; int error; error = xfs_rtcheck_range(args, start, len, 0, &new, &stat); if (error) return error; ASSERT(stat); return 0; } #else #define xfs_rtcheck_alloc_range(a,b,l) (0) #endif /* * Free an extent in the realtime subvolume. Length is expressed in * realtime extents, as is the block number. */ int xfs_rtfree_extent( struct xfs_trans *tp, /* transaction pointer */ struct xfs_rtgroup *rtg, xfs_rtxnum_t start, /* starting rtext number to free */ xfs_rtxlen_t len) /* length of extent freed */ { struct xfs_mount *mp = tp->t_mountp; struct xfs_inode *rbmip = rtg->rtg_inodes[XFS_RTGI_BITMAP]; struct xfs_rtalloc_args args = { .mp = mp, .tp = tp, .rtg = rtg, }; int error; struct timespec64 atime; ASSERT(rbmip->i_itemp != NULL); xfs_assert_ilocked(rbmip, XFS_ILOCK_EXCL); if (XFS_TEST_ERROR(false, mp, XFS_ERRTAG_FREE_EXTENT)) return -EIO; error = xfs_rtcheck_alloc_range(&args, start, len); if (error) return error; /* * Free the range of realtime blocks. */ error = xfs_rtfree_range(&args, start, len); if (error) goto out; /* * Mark more blocks free in the superblock. */ xfs_trans_mod_sb(tp, XFS_TRANS_SB_FREXTENTS, (long)len); /* * If we've now freed all the blocks, reset the file sequence * number to 0 for pre-RTG file systems. */ if (!xfs_has_rtgroups(mp) && tp->t_frextents_delta + mp->m_sb.sb_frextents == mp->m_sb.sb_rextents) { if (!(rbmip->i_diflags & XFS_DIFLAG_NEWRTBM)) rbmip->i_diflags |= XFS_DIFLAG_NEWRTBM; atime = inode_get_atime(VFS_I(rbmip)); atime.tv_sec = 0; inode_set_atime_to_ts(VFS_I(rbmip), atime); xfs_trans_log_inode(tp, rbmip, XFS_ILOG_CORE); } error = 0; out: xfs_rtbuf_cache_relse(&args); return error; } /* * Free some blocks in the realtime subvolume. rtbno and rtlen are in units of * rt blocks, not rt extents; must be aligned to the rt extent size; and rtlen * cannot exceed XFS_MAX_BMBT_EXTLEN. */ int xfs_rtfree_blocks( struct xfs_trans *tp, struct xfs_rtgroup *rtg, xfs_fsblock_t rtbno, xfs_filblks_t rtlen) { struct xfs_mount *mp = tp->t_mountp; xfs_extlen_t mod; int error; ASSERT(rtlen <= XFS_MAX_BMBT_EXTLEN); mod = xfs_blen_to_rtxoff(mp, rtlen); if (mod) { ASSERT(mod == 0); return -EIO; } mod = xfs_rtb_to_rtxoff(mp, rtbno); if (mod) { ASSERT(mod == 0); return -EIO; } error = xfs_rtfree_extent(tp, rtg, xfs_rtb_to_rtx(mp, rtbno), xfs_extlen_to_rtxlen(mp, rtlen)); if (error) return error; if (xfs_has_rtgroups(mp)) xfs_extent_busy_insert(tp, rtg_group(rtg), xfs_rtb_to_rgbno(mp, rtbno), rtlen, 0); return 0; } /* Find all the free records within a given range. */ int xfs_rtalloc_query_range( struct xfs_rtgroup *rtg, struct xfs_trans *tp, xfs_rtxnum_t start, xfs_rtxnum_t end, xfs_rtalloc_query_range_fn fn, void *priv) { struct xfs_mount *mp = rtg_mount(rtg); struct xfs_rtalloc_args args = { .rtg = rtg, .mp = mp, .tp = tp, }; int error = 0; if (start > end) return -EINVAL; if (start == end || start >= rtg->rtg_extents) return 0; end = min(end, rtg->rtg_extents - 1); /* Iterate the bitmap, looking for discrepancies. */ while (start <= end) { struct xfs_rtalloc_rec rec; int is_free; xfs_rtxnum_t rtend; /* Is the first block free? */ error = xfs_rtcheck_range(&args, start, 1, 1, &rtend, &is_free); if (error) break; /* How long does the extent go for? */ error = xfs_rtfind_forw(&args, start, end, &rtend); if (error) break; if (is_free) { rec.ar_startext = start; rec.ar_extcount = rtend - start + 1; error = fn(rtg, tp, &rec, priv); if (error) break; } start = rtend + 1; } xfs_rtbuf_cache_relse(&args); return error; } /* Find all the free records. */ int xfs_rtalloc_query_all( struct xfs_rtgroup *rtg, struct xfs_trans *tp, xfs_rtalloc_query_range_fn fn, void *priv) { return xfs_rtalloc_query_range(rtg, tp, 0, rtg->rtg_extents - 1, fn, priv); } /* Is the given extent all free? */ int xfs_rtalloc_extent_is_free( struct xfs_rtgroup *rtg, struct xfs_trans *tp, xfs_rtxnum_t start, xfs_rtxlen_t len, bool *is_free) { struct xfs_rtalloc_args args = { .mp = rtg_mount(rtg), .rtg = rtg, .tp = tp, }; xfs_rtxnum_t end; int matches; int error; error = xfs_rtcheck_range(&args, start, len, 1, &end, &matches); xfs_rtbuf_cache_relse(&args); if (error) return error; *is_free = matches; return 0; } /* Compute the number of rt extents tracked by a single bitmap block. */ xfs_rtxnum_t xfs_rtbitmap_rtx_per_rbmblock( struct xfs_mount *mp) { unsigned int rbmblock_bytes = mp->m_sb.sb_blocksize; if (xfs_has_rtgroups(mp)) rbmblock_bytes -= sizeof(struct xfs_rtbuf_blkinfo); return rbmblock_bytes * NBBY; } /* * Compute the number of rtbitmap blocks needed to track the given number of rt * extents. */ xfs_filblks_t xfs_rtbitmap_blockcount_len( struct xfs_mount *mp, xfs_rtbxlen_t rtextents) { return howmany_64(rtextents, xfs_rtbitmap_rtx_per_rbmblock(mp)); } /* How many rt extents does each rtbitmap file track? */ static inline xfs_rtbxlen_t xfs_rtbitmap_bitcount( struct xfs_mount *mp) { if (!mp->m_sb.sb_rextents) return 0; /* rtgroup size can be nonzero even if rextents is zero */ if (xfs_has_rtgroups(mp)) return mp->m_sb.sb_rgextents; return mp->m_sb.sb_rextents; } /* * Compute the number of rtbitmap blocks used for a given file system. */ xfs_filblks_t xfs_rtbitmap_blockcount( struct xfs_mount *mp) { return xfs_rtbitmap_blockcount_len(mp, xfs_rtbitmap_bitcount(mp)); } /* * Compute the geometry of the rtsummary file needed to track the given rt * space. */ xfs_filblks_t xfs_rtsummary_blockcount( struct xfs_mount *mp, unsigned int *rsumlevels) { xfs_rtbxlen_t rextents = xfs_rtbitmap_bitcount(mp); unsigned long long rsumwords; *rsumlevels = xfs_compute_rextslog(rextents) + 1; rsumwords = xfs_rtbitmap_blockcount_len(mp, rextents) * (*rsumlevels); return howmany_64(rsumwords, mp->m_blockwsize); } static int xfs_rtfile_alloc_blocks( struct xfs_inode *ip, xfs_fileoff_t offset_fsb, xfs_filblks_t count_fsb, struct xfs_bmbt_irec *map) { struct xfs_mount *mp = ip->i_mount; struct xfs_trans *tp; int nmap = 1; int error; error = xfs_trans_alloc(mp, &M_RES(mp)->tr_growrtalloc, XFS_GROWFSRT_SPACE_RES(mp, count_fsb), 0, 0, &tp); if (error) return error; xfs_ilock(ip, XFS_ILOCK_EXCL); xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); error = xfs_iext_count_extend(tp, ip, XFS_DATA_FORK, XFS_IEXT_ADD_NOSPLIT_CNT); if (error) goto out_trans_cancel; error = xfs_bmapi_write(tp, ip, offset_fsb, count_fsb, XFS_BMAPI_METADATA, 0, map, &nmap); if (error) goto out_trans_cancel; return xfs_trans_commit(tp); out_trans_cancel: xfs_trans_cancel(tp); return error; } /* Get a buffer for the block. */ static int xfs_rtfile_initialize_block( struct xfs_rtgroup *rtg, enum xfs_rtg_inodes type, xfs_fsblock_t fsbno, void *data) { struct xfs_mount *mp = rtg_mount(rtg); struct xfs_inode *ip = rtg->rtg_inodes[type]; struct xfs_trans *tp; struct xfs_buf *bp; void *bufdata; const size_t copylen = mp->m_blockwsize << XFS_WORDLOG; enum xfs_blft buf_type; int error; if (type == XFS_RTGI_BITMAP) buf_type = XFS_BLFT_RTBITMAP_BUF; else if (type == XFS_RTGI_SUMMARY) buf_type = XFS_BLFT_RTSUMMARY_BUF; else return -EINVAL; error = xfs_trans_alloc(mp, &M_RES(mp)->tr_growrtzero, 0, 0, 0, &tp); if (error) return error; xfs_ilock(ip, XFS_ILOCK_EXCL); xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); error = xfs_trans_get_buf(tp, mp->m_ddev_targp, XFS_FSB_TO_DADDR(mp, fsbno), mp->m_bsize, 0, &bp); if (error) { xfs_trans_cancel(tp); return error; } bufdata = bp->b_addr; xfs_trans_buf_set_type(tp, bp, buf_type); bp->b_ops = xfs_rtblock_ops(mp, type); if (xfs_has_rtgroups(mp)) { struct xfs_rtbuf_blkinfo *hdr = bp->b_addr; if (type == XFS_RTGI_BITMAP) hdr->rt_magic = cpu_to_be32(XFS_RTBITMAP_MAGIC); else hdr->rt_magic = cpu_to_be32(XFS_RTSUMMARY_MAGIC); hdr->rt_owner = cpu_to_be64(ip->i_ino); hdr->rt_blkno = cpu_to_be64(XFS_FSB_TO_DADDR(mp, fsbno)); hdr->rt_lsn = 0; uuid_copy(&hdr->rt_uuid, &mp->m_sb.sb_meta_uuid); bufdata += sizeof(*hdr); } if (data) memcpy(bufdata, data, copylen); else memset(bufdata, 0, copylen); xfs_trans_log_buf(tp, bp, 0, mp->m_sb.sb_blocksize - 1); return xfs_trans_commit(tp); } /* * Allocate space to the bitmap or summary file, and zero it, for growfs. * @data must be a contiguous buffer large enough to fill all blocks in the * file; or NULL to initialize the contents to zeroes. */ int xfs_rtfile_initialize_blocks( struct xfs_rtgroup *rtg, enum xfs_rtg_inodes type, xfs_fileoff_t offset_fsb, /* offset to start from */ xfs_fileoff_t end_fsb, /* offset to allocate to */ void *data) /* data to fill the blocks */ { struct xfs_mount *mp = rtg_mount(rtg); const size_t copylen = mp->m_blockwsize << XFS_WORDLOG; while (offset_fsb < end_fsb) { struct xfs_bmbt_irec map; xfs_filblks_t i; int error; error = xfs_rtfile_alloc_blocks(rtg->rtg_inodes[type], offset_fsb, end_fsb - offset_fsb, &map); if (error) return error; /* * Now we need to clear the allocated blocks. * * Do this one block per transaction, to keep it simple. */ for (i = 0; i < map.br_blockcount; i++) { error = xfs_rtfile_initialize_block(rtg, type, map.br_startblock + i, data); if (error) return error; if (data) data += copylen; } offset_fsb = map.br_startoff + map.br_blockcount; } return 0; } int xfs_rtbitmap_create( struct xfs_rtgroup *rtg, struct xfs_inode *ip, struct xfs_trans *tp, bool init) { struct xfs_mount *mp = rtg_mount(rtg); ip->i_disk_size = mp->m_sb.sb_rbmblocks * mp->m_sb.sb_blocksize; if (init && !xfs_has_rtgroups(mp)) { ip->i_diflags |= XFS_DIFLAG_NEWRTBM; inode_set_atime(VFS_I(ip), 0, 0); } xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); return 0; } int xfs_rtsummary_create( struct xfs_rtgroup *rtg, struct xfs_inode *ip, struct xfs_trans *tp, bool init) { struct xfs_mount *mp = rtg_mount(rtg); ip->i_disk_size = mp->m_rsumblocks * mp->m_sb.sb_blocksize; xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); return 0; }