Skip to content
Snippets Groups Projects
alloc.c 96.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • 
    	if (le16_to_cpu(el->l_tree_depth) == 0) {
    		ocfs2_insert_at_leaf(insert_rec, el, type, inode);
    		goto out_update_clusters;
    	}
    
    	right_path = ocfs2_new_inode_path(di_bh);
    	if (!right_path) {
    		ret = -ENOMEM;
    		mlog_errno(ret);
    		goto out;
    	}
    
    	/*
    	 * Determine the path to start with. Rotations need the
    	 * rightmost path, everything else can go directly to the
    	 * target leaf.
    	 */
    	cpos = le32_to_cpu(insert_rec->e_cpos);
    	if (type->ins_appending == APPEND_NONE &&
    	    type->ins_contig == CONTIG_NONE) {
    		rotate = 1;
    		cpos = UINT_MAX;
    	}
    
    	ret = ocfs2_find_path(inode, right_path, cpos);
    	if (ret) {
    		mlog_errno(ret);
    		goto out;
    	}
    
    	/*
    	 * Rotations and appends need special treatment - they modify
    	 * parts of the tree's above them.
    	 *
    	 * Both might pass back a path immediate to the left of the
    	 * one being inserted to. This will be cause
    	 * ocfs2_insert_path() to modify the rightmost records of
    	 * left_path to account for an edge insert.
    	 *
    	 * XXX: When modifying this code, keep in mind that an insert
    	 * can wind up skipping both of these two special cases...
    	 */
    	if (rotate) {
    		ret = ocfs2_rotate_tree_right(inode, handle,
    					      le32_to_cpu(insert_rec->e_cpos),
    					      right_path, &left_path);
    		if (ret) {
    			mlog_errno(ret);
    			goto out;
    		}
    	} else if (type->ins_appending == APPEND_TAIL
    		   && type->ins_contig != CONTIG_LEFT) {
    		ret = ocfs2_append_rec_to_path(inode, handle, insert_rec,
    					       right_path, &left_path);
    		if (ret) {
    			mlog_errno(ret);
    			goto out;
    		}
    	}
    
    	ret = ocfs2_insert_path(inode, handle, left_path, right_path,
    				insert_rec, type);
    	if (ret) {
    		mlog_errno(ret);
    		goto out;
    	}
    
    out_update_clusters:
    	ocfs2_update_dinode_clusters(inode, di,
    				     le32_to_cpu(insert_rec->e_clusters));
    
    	ret = ocfs2_journal_dirty(handle, di_bh);
    	if (ret)
    		mlog_errno(ret);
    
    out:
    	ocfs2_free_path(left_path);
    	ocfs2_free_path(right_path);
    
    	return ret;
    }
    
    static void ocfs2_figure_contig_type(struct inode *inode,
    				     struct ocfs2_insert_type *insert,
    				     struct ocfs2_extent_list *el,
    				     struct ocfs2_extent_rec *insert_rec)
    {
    	int i;
    	enum ocfs2_contig_type contig_type = CONTIG_NONE;
    
    	for(i = 0; i < le16_to_cpu(el->l_next_free_rec); i++) {
    		contig_type = ocfs2_extent_contig(inode, &el->l_recs[i],
    						  insert_rec);
    		if (contig_type != CONTIG_NONE) {
    			insert->ins_contig_index = i;
    			break;
    		}
    	}
    	insert->ins_contig = contig_type;
    }
    
    /*
     * This should only be called against the righmost leaf extent list.
     *
     * ocfs2_figure_appending_type() will figure out whether we'll have to
     * insert at the tail of the rightmost leaf.
     *
     * This should also work against the dinode list for tree's with 0
     * depth. If we consider the dinode list to be the rightmost leaf node
     * then the logic here makes sense.
     */
    static void ocfs2_figure_appending_type(struct ocfs2_insert_type *insert,
    					struct ocfs2_extent_list *el,
    					struct ocfs2_extent_rec *insert_rec)
    {
    	int i;
    	u32 cpos = le32_to_cpu(insert_rec->e_cpos);
    	struct ocfs2_extent_rec *rec;
    
    	insert->ins_appending = APPEND_NONE;
    
    	BUG_ON(el->l_tree_depth);
    
    	if (!el->l_next_free_rec)
    		goto set_tail_append;
    
    	if (ocfs2_is_empty_extent(&el->l_recs[0])) {
    		/* Were all records empty? */
    		if (le16_to_cpu(el->l_next_free_rec) == 1)
    			goto set_tail_append;
    	}
    
    	i = le16_to_cpu(el->l_next_free_rec) - 1;
    	rec = &el->l_recs[i];
    
    	if (cpos >= (le32_to_cpu(rec->e_cpos) + le32_to_cpu(rec->e_clusters)))
    		goto set_tail_append;
    
    	return;
    
    set_tail_append:
    	insert->ins_appending = APPEND_TAIL;
    }
    
    /*
     * Helper function called at the begining of an insert.
     *
     * This computes a few things that are commonly used in the process of
     * inserting into the btree:
     *   - Whether the new extent is contiguous with an existing one.
     *   - The current tree depth.
     *   - Whether the insert is an appending one.
     *   - The total # of free records in the tree.
     *
     * All of the information is stored on the ocfs2_insert_type
     * structure.
     */
    static int ocfs2_figure_insert_type(struct inode *inode,
    				    struct buffer_head *di_bh,
    				    struct buffer_head **last_eb_bh,
    				    struct ocfs2_extent_rec *insert_rec,
    				    struct ocfs2_insert_type *insert)
    {
    	int ret;
    	struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
    	struct ocfs2_extent_block *eb;
    	struct ocfs2_extent_list *el;
    	struct ocfs2_path *path = NULL;
    	struct buffer_head *bh = NULL;
    
    	el = &di->id2.i_list;
    	insert->ins_tree_depth = le16_to_cpu(el->l_tree_depth);
    
    	if (el->l_tree_depth) {
    		/*
    		 * If we have tree depth, we read in the
    		 * rightmost extent block ahead of time as
    		 * ocfs2_figure_insert_type() and ocfs2_add_branch()
    		 * may want it later.
    		 */
    		ret = ocfs2_read_block(OCFS2_SB(inode->i_sb),
    				       le64_to_cpu(di->i_last_eb_blk), &bh,
    				       OCFS2_BH_CACHED, inode);
    		if (ret) {
    			mlog_exit(ret);
    			goto out;
    		}
    
    		eb = (struct ocfs2_extent_block *) bh->b_data;
    		el = &eb->h_list;
    
    	/*
    	 * Unless we have a contiguous insert, we'll need to know if
    	 * there is room left in our allocation tree for another
    	 * extent record.
    	 *
    	 * XXX: This test is simplistic, we can search for empty
    	 * extent records too.
    	 */
    	insert->ins_free_records = le16_to_cpu(el->l_count) -
    		le16_to_cpu(el->l_next_free_rec);
    
    	if (!insert->ins_tree_depth) {
    		ocfs2_figure_contig_type(inode, insert, el, insert_rec);
    		ocfs2_figure_appending_type(insert, el, insert_rec);
    		return 0;
    
    	path = ocfs2_new_inode_path(di_bh);
    	if (!path) {
    		ret = -ENOMEM;
    		mlog_errno(ret);
    		goto out;
    	}
    
    	/*
    	 * In the case that we're inserting past what the tree
    	 * currently accounts for, ocfs2_find_path() will return for
    	 * us the rightmost tree path. This is accounted for below in
    	 * the appending code.
    	 */
    	ret = ocfs2_find_path(inode, path, le32_to_cpu(insert_rec->e_cpos));
    	if (ret) {
    		mlog_errno(ret);
    		goto out;
    	}
    
    	el = path_leaf_el(path);
    
    	/*
    	 * Now that we have the path, there's two things we want to determine:
    	 * 1) Contiguousness (also set contig_index if this is so)
    	 *
    	 * 2) Are we doing an append? We can trivially break this up
             *     into two types of appends: simple record append, or a
             *     rotate inside the tail leaf.
    	 */
    	ocfs2_figure_contig_type(inode, insert, el, insert_rec);
    
    	/*
    	 * The insert code isn't quite ready to deal with all cases of
    	 * left contiguousness. Specifically, if it's an insert into
    	 * the 1st record in a leaf, it will require the adjustment of
    	 * e_clusters on the last record of the path directly to it's
    	 * left. For now, just catch that case and fool the layers
    	 * above us. This works just fine for tree_depth == 0, which
    	 * is why we allow that above.
    	 */
    	if (insert->ins_contig == CONTIG_LEFT &&
    	    insert->ins_contig_index == 0)
    		insert->ins_contig = CONTIG_NONE;
    
    	/*
    	 * Ok, so we can simply compare against last_eb to figure out
    	 * whether the path doesn't exist. This will only happen in
    	 * the case that we're doing a tail append, so maybe we can
    	 * take advantage of that information somehow.
    	 */
    	if (le64_to_cpu(di->i_last_eb_blk) == path_leaf_bh(path)->b_blocknr) {
    		/*
    		 * Ok, ocfs2_find_path() returned us the rightmost
    		 * tree path. This might be an appending insert. There are
    		 * two cases:
    		 *    1) We're doing a true append at the tail:
    		 *	-This might even be off the end of the leaf
    		 *    2) We're "appending" by rotating in the tail
    		 */
    		ocfs2_figure_appending_type(insert, el, insert_rec);
    	}
    
    out:
    	ocfs2_free_path(path);
    
    	if (ret == 0)
    		*last_eb_bh = bh;
    	else
    		brelse(bh);
    	return ret;
    
    /*
     * Insert an extent into an inode btree.
     *
     * The caller needs to update fe->i_clusters
     */
    
    int ocfs2_insert_extent(struct ocfs2_super *osb,
    
    			struct inode *inode,
    			struct buffer_head *fe_bh,
    
    			u32 cpos,
    
    			u64 start_blk,
    			u32 new_clusters,
    			struct ocfs2_alloc_context *meta_ac)
    {
    
    	int status, shift;
    
    	struct buffer_head *last_eb_bh = NULL;
    	struct buffer_head *bh = NULL;
    
    	struct ocfs2_insert_type insert = {0, };
    	struct ocfs2_extent_rec rec;
    
    	mlog(0, "add %u clusters at position %u to inode %llu\n",
    	     new_clusters, cpos, (unsigned long long)OCFS2_I(inode)->ip_blkno);
    
    	mlog_bug_on_msg(!ocfs2_sparse_alloc(osb) &&
    			(OCFS2_I(inode)->ip_clusters != cpos),
    			"Device %s, asking for sparse allocation: inode %llu, "
    			"cpos %u, clusters %u\n",
    			osb->dev_str,
    			(unsigned long long)OCFS2_I(inode)->ip_blkno, cpos,
    			OCFS2_I(inode)->ip_clusters);
    
    	rec.e_cpos = cpu_to_le32(cpos);
    	rec.e_blkno = cpu_to_le64(start_blk);
    	rec.e_clusters = cpu_to_le32(new_clusters);
    
    	status = ocfs2_figure_insert_type(inode, fe_bh, &last_eb_bh, &rec,
    					  &insert);
    	if (status < 0) {
    		mlog_errno(status);
    		goto bail;
    
    	mlog(0, "Insert.appending: %u, Insert.Contig: %u, "
    	     "Insert.contig_index: %d, Insert.free_records: %d, "
    	     "Insert.tree_depth: %d\n",
    	     insert.ins_appending, insert.ins_contig, insert.ins_contig_index,
    	     insert.ins_free_records, insert.ins_tree_depth);
    
    	/*
    	 * Avoid growing the tree unless we're out of records and the
    	 * insert type requres one.
    	 */
    	if (insert.ins_contig != CONTIG_NONE || insert.ins_free_records)
    		goto out_add;
    
    
    	shift = ocfs2_find_branch_target(osb, inode, fe_bh, &bh);
    	if (shift < 0) {
    		status = shift;
    		mlog_errno(status);
    		goto bail;
    	}
    
    	/* We traveled all the way to the bottom of the allocation tree
    	 * and didn't find room for any more extents - we need to add
    	 * another tree level */
    	if (shift) {
    		BUG_ON(bh);
    
    		mlog(0, "need to shift tree depth "
    		     "(current = %d)\n", insert.ins_tree_depth);
    
    
    		/* ocfs2_shift_tree_depth will return us a buffer with
    		 * the new extent block (so we can pass that to
    		 * ocfs2_add_branch). */
    		status = ocfs2_shift_tree_depth(osb, handle, inode, fe_bh,
    						meta_ac, &bh);
    		if (status < 0) {
    			mlog_errno(status);
    			goto bail;
    		}
    
    		insert.ins_tree_depth++;
    
    		/* Special case: we have room now if we shifted from
    		 * tree_depth 0 */
    
    		if (insert.ins_tree_depth == 1)
    
    			goto out_add;
    	}
    
    	/* call ocfs2_add_branch to add the final part of the tree with
    	 * the new data. */
    
    	mlog(0, "add branch. bh = %p\n", bh);
    
    	status = ocfs2_add_branch(osb, handle, inode, fe_bh, bh, last_eb_bh,
    				  meta_ac);
    	if (status < 0) {
    		mlog_errno(status);
    		goto bail;
    	}
    
    out_add:
    
    	/* Finally, we can add clusters. This might rotate the tree for us. */
    	status = ocfs2_do_insert_extent(inode, handle, fe_bh, &rec, &insert);
    
    	if (status < 0)
    		mlog_errno(status);
    
    bail:
    	if (bh)
    		brelse(bh);
    
    	if (last_eb_bh)
    		brelse(last_eb_bh);
    
    	mlog_exit(status);
    	return status;
    }
    
    static inline int ocfs2_truncate_log_needs_flush(struct ocfs2_super *osb)
    {
    	struct buffer_head *tl_bh = osb->osb_tl_bh;
    	struct ocfs2_dinode *di;
    	struct ocfs2_truncate_log *tl;
    
    	di = (struct ocfs2_dinode *) tl_bh->b_data;
    	tl = &di->id2.i_dealloc;
    
    	mlog_bug_on_msg(le16_to_cpu(tl->tl_used) > le16_to_cpu(tl->tl_count),
    			"slot %d, invalid truncate log parameters: used = "
    			"%u, count = %u\n", osb->slot_num,
    			le16_to_cpu(tl->tl_used), le16_to_cpu(tl->tl_count));
    	return le16_to_cpu(tl->tl_used) == le16_to_cpu(tl->tl_count);
    }
    
    static int ocfs2_truncate_log_can_coalesce(struct ocfs2_truncate_log *tl,
    					   unsigned int new_start)
    {
    	unsigned int tail_index;
    	unsigned int current_tail;
    
    	/* No records, nothing to coalesce */
    	if (!le16_to_cpu(tl->tl_used))
    		return 0;
    
    	tail_index = le16_to_cpu(tl->tl_used) - 1;
    	current_tail = le32_to_cpu(tl->tl_recs[tail_index].t_start);
    	current_tail += le32_to_cpu(tl->tl_recs[tail_index].t_clusters);
    
    	return current_tail == new_start;
    }
    
    static int ocfs2_truncate_log_append(struct ocfs2_super *osb,
    
    				     u64 start_blk,
    				     unsigned int num_clusters)
    {
    	int status, index;
    	unsigned int start_cluster, tl_count;
    	struct inode *tl_inode = osb->osb_tl_inode;
    	struct buffer_head *tl_bh = osb->osb_tl_bh;
    	struct ocfs2_dinode *di;
    	struct ocfs2_truncate_log *tl;
    
    
    	mlog_entry("start_blk = %llu, num_clusters = %u\n",
    		   (unsigned long long)start_blk, num_clusters);
    
    	BUG_ON(mutex_trylock(&tl_inode->i_mutex));
    
    
    	start_cluster = ocfs2_blocks_to_clusters(osb->sb, start_blk);
    
    	di = (struct ocfs2_dinode *) tl_bh->b_data;
    	tl = &di->id2.i_dealloc;
    	if (!OCFS2_IS_VALID_DINODE(di)) {
    		OCFS2_RO_ON_INVALID_DINODE(osb->sb, di);
    		status = -EIO;
    		goto bail;
    	}
    
    	tl_count = le16_to_cpu(tl->tl_count);
    	mlog_bug_on_msg(tl_count > ocfs2_truncate_recs_per_inode(osb->sb) ||
    			tl_count == 0,
    
    			"Truncate record count on #%llu invalid "
    			"wanted %u, actual %u\n",
    			(unsigned long long)OCFS2_I(tl_inode)->ip_blkno,
    
    			ocfs2_truncate_recs_per_inode(osb->sb),
    			le16_to_cpu(tl->tl_count));
    
    	/* Caller should have known to flush before calling us. */
    	index = le16_to_cpu(tl->tl_used);
    	if (index >= tl_count) {
    		status = -ENOSPC;
    		mlog_errno(status);
    		goto bail;
    	}
    
    	status = ocfs2_journal_access(handle, tl_inode, tl_bh,
    				      OCFS2_JOURNAL_ACCESS_WRITE);
    	if (status < 0) {
    		mlog_errno(status);
    		goto bail;
    	}
    
    	mlog(0, "Log truncate of %u clusters starting at cluster %u to "
    
    	     "%llu (index = %d)\n", num_clusters, start_cluster,
    	     (unsigned long long)OCFS2_I(tl_inode)->ip_blkno, index);
    
    
    	if (ocfs2_truncate_log_can_coalesce(tl, start_cluster)) {
    		/*
    		 * Move index back to the record we are coalescing with.
    		 * ocfs2_truncate_log_can_coalesce() guarantees nonzero
    		 */
    		index--;
    
    		num_clusters += le32_to_cpu(tl->tl_recs[index].t_clusters);
    		mlog(0, "Coalesce with index %u (start = %u, clusters = %u)\n",
    		     index, le32_to_cpu(tl->tl_recs[index].t_start),
    		     num_clusters);
    	} else {
    		tl->tl_recs[index].t_start = cpu_to_le32(start_cluster);
    		tl->tl_used = cpu_to_le16(index + 1);
    	}
    	tl->tl_recs[index].t_clusters = cpu_to_le32(num_clusters);
    
    	status = ocfs2_journal_dirty(handle, tl_bh);
    	if (status < 0) {
    		mlog_errno(status);
    		goto bail;
    	}
    
    bail:
    	mlog_exit(status);
    	return status;
    }
    
    static int ocfs2_replay_truncate_records(struct ocfs2_super *osb,
    
    					 struct inode *data_alloc_inode,
    					 struct buffer_head *data_alloc_bh)
    {
    	int status = 0;
    	int i;
    	unsigned int num_clusters;
    	u64 start_blk;
    	struct ocfs2_truncate_rec rec;
    	struct ocfs2_dinode *di;
    	struct ocfs2_truncate_log *tl;
    	struct inode *tl_inode = osb->osb_tl_inode;
    	struct buffer_head *tl_bh = osb->osb_tl_bh;
    
    	mlog_entry_void();
    
    	di = (struct ocfs2_dinode *) tl_bh->b_data;
    	tl = &di->id2.i_dealloc;
    	i = le16_to_cpu(tl->tl_used) - 1;
    	while (i >= 0) {
    		/* Caller has given us at least enough credits to
    		 * update the truncate log dinode */
    		status = ocfs2_journal_access(handle, tl_inode, tl_bh,
    					      OCFS2_JOURNAL_ACCESS_WRITE);
    		if (status < 0) {
    			mlog_errno(status);
    			goto bail;
    		}
    
    		tl->tl_used = cpu_to_le16(i);
    
    		status = ocfs2_journal_dirty(handle, tl_bh);
    		if (status < 0) {
    			mlog_errno(status);
    			goto bail;
    		}
    
    		/* TODO: Perhaps we can calculate the bulk of the
    		 * credits up front rather than extending like
    		 * this. */
    		status = ocfs2_extend_trans(handle,
    					    OCFS2_TRUNCATE_LOG_FLUSH_ONE_REC);
    		if (status < 0) {
    			mlog_errno(status);
    			goto bail;
    		}
    
    		rec = tl->tl_recs[i];
    		start_blk = ocfs2_clusters_to_blocks(data_alloc_inode->i_sb,
    						    le32_to_cpu(rec.t_start));
    		num_clusters = le32_to_cpu(rec.t_clusters);
    
    		/* if start_blk is not set, we ignore the record as
    		 * invalid. */
    		if (start_blk) {
    			mlog(0, "free record %d, start = %u, clusters = %u\n",
    			     i, le32_to_cpu(rec.t_start), num_clusters);
    
    			status = ocfs2_free_clusters(handle, data_alloc_inode,
    						     data_alloc_bh, start_blk,
    						     num_clusters);
    			if (status < 0) {
    				mlog_errno(status);
    				goto bail;
    			}
    		}
    		i--;
    	}
    
    bail:
    	mlog_exit(status);
    	return status;
    }
    
    
    /* Expects you to already be holding tl_inode->i_mutex */
    
    static int __ocfs2_flush_truncate_log(struct ocfs2_super *osb)
    {
    	int status;
    	unsigned int num_to_flush;
    
    	struct inode *tl_inode = osb->osb_tl_inode;
    	struct inode *data_alloc_inode = NULL;
    	struct buffer_head *tl_bh = osb->osb_tl_bh;
    	struct buffer_head *data_alloc_bh = NULL;
    	struct ocfs2_dinode *di;
    	struct ocfs2_truncate_log *tl;
    
    	mlog_entry_void();
    
    
    	BUG_ON(mutex_trylock(&tl_inode->i_mutex));
    
    
    	di = (struct ocfs2_dinode *) tl_bh->b_data;
    	tl = &di->id2.i_dealloc;
    	if (!OCFS2_IS_VALID_DINODE(di)) {
    		OCFS2_RO_ON_INVALID_DINODE(osb->sb, di);
    		status = -EIO;
    
    	}
    
    	num_to_flush = le16_to_cpu(tl->tl_used);
    
    	mlog(0, "Flush %u records from truncate log #%llu\n",
    	     num_to_flush, (unsigned long long)OCFS2_I(tl_inode)->ip_blkno);
    
    	if (!num_to_flush) {
    		status = 0;
    
    	}
    
    	data_alloc_inode = ocfs2_get_system_file_inode(osb,
    						       GLOBAL_BITMAP_SYSTEM_INODE,
    						       OCFS2_INVALID_SLOT);
    	if (!data_alloc_inode) {
    		status = -EINVAL;
    		mlog(ML_ERROR, "Could not get bitmap inode!\n");
    
    	status = ocfs2_meta_lock(data_alloc_inode, &data_alloc_bh, 1);
    
    	if (status < 0) {
    		mlog_errno(status);
    
    	handle = ocfs2_start_trans(osb, OCFS2_TRUNCATE_LOG_UPDATE);
    
    	if (IS_ERR(handle)) {
    		status = PTR_ERR(handle);
    		mlog_errno(status);
    
    	}
    
    	status = ocfs2_replay_truncate_records(osb, handle, data_alloc_inode,
    					       data_alloc_bh);
    
    	ocfs2_commit_trans(osb, handle);
    
    out_unlock:
    	brelse(data_alloc_bh);
    	ocfs2_meta_unlock(data_alloc_inode, 1);
    
    out_mutex:
    	mutex_unlock(&data_alloc_inode->i_mutex);
    	iput(data_alloc_inode);
    
    	mlog_exit(status);
    	return status;
    }
    
    int ocfs2_flush_truncate_log(struct ocfs2_super *osb)
    {
    	int status;
    	struct inode *tl_inode = osb->osb_tl_inode;
    
    
    	mutex_lock(&tl_inode->i_mutex);
    
    	status = __ocfs2_flush_truncate_log(osb);
    
    	mutex_unlock(&tl_inode->i_mutex);
    
    static void ocfs2_truncate_log_worker(struct work_struct *work)
    
    	struct ocfs2_super *osb =
    		container_of(work, struct ocfs2_super,
    			     osb_truncate_log_wq.work);
    
    
    	mlog_entry_void();
    
    	status = ocfs2_flush_truncate_log(osb);
    	if (status < 0)
    		mlog_errno(status);
    
    	mlog_exit(status);
    }
    
    #define OCFS2_TRUNCATE_LOG_FLUSH_INTERVAL (2 * HZ)
    void ocfs2_schedule_truncate_log_flush(struct ocfs2_super *osb,
    				       int cancel)
    {
    	if (osb->osb_tl_inode) {
    		/* We want to push off log flushes while truncates are
    		 * still running. */
    		if (cancel)
    			cancel_delayed_work(&osb->osb_truncate_log_wq);
    
    		queue_delayed_work(ocfs2_wq, &osb->osb_truncate_log_wq,
    				   OCFS2_TRUNCATE_LOG_FLUSH_INTERVAL);
    	}
    }
    
    static int ocfs2_get_truncate_log_info(struct ocfs2_super *osb,
    				       int slot_num,
    				       struct inode **tl_inode,
    				       struct buffer_head **tl_bh)
    {
    	int status;
    	struct inode *inode = NULL;
    	struct buffer_head *bh = NULL;
    
    	inode = ocfs2_get_system_file_inode(osb,
    					   TRUNCATE_LOG_SYSTEM_INODE,
    					   slot_num);
    	if (!inode) {
    		status = -EINVAL;
    		mlog(ML_ERROR, "Could not get load truncate log inode!\n");
    		goto bail;
    	}
    
    	status = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno, &bh,
    				  OCFS2_BH_CACHED, inode);
    	if (status < 0) {
    		iput(inode);
    		mlog_errno(status);
    		goto bail;
    	}
    
    	*tl_inode = inode;
    	*tl_bh    = bh;
    bail:
    	mlog_exit(status);
    	return status;
    }
    
    /* called during the 1st stage of node recovery. we stamp a clean
     * truncate log and pass back a copy for processing later. if the
     * truncate log does not require processing, a *tl_copy is set to
     * NULL. */
    int ocfs2_begin_truncate_log_recovery(struct ocfs2_super *osb,
    				      int slot_num,
    				      struct ocfs2_dinode **tl_copy)
    {
    	int status;
    	struct inode *tl_inode = NULL;
    	struct buffer_head *tl_bh = NULL;
    	struct ocfs2_dinode *di;
    	struct ocfs2_truncate_log *tl;
    
    	*tl_copy = NULL;
    
    	mlog(0, "recover truncate log from slot %d\n", slot_num);
    
    	status = ocfs2_get_truncate_log_info(osb, slot_num, &tl_inode, &tl_bh);
    	if (status < 0) {
    		mlog_errno(status);
    		goto bail;
    	}
    
    	di = (struct ocfs2_dinode *) tl_bh->b_data;
    	tl = &di->id2.i_dealloc;
    	if (!OCFS2_IS_VALID_DINODE(di)) {
    		OCFS2_RO_ON_INVALID_DINODE(tl_inode->i_sb, di);
    		status = -EIO;
    		goto bail;
    	}
    
    	if (le16_to_cpu(tl->tl_used)) {
    		mlog(0, "We'll have %u logs to recover\n",
    		     le16_to_cpu(tl->tl_used));
    
    		*tl_copy = kmalloc(tl_bh->b_size, GFP_KERNEL);
    		if (!(*tl_copy)) {
    			status = -ENOMEM;
    			mlog_errno(status);
    			goto bail;
    		}
    
    		/* Assuming the write-out below goes well, this copy
    		 * will be passed back to recovery for processing. */
    		memcpy(*tl_copy, tl_bh->b_data, tl_bh->b_size);
    
    		/* All we need to do to clear the truncate log is set
    		 * tl_used. */
    		tl->tl_used = 0;
    
    		status = ocfs2_write_block(osb, tl_bh, tl_inode);
    		if (status < 0) {
    			mlog_errno(status);
    			goto bail;
    		}
    	}
    
    bail:
    	if (tl_inode)
    		iput(tl_inode);
    	if (tl_bh)
    		brelse(tl_bh);
    
    	if (status < 0 && (*tl_copy)) {
    		kfree(*tl_copy);
    		*tl_copy = NULL;
    	}
    
    	mlog_exit(status);
    	return status;
    }
    
    int ocfs2_complete_truncate_log_recovery(struct ocfs2_super *osb,
    					 struct ocfs2_dinode *tl_copy)
    {
    	int status = 0;
    	int i;
    	unsigned int clusters, num_recs, start_cluster;
    	u64 start_blk;
    
    	struct inode *tl_inode = osb->osb_tl_inode;
    	struct ocfs2_truncate_log *tl;
    
    	mlog_entry_void();
    
    	if (OCFS2_I(tl_inode)->ip_blkno == le64_to_cpu(tl_copy->i_blkno)) {
    		mlog(ML_ERROR, "Asked to recover my own truncate log!\n");
    		return -EINVAL;
    	}
    
    	tl = &tl_copy->id2.i_dealloc;
    	num_recs = le16_to_cpu(tl->tl_used);
    
    	mlog(0, "cleanup %u records from %llu\n", num_recs,
    	     (unsigned long long)tl_copy->i_blkno);
    
    	mutex_lock(&tl_inode->i_mutex);
    
    	for(i = 0; i < num_recs; i++) {
    		if (ocfs2_truncate_log_needs_flush(osb)) {
    			status = __ocfs2_flush_truncate_log(osb);
    			if (status < 0) {
    				mlog_errno(status);
    				goto bail_up;
    			}
    		}
    
    
    		handle = ocfs2_start_trans(osb, OCFS2_TRUNCATE_LOG_UPDATE);
    
    		if (IS_ERR(handle)) {
    			status = PTR_ERR(handle);
    			mlog_errno(status);
    			goto bail_up;
    		}
    
    		clusters = le32_to_cpu(tl->tl_recs[i].t_clusters);
    		start_cluster = le32_to_cpu(tl->tl_recs[i].t_start);
    		start_blk = ocfs2_clusters_to_blocks(osb->sb, start_cluster);
    
    		status = ocfs2_truncate_log_append(osb, handle,
    						   start_blk, clusters);
    
    		ocfs2_commit_trans(osb, handle);
    
    		if (status < 0) {
    			mlog_errno(status);
    			goto bail_up;
    		}
    	}
    
    bail_up:
    
    	mutex_unlock(&tl_inode->i_mutex);
    
    
    	mlog_exit(status);
    	return status;
    }
    
    void ocfs2_truncate_log_shutdown(struct ocfs2_super *osb)
    {
    	int status;
    	struct inode *tl_inode = osb->osb_tl_inode;
    
    	mlog_entry_void();
    
    	if (tl_inode) {
    		cancel_delayed_work(&osb->osb_truncate_log_wq);
    		flush_workqueue(ocfs2_wq);
    
    		status = ocfs2_flush_truncate_log(osb);
    		if (status < 0)
    			mlog_errno(status);
    
    		brelse(osb->osb_tl_bh);
    		iput(osb->osb_tl_inode);
    	}
    
    	mlog_exit_void();
    }
    
    int ocfs2_truncate_log_init(struct ocfs2_super *osb)
    {
    	int status;
    	struct inode *tl_inode = NULL;
    	struct buffer_head *tl_bh = NULL;
    
    	mlog_entry_void();
    
    	status = ocfs2_get_truncate_log_info(osb,
    					     osb->slot_num,
    					     &tl_inode,
    					     &tl_bh);
    	if (status < 0)
    		mlog_errno(status);
    
    	/* ocfs2_truncate_log_shutdown keys on the existence of
    	 * osb->osb_tl_inode so we don't set any of the osb variables
    	 * until we're sure all is well. */
    
    	INIT_DELAYED_WORK(&osb->osb_truncate_log_wq,
    			  ocfs2_truncate_log_worker);
    
    	osb->osb_tl_bh    = tl_bh;
    	osb->osb_tl_inode = tl_inode;
    
    	mlog_exit(status);
    	return status;
    }
    
    /* This function will figure out whether the currently last extent
     * block will be deleted, and if it will, what the new last extent
     * block will be so we can update his h_next_leaf_blk field, as well
     * as the dinodes i_last_eb_blk */
    
    static int ocfs2_find_new_last_ext_blk(struct inode *inode,
    
    				       unsigned int clusters_to_del,
    
    				       struct ocfs2_path *path,
    
    				       struct buffer_head **new_last_eb)
    {
    
    	int next_free, ret = 0;
    
    	u32 cpos;
    
    	struct ocfs2_extent_rec *rec;
    
    	struct ocfs2_extent_block *eb;
    	struct ocfs2_extent_list *el;
    	struct buffer_head *bh = NULL;
    
    	*new_last_eb = NULL;
    
    	/* we have no tree, so of course, no last_eb. */
    
    	if (!path->p_tree_depth)
    		goto out;
    
    
    	/* trunc to zero special case - this makes tree_depth = 0
    	 * regardless of what it is.  */
    
    	if (OCFS2_I(inode)->ip_clusters == clusters_to_del)
    
    		goto out;
    
    	el = path_leaf_el(path);
    
    	BUG_ON(!el->l_next_free_rec);
    
    
    	/*
    	 * Make sure that this extent list will actually be empty
    	 * after we clear away the data. We can shortcut out if
    	 * there's more than one non-empty extent in the
    	 * list. Otherwise, a check of the remaining extent is
    	 * necessary.
    	 */
    	next_free = le16_to_cpu(el->l_next_free_rec);
    	rec = NULL;
    
    	if (ocfs2_is_empty_extent(&el->l_recs[0])) {
    
    		if (next_free > 2)
    
    			goto out;
    
    
    		/* We may have a valid extent in index 1, check it. */
    		if (next_free == 2)
    			rec = &el->l_recs[1];
    
    		/*
    		 * Fall through - no more nonempty extents, so we want
    		 * to delete this leaf.
    		 */
    	} else {
    		if (next_free > 1)
    			goto out;
    
    		rec = &el->l_recs[0];
    	}
    
    	if (rec) {
    		/*
    		 * Check it we'll only be trimming off the end of this
    		 * cluster.
    		 */
    		if (le16_to_cpu(rec->e_clusters) > clusters_to_del)
    			goto out;
    	}
    
    	ret = ocfs2_find_cpos_for_left_leaf(inode->i_sb, path, &cpos);
    	if (ret) {
    		mlog_errno(ret);
    		goto out;
    	}
    
    	ret = ocfs2_find_leaf(inode, path_root_el(path), cpos, &bh);
    	if (ret) {
    		mlog_errno(ret);
    		goto out;
    	}
    
    	eb = (struct ocfs2_extent_block *) bh->b_data;