forked from mirrors/linux
		
	xfs: allocate sparse inode chunks on full chunk allocation failure
xfs_ialloc_ag_alloc() makes several attempts to allocate a full inode chunk. If all else fails, reduce the allocation to the sparse length and alignment and attempt to allocate a sparse inode chunk. If sparse chunk allocation succeeds, check whether an inobt record already exists that can track the chunk. If so, inherit and update the existing record. Otherwise, insert a new record for the sparse chunk. Create helpers to align sparse chunk inode records and insert or update existing records in the inode btrees. The xfs_inobt_insert_sprec() helper implements the merge or update semantics required for sparse inode records with respect to both the inobt and finobt. To update the inobt, either insert a new record or merge with an existing record. To update the finobt, use the updated inobt record to either insert or replace an existing record. Signed-off-by: Brian Foster <bfoster@redhat.com> Reviewed-by: Dave Chinner <dchinner@redhat.com> Signed-off-by: Dave Chinner <david@fromorbit.com>
This commit is contained in:
		
							parent
							
								
									4148c347a4
								
							
						
					
					
						commit
						56d1115c9b
					
				
					 4 changed files with 401 additions and 14 deletions
				
			
		| 
						 | 
					@ -377,6 +377,214 @@ xfs_ialloc_inode_init(
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Align startino and allocmask for a recently allocated sparse chunk such that
 | 
				
			||||||
 | 
					 * they are fit for insertion (or merge) into the on-disk inode btrees.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Background:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * When enabled, sparse inode support increases the inode alignment from cluster
 | 
				
			||||||
 | 
					 * size to inode chunk size. This means that the minimum range between two
 | 
				
			||||||
 | 
					 * non-adjacent inode records in the inobt is large enough for a full inode
 | 
				
			||||||
 | 
					 * record. This allows for cluster sized, cluster aligned block allocation
 | 
				
			||||||
 | 
					 * without need to worry about whether the resulting inode record overlaps with
 | 
				
			||||||
 | 
					 * another record in the tree. Without this basic rule, we would have to deal
 | 
				
			||||||
 | 
					 * with the consequences of overlap by potentially undoing recent allocations in
 | 
				
			||||||
 | 
					 * the inode allocation codepath.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Because of this alignment rule (which is enforced on mount), there are two
 | 
				
			||||||
 | 
					 * inobt possibilities for newly allocated sparse chunks. One is that the
 | 
				
			||||||
 | 
					 * aligned inode record for the chunk covers a range of inodes not already
 | 
				
			||||||
 | 
					 * covered in the inobt (i.e., it is safe to insert a new sparse record). The
 | 
				
			||||||
 | 
					 * other is that a record already exists at the aligned startino that considers
 | 
				
			||||||
 | 
					 * the newly allocated range as sparse. In the latter case, record content is
 | 
				
			||||||
 | 
					 * merged in hope that sparse inode chunks fill to full chunks over time.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					STATIC void
 | 
				
			||||||
 | 
					xfs_align_sparse_ino(
 | 
				
			||||||
 | 
						struct xfs_mount		*mp,
 | 
				
			||||||
 | 
						xfs_agino_t			*startino,
 | 
				
			||||||
 | 
						uint16_t			*allocmask)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						xfs_agblock_t			agbno;
 | 
				
			||||||
 | 
						xfs_agblock_t			mod;
 | 
				
			||||||
 | 
						int				offset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						agbno = XFS_AGINO_TO_AGBNO(mp, *startino);
 | 
				
			||||||
 | 
						mod = agbno % mp->m_sb.sb_inoalignmt;
 | 
				
			||||||
 | 
						if (!mod)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* calculate the inode offset and align startino */
 | 
				
			||||||
 | 
						offset = mod << mp->m_sb.sb_inopblog;
 | 
				
			||||||
 | 
						*startino -= offset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Since startino has been aligned down, left shift allocmask such that
 | 
				
			||||||
 | 
						 * it continues to represent the same physical inodes relative to the
 | 
				
			||||||
 | 
						 * new startino.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						*allocmask <<= offset / XFS_INODES_PER_HOLEMASK_BIT;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Determine whether the source inode record can merge into the target. Both
 | 
				
			||||||
 | 
					 * records must be sparse, the inode ranges must match and there must be no
 | 
				
			||||||
 | 
					 * allocation overlap between the records.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					STATIC bool
 | 
				
			||||||
 | 
					__xfs_inobt_can_merge(
 | 
				
			||||||
 | 
						struct xfs_inobt_rec_incore	*trec,	/* tgt record */
 | 
				
			||||||
 | 
						struct xfs_inobt_rec_incore	*srec)	/* src record */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uint64_t			talloc;
 | 
				
			||||||
 | 
						uint64_t			salloc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* records must cover the same inode range */
 | 
				
			||||||
 | 
						if (trec->ir_startino != srec->ir_startino)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* both records must be sparse */
 | 
				
			||||||
 | 
						if (!xfs_inobt_issparse(trec->ir_holemask) ||
 | 
				
			||||||
 | 
						    !xfs_inobt_issparse(srec->ir_holemask))
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* both records must track some inodes */
 | 
				
			||||||
 | 
						if (!trec->ir_count || !srec->ir_count)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* can't exceed capacity of a full record */
 | 
				
			||||||
 | 
						if (trec->ir_count + srec->ir_count > XFS_INODES_PER_CHUNK)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* verify there is no allocation overlap */
 | 
				
			||||||
 | 
						talloc = xfs_inobt_irec_to_allocmask(trec);
 | 
				
			||||||
 | 
						salloc = xfs_inobt_irec_to_allocmask(srec);
 | 
				
			||||||
 | 
						if (talloc & salloc)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Merge the source inode record into the target. The caller must call
 | 
				
			||||||
 | 
					 * __xfs_inobt_can_merge() to ensure the merge is valid.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					STATIC void
 | 
				
			||||||
 | 
					__xfs_inobt_rec_merge(
 | 
				
			||||||
 | 
						struct xfs_inobt_rec_incore	*trec,	/* target */
 | 
				
			||||||
 | 
						struct xfs_inobt_rec_incore	*srec)	/* src */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ASSERT(trec->ir_startino == srec->ir_startino);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* combine the counts */
 | 
				
			||||||
 | 
						trec->ir_count += srec->ir_count;
 | 
				
			||||||
 | 
						trec->ir_freecount += srec->ir_freecount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Merge the holemask and free mask. For both fields, 0 bits refer to
 | 
				
			||||||
 | 
						 * allocated inodes. We combine the allocated ranges with bitwise AND.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						trec->ir_holemask &= srec->ir_holemask;
 | 
				
			||||||
 | 
						trec->ir_free &= srec->ir_free;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Insert a new sparse inode chunk into the associated inode btree. The inode
 | 
				
			||||||
 | 
					 * record for the sparse chunk is pre-aligned to a startino that should match
 | 
				
			||||||
 | 
					 * any pre-existing sparse inode record in the tree. This allows sparse chunks
 | 
				
			||||||
 | 
					 * to fill over time.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This function supports two modes of handling preexisting records depending on
 | 
				
			||||||
 | 
					 * the merge flag. If merge is true, the provided record is merged with the
 | 
				
			||||||
 | 
					 * existing record and updated in place. The merged record is returned in nrec.
 | 
				
			||||||
 | 
					 * If merge is false, an existing record is replaced with the provided record.
 | 
				
			||||||
 | 
					 * If no preexisting record exists, the provided record is always inserted.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * It is considered corruption if a merge is requested and not possible. Given
 | 
				
			||||||
 | 
					 * the sparse inode alignment constraints, this should never happen.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					STATIC int
 | 
				
			||||||
 | 
					xfs_inobt_insert_sprec(
 | 
				
			||||||
 | 
						struct xfs_mount		*mp,
 | 
				
			||||||
 | 
						struct xfs_trans		*tp,
 | 
				
			||||||
 | 
						struct xfs_buf			*agbp,
 | 
				
			||||||
 | 
						int				btnum,
 | 
				
			||||||
 | 
						struct xfs_inobt_rec_incore	*nrec,	/* in/out: new/merged rec. */
 | 
				
			||||||
 | 
						bool				merge)	/* merge or replace */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct xfs_btree_cur		*cur;
 | 
				
			||||||
 | 
						struct xfs_agi			*agi = XFS_BUF_TO_AGI(agbp);
 | 
				
			||||||
 | 
						xfs_agnumber_t			agno = be32_to_cpu(agi->agi_seqno);
 | 
				
			||||||
 | 
						int				error;
 | 
				
			||||||
 | 
						int				i;
 | 
				
			||||||
 | 
						struct xfs_inobt_rec_incore	rec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cur = xfs_inobt_init_cursor(mp, tp, agbp, agno, btnum);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* the new record is pre-aligned so we know where to look */
 | 
				
			||||||
 | 
						error = xfs_inobt_lookup(cur, nrec->ir_startino, XFS_LOOKUP_EQ, &i);
 | 
				
			||||||
 | 
						if (error)
 | 
				
			||||||
 | 
							goto error;
 | 
				
			||||||
 | 
						/* if nothing there, insert a new record and return */
 | 
				
			||||||
 | 
						if (i == 0) {
 | 
				
			||||||
 | 
							error = xfs_inobt_insert_rec(cur, nrec->ir_holemask,
 | 
				
			||||||
 | 
										     nrec->ir_count, nrec->ir_freecount,
 | 
				
			||||||
 | 
										     nrec->ir_free, &i);
 | 
				
			||||||
 | 
							if (error)
 | 
				
			||||||
 | 
								goto error;
 | 
				
			||||||
 | 
							XFS_WANT_CORRUPTED_GOTO(mp, i == 1, error);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * A record exists at this startino. Merge or replace the record
 | 
				
			||||||
 | 
						 * depending on what we've been asked to do.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (merge) {
 | 
				
			||||||
 | 
							error = xfs_inobt_get_rec(cur, &rec, &i);
 | 
				
			||||||
 | 
							if (error)
 | 
				
			||||||
 | 
								goto error;
 | 
				
			||||||
 | 
							XFS_WANT_CORRUPTED_GOTO(mp, i == 1, error);
 | 
				
			||||||
 | 
							XFS_WANT_CORRUPTED_GOTO(mp,
 | 
				
			||||||
 | 
										rec.ir_startino == nrec->ir_startino,
 | 
				
			||||||
 | 
										error);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * This should never fail. If we have coexisting records that
 | 
				
			||||||
 | 
							 * cannot merge, something is seriously wrong.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							XFS_WANT_CORRUPTED_GOTO(mp, __xfs_inobt_can_merge(nrec, &rec),
 | 
				
			||||||
 | 
										error);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							trace_xfs_irec_merge_pre(mp, agno, rec.ir_startino,
 | 
				
			||||||
 | 
										 rec.ir_holemask, nrec->ir_startino,
 | 
				
			||||||
 | 
										 nrec->ir_holemask);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* merge to nrec to output the updated record */
 | 
				
			||||||
 | 
							__xfs_inobt_rec_merge(nrec, &rec);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							trace_xfs_irec_merge_post(mp, agno, nrec->ir_startino,
 | 
				
			||||||
 | 
										  nrec->ir_holemask);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							error = xfs_inobt_rec_check_count(mp, nrec);
 | 
				
			||||||
 | 
							if (error)
 | 
				
			||||||
 | 
								goto error;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						error = xfs_inobt_update(cur, nrec);
 | 
				
			||||||
 | 
						if (error)
 | 
				
			||||||
 | 
							goto error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
 | 
						xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					error:
 | 
				
			||||||
 | 
						xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
 | 
				
			||||||
 | 
						return error;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Allocate new inodes in the allocation group specified by agbp.
 | 
					 * Allocate new inodes in the allocation group specified by agbp.
 | 
				
			||||||
 * Return 0 for success, else error code.
 | 
					 * Return 0 for success, else error code.
 | 
				
			||||||
| 
						 | 
					@ -395,6 +603,8 @@ xfs_ialloc_ag_alloc(
 | 
				
			||||||
	xfs_agino_t	newlen;		/* new number of inodes */
 | 
						xfs_agino_t	newlen;		/* new number of inodes */
 | 
				
			||||||
	int		isaligned = 0;	/* inode allocation at stripe unit */
 | 
						int		isaligned = 0;	/* inode allocation at stripe unit */
 | 
				
			||||||
					/* boundary */
 | 
										/* boundary */
 | 
				
			||||||
 | 
						uint16_t	allocmask = (uint16_t) -1; /* init. to full chunk */
 | 
				
			||||||
 | 
						struct xfs_inobt_rec_incore rec;
 | 
				
			||||||
	struct xfs_perag *pag;
 | 
						struct xfs_perag *pag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memset(&args, 0, sizeof(args));
 | 
						memset(&args, 0, sizeof(args));
 | 
				
			||||||
| 
						 | 
					@ -511,6 +721,45 @@ xfs_ialloc_ag_alloc(
 | 
				
			||||||
			return error;
 | 
								return error;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Finally, try a sparse allocation if the filesystem supports it and
 | 
				
			||||||
 | 
						 * the sparse allocation length is smaller than a full chunk.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (xfs_sb_version_hassparseinodes(&args.mp->m_sb) &&
 | 
				
			||||||
 | 
						    args.mp->m_ialloc_min_blks < args.mp->m_ialloc_blks &&
 | 
				
			||||||
 | 
						    args.fsbno == NULLFSBLOCK) {
 | 
				
			||||||
 | 
							args.type = XFS_ALLOCTYPE_NEAR_BNO;
 | 
				
			||||||
 | 
							args.agbno = be32_to_cpu(agi->agi_root);
 | 
				
			||||||
 | 
							args.fsbno = XFS_AGB_TO_FSB(args.mp, agno, args.agbno);
 | 
				
			||||||
 | 
							args.alignment = args.mp->m_sb.sb_spino_align;
 | 
				
			||||||
 | 
							args.prod = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							args.minlen = args.mp->m_ialloc_min_blks;
 | 
				
			||||||
 | 
							args.maxlen = args.minlen;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * The inode record will be aligned to full chunk size. We must
 | 
				
			||||||
 | 
							 * prevent sparse allocation from AG boundaries that result in
 | 
				
			||||||
 | 
							 * invalid inode records, such as records that start at agbno 0
 | 
				
			||||||
 | 
							 * or extend beyond the AG.
 | 
				
			||||||
 | 
							 *
 | 
				
			||||||
 | 
							 * Set min agbno to the first aligned, non-zero agbno and max to
 | 
				
			||||||
 | 
							 * the last aligned agbno that is at least one full chunk from
 | 
				
			||||||
 | 
							 * the end of the AG.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							args.min_agbno = args.mp->m_sb.sb_inoalignmt;
 | 
				
			||||||
 | 
							args.max_agbno = round_down(args.mp->m_sb.sb_agblocks,
 | 
				
			||||||
 | 
										    args.mp->m_sb.sb_inoalignmt) -
 | 
				
			||||||
 | 
									 args.mp->m_ialloc_blks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							error = xfs_alloc_vextent(&args);
 | 
				
			||||||
 | 
							if (error)
 | 
				
			||||||
 | 
								return error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							newlen = args.len << args.mp->m_sb.sb_inopblog;
 | 
				
			||||||
 | 
							allocmask = (1 << (newlen / XFS_INODES_PER_HOLEMASK_BIT)) - 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (args.fsbno == NULLFSBLOCK) {
 | 
						if (args.fsbno == NULLFSBLOCK) {
 | 
				
			||||||
		*alloc = 0;
 | 
							*alloc = 0;
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
| 
						 | 
					@ -535,6 +784,73 @@ xfs_ialloc_ag_alloc(
 | 
				
			||||||
	 * Convert the results.
 | 
						 * Convert the results.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	newino = XFS_OFFBNO_TO_AGINO(args.mp, args.agbno, 0);
 | 
						newino = XFS_OFFBNO_TO_AGINO(args.mp, args.agbno, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (xfs_inobt_issparse(~allocmask)) {
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * We've allocated a sparse chunk. Align the startino and mask.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							xfs_align_sparse_ino(args.mp, &newino, &allocmask);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							rec.ir_startino = newino;
 | 
				
			||||||
 | 
							rec.ir_holemask = ~allocmask;
 | 
				
			||||||
 | 
							rec.ir_count = newlen;
 | 
				
			||||||
 | 
							rec.ir_freecount = newlen;
 | 
				
			||||||
 | 
							rec.ir_free = XFS_INOBT_ALL_FREE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Insert the sparse record into the inobt and allow for a merge
 | 
				
			||||||
 | 
							 * if necessary. If a merge does occur, rec is updated to the
 | 
				
			||||||
 | 
							 * merged record.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							error = xfs_inobt_insert_sprec(args.mp, tp, agbp, XFS_BTNUM_INO,
 | 
				
			||||||
 | 
										       &rec, true);
 | 
				
			||||||
 | 
							if (error == -EFSCORRUPTED) {
 | 
				
			||||||
 | 
								xfs_alert(args.mp,
 | 
				
			||||||
 | 
						"invalid sparse inode record: ino 0x%llx holemask 0x%x count %u",
 | 
				
			||||||
 | 
									  XFS_AGINO_TO_INO(args.mp, agno,
 | 
				
			||||||
 | 
											   rec.ir_startino),
 | 
				
			||||||
 | 
									  rec.ir_holemask, rec.ir_count);
 | 
				
			||||||
 | 
								xfs_force_shutdown(args.mp, SHUTDOWN_CORRUPT_INCORE);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (error)
 | 
				
			||||||
 | 
								return error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * We can't merge the part we've just allocated as for the inobt
 | 
				
			||||||
 | 
							 * due to finobt semantics. The original record may or may not
 | 
				
			||||||
 | 
							 * exist independent of whether physical inodes exist in this
 | 
				
			||||||
 | 
							 * sparse chunk.
 | 
				
			||||||
 | 
							 *
 | 
				
			||||||
 | 
							 * We must update the finobt record based on the inobt record.
 | 
				
			||||||
 | 
							 * rec contains the fully merged and up to date inobt record
 | 
				
			||||||
 | 
							 * from the previous call. Set merge false to replace any
 | 
				
			||||||
 | 
							 * existing record with this one.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							if (xfs_sb_version_hasfinobt(&args.mp->m_sb)) {
 | 
				
			||||||
 | 
								error = xfs_inobt_insert_sprec(args.mp, tp, agbp,
 | 
				
			||||||
 | 
											       XFS_BTNUM_FINO, &rec,
 | 
				
			||||||
 | 
											       false);
 | 
				
			||||||
 | 
								if (error)
 | 
				
			||||||
 | 
									return error;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							/* full chunk - insert new records to both btrees */
 | 
				
			||||||
 | 
							error = xfs_inobt_insert(args.mp, tp, agbp, newino, newlen,
 | 
				
			||||||
 | 
										 XFS_BTNUM_INO);
 | 
				
			||||||
 | 
							if (error)
 | 
				
			||||||
 | 
								return error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (xfs_sb_version_hasfinobt(&args.mp->m_sb)) {
 | 
				
			||||||
 | 
								error = xfs_inobt_insert(args.mp, tp, agbp, newino,
 | 
				
			||||||
 | 
											 newlen, XFS_BTNUM_FINO);
 | 
				
			||||||
 | 
								if (error)
 | 
				
			||||||
 | 
									return error;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Update AGI counts and newino.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	be32_add_cpu(&agi->agi_count, newlen);
 | 
						be32_add_cpu(&agi->agi_count, newlen);
 | 
				
			||||||
	be32_add_cpu(&agi->agi_freecount, newlen);
 | 
						be32_add_cpu(&agi->agi_freecount, newlen);
 | 
				
			||||||
	pag = xfs_perag_get(args.mp, agno);
 | 
						pag = xfs_perag_get(args.mp, agno);
 | 
				
			||||||
| 
						 | 
					@ -542,20 +858,6 @@ xfs_ialloc_ag_alloc(
 | 
				
			||||||
	xfs_perag_put(pag);
 | 
						xfs_perag_put(pag);
 | 
				
			||||||
	agi->agi_newino = cpu_to_be32(newino);
 | 
						agi->agi_newino = cpu_to_be32(newino);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
	 * Insert records describing the new inode chunk into the btrees.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	error = xfs_inobt_insert(args.mp, tp, agbp, newino, newlen,
 | 
					 | 
				
			||||||
				 XFS_BTNUM_INO);
 | 
					 | 
				
			||||||
	if (error)
 | 
					 | 
				
			||||||
		return error;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (xfs_sb_version_hasfinobt(&args.mp->m_sb)) {
 | 
					 | 
				
			||||||
		error = xfs_inobt_insert(args.mp, tp, agbp, newino, newlen,
 | 
					 | 
				
			||||||
					 XFS_BTNUM_FINO);
 | 
					 | 
				
			||||||
		if (error)
 | 
					 | 
				
			||||||
			return error;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Log allocation group header fields
 | 
						 * Log allocation group header fields
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -478,3 +478,34 @@ xfs_inobt_irec_to_allocmask(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return bitmap;
 | 
						return bitmap;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if defined(DEBUG) || defined(XFS_WARN)
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Verify that an in-core inode record has a valid inode count.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int
 | 
				
			||||||
 | 
					xfs_inobt_rec_check_count(
 | 
				
			||||||
 | 
						struct xfs_mount		*mp,
 | 
				
			||||||
 | 
						struct xfs_inobt_rec_incore	*rec)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int				inocount = 0;
 | 
				
			||||||
 | 
						int				nextbit = 0;
 | 
				
			||||||
 | 
						uint64_t			allocbmap;
 | 
				
			||||||
 | 
						int				wordsz;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wordsz = sizeof(allocbmap) / sizeof(unsigned int);
 | 
				
			||||||
 | 
						allocbmap = xfs_inobt_irec_to_allocmask(rec);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nextbit = xfs_next_bit((uint *) &allocbmap, wordsz, nextbit);
 | 
				
			||||||
 | 
						while (nextbit != -1) {
 | 
				
			||||||
 | 
							inocount++;
 | 
				
			||||||
 | 
							nextbit = xfs_next_bit((uint *) &allocbmap, wordsz,
 | 
				
			||||||
 | 
									       nextbit + 1);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (inocount != rec->ir_count)
 | 
				
			||||||
 | 
							return -EFSCORRUPTED;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif	/* DEBUG */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -65,4 +65,11 @@ extern int xfs_inobt_maxrecs(struct xfs_mount *, int, int);
 | 
				
			||||||
/* ir_holemask to inode allocation bitmap conversion */
 | 
					/* ir_holemask to inode allocation bitmap conversion */
 | 
				
			||||||
uint64_t xfs_inobt_irec_to_allocmask(struct xfs_inobt_rec_incore *);
 | 
					uint64_t xfs_inobt_irec_to_allocmask(struct xfs_inobt_rec_incore *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if defined(DEBUG) || defined(XFS_WARN)
 | 
				
			||||||
 | 
					int xfs_inobt_rec_check_count(struct xfs_mount *,
 | 
				
			||||||
 | 
								      struct xfs_inobt_rec_incore *);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#define xfs_inobt_rec_check_count(mp, rec)	0
 | 
				
			||||||
 | 
					#endif	/* DEBUG */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif	/* __XFS_IALLOC_BTREE_H__ */
 | 
					#endif	/* __XFS_IALLOC_BTREE_H__ */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -738,6 +738,53 @@ TRACE_EVENT(xfs_iomap_prealloc_size,
 | 
				
			||||||
		  __entry->blocks, __entry->shift, __entry->writeio_blocks)
 | 
							  __entry->blocks, __entry->shift, __entry->writeio_blocks)
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TRACE_EVENT(xfs_irec_merge_pre,
 | 
				
			||||||
 | 
						TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, xfs_agino_t agino,
 | 
				
			||||||
 | 
							 uint16_t holemask, xfs_agino_t nagino, uint16_t nholemask),
 | 
				
			||||||
 | 
						TP_ARGS(mp, agno, agino, holemask, nagino, nholemask),
 | 
				
			||||||
 | 
						TP_STRUCT__entry(
 | 
				
			||||||
 | 
							__field(dev_t, dev)
 | 
				
			||||||
 | 
							__field(xfs_agnumber_t, agno)
 | 
				
			||||||
 | 
							__field(xfs_agino_t, agino)
 | 
				
			||||||
 | 
							__field(uint16_t, holemask)
 | 
				
			||||||
 | 
							__field(xfs_agino_t, nagino)
 | 
				
			||||||
 | 
							__field(uint16_t, nholemask)
 | 
				
			||||||
 | 
						),
 | 
				
			||||||
 | 
						TP_fast_assign(
 | 
				
			||||||
 | 
							__entry->dev = mp->m_super->s_dev;
 | 
				
			||||||
 | 
							__entry->agno = agno;
 | 
				
			||||||
 | 
							__entry->agino = agino;
 | 
				
			||||||
 | 
							__entry->holemask = holemask;
 | 
				
			||||||
 | 
							__entry->nagino = nagino;
 | 
				
			||||||
 | 
							__entry->nholemask = holemask;
 | 
				
			||||||
 | 
						),
 | 
				
			||||||
 | 
						TP_printk("dev %d:%d agno %d inobt (%u:0x%x) new (%u:0x%x)",
 | 
				
			||||||
 | 
							  MAJOR(__entry->dev), MINOR(__entry->dev), __entry->agno,
 | 
				
			||||||
 | 
							  __entry->agino, __entry->holemask, __entry->nagino,
 | 
				
			||||||
 | 
							  __entry->nholemask)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TRACE_EVENT(xfs_irec_merge_post,
 | 
				
			||||||
 | 
						TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, xfs_agino_t agino,
 | 
				
			||||||
 | 
							 uint16_t holemask),
 | 
				
			||||||
 | 
						TP_ARGS(mp, agno, agino, holemask),
 | 
				
			||||||
 | 
						TP_STRUCT__entry(
 | 
				
			||||||
 | 
							__field(dev_t, dev)
 | 
				
			||||||
 | 
							__field(xfs_agnumber_t, agno)
 | 
				
			||||||
 | 
							__field(xfs_agino_t, agino)
 | 
				
			||||||
 | 
							__field(uint16_t, holemask)
 | 
				
			||||||
 | 
						),
 | 
				
			||||||
 | 
						TP_fast_assign(
 | 
				
			||||||
 | 
							__entry->dev = mp->m_super->s_dev;
 | 
				
			||||||
 | 
							__entry->agno = agno;
 | 
				
			||||||
 | 
							__entry->agino = agino;
 | 
				
			||||||
 | 
							__entry->holemask = holemask;
 | 
				
			||||||
 | 
						),
 | 
				
			||||||
 | 
						TP_printk("dev %d:%d agno %d inobt (%u:0x%x)", MAJOR(__entry->dev),
 | 
				
			||||||
 | 
							  MINOR(__entry->dev), __entry->agno, __entry->agino,
 | 
				
			||||||
 | 
							  __entry->holemask)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define DEFINE_IREF_EVENT(name) \
 | 
					#define DEFINE_IREF_EVENT(name) \
 | 
				
			||||||
DEFINE_EVENT(xfs_iref_class, name, \
 | 
					DEFINE_EVENT(xfs_iref_class, name, \
 | 
				
			||||||
	TP_PROTO(struct xfs_inode *ip, unsigned long caller_ip), \
 | 
						TP_PROTO(struct xfs_inode *ip, unsigned long caller_ip), \
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue