Skip to content

Commit

Permalink
ocfs2: allow return of new inode block location before allocation of …
Browse files Browse the repository at this point in the history
…the inode

This allows code which needs to know the eventual block number of an inode
but can't allocate it yet due to transaction or lock ordering. For example,
ocfs2_create_inode_in_orphan() currently gives a junk blkno for preparation
of the orphan dir because it can't yet know where the actual inode is placed
- that code is actually in ocfs2_mknod_locked. This is a problem when the
orphan dirs are indexed as the junk inode number will create an index entry
which goes unused (and fails the later removal from the orphan dir).  Now
with these interfaces, ocfs2_create_inode_in_orphan() can run the block
group search (and get back the inode block number) *before* any actual
allocation occurs.

Signed-off-by: Mark Fasheh <[email protected]>
Signed-off-by: Tao Ma <[email protected]>
  • Loading branch information
Mark Fasheh authored and Tao Ma committed Sep 8, 2010
1 parent d513498 commit e49e276
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 0 deletions.
159 changes: 159 additions & 0 deletions fs/ocfs2/suballoc.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ struct ocfs2_suballoc_result {
u64 sr_bg_blkno; /* The bg we allocated from. Set
to 0 when a block group is
contiguous. */
u64 sr_bg_stable_blkno; /*
* Doesn't change, always
* set to target block
* group descriptor
* block.
*/
u64 sr_blkno; /* The first allocated block */
unsigned int sr_bit_offset; /* The bit in the bg */
unsigned int sr_bits; /* How many bits we claimed */
Expand Down Expand Up @@ -149,6 +155,10 @@ void ocfs2_free_ac_resource(struct ocfs2_alloc_context *ac)
brelse(ac->ac_bh);
ac->ac_bh = NULL;
ac->ac_resv = NULL;
if (ac->ac_find_loc_priv) {
kfree(ac->ac_find_loc_priv);
ac->ac_find_loc_priv = NULL;
}
}

