📄 xfs_ialloc_btree.c
字号:
/* * Insert one record/level. Return information to the caller * allowing the next level up to proceed if necessary. */STATIC int /* error */xfs_inobt_insrec( xfs_btree_cur_t *cur, /* btree cursor */ int level, /* level to insert record at */ xfs_agblock_t *bnop, /* i/o: block number inserted */ xfs_inobt_rec_t *recp, /* i/o: record data inserted */ xfs_btree_cur_t **curp, /* output: new cursor replacing cur */ int *stat) /* success/failure */{ xfs_inobt_block_t *block; /* btree block record/key lives in */ xfs_buf_t *bp; /* buffer for block */ int error; /* error return value */ int i; /* loop index */ xfs_inobt_key_t key; /* key value being inserted */ xfs_inobt_key_t *kp=NULL; /* pointer to btree keys */ xfs_agblock_t nbno; /* block number of allocated block */ xfs_btree_cur_t *ncur; /* new cursor to be used at next lvl */ xfs_inobt_key_t nkey; /* new key value, from split */ xfs_inobt_rec_t nrec; /* new record value, for caller */ int numrecs; int optr; /* old ptr value */ xfs_inobt_ptr_t *pp; /* pointer to btree addresses */ int ptr; /* index in btree block for this rec */ xfs_inobt_rec_t *rp=NULL; /* pointer to btree records */ /* * GCC doesn't understand the (arguably complex) control flow in * this function and complains about uninitialized structure fields * without this. */ memset(&nrec, 0, sizeof(nrec)); /* * If we made it to the root level, allocate a new root block * and we're done. */ if (level >= cur->bc_nlevels) { error = xfs_inobt_newroot(cur, &i); *bnop = NULLAGBLOCK; *stat = i; return error; } /* * Make a key out of the record data to be inserted, and save it. */ key.ir_startino = recp->ir_startino; optr = ptr = cur->bc_ptrs[level]; /* * If we're off the left edge, return failure. */ if (ptr == 0) { *stat = 0; return 0; } /* * Get pointers to the btree buffer and block. */ bp = cur->bc_bufs[level]; block = XFS_BUF_TO_INOBT_BLOCK(bp); numrecs = be16_to_cpu(block->bb_numrecs);#ifdef DEBUG if ((error = xfs_btree_check_sblock(cur, block, level, bp))) return error; /* * Check that the new entry is being inserted in the right place. */ if (ptr <= numrecs) { if (level == 0) { rp = XFS_INOBT_REC_ADDR(block, ptr, cur); xfs_btree_check_rec(cur->bc_btnum, recp, rp); } else { kp = XFS_INOBT_KEY_ADDR(block, ptr, cur); xfs_btree_check_key(cur->bc_btnum, &key, kp); } }#endif nbno = NULLAGBLOCK; ncur = NULL; /* * If the block is full, we can't insert the new entry until we * make the block un-full. */ if (numrecs == XFS_INOBT_BLOCK_MAXRECS(level, cur)) { /* * First, try shifting an entry to the right neighbor. */ if ((error = xfs_inobt_rshift(cur, level, &i))) return error; if (i) { /* nothing */ } /* * Next, try shifting an entry to the left neighbor. */ else { if ((error = xfs_inobt_lshift(cur, level, &i))) return error; if (i) { optr = ptr = cur->bc_ptrs[level]; } else { /* * Next, try splitting the current block * in half. If this works we have to * re-set our variables because * we could be in a different block now. */ if ((error = xfs_inobt_split(cur, level, &nbno, &nkey, &ncur, &i))) return error; if (i) { bp = cur->bc_bufs[level]; block = XFS_BUF_TO_INOBT_BLOCK(bp);#ifdef DEBUG if ((error = xfs_btree_check_sblock(cur, block, level, bp))) return error;#endif ptr = cur->bc_ptrs[level]; nrec.ir_startino = nkey.ir_startino; } else { /* * Otherwise the insert fails. */ *stat = 0; return 0; } } } } /* * At this point we know there's room for our new entry in the block * we're pointing at. */ numrecs = be16_to_cpu(block->bb_numrecs); if (level > 0) { /* * It's a non-leaf entry. Make a hole for the new data * in the key and ptr regions of the block. */ kp = XFS_INOBT_KEY_ADDR(block, 1, cur); pp = XFS_INOBT_PTR_ADDR(block, 1, cur);#ifdef DEBUG for (i = numrecs; i >= ptr; i--) { if ((error = xfs_btree_check_sptr(cur, be32_to_cpu(pp[i - 1]), level))) return error; }#endif memmove(&kp[ptr], &kp[ptr - 1], (numrecs - ptr + 1) * sizeof(*kp)); memmove(&pp[ptr], &pp[ptr - 1], (numrecs - ptr + 1) * sizeof(*pp)); /* * Now stuff the new data in, bump numrecs and log the new data. */#ifdef DEBUG if ((error = xfs_btree_check_sptr(cur, *bnop, level))) return error;#endif kp[ptr - 1] = key; pp[ptr - 1] = cpu_to_be32(*bnop); numrecs++; block->bb_numrecs = cpu_to_be16(numrecs); xfs_inobt_log_keys(cur, bp, ptr, numrecs); xfs_inobt_log_ptrs(cur, bp, ptr, numrecs); } else { /* * It's a leaf entry. Make a hole for the new record. */ rp = XFS_INOBT_REC_ADDR(block, 1, cur); memmove(&rp[ptr], &rp[ptr - 1], (numrecs - ptr + 1) * sizeof(*rp)); /* * Now stuff the new record in, bump numrecs * and log the new data. */ rp[ptr - 1] = *recp; numrecs++; block->bb_numrecs = cpu_to_be16(numrecs); xfs_inobt_log_recs(cur, bp, ptr, numrecs); } /* * Log the new number of records in the btree header. */ xfs_inobt_log_block(cur->bc_tp, bp, XFS_BB_NUMRECS);#ifdef DEBUG /* * Check that the key/record is in the right place, now. */ if (ptr < numrecs) { if (level == 0) xfs_btree_check_rec(cur->bc_btnum, rp + ptr - 1, rp + ptr); else xfs_btree_check_key(cur->bc_btnum, kp + ptr - 1, kp + ptr); }#endif /* * If we inserted at the start of a block, update the parents' keys. */ if (optr == 1 && (error = xfs_inobt_updkey(cur, &key, level + 1))) return error; /* * Return the new block number, if any. * If there is one, give back a record value and a cursor too. */ *bnop = nbno; if (nbno != NULLAGBLOCK) { *recp = nrec; *curp = ncur; } *stat = 1; return 0;}/* * Log header fields from a btree block. */STATIC voidxfs_inobt_log_block( xfs_trans_t *tp, /* transaction pointer */ xfs_buf_t *bp, /* buffer containing btree block */ int fields) /* mask of fields: XFS_BB_... */{ int first; /* first byte offset logged */ int last; /* last byte offset logged */ static const short offsets[] = { /* table of offsets */ offsetof(xfs_inobt_block_t, bb_magic), offsetof(xfs_inobt_block_t, bb_level), offsetof(xfs_inobt_block_t, bb_numrecs), offsetof(xfs_inobt_block_t, bb_leftsib), offsetof(xfs_inobt_block_t, bb_rightsib), sizeof(xfs_inobt_block_t) }; xfs_btree_offsets(fields, offsets, XFS_BB_NUM_BITS, &first, &last); xfs_trans_log_buf(tp, bp, first, last);}/* * Log keys from a btree block (nonleaf). */STATIC voidxfs_inobt_log_keys( xfs_btree_cur_t *cur, /* btree cursor */ xfs_buf_t *bp, /* buffer containing btree block */ int kfirst, /* index of first key to log */ int klast) /* index of last key to log */{ xfs_inobt_block_t *block; /* btree block to log from */ int first; /* first byte offset logged */ xfs_inobt_key_t *kp; /* key pointer in btree block */ int last; /* last byte offset logged */ block = XFS_BUF_TO_INOBT_BLOCK(bp); kp = XFS_INOBT_KEY_ADDR(block, 1, cur); first = (int)((xfs_caddr_t)&kp[kfirst - 1] - (xfs_caddr_t)block); last = (int)(((xfs_caddr_t)&kp[klast] - 1) - (xfs_caddr_t)block); xfs_trans_log_buf(cur->bc_tp, bp, first, last);}/* * Log block pointer fields from a btree block (nonleaf). */STATIC voidxfs_inobt_log_ptrs( xfs_btree_cur_t *cur, /* btree cursor */ xfs_buf_t *bp, /* buffer containing btree block */ int pfirst, /* index of first pointer to log */ int plast) /* index of last pointer to log */{ xfs_inobt_block_t *block; /* btree block to log from */ int first; /* first byte offset logged */ int last; /* last byte offset logged */ xfs_inobt_ptr_t *pp; /* block-pointer pointer in btree blk */ block = XFS_BUF_TO_INOBT_BLOCK(bp); pp = XFS_INOBT_PTR_ADDR(block, 1, cur); first = (int)((xfs_caddr_t)&pp[pfirst - 1] - (xfs_caddr_t)block); last = (int)(((xfs_caddr_t)&pp[plast] - 1) - (xfs_caddr_t)block); xfs_trans_log_buf(cur->bc_tp, bp, first, last);}/* * Log records from a btree block (leaf). */STATIC voidxfs_inobt_log_recs( xfs_btree_cur_t *cur, /* btree cursor */ xfs_buf_t *bp, /* buffer containing btree block */ int rfirst, /* index of first record to log */ int rlast) /* index of last record to log */{ xfs_inobt_block_t *block; /* btree block to log from */ int first; /* first byte offset logged */ int last; /* last byte offset logged */ xfs_inobt_rec_t *rp; /* record pointer for btree block */ block = XFS_BUF_TO_INOBT_BLOCK(bp); rp = XFS_INOBT_REC_ADDR(block, 1, cur); first = (int)((xfs_caddr_t)&rp[rfirst - 1] - (xfs_caddr_t)block); last = (int)(((xfs_caddr_t)&rp[rlast] - 1) - (xfs_caddr_t)block); xfs_trans_log_buf(cur->bc_tp, bp, first, last);}/* * Lookup the record. The cursor is made to point to it, based on dir. * Return 0 if can't find any such record, 1 for success. */STATIC int /* error */xfs_inobt_lookup( xfs_btree_cur_t *cur, /* btree cursor */ xfs_lookup_t dir, /* <=, ==, or >= */ int *stat) /* success/failure */{ xfs_agblock_t agbno; /* a.g. relative btree block number */ xfs_agnumber_t agno; /* allocation group number */ xfs_inobt_block_t *block=NULL; /* current btree block */ __int64_t diff; /* difference for the current key */ int error; /* error return value */ int keyno=0; /* current key number */ int level; /* level in the btree */ xfs_mount_t *mp; /* file system mount point */ /* * Get the allocation group header, and the root block number. */ mp = cur->bc_mp; { xfs_agi_t *agi; /* a.g. inode header */ agi = XFS_BUF_TO_AGI(cur->bc_private.i.agbp); agno = be32_to_cpu(agi->agi_seqno); agbno = be32_to_cpu(agi->agi_root); } /* * Iterate over each level in the btree, starting at the root. * For each level above the leaves, find the key we need, based * on the lookup record, then follow the corresponding block * pointer down to the next level. */ for (level = cur->bc_nlevels - 1, diff = 1; level >= 0; level--) { xfs_buf_t *bp; /* buffer pointer for btree block */ xfs_daddr_t d; /* disk address of btree block */ /* * Get the disk address we're looking for. */ d = XFS_AGB_TO_DADDR(mp, agno, agbno); /* * If the old buffer at this level is for a different block, * throw it away, otherwise just use it. */ bp = cur->bc_bufs[level]; if (bp && XFS_BUF_ADDR(bp) != d) bp = NULL; if (!bp) { /* * Need to get a new buffer. Read it, then * set it in the cursor, releasing the old one. */ if ((error = xfs_btree_read_bufs(mp, cur->bc_tp, agno, agbno, 0, &bp, XFS_INO_BTREE_REF))) return error; xfs_btree_setbuf(cur, level, bp); /* * Point to the btree block, now that we have the buffer */ block = XFS_BUF_TO_INOBT_BLOCK(bp); if ((error = xfs_btree_check_sblock(cur, block, level, bp))) return error; } else block = XFS_BUF_TO_INOBT_BLOCK(bp); /* * If we already had a key match at a higher level, we know * we need to use the first entry in this block. */ if (diff == 0) keyno = 1; /* * Otherwise we need to search this block. Do a binary search. */ else { int high; /* high entry number */ xfs_inobt_key_t *kkbase=NULL;/* base of keys in block */ xfs_inobt_rec_t *krbase=NULL;/* base of records in block */ int low; /* low entry number */ /* * Get a pointer to keys or records. */ if (level > 0) kkbase = XFS_INOBT_KEY_ADDR(block, 1, cur); else krbase = XFS_INOBT_REC_ADDR(block, 1, cur); /* * Set low and high entry numbers, 1-based. */ low = 1; if (!(high = be16_to_cpu(block->bb_numrecs))) { /* * If the block is empty, the tree must * be an empty leaf. */ ASSERT(level == 0 && cur->bc_nlevels == 1); cur->bc_ptrs[0] = dir != XFS_LOOKUP_LE; *stat = 0; return 0; } /* * Binary search the block. */ while (low <= high) { xfs_agino_t startino; /* key value */ /* * keyno is average of low and high. */ keyno = (low + high) >> 1; /* * Get startino. */ if (level > 0) { xfs_inobt_key_t *kkp; kkp = kkbase + keyno - 1; startino = be32_to_cpu(kkp->ir_startino); } else { xfs_inobt_rec_t *krp; krp = krbase + keyno - 1; startino = be32_to_cpu(krp->ir_startino); } /* * Compute difference to get next direction. */ diff = (__int64_t) startino - cur->bc_rec.i.ir_startino; /* * Less than, move right. */ if (diff < 0) low = keyno + 1; /* * Greater than, move left. */ else if (diff > 0) high = keyno - 1; /* * Equal, we're done. */ else break; } } /* * If there are more levels, set up for the next level * by getting the block number and filling in the cursor. */ if (level > 0) { /* * If we moved left, need the previous key number, * unless there isn't one. */ if (diff > 0 && --keyno < 1) keyno = 1; agbno = be32_to_cpu(*XFS_INOBT_PTR_ADDR(block, keyno, cur));#ifdef DEBUG if ((error = xfs_btree_check_sptr(cur, agbno, level))) return error;#endif cur->bc_ptrs[level] = keyno; } } /* * Done with the search. * See if we need to adjust the results. */ if (dir != XFS_LOOKUP_LE && diff < 0) { keyno++; /* * If ge search and we went off the end of the block, but it's * not the last block, we're in the wrong block. */ if (dir == XFS_LOOKUP_GE && keyno > be16_to_cpu(block->bb_numrecs) && be32_to_cpu(block->bb_rightsib) != NULLAGBLOCK) { int i; cur->bc_ptrs[0] = keyno; if ((error = xfs_inobt_increment(cur, 0, &i))) return error; ASSERT(i == 1); *stat = 1; return 0; } } else if (dir == XFS_LOOKUP_LE && diff > 0) keyno--; cur->bc_ptrs[0] = keyno; /* * Return if we succeeded or not. */ if (keyno == 0 || keyno > be16_to_cpu(block->bb_numrecs)) *stat = 0; else *stat = ((dir != XFS_LOOKUP_EQ) || (diff == 0)); return 0;}/* * Move 1 record left from cur/level if possible. * Update cur to reflect the new path. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -