/* Called from locking and called from ocfs2_clear_inode. Dump the * cache for a given inode. * * This function is a few more lines longer than necessary due to some * accounting done here, but I think it's worth tracking down those * bugs sooner -- Mark */ void ocfs2_metadata_cache_purge(struct ocfs2_caching_info *ci) { unsigned int tree, to_purge, purged; struct rb_root root = RB_ROOT; BUG_ON(!ci || !ci->ci_ops); ocfs2_metadata_cache_lock(ci); tree = !(ci->ci_flags & OCFS2_CACHE_FL_INLINE); to_purge = ci->ci_num_cached; trace_ocfs2_metadata_cache_purge( (unsigned long long)ocfs2_metadata_cache_owner(ci), to_purge, tree); /* If we're a tree, save off the root so that we can safely * initialize the cache. We do the work to free tree members * without the spinlock. */ if (tree) root = ci->ci_cache.ci_tree; ocfs2_metadata_cache_reset(ci, 0); ocfs2_metadata_cache_unlock(ci); purged = ocfs2_purge_copied_metadata_tree(&root); /* If possible, track the number wiped so that we can more * easily detect counting errors. Unfortunately, this is only * meaningful for trees. */ if (tree && purged != to_purge) mlog(ML_ERROR, "Owner %llu, count = %u, purged = %u\n", (unsigned long long)ocfs2_metadata_cache_owner(ci), to_purge, purged); }
/* Requires ip_lock */ static void ocfs2_append_cache_array(struct ocfs2_caching_info *ci, sector_t block) { BUG_ON(ci->ci_num_cached >= OCFS2_CACHE_INFO_MAX_ARRAY); trace_ocfs2_append_cache_array( (unsigned long long)ocfs2_metadata_cache_owner(ci), (unsigned long long)block, ci->ci_num_cached); ci->ci_cache.ci_array[ci->ci_num_cached] = block; ci->ci_num_cached++; }
static int ocfs2_buffer_cached(struct ocfs2_caching_info *ci, struct buffer_head *bh) { int index = -1; struct ocfs2_meta_cache_item *item = NULL; ocfs2_metadata_cache_lock(ci); trace_ocfs2_buffer_cached_begin( (unsigned long long)ocfs2_metadata_cache_owner(ci), (unsigned long long) bh->b_blocknr, !!(ci->ci_flags & OCFS2_CACHE_FL_INLINE)); if (ci->ci_flags & OCFS2_CACHE_FL_INLINE) index = ocfs2_search_cache_array(ci, bh->b_blocknr); else item = ocfs2_search_cache_tree(ci, bh->b_blocknr); ocfs2_metadata_cache_unlock(ci); trace_ocfs2_buffer_cached_end(index, item); return (index != -1) || (item != NULL); }
static int ocfs2_buffer_cached(struct ocfs2_caching_info *ci, struct buffer_head *bh) { int index = -1; struct ocfs2_meta_cache_item *item = NULL; ocfs2_metadata_cache_lock(ci); mlog(0, "Owner %llu, query block %llu (inline = %u)\n", (unsigned long long)ocfs2_metadata_cache_owner(ci), (unsigned long long) bh->b_blocknr, !!(ci->ci_flags & OCFS2_CACHE_FL_INLINE)); if (ci->ci_flags & OCFS2_CACHE_FL_INLINE) index = ocfs2_search_cache_array(ci, bh->b_blocknr); else item = ocfs2_search_cache_tree(ci, bh->b_blocknr); ocfs2_metadata_cache_unlock(ci); mlog(0, "index = %d, item = %p\n", index, item); return (index != -1) || (item != NULL); }
static int __ocfs2_move_extent(handle_t *handle, struct ocfs2_move_extents_context *context, u32 cpos, u32 len, u32 p_cpos, u32 new_p_cpos, int ext_flags) { int ret = 0, index; struct inode *inode = context->inode; struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); struct ocfs2_extent_rec *rec, replace_rec; struct ocfs2_path *path = NULL; struct ocfs2_extent_list *el; u64 ino = ocfs2_metadata_cache_owner(context->et.et_ci); u64 old_blkno = ocfs2_clusters_to_blocks(inode->i_sb, p_cpos); ret = ocfs2_duplicate_clusters_by_page(handle, inode, cpos, p_cpos, new_p_cpos, len); if (ret) { mlog_errno(ret); goto out; } memset(&replace_rec, 0, sizeof(replace_rec)); replace_rec.e_cpos = cpu_to_le32(cpos); replace_rec.e_leaf_clusters = cpu_to_le16(len); replace_rec.e_blkno = cpu_to_le64(ocfs2_clusters_to_blocks(inode->i_sb, new_p_cpos)); path = ocfs2_new_path_from_et(&context->et); if (!path) { ret = -ENOMEM; mlog_errno(ret); goto out; } ret = ocfs2_find_path(INODE_CACHE(inode), path, cpos); if (ret) { mlog_errno(ret); goto out; } el = path_leaf_el(path); index = ocfs2_search_extent_list(el, cpos); if (index == -1) { ret = ocfs2_error(inode->i_sb, "Inode %llu has an extent at cpos %u which can no longer be found\n", (unsigned long long)ino, cpos); goto out; } rec = &el->l_recs[index]; BUG_ON(ext_flags != rec->e_flags); /* * after moving/defraging to new location, the extent is not going * to be refcounted anymore. */ replace_rec.e_flags = ext_flags & ~OCFS2_EXT_REFCOUNTED; ret = ocfs2_journal_access_di(handle, INODE_CACHE(inode), context->et.et_root_bh, OCFS2_JOURNAL_ACCESS_WRITE); if (ret) { mlog_errno(ret); goto out; } ret = ocfs2_split_extent(handle, &context->et, path, index, &replace_rec, context->meta_ac, &context->dealloc); if (ret) { mlog_errno(ret); goto out; } ocfs2_journal_dirty(handle, context->et.et_root_bh); context->new_phys_cpos = new_p_cpos; /* * need I to append truncate log for old clusters? */ if (old_blkno) { if (ext_flags & OCFS2_EXT_REFCOUNTED) ret = ocfs2_decrease_refcount(inode, handle, ocfs2_blocks_to_clusters(osb->sb, old_blkno), len, context->meta_ac, &context->dealloc, 1); else ret = ocfs2_truncate_log_append(osb, handle, old_blkno, len); } ocfs2_update_inode_fsync_trans(handle, inode, 0); out: ocfs2_free_path(path); return ret; }
ci->ci_num_cached++; } /* By now the caller should have checked that the item does *not* * exist in the tree. * Requires ip_lock. */ static void __ocfs2_insert_cache_tree(struct ocfs2_caching_info *ci, struct ocfs2_meta_cache_item *new) { sector_t block = new->c_block; struct rb_node *parent = NULL; struct rb_node **p = &ci->ci_cache.ci_tree.rb_node; struct ocfs2_meta_cache_item *tmp; trace_ocfs2_insert_cache_tree( (unsigned long long)ocfs2_metadata_cache_owner(ci), (unsigned long long)block, ci->ci_num_cached); while(*p) { parent = *p; tmp = rb_entry(parent, struct ocfs2_meta_cache_item, c_node); if (block < tmp->c_block) p = &(*p)->rb_left; else if (block > tmp->c_block) p = &(*p)->rb_right; else { /* This should never happen! */ mlog(ML_ERROR, "Duplicate block %llu cached!\n", (unsigned long long) block);
int ocfs2_read_blocks(struct ocfs2_caching_info *ci, u64 block, int nr, struct buffer_head *bhs[], int flags, int (*validate)(struct super_block *sb, struct buffer_head *bh)) { int status = 0; int i, ignore_cache = 0; struct buffer_head *bh; struct super_block *sb = ocfs2_metadata_cache_get_super(ci); trace_ocfs2_read_blocks_begin(ci, (unsigned long long)block, nr, flags); BUG_ON(!ci); BUG_ON((flags & OCFS2_BH_READAHEAD) && (flags & OCFS2_BH_IGNORE_CACHE)); if (bhs == NULL) { status = -EINVAL; mlog_errno(status); goto bail; } if (nr < 0) { mlog(ML_ERROR, "asked to read %d blocks!\n", nr); status = -EINVAL; mlog_errno(status); goto bail; } if (nr == 0) { status = 0; goto bail; } ocfs2_metadata_cache_io_lock(ci); for (i = 0 ; i < nr ; i++) { if (bhs[i] == NULL) { bhs[i] = sb_getblk(sb, block++); if (bhs[i] == NULL) { ocfs2_metadata_cache_io_unlock(ci); status = -ENOMEM; mlog_errno(status); goto bail; } } bh = bhs[i]; ignore_cache = (flags & OCFS2_BH_IGNORE_CACHE); /* There are three read-ahead cases here which we need to * be concerned with. All three assume a buffer has * previously been submitted with OCFS2_BH_READAHEAD * and it hasn't yet completed I/O. * * 1) The current request is sync to disk. This rarely * happens these days, and never when performance * matters - the code can just wait on the buffer * lock and re-submit. * * 2) The current request is cached, but not * readahead. ocfs2_buffer_uptodate() will return * false anyway, so we'll wind up waiting on the * buffer lock to do I/O. We re-check the request * with after getting the lock to avoid a re-submit. * * 3) The current request is readahead (and so must * also be a caching one). We short circuit if the * buffer is locked (under I/O) and if it's in the * uptodate cache. The re-check from #2 catches the * case that the previous read-ahead completes just * before our is-it-in-flight check. */ if (!ignore_cache && !ocfs2_buffer_uptodate(ci, bh)) { trace_ocfs2_read_blocks_from_disk( (unsigned long long)bh->b_blocknr, (unsigned long long)ocfs2_metadata_cache_owner(ci)); /* We're using ignore_cache here to say * "go to disk" */ ignore_cache = 1; } trace_ocfs2_read_blocks_bh((unsigned long long)bh->b_blocknr, ignore_cache, buffer_jbd(bh), buffer_dirty(bh)); if (buffer_jbd(bh)) { continue; } if (ignore_cache) { if (buffer_dirty(bh)) { /* This should probably be a BUG, or * at least return an error. */ continue; } /* A read-ahead request was made - if the * buffer is already under read-ahead from a * previously submitted request than we are * done here. */ if ((flags & OCFS2_BH_READAHEAD) && ocfs2_buffer_read_ahead(ci, bh)) continue; lock_buffer(bh); if (buffer_jbd(bh)) { #ifdef CATCH_BH_JBD_RACES mlog(ML_ERROR, "block %llu had the JBD bit set " "while I was in lock_buffer!", (unsigned long long)bh->b_blocknr); BUG(); #else unlock_buffer(bh); continue; #endif } /* Re-check ocfs2_buffer_uptodate() as a * previously read-ahead buffer may have * completed I/O while we were waiting for the * buffer lock. */ if (!(flags & OCFS2_BH_IGNORE_CACHE) && !(flags & OCFS2_BH_READAHEAD) && ocfs2_buffer_uptodate(ci, bh)) { unlock_buffer(bh); continue; } clear_buffer_uptodate(bh); get_bh(bh); /* for end_buffer_read_sync() */ if (validate) set_buffer_needs_validate(bh); bh->b_end_io = end_buffer_read_sync; submit_bh(REQ_OP_READ, 0, bh); continue; } } status = 0; for (i = (nr - 1); i >= 0; i--) { bh = bhs[i]; if (!(flags & OCFS2_BH_READAHEAD)) { if (status) { /* Clear the rest of the buffers on error */ put_bh(bh); bhs[i] = NULL; continue; } /* We know this can't have changed as we hold the * owner sem. Avoid doing any work on the bh if the * journal has it. */ if (!buffer_jbd(bh)) wait_on_buffer(bh); if (!buffer_uptodate(bh)) { /* Status won't be cleared from here on out, * so we can safely record this and loop back * to cleanup the other buffers. Don't need to * remove the clustered uptodate information * for this bh as it's not marked locally * uptodate. */ status = -EIO; put_bh(bh); bhs[i] = NULL; continue; } if (buffer_needs_validate(bh)) { /* We never set NeedsValidate if the * buffer was held by the journal, so * that better not have changed */ BUG_ON(buffer_jbd(bh)); clear_buffer_needs_validate(bh); status = validate(sb, bh); if (status) { put_bh(bh); bhs[i] = NULL; continue; } } } /* Always set the buffer in the cache, even if it was * a forced read, or read-ahead which hasn't yet * completed. */ ocfs2_set_buffer_uptodate(ci, bh); } ocfs2_metadata_cache_io_unlock(ci); trace_ocfs2_read_blocks_end((unsigned long long)block, nr, flags, ignore_cache); bail: return status; }