forked from mirrors/linux
		
	 af32541081
			
		
	
	
		af32541081
		
	
	
	
	
		
			
			Create some simple helpers to reduce the amount of typing whenever we access rtgroup inodes. Conversion was done with this spatch and some minor reformatting: @@ expression rtg; @@ - rtg->rtg_inodes[XFS_RTGI_BITMAP] + rtg_bitmap(rtg) @@ expression rtg; @@ - rtg->rtg_inodes[XFS_RTGI_SUMMARY] + rtg_summary(rtg) and the CLI command: $ spatch --sp-file /tmp/moo.cocci --dir fs/xfs/ --use-gitgrep --in-place Signed-off-by: "Darrick J. Wong" <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de>
		
			
				
	
	
		
			1493 lines
		
	
	
	
		
			35 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1493 lines
		
	
	
	
		
			35 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // 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_bitmap(rtg);
 | |
| 	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;
 | |
| }
 |