void ocfs2_free_alloc_context(struct ocfs2_alloc_context *ac)
Expand Down Expand Up @@ -1689,6 +1699,15 @@ static int ocfs2_search_one_group(struct ocfs2_alloc_context *ac,
if (!ret)
ocfs2_bg_discontig_fix_result(ac, gd, res);

/*
* sr_bg_blkno might have been changed by
* ocfs2_bg_discontig_fix_result
*/
res->sr_bg_stable_blkno = group_bh->b_blocknr;

if (ac->ac_find_loc_only)
goto out_loc_only;

ret = ocfs2_alloc_dinode_update_counts(alloc_inode, handle, ac->ac_bh,
res->sr_bits,
le16_to_cpu(gd->bg_chain));
Expand All @@ -1702,6 +1721,7 @@ static int ocfs2_search_one_group(struct ocfs2_alloc_context *ac,
if (ret < 0)
mlog_errno(ret);

out_loc_only:
*bits_left = le16_to_cpu(gd->bg_free_bits_count);

out:
Expand Down Expand Up @@ -1780,6 +1800,11 @@ static int ocfs2_search_chain(struct ocfs2_alloc_context *ac,
if (!status)
ocfs2_bg_discontig_fix_result(ac, bg, res);

/*
* sr_bg_blkno might have been changed by
* ocfs2_bg_discontig_fix_result
*/
res->sr_bg_stable_blkno = group_bh->b_blocknr;

/*
* Keep track of previous block descriptor read. When
Expand All @@ -1806,6 +1831,9 @@ static int ocfs2_search_chain(struct ocfs2_alloc_context *ac,
}
}

if (ac->ac_find_loc_only)
goto out_loc_only;

status = ocfs2_alloc_dinode_update_counts(alloc_inode, handle,
ac->ac_bh, res->sr_bits,
chain);
Expand All @@ -1828,6 +1856,7 @@ static int ocfs2_search_chain(struct ocfs2_alloc_context *ac,
mlog(0, "Allocated %u bits from suballocator %llu\n", res->sr_bits,
(unsigned long long)le64_to_cpu(fe->i_blkno));

out_loc_only:
*bits_left = le16_to_cpu(bg->bg_free_bits_count);
bail:
brelse(group_bh);
Expand Down Expand Up @@ -2023,6 +2052,136 @@ static inline void ocfs2_save_inode_ac_group(struct inode *dir,
OCFS2_I(dir)->ip_last_used_slot = ac->ac_alloc_slot;
}

int ocfs2_find_new_inode_loc(struct inode *dir,
struct buffer_head *parent_fe_bh,
struct ocfs2_alloc_context *ac,
u64 *fe_blkno)
{
int ret;
handle_t *handle = NULL;
struct ocfs2_suballoc_result *res;

BUG_ON(!ac);
BUG_ON(ac->ac_bits_given != 0);
BUG_ON(ac->ac_bits_wanted != 1);
BUG_ON(ac->ac_which != OCFS2_AC_USE_INODE);

res = kzalloc(sizeof(*res), GFP_NOFS);
if (res == NULL) {
ret = -ENOMEM;
mlog_errno(ret);
goto out;
}

ocfs2_init_inode_ac_group(dir, parent_fe_bh, ac);

/*
* The handle started here is for chain relink. Alternatively,
* we could just disable relink for these calls.
*/
handle = ocfs2_start_trans(OCFS2_SB(dir->i_sb), OCFS2_SUBALLOC_ALLOC);
if (IS_ERR(handle)) {
ret = PTR_ERR(handle);
handle = NULL;
mlog_errno(ret);
goto out;
}

/*
* This will instruct ocfs2_claim_suballoc_bits and
* ocfs2_search_one_group to search but save actual allocation
* for later.
*/
ac->ac_find_loc_only = 1;

ret = ocfs2_claim_suballoc_bits(ac, handle, 1, 1, res);
if (ret < 0) {
mlog_errno(ret);
goto out;
}

ac->ac_find_loc_priv = res;
*fe_blkno = res->sr_blkno;

out:
if (handle)
ocfs2_commit_trans(OCFS2_SB(dir->i_sb), handle);

if (ret)
kfree(res);

return ret;
}

int ocfs2_claim_new_inode_at_loc(handle_t *handle,
struct inode *dir,
struct ocfs2_alloc_context *ac,
u64 *suballoc_loc,
u16 *suballoc_bit,
u64 di_blkno)
{
int ret;
u16 chain;
struct ocfs2_suballoc_result *res = ac->ac_find_loc_priv;
struct buffer_head *bg_bh = NULL;
struct ocfs2_group_desc *bg;
struct ocfs2_dinode *di = (struct ocfs2_dinode *) ac->ac_bh->b_data;

/*
* Since di_blkno is being passed back in, we check for any
* inconsistencies which may have happened between
* calls. These are code bugs as di_blkno is not expected to
* change once returned from ocfs2_find_new_inode_loc()
*/
BUG_ON(res->sr_blkno != di_blkno);

ret = ocfs2_read_group_descriptor(ac->ac_inode, di,
res->sr_bg_stable_blkno, &bg_bh);
if (ret) {
mlog_errno(ret);
goto out;
}

bg = (struct ocfs2_group_desc *) bg_bh->b_data;
chain = le16_to_cpu(bg->bg_chain);

ret = ocfs2_alloc_dinode_update_counts(ac->ac_inode, handle,
ac->ac_bh, res->sr_bits,
chain);
if (ret) {
mlog_errno(ret);
goto out;
}

ret = ocfs2_block_group_set_bits(handle,
ac->ac_inode,
bg,
bg_bh,
res->sr_bit_offset,
res->sr_bits);
if (ret < 0) {
mlog_errno(ret);
goto out;
}

mlog(0, "Allocated %u bits from suballocator %llu\n", res->sr_bits,
(unsigned long long)di_blkno);

atomic_inc(&OCFS2_SB(ac->ac_inode->i_sb)->alloc_stats.bg_allocs);

BUG_ON(res->sr_bits != 1);

*suballoc_loc = res->sr_bg_blkno;
*suballoc_bit = res->sr_bit_offset;
ac->ac_bits_given++;
ocfs2_save_inode_ac_group(dir, ac);

out:
brelse(bg_bh);

return ret;
}

int ocfs2_claim_new_inode(handle_t *handle,
struct inode *dir,
struct buffer_head *parent_fe_bh,
Expand Down
21 changes: 21 additions & 0 deletions fs/ocfs2/suballoc.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ struct ocfs2_alloc_context {
u64 ac_max_block; /* Highest block number to allocate. 0 is
is the same as ~0 - unlimited */

int ac_find_loc_only; /* hack for reflink operation ordering */
struct ocfs2_suballoc_result *ac_find_loc_priv; /* */

struct ocfs2_alloc_reservation *ac_resv;
};

Expand Down Expand Up @@ -197,4 +200,22 @@ int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_extent_tree *et,
struct ocfs2_alloc_context **meta_ac);

int ocfs2_test_inode_bit(struct ocfs2_super *osb, u64 blkno, int *res);



/*
* The following two interfaces are for ocfs2_create_inode_in_orphan().
*/
int ocfs2_find_new_inode_loc(struct inode *dir,
struct buffer_head *parent_fe_bh,
struct ocfs2_alloc_context *ac,
u64 *fe_blkno);

int ocfs2_claim_new_inode_at_loc(handle_t *handle,
struct inode *dir,
struct ocfs2_alloc_context *ac,
u64 *suballoc_loc,
u16 *suballoc_bit,
u64 di_blkno);

#endif /* _CHAINALLOC_H_ */

0 comments on commit e49e276

Please sign in to comment.