inode.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,219 行 · 第 1/5 页
C
2,219 行
/* Reader: end */ if (!p->key) goto no_block; } return NULL;changed: brelse(bh); *err = -EAGAIN; goto no_block;failure: *err = -EIO;no_block: return p;}/** * ext3_find_near - find a place for allocation with sufficient locality * @inode: owner * @ind: descriptor of indirect block. * * This function returns the prefered place for block allocation. * It is used when heuristic for sequential allocation fails. * Rules are: * + if there is a block to the left of our position - allocate near it. * + if pointer will live in indirect block - allocate near that block. * + if pointer will live in inode - allocate in the same * cylinder group. * * In the latter case we colour the starting block by the callers PID to * prevent it from clashing with concurrent allocations for a different inode * in the same block group. The PID is used here so that functionally related * files will be close-by on-disk. * * Caller must make sure that @ind is valid and will stay that way. */static unsigned long ext3_find_near(struct inode *inode, Indirect *ind){ struct ext3_inode_info *ei = EXT3_I(inode); __le32 *start = ind->bh ? (__le32*) ind->bh->b_data : ei->i_data; __le32 *p; unsigned long bg_start; unsigned long colour; /* Try to find previous block */ for (p = ind->p - 1; p >= start; p--) if (*p) return le32_to_cpu(*p); /* No such thing, so let's try location of indirect block */ if (ind->bh) return ind->bh->b_blocknr; /* * It is going to be refered from inode itself? OK, just put it into * the same cylinder group then. */ bg_start = (ei->i_block_group * EXT3_BLOCKS_PER_GROUP(inode->i_sb)) + le32_to_cpu(EXT3_SB(inode->i_sb)->s_es->s_first_data_block); colour = (current->pid % 16) * (EXT3_BLOCKS_PER_GROUP(inode->i_sb) / 16); return bg_start + colour;}/** * ext3_find_goal - find a prefered place for allocation. * @inode: owner * @block: block we want * @chain: chain of indirect blocks * @partial: pointer to the last triple within a chain * @goal: place to store the result. * * Normally this function find the prefered place for block allocation, * stores it in *@goal and returns zero. If the branch had been changed * under us we return -EAGAIN. */static int ext3_find_goal(struct inode *inode, long block, Indirect chain[4], Indirect *partial, unsigned long *goal){ struct ext3_inode_info *ei = EXT3_I(inode); /* Writer: ->i_next_alloc* */ if (block == ei->i_next_alloc_block + 1) { ei->i_next_alloc_block++; ei->i_next_alloc_goal++; } /* Writer: end */ /* Reader: pointers, ->i_next_alloc* */ if (verify_chain(chain, partial)) { /* * try the heuristic for sequential allocation, * failing that at least try to get decent locality. */ if (block == ei->i_next_alloc_block) *goal = ei->i_next_alloc_goal; if (!*goal) *goal = ext3_find_near(inode, partial); return 0; } /* Reader: end */ return -EAGAIN;}/** * ext3_alloc_branch - allocate and set up a chain of blocks. * @inode: owner * @num: depth of the chain (number of blocks to allocate) * @offsets: offsets (in the blocks) to store the pointers to next. * @branch: place to store the chain in. * * This function allocates @num blocks, zeroes out all but the last one, * links them into chain and (if we are synchronous) writes them to disk. * In other words, it prepares a branch that can be spliced onto the * inode. It stores the information about that chain in the branch[], in * the same format as ext3_get_branch() would do. We are calling it after * we had read the existing part of chain and partial points to the last * triple of that (one with zero ->key). Upon the exit we have the same * picture as after the successful ext3_get_block(), excpet that in one * place chain is disconnected - *branch->p is still zero (we did not * set the last link), but branch->key contains the number that should * be placed into *branch->p to fill that gap. * * If allocation fails we free all blocks we've allocated (and forget * their buffer_heads) and return the error value the from failed * ext3_alloc_block() (normally -ENOSPC). Otherwise we set the chain * as described above and return 0. */static int ext3_alloc_branch(handle_t *handle, struct inode *inode, int num, unsigned long goal, int *offsets, Indirect *branch){ int blocksize = inode->i_sb->s_blocksize; int n = 0, keys = 0; int err = 0; int i; int parent = ext3_alloc_block(handle, inode, goal, &err); branch[0].key = cpu_to_le32(parent); if (parent) { for (n = 1; n < num; n++) { struct buffer_head *bh; /* Allocate the next block */ int nr = ext3_alloc_block(handle, inode, parent, &err); if (!nr) break; branch[n].key = cpu_to_le32(nr); keys = n+1; /* * Get buffer_head for parent block, zero it out * and set the pointer to new one, then send * parent to disk. */ bh = sb_getblk(inode->i_sb, parent); branch[n].bh = bh; lock_buffer(bh); BUFFER_TRACE(bh, "call get_create_access"); err = ext3_journal_get_create_access(handle, bh); if (err) { unlock_buffer(bh); brelse(bh); break; } memset(bh->b_data, 0, blocksize); branch[n].p = (__le32*) bh->b_data + offsets[n]; *branch[n].p = branch[n].key; BUFFER_TRACE(bh, "marking uptodate"); set_buffer_uptodate(bh); unlock_buffer(bh); BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata"); err = ext3_journal_dirty_metadata(handle, bh); if (err) break; parent = nr; } } if (n == num) return 0; /* Allocation failed, free what we already allocated */ for (i = 1; i < keys; i++) { BUFFER_TRACE(branch[i].bh, "call journal_forget"); ext3_journal_forget(handle, branch[i].bh); } for (i = 0; i < keys; i++) ext3_free_blocks(handle, inode, le32_to_cpu(branch[i].key), 1); return err;}/** * ext3_splice_branch - splice the allocated branch onto inode. * @inode: owner * @block: (logical) number of block we are adding * @chain: chain of indirect blocks (with a missing link - see * ext3_alloc_branch) * @where: location of missing link * @num: number of blocks we are adding * * This function verifies that chain (up to the missing link) had not * changed, fills the missing link and does all housekeeping needed in * inode (->i_blocks, etc.). In case of success we end up with the full * chain to new block and return 0. Otherwise (== chain had been changed) * we free the new blocks (forgetting their buffer_heads, indeed) and * return -EAGAIN. */static int ext3_splice_branch(handle_t *handle, struct inode *inode, long block, Indirect chain[4], Indirect *where, int num){ int i; int err = 0; struct ext3_inode_info *ei = EXT3_I(inode); /* * If we're splicing into a [td]indirect block (as opposed to the * inode) then we need to get write access to the [td]indirect block * before the splice. */ if (where->bh) { BUFFER_TRACE(where->bh, "get_write_access"); err = ext3_journal_get_write_access(handle, where->bh); if (err) goto err_out; } /* Verify that place we are splicing to is still there and vacant */ /* Writer: pointers, ->i_next_alloc* */ if (!verify_chain(chain, where-1) || *where->p) /* Writer: end */ goto changed; /* That's it */ *where->p = where->key; ei->i_next_alloc_block = block; ei->i_next_alloc_goal = le32_to_cpu(where[num-1].key); /* Writer: end */ /* We are done with atomic stuff, now do the rest of housekeeping */ inode->i_ctime = CURRENT_TIME; ext3_mark_inode_dirty(handle, inode); /* had we spliced it onto indirect block? */ if (where->bh) { /* * akpm: If we spliced it onto an indirect block, we haven't * altered the inode. Note however that if it is being spliced * onto an indirect block at the very end of the file (the * file is growing) then we *will* alter the inode to reflect * the new i_size. But that is not done here - it is done in * generic_commit_write->__mark_inode_dirty->ext3_dirty_inode. */ jbd_debug(5, "splicing indirect only\n"); BUFFER_TRACE(where->bh, "call ext3_journal_dirty_metadata"); err = ext3_journal_dirty_metadata(handle, where->bh); if (err) goto err_out; } else { /* * OK, we spliced it into the inode itself on a direct block. * Inode was dirtied above. */ jbd_debug(5, "splicing direct\n"); } return err;changed: /* * AKPM: if where[i].bh isn't part of the current updating * transaction then we explode nastily. Test this code path. */ jbd_debug(1, "the chain changed: try again\n"); err = -EAGAIN;err_out: for (i = 1; i < num; i++) { BUFFER_TRACE(where[i].bh, "call journal_forget"); ext3_journal_forget(handle, where[i].bh); } /* For the normal collision cleanup case, we free up the blocks. * On genuine filesystem errors we don't even think about doing * that. */ if (err == -EAGAIN) for (i = 0; i < num; i++) ext3_free_blocks(handle, inode, le32_to_cpu(where[i].key), 1); return err;}/* * Allocation strategy is simple: if we have to allocate something, we will * have to go the whole way to leaf. So let's do it before attaching anything * to tree, set linkage between the newborn blocks, write them if sync is * required, recheck the path, free and repeat if check fails, otherwise * set the last missing link (that will protect us from any truncate-generated * removals - all blocks on the path are immune now) and possibly force the * write on the parent block. * That has a nice additional property: no special recovery from the failed * allocations is needed - we simply release blocks and do not touch anything * reachable from inode. * * akpm: `handle' can be NULL if create == 0. * * The BKL may not be held on entry here. Be sure to take it early. */static intext3_get_block_handle(handle_t *handle, struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create, int extend_disksize){ int err = -EIO; int offsets[4]; Indirect chain[4]; Indirect *partial; unsigned long goal; int left; int boundary = 0; int depth = ext3_block_to_path(inode, iblock, offsets, &boundary); struct ext3_inode_info *ei = EXT3_I(inode); J_ASSERT(handle != NULL || create == 0); if (depth == 0) goto out;reread: partial = ext3_get_branch(inode, depth, offsets, chain, &err); /* Simplest case - block found, no allocation needed */ if (!partial) { clear_buffer_new(bh_result);got_it: map_bh(bh_result, inode->i_sb, le32_to_cpu(chain[depth-1].key)); if (boundary) set_buffer_boundary(bh_result); /* Clean up and exit */ partial = chain+depth-1; /* the whole chain */ goto cleanup; } /* Next simple case - plain lookup or failed read of indirect block */ if (!create || err == -EIO) {cleanup: while (partial > chain) { BUFFER_TRACE(partial->bh, "call brelse"); brelse(partial->bh); partial--; } BUFFER_TRACE(bh_result, "returned");out: return err; } /* * Indirect block might be removed by truncate while we were * reading it. Handling of that case (forget what we've got and * reread) is taken out of the main path. */ if (err == -EAGAIN) goto changed; goal = 0; down(&ei->truncate_sem); if (ext3_find_goal(inode, iblock, chain, partial, &goal) < 0) { up(&ei->truncate_sem); goto changed; } left = (chain + depth) - partial; /* * Block out ext3_truncate while we alter the tree */ err = ext3_alloc_branch(handle, inode, left, goal, offsets+(partial-chain), partial); /* The ext3_splice_branch call will free and forget any buffers * on the new chain if there is a failure, but that risks using * up transaction credits, especially for bitmaps where the * credits cannot be returned. Can we handle this somehow? We * may need to return -EAGAIN upwards in the worst case. --sct */ if (!err) err = ext3_splice_branch(handle, inode, iblock, chain, partial, left); /* i_disksize growing is protected by truncate_sem * don't forget to protect it if you're about to implement * concurrent ext3_get_block() -bzzz */ if (!err && extend_disksize && inode->i_size > ei->i_disksize) ei->i_disksize = inode->i_size; up(&ei->truncate_sem); if (err == -EAGAIN) goto changed; if (err) goto cleanup; set_buffer_new(bh_result); goto got_it;changed: while (partial > chain) { jbd_debug(1, "buffer chain changed, retrying\n"); BUFFER_TRACE(partial->bh, "brelsing"); brelse(partial->bh); partial--; } goto reread;}static int ext3_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create){ handle_t *handle = NULL; int ret; if (create) { handle = ext3_journal_current_handle(); J_ASSERT(handle != 0); } ret = ext3_get_block_handle(handle, inode, iblock, bh_result, create, 1); return ret;}#define DIO_CREDITS (EXT3_RESERVE_TRANS_BLOCKS + 32)static intext3_direct_io_get_blocks(struct inode *inode, sector_t iblock, unsigned long max_blocks, struct buffer_head *bh_result, int create){ handle_t *handle = journal_current_handle(); int ret = 0; if (!handle) goto get_block; /* A read */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?