ocfs2: Add extended attributes for ocfs2. V3
cover ocfs2: Add extended attributes for ocfs2. V3
Hi all,
Extended attributes are used for storing POSIX ACLs, SELinux labels,
and user accessible metadata. They are essential for deploying file systems exported for workgroup use via samba. The following patches implement extended attributes on the OCFS2 file system. This patch set implement extended attributes support for ocfs2.
Modification from V2 to V3:
1. Add ea support for symlink file, add xattr_sem, add ip_alloc_sem protect xattr and file data.
2. Remove all kmalloc+kmemcpy issue in set/list/get large numbers of EAs , so the efficiency should be improved a lot.
3. Add a thin layer for ocfs2_insert_extent.
4. Limit xattr entry to be only in 1st block of the bucket and limit xattr name/value pair within the same block.
5. Limit the xattr tree leaf size to be 64K.
Wish you enjoy it.
Regards,
Tiger and Tao
PATCH 1/15 Modify ocfs2_num_free_extents for future xattr usage.
ocfs2_num_free_extents is used to find the free extent record number. The old parameter is "ocfs2_dinode", and it isn't suitable for xattr_value. So change it to "buffer_head *". ocfs2_lock_allocator is also modified in this patch because we need "buffer_head *" to call ocfs2_num_free_extents.
--- fs/ocfs2/alloc.c | 3 ++- fs/ocfs2/alloc.h | 2 +- fs/ocfs2/aops.c | 5 +++-- fs/ocfs2/dir.c | 3 ++- fs/ocfs2/file.c | 11 ++++++----- fs/ocfs2/file.h | 2 +- 6 files changed, 15 insertions(+), 11 deletions(-)
fs/ocfs2/alloc.c +2 -1
@@ -368,12 +368,13 @@ struct ocfs2_merge_ctxt {*/ int ocfs2_num_free_extents (struct ocfs2_super *osb,struct inode *inode, -struct ocfs2_dinode *fe) +struct buffer_head *bh) {int retval;struct ocfs2_extent_list *el;struct ocfs2_extent_block *eb;struct buffer_head *eb_bh =NULL ; +struct ocfs2_dinode *fe = (struct ocfs2_dinode *)bh->b_data;mlog_entry_void ();
fs/ocfs2/alloc.h +1 -1
@@ -47,7 +47,7 @@ int ocfs2_remove_extent(struct inode *inode, struct buffer_head *di_bh, struct ocfs2_cached_dealloc_ctxt *dealloc);int ocfs2_num_free_extents (struct ocfs2_super *osb,struct inode *inode, -struct ocfs2_dinode *fe); +struct buffer_head *bh);/* how many new metadata chunks would an allocation need at maximum? */ static inline int ocfs2_extend_meta_needed (struct ocfs2_dinode *fe) {
fs/ocfs2/aops.c +3 -2
@@ -1709,8 +1709,9 @@ int ocfs2_write_begin_nolock(struct address_space *mapping,* ocfs2_lock_allocators(). It greatly over-estimates * the work to be done. */ - ret =ocfs2_lock_allocators (inode, di, clusters_to_alloc, - extents_to_split, &data_ac, &meta_ac); + ret =ocfs2_lock_allocators (inode, wc->w_di_bh, + clusters_to_alloc, extents_to_split, + &data_ac, &meta_ac);if (ret) {mlog_errno (ret); goto out;
fs/ocfs2/dir.c +2 -1
@@ -1474,7 +1474,8 @@ static int ocfs2_extend_dir(struct ocfs2_super *osb,spin_lock (&OCFS2_I (dir)->ip_lock);if (dir_i_size ==ocfs2_clusters_to_bytes (sb,OCFS2_I (dir)->ip_clusters)) {spin_unlock (&OCFS2_I (dir)->ip_lock); - num_free_extents =ocfs2_num_free_extents (osb, dir, fe); + num_free_extents =ocfs2_num_free_extents (osb, dir, + parent_fe_bh);if (num_free_extents <0 ) { status = num_free_extents;mlog_errno (status);
fs/ocfs2/file.c +6 -5
@@ -521,7 +521,7 @@ int ocfs2_do_extend_allocation(struct ocfs2_super *osb,if (mark_unwritten) flags =OCFS2_EXT_UNWRITTEN ; - free_extents =ocfs2_num_free_extents (osb, inode, fe); + free_extents =ocfs2_num_free_extents (osb, inode, fe_bh);if (free_extents <0 ) { status = free_extents;mlog_errno (status);
@@ -609,7 +609,7 @@ leave:* File systems which don't support holes call this from * ocfs2_extend_allocation(). */ -int ocfs2_lock_allocators (struct inode *inode,struct ocfs2_dinode *di, +int ocfs2_lock_allocators (struct inode *inode,struct buffer_head *di_bh,u32 clusters_to_add,u32 extents_to_split,struct ocfs2_alloc_context **data_ac,struct ocfs2_alloc_context **meta_ac)
@@ -617,6 +617,7 @@ int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_dinode *di,int ret =0 , num_free_extents;unsigned int max_recs_needed = clusters_to_add +2 * extents_to_split;struct ocfs2_super *osb =OCFS2_SB (inode->i_sb); +struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; *meta_ac =NULL ;if (data_ac)
@@ -629,7 +630,7 @@ int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_dinode *di, (unsigned long long )OCFS2_I (inode)->ip_blkno, (long long )i_size_read (inode), le32_to_cpu(di->i_clusters), clusters_to_add, extents_to_split); - num_free_extents =ocfs2_num_free_extents (osb, inode, di); + num_free_extents =ocfs2_num_free_extents (osb, inode, di_bh);if (num_free_extents <0 ) { ret = num_free_extents;mlog_errno (ret);
@@ -724,7 +725,7 @@ static int __ocfs2_extend_allocation(struct inode *inode, u32 logical_start, restart_all:BUG_ON (le32_to_cpu (fe->i_clusters) !=OCFS2_I (inode)->ip_clusters); - status =ocfs2_lock_allocators (inode, fe, clusters_to_add,0 , &data_ac, + status =ocfs2_lock_allocators (inode, bh, clusters_to_add,0 , &data_ac, &meta_ac);if (status) {mlog_errno (status);
@@ -1395,7 +1396,7 @@ static int __ocfs2_remove_inode_range(struct inode *inode,struct ocfs2_alloc_context *meta_ac =NULL ;struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; - ret =ocfs2_lock_allocators (inode, di,0 ,1 ,NULL , &meta_ac); + ret =ocfs2_lock_allocators (inode, di_bh,0 ,1 ,NULL , &meta_ac);if (ret) {mlog_errno (ret);return ret;
fs/ocfs2/file.h +1 -1
@@ -55,7 +55,7 @@ int ocfs2_do_extend_allocation(struct ocfs2_super *osb, enum ocfs2_alloc_restarted *reason_ret);int ocfs2_extend_no_holes (struct inode *inode,u64 new_i_size,u64 zero_to); -int ocfs2_lock_allocators (struct inode *inode,struct ocfs2_dinode *di, +int ocfs2_lock_allocators (struct inode *inode,struct buffer_head *fe,u32 clusters_to_add,u32 extents_to_split,struct ocfs2_alloc_context **data_ac,struct ocfs2_alloc_context **meta_ac);
Signature · 1 line
1.5.4.GIT
PATCH 2/15 Use ocfs2_extent_list instead of ocfs2_dinode.
In reply to Tao Maocfs2_extend_meta_needed, ocfs2_calc_extend_credits and ocfs2_reserve_new_metadata are all useful for extent tree operations. But they are all limited by using ocfs2_dinode as the parameter. Change their parameter to ocfs2_extent_list so that xattr extent list can use them.
--- fs/ocfs2/alloc.c | 3 ++- fs/ocfs2/alloc.h | 12 +++++++++--- fs/ocfs2/aops.c | 3 ++- fs/ocfs2/dir.c | 5 +++-- fs/ocfs2/file.c | 9 +++++---- fs/ocfs2/journal.h | 17 +++++++++++------ fs/ocfs2/suballoc.c | 4 ++-- fs/ocfs2/suballoc.h | 7 ++++++- 8 files changed, 40 insertions(+), 20 deletions(-)
fs/ocfs2/alloc.c +2 -1
@@ -4536,7 +4536,8 @@ static int ocfs2_split_tree(struct inode *inode, struct buffer_head *di_bh, }else rightmost_el =path_leaf_el (path); - credits += path->p_tree_depth +ocfs2_extend_meta_needed (di); + credits += path->p_tree_depth + +ocfs2_extend_meta_needed (&di->id2.i_list); ret =ocfs2_extend_trans (handle, credits);if (ret) {mlog_errno (ret);
fs/ocfs2/alloc.h +9 -3
@@ -48,8 +48,14 @@ int ocfs2_remove_extent(struct inode *inode, struct buffer_head *di_bh,int ocfs2_num_free_extents (struct ocfs2_super *osb,struct inode *inode,struct buffer_head *bh); -/* how many new metadata chunks would an allocation need at maximum? */ -static inline int ocfs2_extend_meta_needed (struct ocfs2_dinode *fe) +/* +* how many new metadata chunks would an allocation need at maximum? +* +* Please note that the caller must make sure that root_el is the root +* of extent tree. So for an inode, it should be &fe->id2.i_list. Otherwise +* the result may be wrong. +*/ +static inline int ocfs2_extend_meta_needed (struct ocfs2_extent_list *root_el) {/* * Rather than do all the work of determining how much we need
@@ -59,7 +65,7 @@ static inline int ocfs2_extend_meta_needed(struct ocfs2_dinode *fe)* new tree_depth==0 extent_block, and one block at the new * top-of-the tree. */ -return le16_to_cpu (fe->id2.i_list.l_tree_depth) +2 ; +return le16_to_cpu (root_el->l_tree_depth) +2 ; }void ocfs2_dinode_new_extent_list (struct inode *inode,struct ocfs2_dinode *di);
fs/ocfs2/aops.c +2 -1
@@ -1717,7 +1717,8 @@ int ocfs2_write_begin_nolock(struct address_space *mapping, goto out; } - credits =ocfs2_calc_extend_credits (inode->i_sb, di, + credits =ocfs2_calc_extend_credits (inode->i_sb, + &di->id2.i_list, clusters_to_alloc); }
fs/ocfs2/dir.c +3 -2
@@ -1425,6 +1425,7 @@ static int ocfs2_extend_dir(struct ocfs2_super *osb,int credits, num_free_extents, drop_alloc_sem =0 ;loff_t dir_i_size;struct ocfs2_dinode *fe = (struct ocfs2_dinode *) parent_fe_bh->b_data; +struct ocfs2_extent_list *el = &fe->id2.i_list;struct ocfs2_alloc_context *data_ac =NULL ;struct ocfs2_alloc_context *meta_ac =NULL ;handle_t *handle =NULL ;
@@ -1483,7 +1484,7 @@ static int ocfs2_extend_dir(struct ocfs2_super *osb, }if (!num_free_extents) { - status =ocfs2_reserve_new_metadata (osb, fe, &meta_ac); + status =ocfs2_reserve_new_metadata (osb, el, &meta_ac);if (status <0 ) {if (status != -ENOSPC )mlog_errno (status);
@@ -1498,7 +1499,7 @@ static int ocfs2_extend_dir(struct ocfs2_super *osb, goto bail; } - credits =ocfs2_calc_extend_credits (sb, fe,1 ); + credits =ocfs2_calc_extend_credits (sb, el,1 ); }else {spin_unlock (&OCFS2_I (dir)->ip_lock); credits =OCFS2_SIMPLE_DIR_EXTEND_CREDITS ;
fs/ocfs2/file.c +5 -4
@@ -540,7 +540,7 @@ int ocfs2_do_extend_allocation(struct ocfs2_super *osb, goto leave; }else if ((!free_extents) && (ocfs2_alloc_context_bits_left (meta_ac) - <ocfs2_extend_meta_needed (fe))) { + <ocfs2_extend_meta_needed (&fe->id2.i_list))) {mlog (0 ,"filesystem is really fragmented...\n" ); status = -EAGAIN ; reason =RESTART_META ;
@@ -652,7 +652,7 @@ int ocfs2_lock_allocators(struct inode *inode, struct buffer_head *di_bh,*/ if (!num_free_extents || (ocfs2_sparse_alloc (osb) && num_free_extents < max_recs_needed)) { - ret =ocfs2_reserve_new_metadata (osb, di, meta_ac); + ret =ocfs2_reserve_new_metadata (osb, &di->id2.i_list, meta_ac);if (ret <0 ) {if (ret != -ENOSPC )mlog_errno (ret);
@@ -732,7 +732,8 @@ restart_all: goto leave; } - credits =ocfs2_calc_extend_credits (osb->sb, fe, clusters_to_add); + credits =ocfs2_calc_extend_credits (osb->sb, &fe->id2.i_list, + clusters_to_add); handle =ocfs2_start_trans (osb, credits);if (IS_ERR (handle)) { status =PTR_ERR (handle);
@@ -790,7 +791,7 @@ restarted_transaction:mlog (0 ,"restarting transaction.\n" );/* TODO: This can be more intelligent. */ credits =ocfs2_calc_extend_credits (osb->sb, - fe, + &fe->id2.i_list, clusters_to_add); status =ocfs2_extend_trans (handle, credits);if (status <0 ) {
fs/ocfs2/journal.h +11 -6
@@ -339,11 +339,16 @@ int ocfs2_journal_dirty_data(handle_t *handle,#define OCFS2_RENAME_CREDITS (3 * OCFS2_INODE_UPDATE_CREDITS + 3 \ + OCFS2_UNLINK_CREDITS) +/* +* Please note that the caller must make sure that root_el is the root +* of extent tree. So for an inode, it should be &fe->id2.i_list. Otherwise +* the result may be wrong. +*/ static inline int ocfs2_calc_extend_credits (struct super_block *sb, -struct ocfs2_dinode *fe, +struct ocfs2_extent_list *root_el,u32 bits_wanted) { -int bitmap_blocks, sysfile_bitmap_blocks, dinode_blocks; +int bitmap_blocks, sysfile_bitmap_blocks, extent_blocks;/* bitmap dinode, group desc. + relinked group. */ bitmap_blocks =OCFS2_SUBALLOC_ALLOC ;
@@ -354,16 +359,16 @@ static inline int ocfs2_calc_extend_credits(struct super_block *sb,* however many metadata chunks needed * a remaining suballoc * alloc. */ sysfile_bitmap_blocks =1 + - (OCFS2_SUBALLOC_ALLOC -1 ) *ocfs2_extend_meta_needed (fe); + (OCFS2_SUBALLOC_ALLOC -1 ) *ocfs2_extend_meta_needed (root_el);/* this does not include *new* metadata blocks, which are -* accounted for in sysfile_bitmap_blocks. fe + +* accounted for in sysfile_bitmap_blocks. root_el + * prev. last_eb_blk + blocks along edge of tree. * calc_symlink_credits passes because we just need 1 * credit for the dinode there. */ - dinode_blocks =1 +1 +le16_to_cpu (fe->id2.i_list.l_tree_depth); + extent_blocks =1 +1 +le16_to_cpu (root_el->l_tree_depth); -return bitmap_blocks + sysfile_bitmap_blocks + dinode_blocks; +return bitmap_blocks + sysfile_bitmap_blocks + extent_blocks; }static inline int ocfs2_calc_symlink_credits (struct super_block *sb)
fs/ocfs2/suballoc.c +2 -2
@@ -494,7 +494,7 @@ bail: }int ocfs2_reserve_new_metadata (struct ocfs2_super *osb, -struct ocfs2_dinode *fe, +struct ocfs2_extent_list *root_el,struct ocfs2_alloc_context **ac) {int status;
@@ -507,7 +507,7 @@ int ocfs2_reserve_new_metadata(struct ocfs2_super *osb, goto bail; } - (*ac)->ac_bits_wanted =ocfs2_extend_meta_needed (fe); + (*ac)->ac_bits_wanted =ocfs2_extend_meta_needed (root_el); (*ac)->ac_which =OCFS2_AC_USE_META ; slot = osb->slot_num; (*ac)->ac_group_search = ocfs2_block_group_search;
fs/ocfs2/suballoc.h +6 -1
@@ -59,8 +59,13 @@ static inline int ocfs2_alloc_context_bits_left(struct ocfs2_alloc_context *ac)return ac->ac_bits_wanted - ac->ac_bits_given; } +/* +* Please note that the caller must make sure that root_el is the root +* of extent tree. So for an inode, it should be &fe->id2.i_list. Otherwise +* the result may be wrong. +*/ int ocfs2_reserve_new_metadata (struct ocfs2_super *osb, -struct ocfs2_dinode *fe, +struct ocfs2_extent_list *root_el,struct ocfs2_alloc_context **ac);int ocfs2_reserve_new_inode (struct ocfs2_super *osb,struct ocfs2_alloc_context **ac);
Signature · 1 line
1.5.4.GIT
PATCH 3/15 Abstract ocfs2_extent_tree in b-tree operations.
In reply to Tao MaIn the old extent tree operation, we take the hypothesis that we are using the ocfs2_extent_list in ocfs2_dinode as the tree root. As xattr will also use ocfs2_extent_list to store large value for a xattr entry, we refactor the tree operation so that xattr can use it directly.
The refactoring includes 4 steps:
1. Abstract set/get of last_eb_blk and update_clusters since they may be stored in different location for dinode and xattr.
2. Add a new structure named ocfs2_extent_tree to indicate the extent tree the operation will work on.
3. Remove all the use of fe_bh and di, use root_bh and root_el in extent tree instead. So now all the fe_bh is replaced with et->root_bh, el with root_el accordingly.
4. Make ocfs2_lock_allocators generic. Now it is limited to be only used in file extend allocation. But the whole function is useful when we want to store large EAs.
Note: This patch hasn't touch the code of ocfs2_commit_truncate since it isn't has been modified with the new tree operation.
So all the truncating code deem that the tree root is ocfs2_dinode.
--- fs/ocfs2/alloc.c | 508 +++++++++++++++++++++++++++++++++------------------ fs/ocfs2/alloc.h | 23 ++- fs/ocfs2/aops.c | 11 +- fs/ocfs2/dir.c | 7 +- fs/ocfs2/file.c | 104 ++--------- fs/ocfs2/file.h | 4 - fs/ocfs2/suballoc.c | 82 ++++++++ fs/ocfs2/suballoc.h | 5 + 8 files changed, 456 insertions(+), 288 deletions(-)
fs/ocfs2/alloc.c +327 -181
@@ -49,6 +49,143 @@#include "buffer_head_io.h" +/* +* ocfs2_extent_tree and ocfs2_extent_tree_operations are used to abstract +* the b-tree operations in ocfs2. Now all the b-tree operations are not +* limited to ocfs2_dinode only. Any data which need to allocate clusters +* to store can use b-tree. And it only needs to implement its ocfs2_extent_tree +* and operation. +* +* ocfs2_extent_tree contains info for the root of the b-tree, it must have a +* root ocfs2_extent_list and a root_bh so that they can be used in the b-tree +* functions. +* ocfs2_extent_tree_operations abstract the normal operations we do for +* the root of extent b-tree. +*/ +struct ocfs2_extent_tree ; + +struct ocfs2_extent_tree_operations { +void (*set_last_eb_blk) (struct ocfs2_extent_tree *et,u64 blkno); +u64 (*get_last_eb_blk) (struct ocfs2_extent_tree *et); +void (*update_clusters) (struct inode *inode, +struct ocfs2_extent_tree *et, +u32 new_clusters); +int (*sanity_check) (struct inode *inode,struct ocfs2_extent_tree *et); +}; + +struct ocfs2_extent_tree { +enum ocfs2_extent_tree_type type; +struct ocfs2_extent_tree_operations *eops; +struct buffer_head *root_bh; +struct ocfs2_extent_list *root_el; +}; + +static void ocfs2_dinode_set_last_eb_blk (struct ocfs2_extent_tree *et, +u64 blkno) +{ +struct ocfs2_dinode *di = (struct ocfs2_dinode *)et->root_bh->b_data; + +BUG_ON (et->type !=OCFS2_DINODE_EXTENT ); + di->i_last_eb_blk =cpu_to_le64 (blkno); +} + +static u64 ocfs2_dinode_get_last_eb_blk (struct ocfs2_extent_tree *et) +{ +struct ocfs2_dinode *di = (struct ocfs2_dinode *)et->root_bh->b_data; + +BUG_ON (et->type !=OCFS2_DINODE_EXTENT ); +return le64_to_cpu (di->i_last_eb_blk); +} + +static void ocfs2_dinode_update_clusters (struct inode *inode, +struct ocfs2_extent_tree *et, +u32 clusters) +{ +struct ocfs2_dinode *di = + (struct ocfs2_dinode *)et->root_bh->b_data; + +le32_add_cpu (&di->i_clusters, clusters); +spin_lock (&OCFS2_I (inode)->ip_lock); +OCFS2_I (inode)->ip_clusters =le32_to_cpu (di->i_clusters); +spin_unlock (&OCFS2_I (inode)->ip_lock); +} + +static int ocfs2_dinode_sanity_check (struct inode *inode, +struct ocfs2_extent_tree *et) +{ +int ret =0 ; +struct ocfs2_dinode *di; + +BUG_ON (et->type !=OCFS2_DINODE_EXTENT ); + + di = (struct ocfs2_dinode *)et->root_bh->b_data; +if (!OCFS2_IS_VALID_DINODE (di)) { + ret = -EIO ; +ocfs2_error (inode->i_sb, +"Inode %llu has invalid path root" , + (unsigned long long )OCFS2_I (inode)->ip_blkno); + } + +return ret; +} + +static struct ocfs2_extent_tree_operations ocfs2_dinode_et_ops = { + .set_last_eb_blk = ocfs2_dinode_set_last_eb_blk, + .get_last_eb_blk = ocfs2_dinode_get_last_eb_blk, + .update_clusters = ocfs2_dinode_update_clusters, + .sanity_check = ocfs2_dinode_sanity_check, +}; + +static struct ocfs2_extent_tree * +ocfs2_new_extent_tree (struct buffer_head *bh, +enum ocfs2_extent_tree_type et_type) +{ +struct ocfs2_extent_tree *et; + + et =kzalloc (sizeof (*et),GFP_NOFS ); +if (!et) +return NULL ; + + et->type = et_type; +get_bh (bh); + et->root_bh = bh; + +/* current we only support dinode extent. */ +BUG_ON (et->type !=OCFS2_DINODE_EXTENT ); +if (et_type ==OCFS2_DINODE_EXTENT ) { + et->root_el = &((struct ocfs2_dinode *)bh->b_data)->id2.i_list; + et->eops = &ocfs2_dinode_et_ops; + } + +return et; +} + +static void ocfs2_free_extent_tree (struct ocfs2_extent_tree *et) +{ +if (et) { +brelse (et->root_bh); +kfree (et); + } +} + +static inline void ocfs2_set_last_eb_blk (struct ocfs2_extent_tree *et, +u64 new_last_eb_blk) +{ + et->eops->set_last_eb_blk (et, new_last_eb_blk); +} + +static inline u64 ocfs2_get_last_eb_blk (struct ocfs2_extent_tree *et) +{ +return et->eops->get_last_eb_blk (et); +} + +static inline void ocfs2_update_clusters (struct inode *inode, +struct ocfs2_extent_tree *et, +u32 clusters) +{ + et->eops->update_clusters (inode, et, clusters); +} +static void ocfs2_free_truncate_context (struct ocfs2_truncate_context *tc);static int ocfs2_cache_extent_block_free (struct ocfs2_cached_dealloc_ctxt *ctxt,struct ocfs2_extent_block *eb);
@@ -205,17 +342,6 @@ static struct ocfs2_path *ocfs2_new_path(struct buffer_head *root_bh, }/* -* Allocate and initialize a new path based on a disk inode tree. -*/ -static struct ocfs2_path *ocfs2_new_inode_path (struct buffer_head *di_bh) -{ -struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; -struct ocfs2_extent_list *el = &di->id2.i_list; - -return ocfs2_new_path (di_bh, el); -} - -/* * Convenience function to journal all components in a path. */ static int ocfs2_journal_access_path(struct inode *inode, handle_t *handle,
@@ -368,24 +494,33 @@ struct ocfs2_merge_ctxt {*/ int ocfs2_num_free_extents (struct ocfs2_super *osb,struct inode *inode, -struct buffer_head *bh) +struct buffer_head *root_bh, +enum ocfs2_extent_tree_type type) {int retval; -struct ocfs2_extent_list *el; +struct ocfs2_extent_list *el =NULL ;struct ocfs2_extent_block *eb;struct buffer_head *eb_bh =NULL ; -struct ocfs2_dinode *fe = (struct ocfs2_dinode *)bh->b_data; +u64 last_eb_blk =0 ;mlog_entry_void (); -if (!OCFS2_IS_VALID_DINODE (fe)) { -OCFS2_RO_ON_INVALID_DINODE (inode->i_sb, fe); - retval = -EIO ; - goto bail; +if (type ==OCFS2_DINODE_EXTENT ) { +struct ocfs2_dinode *fe = + (struct ocfs2_dinode *)root_bh->b_data; +if (!OCFS2_IS_VALID_DINODE (fe)) { +OCFS2_RO_ON_INVALID_DINODE (inode->i_sb, fe); + retval = -EIO ; + goto bail; + } + +if (fe->i_last_eb_blk) + last_eb_blk =le64_to_cpu (fe->i_last_eb_blk); + el = &fe->id2.i_list; } -if (fe->i_last_eb_blk) { - retval =ocfs2_read_block (osb,le64_to_cpu (fe->i_last_eb_blk), +if (last_eb_blk) { + retval =ocfs2_read_block (osb, last_eb_blk, &eb_bh,OCFS2_BH_CACHED , inode);if (retval <0 ) {mlog_errno (retval);
@@ -393,8 +528,7 @@ int ocfs2_num_free_extents(struct ocfs2_super *osb, } eb = (struct ocfs2_extent_block *) eb_bh->b_data; el = &eb->h_list; - }else - el = &fe->id2.i_list; + }BUG_ON (el->l_tree_depth !=0 );
@@ -532,7 +666,7 @@ static inline u32 ocfs2_sum_rightmost_rec(struct ocfs2_extent_list *el)static int ocfs2_add_branch (struct ocfs2_super *osb,handle_t *handle,struct inode *inode, -struct buffer_head *fe_bh, +struct ocfs2_extent_tree *et,struct buffer_head *eb_bh,struct buffer_head **last_eb_bh,struct ocfs2_alloc_context *meta_ac)
@@ -541,7 +675,6 @@ static int ocfs2_add_branch(struct ocfs2_super *osb,u64 next_blkno, new_last_eb_blk;struct buffer_head *bh;struct buffer_head **new_eb_bhs =NULL ; -struct ocfs2_dinode *fe;struct ocfs2_extent_block *eb;struct ocfs2_extent_list *eb_el;struct ocfs2_extent_list *el;
@@ -551,13 +684,11 @@ static int ocfs2_add_branch(struct ocfs2_super *osb,BUG_ON (!last_eb_bh || !*last_eb_bh); - fe = (struct ocfs2_dinode *) fe_bh->b_data; -if (eb_bh) { eb = (struct ocfs2_extent_block *) eb_bh->b_data; el = &eb->h_list; }else - el = &fe->id2.i_list; + el = et->root_el;/* we never add a branch to a leaf. */ BUG_ON (!el->l_tree_depth);
@@ -647,7 +778,7 @@ static int ocfs2_add_branch(struct ocfs2_super *osb,mlog_errno (status); goto bail; } - status =ocfs2_journal_access (handle, inode, fe_bh, + status =ocfs2_journal_access (handle, inode, et->root_bh,OCFS2_JOURNAL_ACCESS_WRITE );if (status <0 ) {mlog_errno (status);
@@ -663,7 +794,7 @@ static int ocfs2_add_branch(struct ocfs2_super *osb, }/* Link the new branch into the rest of the tree (el will -* either be on the fe, or the extent block passed in. */ +* either be on the root_bh, or the extent block passed in. */ i =le16_to_cpu (el->l_next_free_rec); el->l_recs[i].e_blkno =cpu_to_le64 (next_blkno); el->l_recs[i].e_cpos =cpu_to_le32 (new_cpos);
@@ -672,7 +803,7 @@ static int ocfs2_add_branch(struct ocfs2_super *osb,/* fe needs a new last extent block pointer, as does the * next_leaf on the previously last-extent-block. */ - fe->i_last_eb_blk =cpu_to_le64 (new_last_eb_blk); +ocfs2_set_last_eb_blk (et, new_last_eb_blk); eb = (struct ocfs2_extent_block *) (*last_eb_bh)->b_data; eb->h_next_leaf_blk =cpu_to_le64 (new_last_eb_blk);
@@ -680,7 +811,7 @@ static int ocfs2_add_branch(struct ocfs2_super *osb, status =ocfs2_journal_dirty (handle, *last_eb_bh);if (status <0 )mlog_errno (status); - status =ocfs2_journal_dirty (handle, fe_bh); + status =ocfs2_journal_dirty (handle, et->root_bh);if (status <0 )mlog_errno (status);if (eb_bh) {
@@ -718,16 +849,15 @@ bail:static int ocfs2_shift_tree_depth (struct ocfs2_super *osb,handle_t *handle,struct inode *inode, -struct buffer_head *fe_bh, +struct ocfs2_extent_tree *et,struct ocfs2_alloc_context *meta_ac,struct buffer_head **ret_new_eb_bh) {int status, i;u32 new_clusters;struct buffer_head *new_eb_bh =NULL ; -struct ocfs2_dinode *fe;struct ocfs2_extent_block *eb; -struct ocfs2_extent_list *fe_el; +struct ocfs2_extent_list *root_el;struct ocfs2_extent_list *eb_el;mlog_entry_void ();
@@ -747,8 +877,7 @@ static int ocfs2_shift_tree_depth(struct ocfs2_super *osb, } eb_el = &eb->h_list; - fe = (struct ocfs2_dinode *) fe_bh->b_data; - fe_el = &fe->id2.i_list; + root_el = et->root_el; status =ocfs2_journal_access (handle, inode, new_eb_bh,OCFS2_JOURNAL_ACCESS_CREATE );
@@ -757,11 +886,11 @@ static int ocfs2_shift_tree_depth(struct ocfs2_super *osb, goto bail; } -/* copy the fe data into the new extent block */ - eb_el->l_tree_depth = fe_el->l_tree_depth; - eb_el->l_next_free_rec = fe_el->l_next_free_rec; -for (i =0 ; i <le16_to_cpu (fe_el->l_next_free_rec); i++) - eb_el->l_recs[i] = fe_el->l_recs[i]; +/* copy the root extent list data into the new extent block */ + eb_el->l_tree_depth = root_el->l_tree_depth; + eb_el->l_next_free_rec = root_el->l_next_free_rec; +for (i =0 ; i <le16_to_cpu (root_el->l_next_free_rec); i++) + eb_el->l_recs[i] = root_el->l_recs[i]; status =ocfs2_journal_dirty (handle, new_eb_bh);if (status <0 ) {
@@ -769,7 +898,7 @@ static int ocfs2_shift_tree_depth(struct ocfs2_super *osb, goto bail; } - status =ocfs2_journal_access (handle, inode, fe_bh, + status =ocfs2_journal_access (handle, inode, et->root_bh,OCFS2_JOURNAL_ACCESS_WRITE );if (status <0 ) {mlog_errno (status);
@@ -778,21 +907,21 @@ static int ocfs2_shift_tree_depth(struct ocfs2_super *osb, new_clusters =ocfs2_sum_rightmost_rec (eb_el); -/* update fe now */ -le16_add_cpu (&fe_el->l_tree_depth,1 ); - fe_el->l_recs[0 ].e_cpos =0 ; - fe_el->l_recs[0 ].e_blkno = eb->h_blkno; - fe_el->l_recs[0 ].e_int_clusters =cpu_to_le32 (new_clusters); -for (i =1 ; i <le16_to_cpu (fe_el->l_next_free_rec); i++) -memset (&fe_el->l_recs[i],0 ,sizeof (struct ocfs2_extent_rec )); - fe_el->l_next_free_rec =cpu_to_le16 (1 ); +/* update root_bh now */ +le16_add_cpu (&root_el->l_tree_depth,1 ); + root_el->l_recs[0 ].e_cpos =0 ; + root_el->l_recs[0 ].e_blkno = eb->h_blkno; + root_el->l_recs[0 ].e_int_clusters =cpu_to_le32 (new_clusters); +for (i =1 ; i <le16_to_cpu (root_el->l_next_free_rec); i++) +memset (&root_el->l_recs[i],0 ,sizeof (struct ocfs2_extent_rec )); + root_el->l_next_free_rec =cpu_to_le16 (1 );/* If this is our 1st tree depth shift, then last_eb_blk * becomes the allocated extent block */ -if (fe_el->l_tree_depth ==cpu_to_le16 (1 )) - fe->i_last_eb_blk = eb->h_blkno; +if (root_el->l_tree_depth ==cpu_to_le16 (1 )) +ocfs2_set_last_eb_blk (et,le64_to_cpu (eb->h_blkno)); - status =ocfs2_journal_dirty (handle, fe_bh); + status =ocfs2_journal_dirty (handle, et->root_bh);if (status <0 ) {mlog_errno (status); goto bail;
@@ -818,22 +947,21 @@ bail:* 1) a lowest extent block is found, then we pass it back in * *lowest_eb_bh and return '0' * -* 2) the search fails to find anything, but the dinode has room. We +* 2) the search fails to find anything, but the root_el has room. We * pass NULL back in *lowest_eb_bh, but still return '0' * -* 3) the search fails to find anything AND the dinode is full, in +* 3) the search fails to find anything AND the root_el is full, in * which case we return > 0 * * return status < 0 indicates an error. */ static int ocfs2_find_branch_target (struct ocfs2_super *osb,struct inode *inode, -struct buffer_head *fe_bh, +struct ocfs2_extent_tree *et,struct buffer_head **target_bh) {int status =0 , i;u64 blkno; -struct ocfs2_dinode *fe;struct ocfs2_extent_block *eb;struct ocfs2_extent_list *el;struct buffer_head *bh =NULL ;
@@ -843,8 +971,7 @@ static int ocfs2_find_branch_target(struct ocfs2_super *osb, *target_bh =NULL ; - fe = (struct ocfs2_dinode *) fe_bh->b_data; - el = &fe->id2.i_list; + el = et->root_el;while (le16_to_cpu (el->l_tree_depth) >1 ) {if (le16_to_cpu (el->l_next_free_rec) ==0 ) {
@@ -896,8 +1023,8 @@ static int ocfs2_find_branch_target(struct ocfs2_super *osb,/* If we didn't find one and the fe doesn't have any room, * then return '1' */ -if (!lowest_bh - && (fe->id2.i_list.l_next_free_rec == fe->id2.i_list.l_count)) + el = et->root_el; +if (!lowest_bh && (el->l_next_free_rec == el->l_count)) status =1 ; *target_bh = lowest_bh;
@@ -920,19 +1047,19 @@ bail:* *last_eb_bh will be updated by ocfs2_add_branch(). */ static int ocfs2_grow_tree (struct inode *inode,handle_t *handle, -struct buffer_head *di_bh,int *final_depth, +struct ocfs2_extent_tree *et,int *final_depth,struct buffer_head **last_eb_bh,struct ocfs2_alloc_context *meta_ac) {int ret, shift; -struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; -int depth =le16_to_cpu (di->id2.i_list.l_tree_depth); +struct ocfs2_extent_list *el = et->root_el; +int depth =le16_to_cpu (el->l_tree_depth);struct ocfs2_super *osb =OCFS2_SB (inode->i_sb);struct buffer_head *bh =NULL ;BUG_ON (meta_ac ==NULL ); - shift =ocfs2_find_branch_target (osb, inode, di_bh, &bh); + shift =ocfs2_find_branch_target (osb, inode, et, &bh);if (shift <0 ) { ret = shift;mlog_errno (ret);
@@ -949,7 +1076,7 @@ static int ocfs2_grow_tree(struct inode *inode, handle_t *handle,/* ocfs2_shift_tree_depth will return us a buffer with * the new extent block (so we can pass that to * ocfs2_add_branch). */ - ret =ocfs2_shift_tree_depth (osb, handle, inode, di_bh, + ret =ocfs2_shift_tree_depth (osb, handle, inode, et, meta_ac, &bh);if (ret <0 ) {mlog_errno (ret);
@@ -976,7 +1103,7 @@ static int ocfs2_grow_tree(struct inode *inode, handle_t *handle,/* call ocfs2_add_branch to add the final part of the tree with * the new data. */ mlog (0 ,"add branch. bh = %p\n" , bh); - ret =ocfs2_add_branch (osb, handle, inode, di_bh, bh, last_eb_bh, + ret =ocfs2_add_branch (osb, handle, inode, et, bh, last_eb_bh, meta_ac);if (ret <0 ) {mlog_errno (ret);
@@ -2068,11 +2195,11 @@ static int ocfs2_rotate_subtree_left(struct inode *inode, handle_t *handle,struct ocfs2_path *right_path, int subtree_index, struct ocfs2_cached_dealloc_ctxt *dealloc, - int *deleted) + int *deleted, + struct ocfs2_extent_tree *et) { int ret, i, del_right_subtree = 0, right_has_empty = 0; -struct buffer_head *root_bh, *di_bh =path_root_bh (right_path); -struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; +struct buffer_head *root_bh, *et_root_bh =path_root_bh (right_path);struct ocfs2_extent_list *right_leaf_el, *left_leaf_el;struct ocfs2_extent_block *eb;
@@ -2124,7 +2251,7 @@ static int ocfs2_rotate_subtree_left(struct inode *inode, handle_t *handle,* We have to update i_last_eb_blk during the meta * data delete. */ - ret =ocfs2_journal_access (handle, inode, di_bh, + ret =ocfs2_journal_access (handle, inode, et_root_bh,OCFS2_JOURNAL_ACCESS_WRITE );if (ret) {mlog_errno (ret);
@@ -2199,7 +2326,7 @@ static int ocfs2_rotate_subtree_left(struct inode *inode, handle_t *handle,ocfs2_update_edge_lengths (inode, handle, left_path); eb = (struct ocfs2_extent_block *)path_leaf_bh (left_path)->b_data; - di->i_last_eb_blk = eb->h_blkno; +ocfs2_set_last_eb_blk (et,le64_to_cpu (eb->h_blkno));/* * Removal of the extent in the left leaf was skipped
@@ -2209,7 +2336,7 @@ static int ocfs2_rotate_subtree_left(struct inode *inode, handle_t *handle,if (right_has_empty)ocfs2_remove_empty_extent (left_leaf_el); - ret =ocfs2_journal_dirty (handle, di_bh); + ret =ocfs2_journal_dirty (handle, et_root_bh);if (ret)mlog_errno (ret);
@@ -2332,7 +2459,8 @@ static int __ocfs2_rotate_tree_left(struct inode *inode, handle_t *handle, int orig_credits, struct ocfs2_path *path, struct ocfs2_cached_dealloc_ctxt *dealloc, - struct ocfs2_path **empty_extent_path) + struct ocfs2_path **empty_extent_path, + struct ocfs2_extent_tree *et) { int ret, subtree_root, deleted;u32 right_cpos;
@@ -2405,7 +2533,7 @@ static int __ocfs2_rotate_tree_left(struct inode *inode, ret =ocfs2_rotate_subtree_left (inode, handle, left_path, right_path, subtree_root, - dealloc, &deleted); + dealloc, &deleted, et);if (ret == -EAGAIN ) {/* * The rotation has to temporarily stop due to
@@ -2448,29 +2576,20 @@ out: }static int ocfs2_remove_rightmost_path (struct inode *inode,handle_t *handle, -struct ocfs2_path *path, -struct ocfs2_cached_dealloc_ctxt *dealloc) +struct ocfs2_path *path, +struct ocfs2_cached_dealloc_ctxt *dealloc, +struct ocfs2_extent_tree *et) {int ret, subtree_index;u32 cpos;struct ocfs2_path *left_path =NULL ; -struct ocfs2_dinode *di;struct ocfs2_extent_block *eb;struct ocfs2_extent_list *el; -/* -* XXX: This code assumes that the root is an inode, which is -* true for now but may change as tree code gets generic. -*/ - di = (struct ocfs2_dinode *)path_root_bh (path)->b_data; -if (!OCFS2_IS_VALID_DINODE (di)) { - ret = -EIO ; -ocfs2_error (inode->i_sb, -"Inode %llu has invalid path root" , - (unsigned long long )OCFS2_I (inode)->ip_blkno); - goto out; - } + ret = et->eops->sanity_check (inode, et); +if (ret) + goto out;/* * There's two ways we handle this depending on * whether path is the only existing one.
@@ -2527,7 +2646,7 @@ static int ocfs2_remove_rightmost_path(struct inode *inode, handle_t *handle,ocfs2_update_edge_lengths (inode, handle, left_path); eb = (struct ocfs2_extent_block *)path_leaf_bh (left_path)->b_data; - di->i_last_eb_blk = eb->h_blkno; +ocfs2_set_last_eb_blk (et,le64_to_cpu (eb->h_blkno)); }else {/* * 'path' is also the leftmost path which
@@ -2538,12 +2657,12 @@ static int ocfs2_remove_rightmost_path(struct inode *inode, handle_t *handle,*/ ocfs2_unlink_path (inode, handle, dealloc, path,1 ); - el = &di->id2.i_list; + el = et->root_el; el->l_tree_depth =0 ; el->l_next_free_rec =0 ;memset (&el->l_recs[0 ],0 ,sizeof (struct ocfs2_extent_rec )); - di->i_last_eb_blk =0 ; +ocfs2_set_last_eb_blk (et,0 ); }ocfs2_journal_dirty (handle,path_root_bh (path));
@@ -2571,7 +2690,8 @@ out:*/ static int ocfs2_rotate_tree_left (struct inode *inode,handle_t *handle,struct ocfs2_path *path, -struct ocfs2_cached_dealloc_ctxt *dealloc) +struct ocfs2_cached_dealloc_ctxt *dealloc, +struct ocfs2_extent_tree *et) {int ret, orig_credits = handle->h_buffer_credits;struct ocfs2_path *tmp_path =NULL , *restart_path =NULL ;
@@ -2585,7 +2705,7 @@ static int ocfs2_rotate_tree_left(struct inode *inode, handle_t *handle,if (path->p_tree_depth ==0 ) { rightmost_no_delete:/* -* In-inode extents. This is trivially handled, so do +* Inline extents. This is trivially handled, so do * it up front. */ ret = ocfs2_rotate_rightmost_leaf_left(inode, handle,
@@ -2639,7 +2759,7 @@ rightmost_no_delete:*/ ret =ocfs2_remove_rightmost_path (inode, handle, path, - dealloc); + dealloc, et);if (ret)mlog_errno (ret); goto out;
@@ -2651,7 +2771,7 @@ rightmost_no_delete:*/ try_rotate: ret =__ocfs2_rotate_tree_left (inode, handle, orig_credits, path, - dealloc, &restart_path); + dealloc, &restart_path, et);if (ret && ret != -EAGAIN ) {mlog_errno (ret); goto out;
@@ -2663,7 +2783,7 @@ try_rotate: ret =__ocfs2_rotate_tree_left (inode, handle, orig_credits, tmp_path, dealloc, - &restart_path); + &restart_path, et);if (ret && ret != -EAGAIN ) {mlog_errno (ret); goto out;
@@ -2949,6 +3069,7 @@ static int ocfs2_merge_rec_left(struct inode *inode,handle_t *handle, struct ocfs2_extent_rec *split_rec, struct ocfs2_cached_dealloc_ctxt *dealloc, + struct ocfs2_extent_tree *et, int index) { int ret, i, subtree_index = 0, has_empty_extent = 0;
@@ -3069,7 +3190,8 @@ static int ocfs2_merge_rec_left(struct inode *inode, le16_to_cpu(el->l_next_free_rec) == 1) { ret =ocfs2_remove_rightmost_path (inode, handle, - right_path, dealloc); + right_path, + dealloc, et);if (ret) {mlog_errno (ret); goto out;
@@ -3096,7 +3218,8 @@ static int ocfs2_try_to_merge_extent(struct inode *inode,int split_index, struct ocfs2_extent_rec *split_rec, struct ocfs2_cached_dealloc_ctxt *dealloc, - struct ocfs2_merge_ctxt *ctxt) + struct ocfs2_merge_ctxt *ctxt, + struct ocfs2_extent_tree *et) { int ret = 0;
@@ -3114,7 +3237,7 @@ static int ocfs2_try_to_merge_extent(struct inode *inode,* illegal. */ ret =ocfs2_rotate_tree_left (inode, handle, path, - dealloc); + dealloc, et);if (ret) {mlog_errno (ret); goto out;
@@ -3157,7 +3280,8 @@ static int ocfs2_try_to_merge_extent(struct inode *inode,BUG_ON (!ocfs2_is_empty_extent (&el->l_recs[0 ]));/* The merge left us with an empty extent, remove it. */ - ret =ocfs2_rotate_tree_left (inode, handle, path, dealloc); + ret =ocfs2_rotate_tree_left (inode, handle, path, + dealloc, et);if (ret) {mlog_errno (ret); goto out;
@@ -3171,7 +3295,7 @@ static int ocfs2_try_to_merge_extent(struct inode *inode,*/ ret =ocfs2_merge_rec_left (inode, path, handle, rec, - dealloc, + dealloc, et, split_index);if (ret) {
@@ -3180,7 +3304,7 @@ static int ocfs2_try_to_merge_extent(struct inode *inode, } ret =ocfs2_rotate_tree_left (inode, handle, path, - dealloc); + dealloc, et);/* * Error from this last rotate is not critical, so * print but don't bubble it up.
@@ -3200,7 +3324,7 @@ static int ocfs2_try_to_merge_extent(struct inode *inode, ret =ocfs2_merge_rec_left (inode, path, handle, split_rec, - dealloc, + dealloc, et, split_index);if (ret) {mlog_errno (ret);
@@ -3223,7 +3347,7 @@ static int ocfs2_try_to_merge_extent(struct inode *inode,* our leaf. Try to rotate it away. */ ret =ocfs2_rotate_tree_left (inode, handle, path, - dealloc); + dealloc, et);if (ret)mlog_errno (ret); ret =0 ;
@@ -3357,16 +3481,6 @@ rotate:ocfs2_rotate_leaf (el, insert_rec); } -static inline void ocfs2_update_dinode_clusters (struct inode *inode, -struct ocfs2_dinode *di, -u32 clusters) -{ -le32_add_cpu (&di->i_clusters, clusters); -spin_lock (&OCFS2_I (inode)->ip_lock); -OCFS2_I (inode)->ip_clusters =le32_to_cpu (di->i_clusters); -spin_unlock (&OCFS2_I (inode)->ip_lock); -} - static void ocfs2_adjust_rightmost_records(struct inode *inode, handle_t *handle, struct ocfs2_path *path,
@@ -3568,8 +3682,8 @@ static void ocfs2_split_record(struct inode *inode, }/* -* This function only does inserts on an allocation b-tree. For dinode -* lists, ocfs2_insert_at_leaf() is called directly. +* This function only does inserts on an allocation b-tree. For tree +* depth = 0, ocfs2_insert_at_leaf() is called directly. * * right_path is the path we want to do the actual insert * in. left_path should only be passed in if we need to update that
@@ -3666,7 +3780,7 @@ out:static int ocfs2_do_insert_extent (struct inode *inode,handle_t *handle, -struct buffer_head *di_bh, +struct ocfs2_extent_tree *et,struct ocfs2_extent_rec *insert_rec,struct ocfs2_insert_type *type) {
@@ -3674,13 +3788,11 @@ static int ocfs2_do_insert_extent(struct inode *inode,u32 cpos;struct ocfs2_path *right_path =NULL ;struct ocfs2_path *left_path =NULL ; -struct ocfs2_dinode *di;struct ocfs2_extent_list *el; - di = (struct ocfs2_dinode *) di_bh->b_data; - el = &di->id2.i_list; + el = et->root_el; - ret =ocfs2_journal_access (handle, inode, di_bh, + ret =ocfs2_journal_access (handle, inode, et->root_bh,OCFS2_JOURNAL_ACCESS_WRITE );if (ret) {mlog_errno (ret);
@@ -3692,7 +3804,7 @@ static int ocfs2_do_insert_extent(struct inode *inode, goto out_update_clusters; } - right_path =ocfs2_new_inode_path (di_bh); + right_path =ocfs2_new_path (et->root_bh, et->root_el);if (!right_path) { ret = -ENOMEM ;mlog_errno (ret);
@@ -3742,7 +3854,7 @@ static int ocfs2_do_insert_extent(struct inode *inode,* ocfs2_rotate_tree_right() might have extended the * transaction without re-journaling our tree root. */ - ret =ocfs2_journal_access (handle, inode, di_bh, + ret =ocfs2_journal_access (handle, inode, et->root_bh,OCFS2_JOURNAL_ACCESS_WRITE );if (ret) {mlog_errno (ret);
@@ -3767,10 +3879,10 @@ static int ocfs2_do_insert_extent(struct inode *inode, out_update_clusters:if (type->ins_split ==SPLIT_NONE ) -ocfs2_update_dinode_clusters (inode, di, -le16_to_cpu (insert_rec->e_leaf_clusters)); +ocfs2_update_clusters (inode, et, +le16_to_cpu (insert_rec->e_leaf_clusters)); - ret =ocfs2_journal_dirty (handle, di_bh); + ret =ocfs2_journal_dirty (handle, et->root_bh);if (ret)mlog_errno (ret);
@@ -3924,8 +4036,8 @@ static void ocfs2_figure_contig_type(struct inode *inode,* 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 +* This should also work against the root extent list for tree's with 0 +* depth. If we consider the root extent list to be the rightmost leaf node * then the logic here makes sense. */ static void ocfs2_figure_appending_type(struct ocfs2_insert_type *insert,
@@ -3976,14 +4088,13 @@ set_tail_append:* structure. */ static int ocfs2_figure_insert_type (struct inode *inode, -struct buffer_head *di_bh, +struct ocfs2_extent_tree *et,struct buffer_head **last_eb_bh,struct ocfs2_extent_rec *insert_rec,int *free_records,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 ;
@@ -3991,7 +4102,7 @@ static int ocfs2_figure_insert_type(struct inode *inode, insert->ins_split =SPLIT_NONE ; - el = &di->id2.i_list; + el = et->root_el; insert->ins_tree_depth =le16_to_cpu (el->l_tree_depth);if (el->l_tree_depth) {
@@ -4002,7 +4113,7 @@ static int ocfs2_figure_insert_type(struct inode *inode,* may want it later. */ ret =ocfs2_read_block (OCFS2_SB (inode->i_sb), -le64_to_cpu (di->i_last_eb_blk), &bh, +ocfs2_get_last_eb_blk (et), &bh,OCFS2_BH_CACHED , inode);if (ret) {mlog_exit (ret);
@@ -4029,7 +4140,7 @@ static int ocfs2_figure_insert_type(struct inode *inode,return 0 ; } - path =ocfs2_new_inode_path (di_bh); + path =ocfs2_new_path (et->root_bh, et->root_el);if (!path) { ret = -ENOMEM ;mlog_errno (ret);
@@ -4079,7 +4190,8 @@ static int ocfs2_figure_insert_type(struct inode *inode,* 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) { +if (ocfs2_get_last_eb_blk (et) == +path_leaf_bh (path)->b_blocknr) {/* * Ok, ocfs2_find_path() returned us the rightmost * tree path. This might be an appending insert. There are
@@ -4109,21 +4221,30 @@ out:int ocfs2_insert_extent (struct ocfs2_super *osb,handle_t *handle,struct inode *inode, -struct buffer_head *fe_bh, +struct buffer_head *root_bh,u32 cpos,u64 start_blk,u32 new_clusters,u8 flags, -struct ocfs2_alloc_context *meta_ac) +struct ocfs2_alloc_context *meta_ac, +enum ocfs2_extent_tree_type et_type) {int status;int uninitialized_var (free_records );struct buffer_head *last_eb_bh =NULL ;struct ocfs2_insert_type insert = {0 , };struct ocfs2_extent_rec rec; +struct ocfs2_extent_tree *et =NULL ;BUG_ON (OCFS2_I (inode)->ip_dyn_features &OCFS2_INLINE_DATA_FL ); + et =ocfs2_new_extent_tree (root_bh, et_type); +if (!et) { + status = -ENOMEM ; +mlog_errno (status); + goto bail; + } +mlog (0 ,"add %u clusters at position %u to inode %llu\n" , new_clusters, cpos, (unsigned long long )OCFS2_I (inode)->ip_blkno);
@@ -4141,7 +4262,7 @@ int ocfs2_insert_extent(struct ocfs2_super *osb, rec.e_leaf_clusters =cpu_to_le16 (new_clusters); rec.e_flags = flags; - status =ocfs2_figure_insert_type (inode, fe_bh, &last_eb_bh, &rec, + status =ocfs2_figure_insert_type (inode, et, &last_eb_bh, &rec, &free_records, &insert);if (status <0 ) {mlog_errno (status);
@@ -4155,7 +4276,7 @@ int ocfs2_insert_extent(struct ocfs2_super *osb, free_records, insert.ins_tree_depth);if (insert.ins_contig ==CONTIG_NONE && free_records ==0 ) { - status =ocfs2_grow_tree (inode, handle, fe_bh, + status =ocfs2_grow_tree (inode, handle, et, &insert.ins_tree_depth, &last_eb_bh, meta_ac);if (status) {
@@ -4165,16 +4286,18 @@ int ocfs2_insert_extent(struct ocfs2_super *osb, }/* Finally, we can add clusters. This might rotate the tree for us. */ - status =ocfs2_do_insert_extent (inode, handle, fe_bh, &rec, &insert); + status =ocfs2_do_insert_extent (inode, handle, et, &rec, &insert);if (status <0 )mlog_errno (status); -else +else if (et->type ==OCFS2_DINODE_EXTENT )ocfs2_extent_map_insert_rec (inode, &rec); bail:if (last_eb_bh)brelse (last_eb_bh); +if (et) +ocfs2_free_extent_tree (et);mlog_exit (status);return status; }
@@ -4202,7 +4325,7 @@ static void ocfs2_make_right_split_rec(struct super_block *sb, static int ocfs2_split_and_insert(struct inode *inode, handle_t *handle, struct ocfs2_path *path, - struct buffer_head *di_bh, + struct ocfs2_extent_tree *et, struct buffer_head **last_eb_bh, int split_index, struct ocfs2_extent_rec *orig_split_rec,
@@ -4216,7 +4339,6 @@ static int ocfs2_split_and_insert(struct inode *inode,struct ocfs2_extent_rec split_rec = *orig_split_rec;struct ocfs2_insert_type insert;struct ocfs2_extent_block *eb; -struct ocfs2_dinode *di; leftright:/*
@@ -4225,8 +4347,7 @@ leftright:*/ rec =path_leaf_el (path)->l_recs[split_index]; - di = (struct ocfs2_dinode *)di_bh->b_data; - rightmost_el = &di->id2.i_list; + rightmost_el = et->root_el; depth =le16_to_cpu (rightmost_el->l_tree_depth);if (depth) {
@@ -4237,8 +4358,8 @@ leftright:if (le16_to_cpu (rightmost_el->l_next_free_rec) ==le16_to_cpu (rightmost_el->l_count)) { - ret =ocfs2_grow_tree (inode, handle, di_bh, &depth, last_eb_bh, - meta_ac); + ret =ocfs2_grow_tree (inode, handle, et, + &depth, last_eb_bh, meta_ac);if (ret) {mlog_errno (ret); goto out;
@@ -4275,8 +4396,7 @@ leftright: do_leftright =1 ; } - ret =ocfs2_do_insert_extent (inode, handle, di_bh, &split_rec, - &insert); + ret =ocfs2_do_insert_extent (inode, handle, et, &split_rec, &insert);if (ret) {mlog_errno (ret); goto out;
@@ -4318,8 +4438,9 @@ out:* of the tree is required. All other cases will degrade into a less * optimal tree layout. * -* last_eb_bh should be the rightmost leaf block for any inode with a -* btree. Since a split may grow the tree or a merge might shrink it, the caller cannot trust the contents of that buffer after this call. +* last_eb_bh should be the rightmost leaf block for any extent +* btree. Since a split may grow the tree or a merge might shrink it, +* the caller cannot trust the contents of that buffer after this call. * * This code is optimized for readability - several passes might be * made over certain portions of the tree. All of those blocks will
@@ -4327,7 +4448,7 @@ out:* extra overhead is not expressed in terms of disk reads. */ static int __ocfs2_mark_extent_written(struct inode *inode, - struct buffer_head *di_bh, + struct ocfs2_extent_tree *et, handle_t *handle, struct ocfs2_path *path, int split_index,
@@ -4367,10 +4488,9 @@ static int __ocfs2_mark_extent_written(struct inode *inode,*/ if (path->p_tree_depth) {struct ocfs2_extent_block *eb; -struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; ret =ocfs2_read_block (OCFS2_SB (inode->i_sb), -le64_to_cpu (di->i_last_eb_blk), +ocfs2_get_last_eb_blk (et), &last_eb_bh,OCFS2_BH_CACHED , inode);if (ret) {mlog_exit (ret);
@@ -4404,7 +4524,7 @@ static int __ocfs2_mark_extent_written(struct inode *inode,if (ctxt.c_split_covers_rec) el->l_recs[split_index] = *split_rec;else - ret =ocfs2_split_and_insert (inode, handle, path, di_bh, + ret =ocfs2_split_and_insert (inode, handle, path, et, &last_eb_bh, split_index, split_rec, meta_ac);if (ret)
@@ -4412,7 +4532,7 @@ static int __ocfs2_mark_extent_written(struct inode *inode, }else { ret =ocfs2_try_to_merge_extent (inode, handle, path, split_index, split_rec, - dealloc, &ctxt); + dealloc, &ctxt, et);if (ret)mlog_errno (ret); }
@@ -4430,16 +4550,18 @@ out:* * The caller is responsible for passing down meta_ac if we'll need it. */ -int ocfs2_mark_extent_written (struct inode *inode,struct buffer_head *di_bh, +int ocfs2_mark_extent_written (struct inode *inode,struct buffer_head *root_bh,handle_t *handle,u32 cpos,u32 len,u32 phys,struct ocfs2_alloc_context *meta_ac, -struct ocfs2_cached_dealloc_ctxt *dealloc) +struct ocfs2_cached_dealloc_ctxt *dealloc, +enum ocfs2_extent_tree_type et_type) {int ret, index;u64 start_blkno =ocfs2_clusters_to_blocks (inode->i_sb, phys);struct ocfs2_extent_rec split_rec;struct ocfs2_path *left_path =NULL ;struct ocfs2_extent_list *el; +struct ocfs2_extent_tree *et =NULL ;mlog (0 ,"Inode %lu cpos %u, len %u, phys %u (%llu)\n" , inode->i_ino, cpos, len, phys, (unsigned long long )start_blkno);
@@ -4453,13 +4575,21 @@ int ocfs2_mark_extent_written(struct inode *inode, struct buffer_head *di_bh, goto out; } + et =ocfs2_new_extent_tree (root_bh, et_type); +if (!et) { + ret = -ENOMEM ; +mlog_errno (ret); + goto out; + } +/* * XXX: This should be fixed up so that we just re-insert the * next extent records. */ -ocfs2_extent_map_trunc (inode,0 ); +if (et_type ==OCFS2_DINODE_EXTENT ) +ocfs2_extent_map_trunc (inode,0 ); - left_path =ocfs2_new_inode_path (di_bh); + left_path =ocfs2_new_path (et->root_bh, et->root_el);if (!left_path) { ret = -ENOMEM ;mlog_errno (ret);
@@ -4490,23 +4620,25 @@ int ocfs2_mark_extent_written(struct inode *inode, struct buffer_head *di_bh, split_rec.e_flags =path_leaf_el (left_path)->l_recs[index].e_flags; split_rec.e_flags &= ~OCFS2_EXT_UNWRITTEN ; - ret =__ocfs2_mark_extent_written (inode, di_bh, handle, left_path, - index, &split_rec, meta_ac, dealloc); + ret =__ocfs2_mark_extent_written (inode, et, handle, left_path, + index, &split_rec, meta_ac, + dealloc);if (ret)mlog_errno (ret); out:ocfs2_free_path (left_path); +if (et) +ocfs2_free_extent_tree (et);return ret; } -static int ocfs2_split_tree (struct inode *inode,struct buffer_head *di_bh, +static int ocfs2_split_tree (struct inode *inode,struct ocfs2_extent_tree *et,handle_t *handle,struct ocfs2_path *path,int index,u32 new_range,struct ocfs2_alloc_context *meta_ac) {int ret, depth, credits = handle->h_buffer_credits; -struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;struct buffer_head *last_eb_bh =NULL ;struct ocfs2_extent_block *eb;struct ocfs2_extent_list *rightmost_el, *el;
@@ -4524,7 +4656,7 @@ static int ocfs2_split_tree(struct inode *inode, struct buffer_head *di_bh, depth = path->p_tree_depth;if (depth >0 ) { ret =ocfs2_read_block (OCFS2_SB (inode->i_sb), -le64_to_cpu (di->i_last_eb_blk), +ocfs2_get_last_eb_blk (et), &last_eb_bh,OCFS2_BH_CACHED , inode);if (ret <0 ) {mlog_errno (ret);
@@ -4537,7 +4669,7 @@ static int ocfs2_split_tree(struct inode *inode, struct buffer_head *di_bh, rightmost_el =path_leaf_el (path); credits += path->p_tree_depth + -ocfs2_extend_meta_needed (&di->id2.i_list); +ocfs2_extend_meta_needed (et->root_el); ret =ocfs2_extend_trans (handle, credits);if (ret) {mlog_errno (ret);
@@ -4546,7 +4678,7 @@ static int ocfs2_split_tree(struct inode *inode, struct buffer_head *di_bh,if (le16_to_cpu (rightmost_el->l_next_free_rec) ==le16_to_cpu (rightmost_el->l_count)) { - ret =ocfs2_grow_tree (inode, handle, di_bh, &depth, &last_eb_bh, + ret =ocfs2_grow_tree (inode, handle, et, &depth, &last_eb_bh, meta_ac);if (ret) {mlog_errno (ret);
@@ -4560,7 +4692,7 @@ static int ocfs2_split_tree(struct inode *inode, struct buffer_head *di_bh, insert.ins_split =SPLIT_RIGHT ; insert.ins_tree_depth = depth; - ret =ocfs2_do_insert_extent (inode, handle, di_bh, &split_rec, &insert); + ret =ocfs2_do_insert_extent (inode, handle, et, &split_rec, &insert);if (ret)mlog_errno (ret);
@@ -4572,7 +4704,8 @@ out:static int ocfs2_truncate_rec (struct inode *inode,handle_t *handle,struct ocfs2_path *path,int index,struct ocfs2_cached_dealloc_ctxt *dealloc, -u32 cpos,u32 len) +u32 cpos,u32 len, +struct ocfs2_extent_tree *et) {int ret;u32 left_cpos, rec_range, trunc_range;
@@ -4584,7 +4717,7 @@ static int ocfs2_truncate_rec(struct inode *inode, handle_t *handle,struct ocfs2_extent_block *eb;if (ocfs2_is_empty_extent (&el->l_recs[0 ]) && index >0 ) { - ret =ocfs2_rotate_tree_left (inode, handle, path, dealloc); + ret =ocfs2_rotate_tree_left (inode, handle, path, dealloc, et);if (ret) {mlog_errno (ret); goto out;
@@ -4715,7 +4848,7 @@ static int ocfs2_truncate_rec(struct inode *inode, handle_t *handle,ocfs2_journal_dirty (handle,path_leaf_bh (path)); - ret =ocfs2_rotate_tree_left (inode, handle, path, dealloc); + ret =ocfs2_rotate_tree_left (inode, handle, path, dealloc, et);if (ret) {mlog_errno (ret); goto out;
@@ -4726,20 +4859,29 @@ out:return ret; } -int ocfs2_remove_extent (struct inode *inode,struct buffer_head *di_bh, +int ocfs2_remove_extent (struct inode *inode,struct buffer_head *root_bh,u32 cpos,u32 len,handle_t *handle,struct ocfs2_alloc_context *meta_ac, -struct ocfs2_cached_dealloc_ctxt *dealloc) +struct ocfs2_cached_dealloc_ctxt *dealloc, +enum ocfs2_extent_tree_type et_type) {int ret, index;u32 rec_range, trunc_range;struct ocfs2_extent_rec *rec;struct ocfs2_extent_list *el; -struct ocfs2_path *path; +struct ocfs2_path *path =NULL ; +struct ocfs2_extent_tree *et =NULL ; + + et =ocfs2_new_extent_tree (root_bh, et_type); +if (!et) { + ret = -ENOMEM ; +mlog_errno (ret); + goto out; + }ocfs2_extent_map_trunc (inode,0 ); - path =ocfs2_new_inode_path (di_bh); + path =ocfs2_new_path (et->root_bh, et->root_el);if (!path) { ret = -ENOMEM ;mlog_errno (ret);
@@ -4792,13 +4934,13 @@ int ocfs2_remove_extent(struct inode *inode, struct buffer_head *di_bh,if (le32_to_cpu (rec->e_cpos) == cpos || rec_range == trunc_range) { ret =ocfs2_truncate_rec (inode, handle, path, index, dealloc, - cpos, len); + cpos, len, et);if (ret) {mlog_errno (ret); goto out; } }else { - ret =ocfs2_split_tree (inode, di_bh, handle, path, index, + ret =ocfs2_split_tree (inode, et, handle, path, index, trunc_range, meta_ac);if (ret) {mlog_errno (ret);
@@ -4847,7 +4989,7 @@ int ocfs2_remove_extent(struct inode *inode, struct buffer_head *di_bh, } ret =ocfs2_truncate_rec (inode, handle, path, index, dealloc, - cpos, len); + cpos, len, et);if (ret) {mlog_errno (ret); goto out;
@@ -4856,6 +4998,8 @@ int ocfs2_remove_extent(struct inode *inode, struct buffer_head *di_bh, out:ocfs2_free_path (path); +if (et) +ocfs2_free_extent_tree (et);return ret; }
@@ -6364,7 +6508,8 @@ int ocfs2_convert_inline_data_to_extents(struct inode *inode,* the in-inode data from our pages. */ ret =ocfs2_insert_extent (osb, handle, inode, di_bh, -0 , block,1 ,0 ,NULL ); +0 , block,1 ,0 , +NULL ,OCFS2_DINODE_EXTENT );if (ret) {mlog_errno (ret); goto out_commit;
@@ -6406,13 +6551,14 @@ int ocfs2_commit_truncate(struct ocfs2_super *osb,handle_t *handle =NULL ;struct inode *tl_inode = osb->osb_tl_inode;struct ocfs2_path *path =NULL ; +struct ocfs2_dinode *di = (struct ocfs2_dinode *)fe_bh->b_data;mlog_entry_void (); new_highest_cpos =ocfs2_clusters_for_bytes (osb->sb,i_size_read (inode)); - path =ocfs2_new_inode_path (fe_bh); + path =ocfs2_new_path (fe_bh, &di->id2.i_list);if (!path) { status = -ENOMEM ;mlog_errno (status);
fs/ocfs2/alloc.h +16 -7
@@ -26,28 +26,37 @@#ifndef OCFS2_ALLOC_H #define OCFS2_ALLOC_H +enum ocfs2_extent_tree_type { +OCFS2_DINODE_EXTENT =0 , +}; +struct ocfs2_alloc_context ;int ocfs2_insert_extent (struct ocfs2_super *osb,handle_t *handle,struct inode *inode, -struct buffer_head *fe_bh, +struct buffer_head *root_bh,u32 cpos,u64 start_blk,u32 new_clusters,u8 flags, -struct ocfs2_alloc_context *meta_ac); +struct ocfs2_alloc_context *meta_ac, +enum ocfs2_extent_tree_type et_type);struct ocfs2_cached_dealloc_ctxt ; -int ocfs2_mark_extent_written (struct inode *inode,struct buffer_head *di_bh, +int ocfs2_mark_extent_written (struct inode *inode,struct buffer_head *root_bh,handle_t *handle,u32 cpos,u32 len,u32 phys,struct ocfs2_alloc_context *meta_ac, -struct ocfs2_cached_dealloc_ctxt *dealloc); -int ocfs2_remove_extent (struct inode *inode,struct buffer_head *di_bh, +struct ocfs2_cached_dealloc_ctxt *dealloc, +enum ocfs2_extent_tree_type et_type); +int ocfs2_remove_extent (struct inode *inode,struct buffer_head *root_bh,u32 cpos,u32 len,handle_t *handle,struct ocfs2_alloc_context *meta_ac, -struct ocfs2_cached_dealloc_ctxt *dealloc); +struct ocfs2_cached_dealloc_ctxt *dealloc, +enum ocfs2_extent_tree_type et_type);int ocfs2_num_free_extents (struct ocfs2_super *osb,struct inode *inode, -struct buffer_head *bh); +struct buffer_head *root_bh, +enum ocfs2_extent_tree_type et_type); +/* * how many new metadata chunks would an allocation need at maximum? *
fs/ocfs2/aops.c +9 -2
@@ -1275,7 +1275,8 @@ static int ocfs2_write_cluster(struct address_space *mapping, }else if (unwritten) { ret =ocfs2_mark_extent_written (inode, wc->w_di_bh, wc->w_handle, cpos,1 , phys, - meta_ac, &wc->w_dealloc); + meta_ac, &wc->w_dealloc, +OCFS2_DINODE_EXTENT );if (ret <0 ) {mlog_errno (ret); goto out;
@@ -1709,7 +1710,13 @@ int ocfs2_write_begin_nolock(struct address_space *mapping,* ocfs2_lock_allocators(). It greatly over-estimates * the work to be done. */ - ret =ocfs2_lock_allocators (inode, wc->w_di_bh, +mlog (0 ,"extend inode %llu, i_size = %lld, di->i_clusters = %u," +" clusters_to_add = %u, extents_to_split = %u\n" , + (unsigned long long )OCFS2_I (inode)->ip_blkno, + (long long )i_size_read (inode),le32_to_cpu (di->i_clusters), + clusters_to_alloc, extents_to_split); + + ret =ocfs2_lock_allocators (inode, wc->w_di_bh, &di->id2.i_list, clusters_to_alloc, extents_to_split, &data_ac, &meta_ac);if (ret) {
fs/ocfs2/dir.c +4 -3
@@ -1307,7 +1307,7 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh,* related blocks have been journaled already. */ ret =ocfs2_insert_extent (osb, handle, dir, di_bh,0 , blkno, len,0 , -NULL ); +NULL ,OCFS2_DINODE_EXTENT );if (ret) {mlog_errno (ret); goto out;
@@ -1333,7 +1333,7 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh, blkno =ocfs2_clusters_to_blocks (dir->i_sb, bit_off); ret =ocfs2_insert_extent (osb, handle, dir, di_bh,1 , blkno, - len,0 ,NULL ); + len,0 ,NULL ,OCFS2_DINODE_EXTENT );if (ret) {mlog_errno (ret); goto out;
@@ -1476,7 +1476,8 @@ static int ocfs2_extend_dir(struct ocfs2_super *osb,if (dir_i_size ==ocfs2_clusters_to_bytes (sb,OCFS2_I (dir)->ip_clusters)) {spin_unlock (&OCFS2_I (dir)->ip_lock); num_free_extents =ocfs2_num_free_extents (osb, dir, - parent_fe_bh); + parent_fe_bh, +OCFS2_DINODE_EXTENT );if (num_free_extents <0 ) { status = num_free_extents;mlog_errno (status);
fs/ocfs2/file.c +13 -91
140 lines not on this page · raw message
fs/ocfs2/file.h +0 -4
@@ -55,10 +55,6 @@ int ocfs2_do_extend_allocation(struct ocfs2_super *osb, enum ocfs2_alloc_restarted *reason_ret);int ocfs2_extend_no_holes (struct inode *inode,u64 new_i_size,u64 zero_to); -int ocfs2_lock_allocators (struct inode *inode,struct buffer_head *fe, -u32 clusters_to_add,u32 extents_to_split, -struct ocfs2_alloc_context **data_ac, -struct ocfs2_alloc_context **meta_ac);int ocfs2_setattr (struct dentry *dentry,struct iattr *attr);int ocfs2_getattr (struct vfsmount *mnt,struct dentry *dentry,struct kstat *stat);
fs/ocfs2/suballoc.c +82 -0
85 lines not on this page · raw message
fs/ocfs2/suballoc.h +5 -0
@@ -161,4 +161,9 @@ u64 ocfs2_which_cluster_group(struct inode *inode, u32 cluster);int ocfs2_check_group_descriptor (struct super_block *sb,struct ocfs2_dinode *di,struct ocfs2_group_desc *gd); +int ocfs2_lock_allocators (struct inode *inode,struct buffer_head *root_bh, +struct ocfs2_extent_list *root_el, +u32 clusters_to_add,u32 extents_to_split, +struct ocfs2_alloc_context **data_ac, +struct ocfs2_alloc_context **meta_ac);#endif /* _CHAINALLOC_H_ */
Signature · 1 line
1.5.4.GIT
PATCH 3/15 Abstract ocfs2_extent_tree in b-tree operations.
Quoted 18 lines · Tao Ma
In the old extent tree operation, we take the hypothesis that we are using the
ocfs2_extent_listinocfs2_dinodeas the tree root. As xattr will also useocfs2_extent_listto store large value for a xattr entry, we refactor the tree operation so that xattr can use it directly.The refactoring includes 4 steps:
1. Abstract set/get oflast_eb_blkandupdate_clusterssince they may be stored in different location for dinode and xattr.
2. Add a new structure namedocfs2_extent_treeto indicate the extent tree the operation will work on.
3. Remove all the use offe_bhand di, useroot_bhandroot_elin extent tree instead. So now all thefe_bhis replaced withet->root_bh, el withroot_elaccordingly.
4. Makeocfs2_lock_allocatorsgeneric. Now it is limited to be only used in file extend allocation. But the whole function is useful when we want to store large EAs.
Quoted 3 lines
Note: This patch hasn't touch the code of
ocfs2_commit_truncatesince it isn't has been modified with the new tree operation.
So all the truncating code deem that the tree root isocfs2_dinode.
Ok, I love this patch. I just have a couple questions.
fs/ocfs2/alloc.c line 74
Quoted 8 lines
fs/ocfs2/alloc.c +8
-0+struct ocfs2_extent_tree_operations { +void (*set_last_eb_blk) (struct ocfs2_extent_tree *et,u64 blkno); +u64 (*get_last_eb_blk) (struct ocfs2_extent_tree *et); +void (*update_clusters) (struct inode *inode, +struct ocfs2_extent_tree *et, +u32 new_clusters); +int (*sanity_check) (struct inode *inode,struct ocfs2_extent_tree *et); +};
Can we please prefix these? et_set_last_eb_blk,et_update_clusters, etc.
fs/ocfs2/alloc.c line 81
Quoted 6 lines
fs/ocfs2/alloc.c +6
-0+struct ocfs2_extent_tree { +enum ocfs2_extent_tree_type type; +struct ocfs2_extent_tree_operations *eops; +struct buffer_head *root_bh; +struct ocfs2_extent_list *root_el; +};
And these too? et_type, et_ops, et_root_bh, et_root_el.
Quoted 22 lines
int ocfs2_num_free_extents(struct
ocfs2_super*osb,fs/ocfs2/alloc.c +6
-7struct inode *inode, - struct buffer_head *bh) + struct buffer_head *root_bh, + enum ocfs2_extent_tree_type type) { int retval; -struct ocfs2_extent_list *el; +struct ocfs2_extent_list *el =NULL ;struct ocfs2_extent_block *eb;struct buffer_head *eb_bh =NULL ; -struct ocfs2_dinode *fe = (struct ocfs2_dinode *)bh->b_data; +u64 last_eb_blk =0 ;mlog_entry_void (); -if (!OCFS2_IS_VALID_DINODE (fe)) { -OCFS2_RO_ON_INVALID_DINODE (inode->i_sb, fe); - retval = -EIO ; - goto bail; +if (type ==OCFS2_DINODE_EXTENT ) { + struct ocfs2_dinode *fe =
fs/ocfs2/alloc.c struct ocfs2_merge_ctxt { · line 519
Quoted 10 lines
fs/ocfs2/alloc.c +10
-0+ (struct ocfs2_dinode *)root_bh->b_data; +if (!OCFS2_IS_VALID_DINODE (fe)) { +OCFS2_RO_ON_INVALID_DINODE (inode->i_sb, fe); + retval = -EIO ; + goto bail; + } + +if (fe->i_last_eb_blk) + last_eb_blk =le64_to_cpu (fe->i_last_eb_blk); + el = &fe->id2.i_list;
Why isn't this using ocfs2_new_extent_tree() and thenocfs2_get_last_eb_lock()? You do that every other place, and it keeps you from having to special case each type here.
Joel
Signature · 9 lines
"Maybe the time has drawn the faces I recall. But things in this life change very slowly, If they ever change at all." Joel Becker Principal Software Developer Oracle E-mail: joel.becker at oracle.com Phone: (650) 506-8127
PATCH 3/15 Abstract ocfs2_extent_tree in b-tree operations.
Quoted 12 lines · Joel Becker
fs/ocfs2/alloc.c line 74
Quoted 9 lines
fs/ocfs2/alloc.c +8
-0+struct ocfs2_extent_tree_operations { +void (*set_last_eb_blk) (struct ocfs2_extent_tree *et,u64 blkno); +u64 (*get_last_eb_blk) (struct ocfs2_extent_tree *et); +void (*update_clusters) (struct inode *inode, +struct ocfs2_extent_tree *et, +u32 new_clusters); +int (*sanity_check) (struct inode *inode,struct ocfs2_extent_tree *et); +};Can we please prefix these?
et_set_last_eb_blk,et_update_clusters, etc.
ok.
Quoted 9 lines
fs/ocfs2/alloc.c line 81
Quoted 7 lines
fs/ocfs2/alloc.c +6
-0+struct ocfs2_extent_tree { +enum ocfs2_extent_tree_type type; +struct ocfs2_extent_tree_operations *eops; +struct buffer_head *root_bh; +struct ocfs2_extent_list *root_el; +};And these too?
et_type,et_ops,et_root_bh,et_root_el.
ok.
Quoted 29 lines
Quoted 29 lines
int ocfs2_num_free_extents(struct
ocfs2_super*osb,fs/ocfs2/alloc.c +12
-7struct inode *inode, - struct buffer_head *bh) + struct buffer_head *root_bh, + enum ocfs2_extent_tree_type type) { int retval; -struct ocfs2_extent_list *el; +struct ocfs2_extent_list *el =NULL ;struct ocfs2_extent_block *eb;struct buffer_head *eb_bh =NULL ; -struct ocfs2_dinode *fe = (struct ocfs2_dinode *)bh->b_data; +u64 last_eb_blk =0 ;mlog_entry_void (); -if (!OCFS2_IS_VALID_DINODE (fe)) { -OCFS2_RO_ON_INVALID_DINODE (inode->i_sb, fe); - retval = -EIO ; - goto bail; +if (type ==OCFS2_DINODE_EXTENT ) { +struct ocfs2_dinode *fe = + (struct ocfs2_dinode *)root_bh->b_data; +if (!OCFS2_IS_VALID_DINODE (fe)) { +OCFS2_RO_ON_INVALID_DINODE (inode->i_sb, fe); + retval = -EIO ; + goto bail; + }
Quoted 8 lines
fs/ocfs2/alloc.c struct ocfs2_merge_ctxt { · line 519
Quoted 4 lines
fs/ocfs2/alloc.c +4
-0+ +if (fe->i_last_eb_blk) + last_eb_blk =le64_to_cpu (fe->i_last_eb_blk); + el = &fe->id2.i_list;Why isn't this using
ocfs2_new_extent_tree()and thenocfs2_get_last_eb_lock()? You do that every other place, and it keeps you from having to special case each type here.
yes, you are right. Will modify it. Thanks.
Regards,
Tao
PATCH 4/15 Make extend allocation generic.
In reply to Tao MaThe old ocfs2_do_extend_allocation is restrictly to be used in file extension. Now a new function named ocfs2_do_cluster_allocation will handle the issue of generic extend allocation and it is created in suballoc.c.
--- fs/ocfs2/aops.c | 8 ++-- fs/ocfs2/dir.c | 6 +- fs/ocfs2/file.c | 136 ++++++++++----------------------------------------- fs/ocfs2/file.h | 26 ++++------ fs/ocfs2/namei.c | 8 ++-- fs/ocfs2/suballoc.c | 110 +++++++++++++++++++++++++++++++++++++++++ fs/ocfs2/suballoc.h | 18 +++++++ 7 files changed, 177 insertions(+), 135 deletions(-)
fs/ocfs2/aops.c +4 -4
@@ -1252,10 +1252,10 @@ static int ocfs2_write_cluster(struct address_space *mapping,* any additional semaphores or cluster locks. */ tmp_pos = cpos; - ret =ocfs2_do_extend_allocation (OCFS2_SB (inode->i_sb), inode, - &tmp_pos,1 ,0 , wc->w_di_bh, - wc->w_handle, data_ac, - meta_ac,NULL ); + ret =ocfs2_add_inode_data (OCFS2_SB (inode->i_sb), inode, + &tmp_pos,1 ,0 , wc->w_di_bh, + wc->w_handle, data_ac, + meta_ac,NULL );/* * This shouldn't happen because we must have already * calculated the correct meta data allocation required. The
fs/ocfs2/dir.c +3 -3
@@ -1378,9 +1378,9 @@ static int ocfs2_do_extend_dir(struct super_block *sb,if (extend) {u32 offset =OCFS2_I (dir)->ip_clusters; - status =ocfs2_do_extend_allocation (OCFS2_SB (sb), dir, &offset, -1 ,0 , parent_fe_bh, handle, - data_ac, meta_ac,NULL ); + status =ocfs2_add_inode_data (OCFS2_SB (sb), dir, &offset, +1 ,0 , parent_fe_bh, handle, + data_ac, meta_ac,NULL );BUG_ON (status == -EAGAIN );if (status <0 ) {mlog_errno (status);
fs/ocfs2/file.c +27 -109
157 lines not on this page · raw message
fs/ocfs2/file.h +11 -15
@@ -31,6 +31,7 @@ extern const struct file_operations ocfs2_dops;extern const struct inode_operations ocfs2_file_iops;extern const struct inode_operations ocfs2_special_file_iops;struct ocfs2_alloc_context ; +enum ocfs2_alloc_restarted ;struct ocfs2_file_private {struct file *fp_file;
@@ -38,21 +39,16 @@ struct ocfs2_file_private {struct ocfs2_lock_res fp_flock; }; -enum ocfs2_alloc_restarted { -RESTART_NONE =0 , -RESTART_TRANS , -RESTART_META -}; -int ocfs2_do_extend_allocation (struct ocfs2_super *osb, -struct inode *inode, -u32 *logical_offset, -u32 clusters_to_add, -int mark_unwritten, -struct buffer_head *fe_bh, -handle_t *handle, -struct ocfs2_alloc_context *data_ac, -struct ocfs2_alloc_context *meta_ac, -enum ocfs2_alloc_restarted *reason_ret); +int ocfs2_add_inode_data (struct ocfs2_super *osb, +struct inode *inode, +u32 *logical_offset, +u32 clusters_to_add, +int mark_unwritten, +struct buffer_head *fe_bh, +handle_t *handle, +struct ocfs2_alloc_context *data_ac, +struct ocfs2_alloc_context *meta_ac, +enum ocfs2_alloc_restarted *reason_ret);int ocfs2_extend_no_holes (struct inode *inode,u64 new_i_size,u64 zero_to);int ocfs2_setattr (struct dentry *dentry,struct iattr *attr);
fs/ocfs2/namei.c +4 -4
@@ -1598,10 +1598,10 @@ static int ocfs2_symlink(struct inode *dir,u32 offset =0 ; inode->i_op = &ocfs2_symlink_inode_operations; - status =ocfs2_do_extend_allocation (osb, inode, &offset,1 ,0 , - new_fe_bh, - handle, data_ac,NULL , -NULL ); + status =ocfs2_add_inode_data (osb, inode, &offset,1 ,0 , + new_fe_bh, + handle, data_ac,NULL , +NULL );if (status <0 ) {if (status != -ENOSPC && status != -EINTR ) { mlog(ML_ERROR,
fs/ocfs2/suballoc.c +110 -0
113 lines not on this page · raw message
fs/ocfs2/suballoc.h +18 -0
@@ -166,4 +166,22 @@ int ocfs2_lock_allocators(struct inode *inode, struct buffer_head *root_bh, u32 clusters_to_add, u32 extents_to_split, struct ocfs2_alloc_context **data_ac, struct ocfs2_alloc_context **meta_ac); + +enum ocfs2_alloc_restarted { +RESTART_NONE =0 , +RESTART_TRANS , +RESTART_META +}; +int ocfs2_add_clusters_in_btree (struct ocfs2_super *osb, +struct inode *inode, +u32 *logical_offset, +u32 clusters_to_add, +int mark_unwritten, +struct buffer_head *root_bh, +struct ocfs2_extent_list *root_el, +handle_t *handle, +struct ocfs2_alloc_context *data_ac, +struct ocfs2_alloc_context *meta_ac, +enum ocfs2_alloc_restarted *reason_ret, +enum ocfs2_extent_tree_type type);#endif /* _CHAINALLOC_H_ */
Signature · 1 line
1.5.4.GIT
PATCH 4/15 Make extend allocation generic.
Quoted 4 lines · Tao Ma
The old
ocfs2_do_extend_allocationis restrictly to be used in file extension. Now a new function namedocfs2_do_cluster_allocationwill handle the issue of generic extend allocation and it is created insuballoc.c.
<snip>
fs/ocfs2/suballoc.c out: · line 1995
Quoted 12 lines
fs/ocfs2/suballoc.c +12
-0+int ocfs2_add_clusters_in_btree (struct ocfs2_super *osb, +struct inode *inode, +u32 *logical_offset, +u32 clusters_to_add, +int mark_unwritten, +struct buffer_head *root_bh, +struct ocfs2_extent_list *root_el, +handle_t *handle, +struct ocfs2_alloc_context *data_ac, +struct ocfs2_alloc_context *meta_ac, +enum ocfs2_alloc_restarted *reason_ret, +enum ocfs2_extent_tree_type type)
It seems to me that if you have root_bh and type, you can create
an ocfs2_extent_tree and calculate root_el from that. So you don't need root_el in this function's arguments, and callers don't need to know how to compute it.
Joel
Quoted 90 lines
fs/ocfs2/suballoc.c +90
-0+{ +int status =0 ; +int free_extents; +enum ocfs2_alloc_restarted reason =RESTART_NONE ; +u32 bit_off, num_bits; +u64 block; +u8 flags =0 ; + +BUG_ON (!clusters_to_add); + +if (mark_unwritten) + flags =OCFS2_EXT_UNWRITTEN ; + + free_extents =ocfs2_num_free_extents (osb, inode, root_bh, type); +if (free_extents <0 ) { + status = free_extents; +mlog_errno (status); + goto leave; + } + +/* there are two cases which could cause us to EAGAIN in the +* we-need-more-metadata case: +* 1) we haven't reserved *any* +* 2) we are so fragmented, we've needed to add metadata too +* many times. */ +if (!free_extents && !meta_ac) { +mlog (0 ,"we haven't reserved any metadata!\n" ); + status = -EAGAIN ; + reason =RESTART_META ; + goto leave; + }else if ((!free_extents) + && (ocfs2_alloc_context_bits_left (meta_ac) + <ocfs2_extend_meta_needed (root_el))) { +mlog (0 ,"filesystem is really fragmented...\n" ); + status = -EAGAIN ; + reason =RESTART_META ; + goto leave; + } + + status =__ocfs2_claim_clusters (osb, handle, data_ac,1 , + clusters_to_add, &bit_off, &num_bits); +if (status <0 ) { +if (status != -ENOSPC ) +mlog_errno (status); + goto leave; + } + +BUG_ON (num_bits > clusters_to_add); + +/* reserve our write early -- insert_extent may update the inode */ + status =ocfs2_journal_access (handle, inode, root_bh, +OCFS2_JOURNAL_ACCESS_WRITE ); +if (status <0 ) { +mlog_errno (status); + goto leave; + } + + block =ocfs2_clusters_to_blocks (osb->sb, bit_off); +mlog (0 ,"Allocating %u clusters at block %u for inode %llu\n" , + num_bits, bit_off, (unsigned long long )OCFS2_I (inode)->ip_blkno); + status =ocfs2_insert_extent (osb, handle, inode, root_bh, + *logical_offset, block, num_bits, + flags, meta_ac, type); +if (status <0 ) { +mlog_errno (status); + goto leave; + } + + status =ocfs2_journal_dirty (handle, root_bh); +if (status <0 ) { +mlog_errno (status); + goto leave; + } + + clusters_to_add -= num_bits; + *logical_offset += num_bits; + +if (clusters_to_add) { +mlog (0 ,"need to alloc once more, wanted = %u\n" , + clusters_to_add); + status = -EAGAIN ; + reason =RESTART_TRANS ; + } + +leave: +mlog_exit (status); +if (reason_ret) + *reason_ret = reason; +return status; +}
Signature · 8 lines
"You don't make the poor richer by making the rich poorer." - Sir Winston Churchill Joel Becker Principal Software Developer Oracle E-mail: joel.becker at oracle.com Phone: (650) 506-8127
PATCH 4/15 Make extend allocation generic.
Quoted 17 lines · Joel Becker
Quoted 4 lines · Tao Ma
The old
ocfs2_do_extend_allocationis restrictly to be used in file extension. Now a new function namedocfs2_do_cluster_allocationwill handle the issue of generic extend allocation and it is created insuballoc.c.<snip>
Quoted 9 lines
fs/ocfs2/suballoc.c +9
-0+int ocfs2_add_clusters_in_btree(struct ocfs2_super *osb, + struct inode *inode, + u32 *logical_offset, + u32 clusters_to_add, + int mark_unwritten, + struct buffer_head *root_bh, + struct ocfs2_extent_list *root_el, + handle_t *handle, + struct ocfs2_alloc_context *data_ac,
Quoted 8 lines
fs/ocfs2/suballoc.c out: · line 1995
Quoted 3 lines
fs/ocfs2/suballoc.c +3
-0+struct ocfs2_alloc_context *meta_ac, + enum ocfs2_alloc_restarted *reason_ret, + enum ocfs2_extent_tree_type type)It seems to me that if you have
root_bhand type, you can create
anocfs2_extent_treeand calculateroot_elfrom that. So you don't needroot_elin this function's arguments, and callers don't need to know how to compute it.
No we can't. For a ocfs2_xattr_value_root. It can be stored in any place of a buffer_head, so we have to give it to this function.
Regards,
Tao
PATCH 4/15 Make extend allocation generic.
Quoted 26 lines · Tao Ma
Quoted 17 lines · Joel Becker
Quoted 4 lines · Tao Ma
The old
ocfs2_do_extend_allocationis restrictly to be used in file extension. Now a new function namedocfs2_do_cluster_allocationwill handle the issue of generic extend allocation and it is created insuballoc.c.<snip>
Quoted 9 lines
fs/ocfs2/suballoc.c +9
-0+int ocfs2_add_clusters_in_btree(struct ocfs2_super *osb, + struct inode *inode, + u32 *logical_offset, + u32 clusters_to_add, + int mark_unwritten, + struct buffer_head *root_bh, + struct ocfs2_extent_list *root_el, + handle_t *handle, + struct ocfs2_alloc_context *data_ac,Quoted 8 lines
fs/ocfs2/suballoc.c out: · line 1995
Quoted 3 lines
fs/ocfs2/suballoc.c +3
-0+struct ocfs2_alloc_context *meta_ac, + enum ocfs2_alloc_restarted *reason_ret, + enum ocfs2_extent_tree_type type)It seems to me that if you have
root_bhand type, you can create
anocfs2_extent_treeand calculateroot_elfrom that. So you don't needroot_elin this function's arguments, and callers don't need to know how to compute it.
Quoted 2 lines
No we can't. For a
ocfs2_xattr_value_root. It can be stored in any place of abuffer_head, so we have to give it to this function.
But don't you later turn a private pointer into that thing?
Joel
Signature · 8 lines
"I have never let my schooling interfere with my education."
- Mark Twain
Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127PATCH 4/15 Make extend allocation generic.
Quoted 25 lines · Joel Becker
Quoted 24 lines · Tao Ma
Quoted 23 lines · Joel Becker
Quoted 4 lines · Tao Ma
The old
ocfs2_do_extend_allocationis restrictly to be used in file extension. Now a new function namedocfs2_do_cluster_allocationwill handle the issue of generic extend allocation and it is created insuballoc.c.<snip>
fs/ocfs2/suballoc.c out: · line 1995
Quoted 12 lines
fs/ocfs2/suballoc.c +12
-0+int ocfs2_add_clusters_in_btree (struct ocfs2_super *osb, +struct inode *inode, +u32 *logical_offset, +u32 clusters_to_add, +int mark_unwritten, +struct buffer_head *root_bh, +struct ocfs2_extent_list *root_el, +handle_t *handle, +struct ocfs2_alloc_context *data_ac, +struct ocfs2_alloc_context *meta_ac, +enum ocfs2_alloc_restarted *reason_ret, +enum ocfs2_extent_tree_type type)It seems to me that if you have
root_bhand type, you can create
anocfs2_extent_treeand calculateroot_elfrom that. So you don't needroot_elin this function's arguments, and callers don't need to know how to compute it.
Quoted 4 lines
Quoted 2 lines
No we can't. For a
ocfs2_xattr_value_root. It can be stored in any place of abuffer_head, so we have to give it to this function.But don't you later turn a private pointer into that thing?
I think I know your meaning now.
I have gone through the function and it doesn't create ocfs2_extent_tree. So I don't think it is worth for us to just erase a "parameter" in kernel stack while doing kmalloc+kfree, right?
Regards,
Tao
PATCH 4/15 Make extend allocation generic.
Quoted 28 lines · Tao Ma
Quoted 25 lines · Joel Becker
Quoted 24 lines · Tao Ma
Quoted 23 lines · Joel Becker
Quoted 4 lines · Tao Ma
The old
ocfs2_do_extend_allocationis restrictly to be used in file extension. Now a new function namedocfs2_do_cluster_allocationwill handle the issue of generic extend allocation and it is created insuballoc.c.<snip>
fs/ocfs2/suballoc.c out: · line 1995
Quoted 12 lines
fs/ocfs2/suballoc.c +12
-0+int ocfs2_add_clusters_in_btree (struct ocfs2_super *osb, +struct inode *inode, +u32 *logical_offset, +u32 clusters_to_add, +int mark_unwritten, +struct buffer_head *root_bh, +struct ocfs2_extent_list *root_el, +handle_t *handle, +struct ocfs2_alloc_context *data_ac, +struct ocfs2_alloc_context *meta_ac, +enum ocfs2_alloc_restarted *reason_ret, +enum ocfs2_extent_tree_type type)It seems to me that if you have
root_bhand type, you can create
anocfs2_extent_treeand calculateroot_elfrom that. So you don't needroot_elin this function's arguments, and callers don't need to know how to compute it.
Quoted 8 lines
Quoted 4 lines
Quoted 2 lines
No we can't. For a
ocfs2_xattr_value_root. It can be stored in any place of abuffer_head, so we have to give it to this function.But don't you later turn a private pointer into that thing?
I think I know your meaning now.
I have gone through the function and it doesn't createocfs2_extent_tree. So I don't think it is worth for us to just erase a "parameter" in kernel stack while doing kmalloc+kfree, right?
Why are you kmalloc()ing at all? Every place you useocfs2_extent_tree() does this:
struct ocfs2_extent_tree *et = NULL; et = ocfs2_new_extent_tree(bh, type); do_some_alloc(et); if (et) ocfs2_free_extent_tree(et);
You never actually pass the extent tree up to the caller or store it on a pointer somewhere. You could just as easily do this:
struct ocfs2_extent_tree et; ocfs2_fill_extent_tree(&et, bh, type); do_some_alloc(&et); ocfs2_drop_extent_tree(&et);
and never kmalloc at all. The et just lives on the current function's stack, safely passed to all sub functions.
If you ever do need to allocate an extent tree, you can always
kzalloc it and then call fill_extent_tree. But since allocating is the very rare case, why do it by default?
Joel
Signature · 12 lines
"There is shadow under this red rock. (Come in under the shadow of this red rock) And I will show you something different from either Your shadow at morning striding behind you Or your shadow at evening rising to meet you. I will show you fear in a handful of dust." Joel Becker Principal Software Developer Oracle E-mail: joel.becker at oracle.com Phone: (650) 506-8127
PATCH 4/15 Make extend allocation generic.
Quoted 55 lines · Joel Becker
Quoted 27 lines · Tao Ma
Quoted 25 lines · Joel Becker
Quoted 24 lines · Tao Ma
Quoted 23 lines · Joel Becker
Quoted 4 lines · Tao Ma
The old
ocfs2_do_extend_allocationis restrictly to be used in file extension. Now a new function namedocfs2_do_cluster_allocationwill handle the issue of generic extend allocation and it is created insuballoc.c.<snip>
fs/ocfs2/suballoc.c out: · line 1995
Quoted 12 lines
fs/ocfs2/suballoc.c +12
-0+int ocfs2_add_clusters_in_btree (struct ocfs2_super *osb, +struct inode *inode, +u32 *logical_offset, +u32 clusters_to_add, +int mark_unwritten, +struct buffer_head *root_bh, +struct ocfs2_extent_list *root_el, +handle_t *handle, +struct ocfs2_alloc_context *data_ac, +struct ocfs2_alloc_context *meta_ac, +enum ocfs2_alloc_restarted *reason_ret, +enum ocfs2_extent_tree_type type)It seems to me that if you have
root_bhand type, you can create
anocfs2_extent_treeand calculateroot_elfrom that. So you don't needroot_elin this function's arguments, and callers don't need to know how to compute it.Quoted 7 lines
Quoted 3 lines
Quoted 2 lines
No we can't. For a
ocfs2_xattr_value_root. It can be stored in any place of abuffer_head, so we have to give it to this function.But don't you later turn a private pointer into that thing?
I think I know your meaning now.
I have gone through the function and it doesn't createocfs2_extent_tree. So I don't think it is worth for us to just erase a "parameter" in kernel stack while doing kmalloc+kfree, right?Why are you kmalloc()ing at all? Every place you use
ocfs2_extent_tree()does this:struct ocfs2_extent_tree *et = NULL; et = ocfs2_new_extent_tree(bh, type); do_some_alloc(et); if (et) ocfs2_free_extent_tree(et);You never actually pass the extent tree up to the caller or store it on a pointer somewhere. You could just as easily do this:
struct ocfs2_extent_tree et; ocfs2_fill_extent_tree(&et, bh, type); do_some_alloc(&et); ocfs2_drop_extent_tree(&et);
Quoted 5 lines
and never kmalloc at all. The et just lives on the current function's stack, safely passed to all sub functions.
If you ever do need to allocate an extent tree, you can always
kzalloc it and then callfill_extent_tree. But since allocating is the very rare case, why do it by default?
yes, you are right. I will adjust the initialization of ocfs2_extent_tree. thanks.
But as for the original case which you ask me not to pass root_el, it isn't suitable since root_el need less stack length and more readable than we initiate a ocfs2_extent_tree here.
btw, do you have time to review all the other patches? If yes, I will wait until you review all of them and send out next round then.
Regards,
Tao
PATCH 4/15 Make extend allocation generic.
Quoted 2 lines · Tao Ma
btw, do you have time to review all the other patches? If yes, I will wait until you review all of them and send out next round then.
Can you send the latest stuff, with all fixes that have already been discussed up until today, and then address any other issues Joel might find in his review as follow-up patches, based on the branch I create in ocfs2.git?
I definitely want whatever review Joel can give, but I also want to get this up in ocfs2.git ASAP. Since I'm very confident that the disk format is essentially frozen, we can merge more or less immediately and get this stuff exposure in the various trees that pick up ocfs2.git.
I'll worry about folding follow-up patches back into the main series once all review is in. That should free you to concentrate on addressing issues as they come up.
Thanks,
--Mark (Dusting off his merge-monkey hat)
--
Mark Fasheh
PATCH 4/15 Make extend allocation generic.
Quoted 9 lines · Mark Fasheh
Quoted 2 lines · Tao Ma
btw, do you have time to review all the other patches? If yes, I will wait until you review all of them and send out next round then.
Can you send the latest stuff, with all fixes that have already been discussed up until today, and then address any other issues Joel might find in his review as follow-up patches, based on the branch I create in ocfs2.git?
Quoted 4 lines
I definitely want whatever review Joel can give, but I also want to get this up in ocfs2.git ASAP. Since I'm very confident that the disk format is essentially frozen, we can merge more or less immediately and get this stuff exposure in the various trees that pick up ocfs2.git.
I agree with Mark. If the disk format is frozen - and I trust
you both on that - we want to get exposure more than we want to be perfect. Spin what you have and send it out.
I'm reviewing it so I can understand it :-) I don't think I
will be able to nail down a timeframe when I'll be done.
Joel
Signature · 9 lines
Dort wo man B?cher verbrennt, verbrennt man am Ende auch Mensch. (Wherever they burn books, they will also end up burning people.) - Heinrich Heine Joel Becker Principal Software Developer Oracle E-mail: joel.becker at oracle.com Phone: (650) 506-8127
PATCH 4/15 Make extend allocation generic.
In reply to Mark FashehHi Mark,
OK, I will contact Tiger to see whether we can send them out next Monday.
Regards,
Tao
Quoted 23 lines · Mark Fasheh
Quoted 2 lines · Tao Ma
btw, do you have time to review all the other patches? If yes, I will wait until you review all of them and send out next round then.
Can you send the latest stuff, with all fixes that have already been discussed up until today, and then address any other issues Joel might find in his review as follow-up patches, based on the branch I create in ocfs2.git?
I definitely want whatever review Joel can give, but I also want to get this up in ocfs2.git ASAP. Since I'm very confident that the disk format is essentially frozen, we can merge more or less immediately and get this stuff exposure in the various trees that pick up ocfs2.git.
I'll worry about folding follow-up patches back into the main series once all review is in. That should free you to concentrate on addressing issues as they come up.
Thanks,
--Mark (Dusting off his merge-monkey hat)--
Mark Fasheh
PATCH 4/15 Make extend allocation generic.
In reply to Tao MaQuoted 8 lines · Tao Ma
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
(quoted diff) +8
-0+int ocfs2_add_clusters_in_btree(struct ocfs2_super *osb, + struct inode *inode, + u32 *logical_offset, + u32 clusters_to_add, + int mark_unwritten, + struct buffer_head *root_bh, + struct ocfs2_extent_list *root_el, + handle_t *handle,
Quoted 8 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Review
Quoted 4 lines
(quoted diff) +4
-0+ struct ocfs2_alloc_context *data_ac, + struct ocfs2_alloc_context *meta_ac, + enum ocfs2_alloc_restarted *reason_ret, + enum ocfs2_extent_tree_type type)But as for the original case which you ask me not to pass
root_el, it isn't suitable sinceroot_elneed less stack length and more readable than we initiate aocfs2_extent_treehere.
That's a 12 argument function there. That's ugly. This isn't
about actual stack usage (you're right, we don't gain stack in my scheme). It's about readable functions.
In fact, struct ocfs2_alloc_context exists so that Mark could
pass one _ac down to a function rather than 5 arguments.
Why aren't you passing a struct ocfs2_extent_tree to this
function anyway? You could do it in the caller, pass it down, and then this function takes one *et rather than *root_bh+*root_el+type. Just a thought.
Mark knows this code better than I, so if he jumps in and says
"I find this 12 argument function more readable than what you propose", I will happily consider myself outvoted :-)
Joel
Signature · 8 lines
"If at first you don't succeed, cover all traces that you tried."
-Unknown
Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127PATCH 4/15 Make extend allocation generic.
Quoted 18 lines · Joel Becker
Quoted 8 lines · Tao Ma
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
(quoted diff) +8
-0+int ocfs2_add_clusters_in_btree(struct ocfs2_super *osb, + struct inode *inode, + u32 *logical_offset, + u32 clusters_to_add, + int mark_unwritten, + struct buffer_head *root_bh, + struct ocfs2_extent_list *root_el, + handle_t *handle,Quoted 8 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Review
Quoted 4 lines
(quoted diff) +4
-0+ struct ocfs2_alloc_context *data_ac, + struct ocfs2_alloc_context *meta_ac, + enum ocfs2_alloc_restarted *reason_ret, + enum ocfs2_extent_tree_type type)But as for the original case which you ask me not to pass
root_el, it isn't suitable sinceroot_elneed less stack length and more readable than we initiate aocfs2_extent_treehere.
Quoted 9 lines
That's a 12 argument function there. That's ugly. This isn't
about actual stack usage (you're right, we don't gain stack in my scheme). It's about readable functions.
In fact, structocfs2_alloc_contextexists so that Mark could
pass one_acdown to a function rather than 5 arguments.
Why aren't you passing a structocfs2_extent_treeto this
function anyway? You could do it in the caller, pass it down, and then this function takes one *et rather than *root_bh+*root_el+type. Just a thought.
I'd like to limit ocfs2_extent_tree in fs/ocfs2/alloc.c, so that the other files(dir.c, file.c, aops.c) don't need to know this structure. So is it suitable here?
Quoted 3 lines
Mark knows this code better than I, so if he jumps in and says
"I find this 12 argument function more readable than what you propose", I will happily consider myself outvoted :-)
OK, so Mark, please give me your vote. ;)
Regards,
Tao
PATCH 5/15 Add xattr header in ocfs2. v3
In reply to Tao MaModification from V2 to V3:
1. Add annotations for every field.
2. Change csum to be _le64 include xb_csum and xh_csum.
3. Change the definition of xe_local and xe_type and add set/get helper function for them.
--- fs/ocfs2/ocfs2_fs.h | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 118 insertions(+), 0 deletions(-)
fs/ocfs2/ocfs2_fs.h +118 -0
@@ -64,6 +64,7 @@#define OCFS2_INODE_SIGNATURE "INODE01"#define OCFS2_EXTENT_BLOCK_SIGNATURE "EXBLK01"#define OCFS2_GROUP_DESC_SIGNATURE "GROUP01" +#define OCFS2_XATTR_BLOCK_SIGNATURE "XATTR01"/* Compatibility flags */ #define OCFS2_HAS_COMPAT_FEATURE (sb,mask) \
@@ -712,6 +713,123 @@ struct ocfs2_group_desc/*40*/ __u8 bg_bitmap[0 ]; }; +/* +* On disk extended attribute structure for OCFS2. +*/ + +/* +* ocfs2_xattr_entry indicates one extend attribute. +* +* Note that it can be stored in inode, one block or one xattr bucket. +*/ +struct ocfs2_xattr_entry { +__le32 xe_name_hash;/* hash value of xattr prefix+suffix. */ +__le16 xe_name_offset;/* byte offset from the 1st etnry in the local +local xattr storage(inode, xattr block or +xattr bucket). */ +__u8 xe_name_len;/* xattr name len, does't include prefix. */ +__u8 xe_type;/* the low 7 bits indicates the name prefix's +* type and the highest 1 bits indicate whether +* the EA is stored in the local storage. */ +__le64 xe_value_size;/* real xattr value length. */ +}; + +/* +* On disk structure for xattr header. +* +* One ocfs2_xattr_header describes how many ocfs2_xattr_entry records in +* the local xattr storage. +*/ +struct ocfs2_xattr_header { +__le16 xh_count;/* contains the count of how +many records are in the +local xattr storage. */ +__le16 xh_reserved1; +__le32 xh_reserved2; +__le64 xh_csum; +struct ocfs2_xattr_entry xh_entries[0 ];/* xattr entry list. */ +}; + +/* +* On disk structure for xattr value root. +* +* It is used when one extended attribute's size is larger, and we will save it +* in an outside cluster. It will stored in a b-tree like file content. +*/ +struct ocfs2_xattr_value_root { +/*00*/ __le32 xr_clusters;/* clusters covered by xattr value. */ +__le32 xr_reserved0; +__le64 xr_last_eb_blk;/* Pointer to last extent block */ +/*10*/ struct ocfs2_extent_list xr_list;/* Extent record list */ +}; + +/* +* On disk structure for xattr tree root. +* +* It is used when there are too many extended attributes for one file. These +* attributes will be organized and stored in an indexed-btree. +*/ +struct ocfs2_xattr_tree_root { +/*00*/ __le32 xt_clusters;/* clusters covered by xattr. */ +__le32 xt_reserved0; +__le64 xt_last_eb_blk;/* Pointer to last extent block */ +/*10*/ struct ocfs2_extent_list xt_list;/* Extent record list */ +}; + +#define OCFS2_XATTR_INDEXED 0x1 + +/* +* On disk structure for xattr block. +*/ +struct ocfs2_xattr_block { +/*00*/ __u8 xb_signature[8 ];/* Signature for verification */ +__le16 xb_suballoc_slot;/* Slot suballocator this +block belongs to. */ +__le16 xb_suballoc_bit;/* Bit offset in suballocator +block group */ +__le32 xb_fs_generation;/* Must match super block */ +/*10*/ __le64 xb_blkno;/* Offset on disk, in blocks */ +__le64 xb_csum; +/*20*/ __le16 xb_flags;/* Indicates whether this block contains +real xattr or a xattr tree. */ +__le16 xb_reserved0; +__le32 xb_reserved1; +__le64 xb_reserved2; +/*30*/ union { +struct ocfs2_xattr_header xb_header;/* xattr header if this +block contains xattr */ +struct ocfs2_xattr_tree_root xb_root;/* xattr tree root if this +block cotains xattr +tree. */ + } xb_attrs; +}; + +#define OCFS2_XATTR_ENTRY_LOCAL 0x80 +#define OCFS2_XATTR_TYPE_MASK 0x7F +static inline void ocfs2_xattr_set_local (struct ocfs2_xattr_entry *xe, +int local) +{ +if (local) + xe->xe_type |=OCFS2_XATTR_ENTRY_LOCAL ; +else + xe->xe_type &= ~OCFS2_XATTR_ENTRY_LOCAL ; +} + +static inline int ocfs2_xattr_is_local (struct ocfs2_xattr_entry *xe) +{ +return xe->xe_type &OCFS2_XATTR_ENTRY_LOCAL ; +} + +static inline void ocfs2_xattr_set_type (struct ocfs2_xattr_entry *xe,int type) +{ + xe->xe_type |= type; +} + +static inline int ocfs2_xattr_get_type (struct ocfs2_xattr_entry *xe) +{ +return xe->xe_type &OCFS2_XATTR_TYPE_MASK ; +} +#ifdef __KERNEL__static inline int ocfs2_fast_symlink_chars (struct super_block *sb) {
Signature · 1 line
1.5.4.GIT
PATCH 5/15 Add xattr header in ocfs2. v3
Quoted 127 lines · Tao Ma
Modification from V2 to V3:
1. Add annotations for every field.
2. Change csum to be_le64includexb_csumandxh_csum.
3. Change the definition ofxe_localandxe_typeand add set/get helper function for them.Signed-off-by Tao Ma <[email protected]>--- fs/ocfs2/ocfs2_fs.h | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 118 insertions(+), 0 deletions(-)fs/ocfs2/ocfs2_fs.h +101
-0@@ -64,6 +64,7 @@#define OCFS2_INODE_SIGNATURE "INODE01"#define OCFS2_EXTENT_BLOCK_SIGNATURE "EXBLK01"#define OCFS2_GROUP_DESC_SIGNATURE "GROUP01" +#define OCFS2_XATTR_BLOCK_SIGNATURE "XATTR01"/* Compatibility flags */ #define OCFS2_HAS_COMPAT_FEATURE (sb,mask) \@@ -712,6 +713,123 @@ struct ocfs2_group_desc/*40*/ __u8 bg_bitmap[0 ]; }; +/* +* On disk extended attribute structure for OCFS2. +*/ + +/* +* ocfs2_xattr_entry indicates one extend attribute. +* +* Note that it can be stored in inode, one block or one xattr bucket. +*/ +struct ocfs2_xattr_entry { +__le32 xe_name_hash;/* hash value of xattr prefix+suffix. */ +__le16 xe_name_offset;/* byte offset from the 1st etnry in the local +local xattr storage(inode, xattr block or +xattr bucket). */ +__u8 xe_name_len;/* xattr name len, does't include prefix. */ +__u8 xe_type;/* the low 7 bits indicates the name prefix's +* type and the highest 1 bits indicate whether +* the EA is stored in the local storage. */ +__le64 xe_value_size;/* real xattr value length. */ +}; + +/* +* On disk structure for xattr header. +* +* One ocfs2_xattr_header describes how many ocfs2_xattr_entry records in +* the local xattr storage. +*/ +struct ocfs2_xattr_header { +__le16 xh_count;/* contains the count of how +many records are in the +local xattr storage. */ +__le16 xh_reserved1; +__le32 xh_reserved2; +__le64 xh_csum; +struct ocfs2_xattr_entry xh_entries[0 ];/* xattr entry list. */ +}; + +/* +* On disk structure for xattr value root. +* +* It is used when one extended attribute's size is larger, and we will save it +* in an outside cluster. It will stored in a b-tree like file content. +*/ +struct ocfs2_xattr_value_root { +/*00*/ __le32 xr_clusters;/* clusters covered by xattr value. */ +__le32 xr_reserved0; +__le64 xr_last_eb_blk;/* Pointer to last extent block */ +/*10*/ struct ocfs2_extent_list xr_list;/* Extent record list */ +}; + +/* +* On disk structure for xattr tree root. +* +* It is used when there are too many extended attributes for one file. These +* attributes will be organized and stored in an indexed-btree. +*/ +struct ocfs2_xattr_tree_root { +/*00*/ __le32 xt_clusters;/* clusters covered by xattr. */ +__le32 xt_reserved0; +__le64 xt_last_eb_blk;/* Pointer to last extent block */ +/*10*/ struct ocfs2_extent_list xt_list;/* Extent record list */ +}; + +#define OCFS2_XATTR_INDEXED 0x1 + +/* +* On disk structure for xattr block. +*/ +struct ocfs2_xattr_block { +/*00*/ __u8 xb_signature[8 ];/* Signature for verification */ +__le16 xb_suballoc_slot;/* Slot suballocator this +block belongs to. */ +__le16 xb_suballoc_bit;/* Bit offset in suballocator +block group */ +__le32 xb_fs_generation;/* Must match super block */ +/*10*/ __le64 xb_blkno;/* Offset on disk, in blocks */ +__le64 xb_csum; +/*20*/ __le16 xb_flags;/* Indicates whether this block contains +real xattr or a xattr tree. */ +__le16 xb_reserved0; +__le32 xb_reserved1; +__le64 xb_reserved2; +/*30*/ union { +struct ocfs2_xattr_header xb_header;/* xattr header if this +block contains xattr */ +struct ocfs2_xattr_tree_root xb_root;/* xattr tree root if this +block cotains xattr +tree. */ + } xb_attrs; +}; + +#define OCFS2_XATTR_ENTRY_LOCAL 0x80 +#define OCFS2_XATTR_TYPE_MASK 0x7F +static inline void ocfs2_xattr_set_local (struct ocfs2_xattr_entry *xe, +int local) +{ +if (local) + xe->xe_type |=OCFS2_XATTR_ENTRY_LOCAL ; +else + xe->xe_type &= ~OCFS2_XATTR_ENTRY_LOCAL ;
fs/ocfs2/ocfs2_fs.h struct ocfs2_group_desc · line 825
Quoted 10 lines
fs/ocfs2/ocfs2_fs.h +10
-0+} + +static inline int ocfs2_xattr_is_local (struct ocfs2_xattr_entry *xe) +{ +return xe->xe_type &OCFS2_XATTR_ENTRY_LOCAL ; +} + +static inline void ocfs2_xattr_set_type (struct ocfs2_xattr_entry *xe,int type) +{ + xe->xe_type |= type;
I think it's a good idea to mask out the topmost bit in 'type' here, just in caes the function gets passed a bogus value, it at least won't affect whether the xe is considered local or not.
Otherwise, this patch looks great.
Thanks,
--Mark
--
Mark Fasheh
PATCH 5/15 Add xattr header in ocfs2.v3 revised.
Hi Mark,
Thanks for your advice about xe_type set. Here is the revised patch.
Modification from V2 to V3:
1. Add annotations for every field.
2. Change csum to be _le64 include xb_csum and xh_csum.
3. Change the definition of xe_local and xe_type(make xe_local the bottom-most bit of a _u8). And add set/get helper function for xe_local and xe_type.
--- fs/ocfs2/ocfs2_fs.h | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 118 insertions(+), 0 deletions(-)
fs/ocfs2/ocfs2_fs.h +118 -0
@@ -64,6 +64,7 @@#define OCFS2_INODE_SIGNATURE "INODE01"#define OCFS2_EXTENT_BLOCK_SIGNATURE "EXBLK01"#define OCFS2_GROUP_DESC_SIGNATURE "GROUP01" +#define OCFS2_XATTR_BLOCK_SIGNATURE "XATTR01"/* Compatibility flags */ #define OCFS2_HAS_COMPAT_FEATURE (sb,mask) \
@@ -712,6 +713,123 @@ struct ocfs2_group_desc/*40*/ __u8 bg_bitmap[0 ]; }; +/* +* On disk extended attribute structure for OCFS2. +*/ + +/* +* ocfs2_xattr_entry indicates one extend attribute. +* +* Note that it can be stored in inode, one block or one xattr bucket. +*/ +struct ocfs2_xattr_entry { +__le32 xe_name_hash;/* hash value of xattr prefix+suffix. */ +__le16 xe_name_offset;/* byte offset from the 1st etnry in the local +local xattr storage(inode, xattr block or +xattr bucket). */ +__u8 xe_name_len;/* xattr name len, does't include prefix. */ +__u8 xe_type;/* the low 7 bits indicates the name prefix's +* type and the highest 1 bits indicate whether +* the EA is stored in the local storage. */ +__le64 xe_value_size;/* real xattr value length. */ +}; + +/* +* On disk structure for xattr header. +* +* One ocfs2_xattr_header describes how many ocfs2_xattr_entry records in +* the local xattr storage. +*/ +struct ocfs2_xattr_header { +__le16 xh_count;/* contains the count of how +many records are in the +local xattr storage. */ +__le16 xh_reserved1; +__le32 xh_reserved2; +__le64 xh_csum; +struct ocfs2_xattr_entry xh_entries[0 ];/* xattr entry list. */ +}; + +/* +* On disk structure for xattr value root. +* +* It is used when one extended attribute's size is larger, and we will save it +* in an outside cluster. It will stored in a b-tree like file content. +*/ +struct ocfs2_xattr_value_root { +/*00*/ __le32 xr_clusters;/* clusters covered by xattr value. */ +__le32 xr_reserved0; +__le64 xr_last_eb_blk;/* Pointer to last extent block */ +/*10*/ struct ocfs2_extent_list xr_list;/* Extent record list */ +}; + +/* +* On disk structure for xattr tree root. +* +* It is used when there are too many extended attributes for one file. These +* attributes will be organized and stored in an indexed-btree. +*/ +struct ocfs2_xattr_tree_root { +/*00*/ __le32 xt_clusters;/* clusters covered by xattr. */ +__le32 xt_reserved0; +__le64 xt_last_eb_blk;/* Pointer to last extent block */ +/*10*/ struct ocfs2_extent_list xt_list;/* Extent record list */ +}; + +#define OCFS2_XATTR_INDEXED 0x1 + +/* +* On disk structure for xattr block. +*/ +struct ocfs2_xattr_block { +/*00*/ __u8 xb_signature[8 ];/* Signature for verification */ +__le16 xb_suballoc_slot;/* Slot suballocator this +block belongs to. */ +__le16 xb_suballoc_bit;/* Bit offset in suballocator +block group */ +__le32 xb_fs_generation;/* Must match super block */ +/*10*/ __le64 xb_blkno;/* Offset on disk, in blocks */ +__le64 xb_csum; +/*20*/ __le16 xb_flags;/* Indicates whether this block contains +real xattr or a xattr tree. */ +__le16 xb_reserved0; +__le32 xb_reserved1; +__le64 xb_reserved2; +/*30*/ union { +struct ocfs2_xattr_header xb_header;/* xattr header if this +block contains xattr */ +struct ocfs2_xattr_tree_root xb_root;/* xattr tree root if this +block cotains xattr +tree. */ + } xb_attrs; +}; + +#define OCFS2_XATTR_ENTRY_LOCAL 0x80 +#define OCFS2_XATTR_TYPE_MASK 0x7F +static inline void ocfs2_xattr_set_local (struct ocfs2_xattr_entry *xe, +int local) +{ +if (local) + xe->xe_type |=OCFS2_XATTR_ENTRY_LOCAL ; +else + xe->xe_type &= ~OCFS2_XATTR_ENTRY_LOCAL ; +} + +static inline int ocfs2_xattr_is_local (struct ocfs2_xattr_entry *xe) +{ +return xe->xe_type &OCFS2_XATTR_ENTRY_LOCAL ; +} + +static inline void ocfs2_xattr_set_type (struct ocfs2_xattr_entry *xe,int type) +{ + xe->xe_type |= type &OCFS2_XATTR_TYPE_MASK ; +} + +static inline int ocfs2_xattr_get_type (struct ocfs2_xattr_entry *xe) +{ +return xe->xe_type &OCFS2_XATTR_TYPE_MASK ; +} +#ifdef __KERNEL__static inline int ocfs2_fast_symlink_chars (struct super_block *sb) {
Signature · 1 line
1.5.4.GIT
PATCH 5/15 Add xattr header in ocfs2.v3 revised.
Quoted 2 lines · Tao Ma
Hi Mark,
Thanks for your advice aboutxe_typeset. Here is the revised patch.
Yep, much better.
--Mark
--
Mark Fasheh
PATCH 6/15 Add helper function in uptodate for removing xattr clusters.V3
In reply to Tao MaModification from V2 to V3:
Change ocfs2_remove_block_from_cache to be static.
The old uptodate only handles the issue of removing one buffer_head from ocfs2 inode's buffer cache. With xattr clusters, we may need to remove some clusters from the cache. So add helper function for it.
--- fs/ocfs2/uptodate.c | 32 ++++++++++++++++++++++++++------ fs/ocfs2/uptodate.h | 3 +++ 2 files changed, 29 insertions(+), 6 deletions(-)
fs/ocfs2/uptodate.c +26 -6
@@ -511,14 +511,10 @@ static void ocfs2_remove_metadata_tree(struct ocfs2_caching_info *ci, ci->ci_num_cached--; } -/* Called when we remove a chunk of metadata from an inode. We don't -* bother reverting things to an inlined array in the case of a remove -* which moves us back under the limit. */ -void ocfs2_remove_from_cache (struct inode *inode, -struct buffer_head *bh) +static void ocfs2_remove_block_from_cache (struct inode *inode, +sector_t block) {int index; -sector_t block = bh->b_blocknr;struct ocfs2_meta_cache_item *item =NULL ;struct ocfs2_inode_info *oi =OCFS2_I (inode);struct ocfs2_caching_info *ci = &oi->ip_metadata_cache;
@@ -544,6 +540,30 @@ void ocfs2_remove_from_cache(struct inode *inode,kmem_cache_free (ocfs2_uptodate_cachep, item); } +/* +* Called when we remove a chunk of metadata from an inode. We don't +* bother reverting things to an inlined array in the case of a remove +* which moves us back under the limit. +*/ +void ocfs2_remove_from_cache (struct inode *inode, +struct buffer_head *bh) +{ +sector_t block = bh->b_blocknr; + +ocfs2_remove_block_from_cache (inode, block); +} + +/* Called when we remove xattr clusters from an inode. */ +void ocfs2_remove_xattr_clusters_from_cache (struct inode *inode, +sector_t block, +u32 c_len) +{ +u64 i, b_len =ocfs2_clusters_to_blocks (inode->i_sb,1 ) * c_len; + +for (i =0 ; i < b_len; i++, block++) +ocfs2_remove_block_from_cache (inode, block); +} +int __init init_ocfs2_uptodate_cache (void ) { ocfs2_uptodate_cachep = kmem_cache_create("ocfs2_uptodate",
fs/ocfs2/uptodate.h +3 -0
@@ -40,6 +40,9 @@ void ocfs2_set_new_buffer_uptodate(struct inode *inode, struct buffer_head *bh);void ocfs2_remove_from_cache (struct inode *inode,struct buffer_head *bh); +void ocfs2_remove_xattr_clusters_from_cache (struct inode *inode, +sector_t block, +u32 c_len);int ocfs2_buffer_read_ahead (struct inode *inode,struct buffer_head *bh);
Signature · 1 line
1.5.4.GIT
PATCH 6/15 Add helper function in uptodate for removing xattr clusters.V3
Quoted 8 lines · Tao Ma
Modification from V2 to V3:
Changeocfs2_remove_block_from_cacheto be static.The old uptodate only handles the issue of removing one
buffer_headfrom ocfs2 inode's buffer cache. With xattr clusters, we may need to remove some clusters from the cache. So add helper function for it.Signed-off-by Tao Ma <[email protected]>
This looks great, thanks.
--Mark
--
Mark Fasheh
PATCH 7/15 Add extent tree operation for xattr value.v3
In reply to Tao MaModification from V2 to V3:
Add some thin wrappers around ocfs2_insert_extent.
3 functions ocfs2_inode_insert_extent, ocfs2_xattr_value_insert_extent and ocfs2_xattr_tree_insert_extent(for xattr index btree, and it will show up in the last patch). All the old callers in file.c etc will call ocfs2_dinode_insert_extent, while the other two handle the xattr issue. And the init of extent tree are handled by these functions.
When storing xattr value which is too large, we will allocate some clusters for it and here ocfs2_extent_list and ocfs2_extent_rec will also be used. In order to re-use the b-tree operation code, a new parameter named "private" is added into ocfs2_extent_tree and it is used to indicate the root of ocfs2_exent_list. The reason is that we can't deduce the root from the buffer_head now. It may be in an inode, an ocfs2_xattr_block or even worse, in any place in an ocfs2_xattr_bucket.
--- fs/ocfs2/Makefile | 3 +- fs/ocfs2/alloc.c | 165 +++++++++++++++++++----- fs/ocfs2/alloc.h | 39 ++++-- fs/ocfs2/aops.c | 5 +- fs/ocfs2/cluster/masklog.c | 1 + fs/ocfs2/cluster/masklog.h | 1 + fs/ocfs2/dir.c | 11 +- fs/ocfs2/extent_map.c | 60 +++++++++ fs/ocfs2/extent_map.h | 3 + fs/ocfs2/file.c | 9 +- fs/ocfs2/suballoc.c | 24 +++- fs/ocfs2/suballoc.h | 6 +- fs/ocfs2/xattr.c | 305 ++++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 568 insertions(+), 64 deletions(-) create mode 100644 fs/ocfs2/xattr.c
fs/ocfs2/Makefile +2 -1
@@ -34,7 +34,8 @@ ocfs2-objs := \ symlink.o \ sysfile.o \ uptodate.o \ - ver.o + ver.o \ + xattr.o \ ocfs2_stackglue-objs := stackglue.o ocfs2_stack_o2cb-objs := stack_o2cb.o
fs/ocfs2/alloc.c +135 -30
254 lines not on this page · raw message
fs/ocfs2/alloc.h +26 -13
@@ -28,34 +28,47 @@enum ocfs2_extent_tree_type {OCFS2_DINODE_EXTENT =0 , +OCFS2_XATTR_VALUE_EXTENT , };struct ocfs2_alloc_context ; -int ocfs2_insert_extent (struct ocfs2_super *osb, -handle_t *handle, -struct inode *inode, -struct buffer_head *root_bh, -u32 cpos, -u64 start_blk, -u32 new_clusters, -u8 flags, -struct ocfs2_alloc_context *meta_ac, -enum ocfs2_extent_tree_type et_type); +int ocfs2_dinode_insert_extent (struct ocfs2_super *osb, +handle_t *handle, +struct inode *inode, +struct buffer_head *root_bh, +u32 cpos, +u64 start_blk, +u32 new_clusters, +u8 flags, +struct ocfs2_alloc_context *meta_ac); +int ocfs2_xattr_value_insert_extent (struct ocfs2_super *osb, +handle_t *handle, +struct inode *inode, +struct buffer_head *root_bh, +u32 cpos, +u64 start_blk, +u32 new_clusters, +u8 flags, +struct ocfs2_alloc_context *meta_ac, +void *private);struct ocfs2_cached_dealloc_ctxt ;int ocfs2_mark_extent_written (struct inode *inode,struct buffer_head *root_bh,handle_t *handle,u32 cpos,u32 len,u32 phys,struct ocfs2_alloc_context *meta_ac,struct ocfs2_cached_dealloc_ctxt *dealloc, -enum ocfs2_extent_tree_type et_type); +enum ocfs2_extent_tree_type et_type, +void *private);int ocfs2_remove_extent (struct inode *inode,struct buffer_head *root_bh,u32 cpos,u32 len,handle_t *handle,struct ocfs2_alloc_context *meta_ac,struct ocfs2_cached_dealloc_ctxt *dealloc, -enum ocfs2_extent_tree_type et_type); +enum ocfs2_extent_tree_type et_type, +void *private);int ocfs2_num_free_extents (struct ocfs2_super *osb,struct inode *inode,struct buffer_head *root_bh, -enum ocfs2_extent_tree_type et_type); +enum ocfs2_extent_tree_type et_type, +void *private);/* * how many new metadata chunks would an allocation need at maximum?
fs/ocfs2/aops.c +3 -2
@@ -1276,7 +1276,7 @@ static int ocfs2_write_cluster(struct address_space *mapping, ret =ocfs2_mark_extent_written (inode, wc->w_di_bh, wc->w_handle, cpos,1 , phys, meta_ac, &wc->w_dealloc, -OCFS2_DINODE_EXTENT ); +OCFS2_DINODE_EXTENT ,NULL );if (ret <0 ) {mlog_errno (ret); goto out;
@@ -1718,7 +1718,8 @@ int ocfs2_write_begin_nolock(struct address_space *mapping, ret =ocfs2_lock_allocators (inode, wc->w_di_bh, &di->id2.i_list, clusters_to_alloc, extents_to_split, - &data_ac, &meta_ac); + &data_ac, &meta_ac, +OCFS2_DINODE_EXTENT ,NULL );if (ret) {mlog_errno (ret); goto out;
fs/ocfs2/cluster/masklog.c +1 -0
@@ -109,6 +109,7 @@ static struct mlog_attribute mlog_attrs[MLOG_MAX_BITS] = {define_mask (CONN ),define_mask (QUORUM ),define_mask (EXPORT ), +define_mask (XATTR ),define_mask (ERROR ),define_mask (NOTICE ), define_mask(KTHREAD),
fs/ocfs2/cluster/masklog.h +1 -0
@@ -112,6 +112,7 @@#define ML_CONN 0x0000000004000000ULL/* net connection management */ #define ML_QUORUM 0x0000000008000000ULL/* net connection quorum */ #define ML_EXPORT 0x0000000010000000ULL/* ocfs2 export operations */ +#define ML_XATTR 0x0000000020000000ULL/* ocfs2 extended attributes */ /* bits that are infrequently given and frequently matched in the high word */ #define ML_ERROR 0x0000000100000000ULL/* sent to KERN_ERR */ #define ML_NOTICE 0x0000000200000000ULL/* setn to KERN_NOTICE */
fs/ocfs2/dir.c +6 -5
@@ -1306,8 +1306,8 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh,* This should never fail as our extent list is empty and all * related blocks have been journaled already. */ - ret =ocfs2_insert_extent (osb, handle, dir, di_bh,0 , blkno, len,0 , -NULL ,OCFS2_DINODE_EXTENT ); + ret =ocfs2_dinode_insert_extent (osb, handle, dir, di_bh,0 , blkno, + len,0 ,NULL );if (ret) {mlog_errno (ret); goto out;
@@ -1332,8 +1332,8 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh, } blkno =ocfs2_clusters_to_blocks (dir->i_sb, bit_off); - ret =ocfs2_insert_extent (osb, handle, dir, di_bh,1 , blkno, - len,0 ,NULL ,OCFS2_DINODE_EXTENT ); + ret =ocfs2_dinode_insert_extent (osb, handle, dir, di_bh,1 , + blkno, len,0 ,NULL );if (ret) {mlog_errno (ret); goto out;
@@ -1477,7 +1477,8 @@ static int ocfs2_extend_dir(struct ocfs2_super *osb,spin_unlock (&OCFS2_I (dir)->ip_lock); num_free_extents =ocfs2_num_free_extents (osb, dir, parent_fe_bh, -OCFS2_DINODE_EXTENT ); +OCFS2_DINODE_EXTENT , +NULL );if (num_free_extents <0 ) { status = num_free_extents;mlog_errno (status);
fs/ocfs2/extent_map.c +60 -0
66 lines not on this page · raw message
fs/ocfs2/extent_map.h +3 -0
@@ -50,4 +50,7 @@ int ocfs2_get_clusters(struct inode *inode, u32 v_cluster, u32 *p_cluster,int ocfs2_extent_map_get_blocks (struct inode *inode,u64 v_blkno,u64 *p_blkno,u64 *ret_count,unsigned int *extent_flags); +int ocfs2_xattr_get_clusters (struct inode *inode,u32 v_cluster, +u32 *p_cluster,u32 *num_clusters, +struct ocfs2_extent_list *el);#endif /* _EXTENT_MAP_H */
fs/ocfs2/file.c +5 -4
@@ -515,7 +515,7 @@ int ocfs2_add_inode_data(struct ocfs2_super *osb, clusters_to_add, mark_unwritten, fe_bh, el, handle, data_ac, meta_ac, reason_ret, - OCFS2_DINODE_EXTENT); + OCFS2_DINODE_EXTENT, NULL); } static int __ocfs2_extend_allocation(struct inode *inode, u32 logical_start,
@@ -565,7 +565,7 @@ restart_all: clusters_to_add); status =ocfs2_lock_allocators (inode, bh, &fe->id2.i_list, clusters_to_add,0 , &data_ac, - &meta_ac); + &meta_ac,OCFS2_DINODE_EXTENT ,NULL );if (status) {mlog_errno (status); goto leave;
@@ -1237,7 +1237,8 @@ static int __ocfs2_remove_inode_range(struct inode *inode,struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; ret =ocfs2_lock_allocators (inode, di_bh, &di->id2.i_list, -0 ,1 ,NULL , &meta_ac); +0 ,1 ,NULL , &meta_ac, +OCFS2_DINODE_EXTENT ,NULL );if (ret) {mlog_errno (ret);return ret;
@@ -1268,7 +1269,7 @@ static int __ocfs2_remove_inode_range(struct inode *inode, } ret =ocfs2_remove_extent (inode, di_bh, cpos, len, handle, meta_ac, - dealloc,OCFS2_DINODE_EXTENT ); + dealloc,OCFS2_DINODE_EXTENT ,NULL );if (ret) {mlog_errno (ret); goto out_commit;
fs/ocfs2/suballoc.c +17 -7
@@ -1906,7 +1906,8 @@ int ocfs2_lock_allocators(struct inode *inode, struct buffer_head *root_bh,struct ocfs2_extent_list *root_el, u32 clusters_to_add, u32 extents_to_split, struct ocfs2_alloc_context **data_ac, - struct ocfs2_alloc_context **meta_ac) + struct ocfs2_alloc_context **meta_ac, + enum ocfs2_extent_tree_type type, void *private) { int ret = 0, num_free_extents;unsigned int max_recs_needed = clusters_to_add +2 * extents_to_split;
@@ -1919,7 +1920,7 @@ int ocfs2_lock_allocators(struct inode *inode, struct buffer_head *root_bh,BUG_ON (clusters_to_add !=0 && data_ac ==NULL ); num_free_extents =ocfs2_num_free_extents (osb, inode, root_bh, -OCFS2_DINODE_EXTENT ); + type, private);if (num_free_extents <0 ) { ret = num_free_extents;mlog_errno (ret);
@@ -1992,7 +1993,8 @@ int ocfs2_add_clusters_in_btree(struct ocfs2_super *osb,struct ocfs2_alloc_context *data_ac, struct ocfs2_alloc_context *meta_ac, enum ocfs2_alloc_restarted *reason_ret, - enum ocfs2_extent_tree_type type) + enum ocfs2_extent_tree_type type, + void *private) { int status = 0;int free_extents;
@@ -2006,7 +2008,8 @@ int ocfs2_add_clusters_in_btree(struct ocfs2_super *osb,if (mark_unwritten) flags =OCFS2_EXT_UNWRITTEN ; - free_extents =ocfs2_num_free_extents (osb, inode, root_bh, type); + free_extents =ocfs2_num_free_extents (osb, inode, root_bh, type, + private);if (free_extents <0 ) { status = free_extents;mlog_errno (status);
@@ -2053,9 +2056,16 @@ int ocfs2_add_clusters_in_btree(struct ocfs2_super *osb, block =ocfs2_clusters_to_blocks (osb->sb, bit_off);mlog (0 ,"Allocating %u clusters at block %u for inode %llu\n" , num_bits, bit_off, (unsigned long long )OCFS2_I (inode)->ip_blkno); - status =ocfs2_insert_extent (osb, handle, inode, root_bh, - *logical_offset, block, num_bits, - flags, meta_ac, type); +if (type ==OCFS2_DINODE_EXTENT ) + status =ocfs2_dinode_insert_extent (osb, handle, inode, root_bh, + *logical_offset, block, + num_bits, flags, meta_ac); +else + status =ocfs2_xattr_value_insert_extent (osb, handle, + inode, root_bh, + *logical_offset, + block, num_bits, flags, + meta_ac, private);if (status <0 ) {mlog_errno (status); goto leave;
fs/ocfs2/suballoc.h +4 -2
@@ -165,7 +165,8 @@ int ocfs2_lock_allocators(struct inode *inode, struct buffer_head *root_bh,struct ocfs2_extent_list *root_el, u32 clusters_to_add, u32 extents_to_split, struct ocfs2_alloc_context **data_ac, - struct ocfs2_alloc_context **meta_ac); + struct ocfs2_alloc_context **meta_ac, + enum ocfs2_extent_tree_type type, void *private);enum ocfs2_alloc_restarted {RESTART_NONE =0 ,
@@ -183,5 +184,6 @@ int ocfs2_add_clusters_in_btree(struct ocfs2_super *osb,struct ocfs2_alloc_context *data_ac, struct ocfs2_alloc_context *meta_ac, enum ocfs2_alloc_restarted *reason_ret, - enum ocfs2_extent_tree_type type); + enum ocfs2_extent_tree_type type, + void *private);#endif /* _CHAINALLOC_H_ */
fs/ocfs2/xattr.c new file +305 -0
305 lines not on this page · raw message
Signature · 1 line
1.5.4.GIT
PATCH 7/15 Add extent tree operation for xattr value.v3
fs/ocfs2/alloc.c struct ocfs2_extent_tree { · line 82
Quoted 8 lines · Tao Ma
fs/ocfs2/alloc.c +1
-0@@ -78,6 +78,7 @@ struct ocfs2_extent_tree {struct ocfs2_extent_tree_operations *eops;struct buffer_head *root_bh;struct ocfs2_extent_list *root_el; +void *private; };
Why 'private'? Why not 'object'? I don't like this if block:
Quoted 15 lines
fs/ocfs2/alloc.c +3
-2static struct ocfs2_extent_tree *ocfs2_new_extent_tree (struct buffer_head *bh, -enum ocfs2_extent_tree_type et_type) +enum ocfs2_extent_tree_type et_type, +void *private) {struct ocfs2_extent_tree *et;@@ -149,12 +191,16 @@ static struct ocfs2_extent_tree* et->type = et_type;get_bh (bh); et->root_bh = bh; + et->private = private; -/* current we only support dinode extent. */
fs/ocfs2/alloc.c static struct ocfs2_extent_tree* · line 204
Quoted 10 lines
fs/ocfs2/alloc.c +5
-1-BUG_ON (et->type !=OCFS2_DINODE_EXTENT );if (et_type ==OCFS2_DINODE_EXTENT ) { et->root_el = &((struct ocfs2_dinode *)bh->b_data)->id2.i_list; et->eops = &ocfs2_dinode_et_ops; + }else if (et_type ==OCFS2_XATTR_VALUE_EXTENT ) { +struct ocfs2_xattr_value_root *xv = + (struct ocfs2_xattr_value_root *) private; + et->root_el = &xv->xr_list; + et->eops = &ocfs2_xattr_et_ops; }
Why don't you have an operation for getting the el? Really, the
way I see it, you should have:
void ocfs2_fill_extent_tree(*et, *bh, void *object, type)
{
et->et_type = type;
et->et_ops = extent_tree_ops[type];
get_bh(bh);
et->et_root_bh = bh;
et->et_object = object ? object : (void *)bh->b_data;
et->et_root_el = et->et_ops->et_get_root_el(et);
}If you pass NULL for *object, it knows to use the start of the bh. This works for dinode, etc. But you can pass a non-NULL object for the xattr_value_root. The get_root_el operations know how to take et_object and return the el:
ocfs2_dinode_get_root_el(*et)
{
struct ocfs2_dinode *di = et->et_object;
return &di->id2.i_list;
}
ocfs2_xattr_value_root_get_el(*et)
{
struct ocfs2_xattr_value_root *xv = et->et_object;
return &xv->xr_list;
}Quoted 11 lines
fs/ocfs2/alloc.c +2
-1@@ -495,7 +541,8 @@ struct ocfs2_merge_ctxt {int ocfs2_num_free_extents (struct ocfs2_super *osb,struct inode *inode,struct buffer_head *root_bh, -enum ocfs2_extent_tree_type type) +enum ocfs2_extent_tree_type type, +void *private) {int retval;struct ocfs2_extent_list *el =NULL ;
fs/ocfs2/alloc.c int ocfs2_num_free_extents(struct ocfs2_super *osb, · line 572
Quoted 9 lines
fs/ocfs2/alloc.c +6
-0@@ -517,6 +564,12 @@ int ocfs2_num_free_extents(struct ocfs2_super *osb,if (fe->i_last_eb_blk) last_eb_blk =le64_to_cpu (fe->i_last_eb_blk); el = &fe->id2.i_list; + }else if (type ==OCFS2_XATTR_VALUE_EXTENT ) { +struct ocfs2_xattr_value_root *xv = + (struct ocfs2_xattr_value_root *) private; + + last_eb_blk =le64_to_cpu (xv->xr_last_eb_blk); + el = &xv->xr_list;
Again, just to avoid the extent_tree object, you are
hand-accessing the last_eb_block and el entries. I really think you should be passing an *et into this function.
Quoted 10 lines
fs/ocfs2/alloc.c +0
-10-int ocfs2_insert_extent (struct ocfs2_super *osb, -handle_t *handle, -struct inode *inode, -struct buffer_head *root_bh, -u32 cpos, -u64 start_blk, -u32 new_clusters, -u8 flags, -struct ocfs2_alloc_context *meta_ac, -enum ocfs2_extent_tree_type et_type)
fs/ocfs2/alloc.c out: · line 4283
Quoted 10 lines
fs/ocfs2/alloc.c +10
-0+static int ocfs2_insert_extent (struct ocfs2_super *osb, +handle_t *handle, +struct inode *inode, +struct buffer_head *root_bh, +u32 cpos, +u64 start_blk, +u32 new_clusters, +u8 flags, +struct ocfs2_alloc_context *meta_ac, +struct ocfs2_extent_tree *et)
Here you are passing in the et, so you don't need root_bh
anymore, right
fs/ocfs2/alloc.c int ocfs2_mark_extent_written(struct inode *inode, struct buffer_head *root_bh, · line 4662
Quoted 7 lines
fs/ocfs2/alloc.c +2
-1@@ -4554,7 +4658,8 @@ int ocfs2_mark_extent_written(struct inode *inode, struct buffer_head *root_bh, handle_t *handle, u32 cpos, u32 len, u32 phys, struct ocfs2_alloc_context *meta_ac, struct ocfs2_cached_dealloc_ctxt *dealloc, - enum ocfs2_extent_tree_type et_type) + enum ocfs2_extent_tree_type et_type, + void *private)
Here again you're passing root_bh, et_type, and private. Why
not just pass in the et?
Quoted 49 lines
fs/ocfs2/alloc.c +49
-0+int ocfs2_xattr_get_clusters (struct inode *inode,u32 v_cluster, +u32 *p_cluster,u32 *num_clusters, +struct ocfs2_extent_list *el) +{ +int ret =0 , i; +struct buffer_head *eb_bh =NULL ; +struct ocfs2_extent_block *eb; +struct ocfs2_extent_rec *rec; +u32 coff; + +if (el->l_tree_depth) { + ret =ocfs2_find_leaf (inode, el, v_cluster, &eb_bh); +if (ret) { +mlog_errno (ret); + goto out; + } + + eb = (struct ocfs2_extent_block *) eb_bh->b_data; + el = &eb->h_list; + +if (el->l_tree_depth) { +ocfs2_error (inode->i_sb, +"Inode %lu has non zero tree depth in " +"xattr leaf block %llu\n" , inode->i_ino, + (unsigned long long )eb_bh->b_blocknr); + ret = -EROFS ; + goto out; + } + } + + i =ocfs2_search_extent_list (el, v_cluster); +if (i ==-1 ) { + ret = -EROFS ; +mlog_errno (ret); + goto out; + }else { + rec = &el->l_recs[i]; +BUG_ON (v_cluster <le32_to_cpu (rec->e_cpos)); + +if (!rec->e_blkno) { +ocfs2_error (inode->i_sb,"Inode %lu has bad extent " +"record (%u, %u, 0) in xattr" , inode->i_ino, +le32_to_cpu (rec->e_cpos), +ocfs2_rec_clusters (el, rec)); + ret = -EROFS ; + goto out; + } + coff = v_cluster -le32_to_cpu (rec->e_cpos); + *p_cluster = ocfs2_blocks_to_clusters(inode->i_sb,
fs/ocfs2/alloc.c int ocfs2_mark_extent_written(struct inode *inode, struct buffer_head *root_bh,
Quoted 10 lines
fs/ocfs2/alloc.c +10
-0+ le64_to_cpu(rec->e_blkno)); + *p_cluster = *p_cluster + coff; +if (num_clusters) + *num_clusters =ocfs2_rec_clusters (el, rec) - coff; + } +out: +if (eb_bh) +brelse (eb_bh); +return ret; +}
Part of me wonders if you could have refactoredocfs2_get_clusters to share most of this duplicated code. But I do see the difference, and we can always come back to it.
fs/ocfs2/suballoc.c int ocfs2_lock_allocators(struct inode *inode, struct buffer_head *root_bh, · line 1910
Quoted 11 lines
fs/ocfs2/suballoc.c +2
-1@@ -1906,7 +1906,8 @@ int ocfs2_lock_allocators(struct inode *inode, struct buffer_head *root_bh,struct ocfs2_extent_list *root_el, u32 clusters_to_add, u32 extents_to_split, struct ocfs2_alloc_context **data_ac, - struct ocfs2_alloc_context **meta_ac) + struct ocfs2_alloc_context **meta_ac, + enum ocfs2_extent_tree_type type, void *private)
Pass in a struct ocfs2_extent_list and you don't have to passroot_bh. root_el, type, or private.
fs/ocfs2/suballoc.c int ocfs2_add_clusters_in_btree(struct ocfs2_super *osb, · line 1997
Quoted 7 lines
fs/ocfs2/suballoc.c +2
-1@@ -1992,7 +1993,8 @@ int ocfs2_add_clusters_in_btree(struct ocfs2_super *osb,struct ocfs2_alloc_context *data_ac, struct ocfs2_alloc_context *meta_ac, enum ocfs2_alloc_restarted *reason_ret, - enum ocfs2_extent_tree_type type) + enum ocfs2_extent_tree_type type, + void *private)
Same here. I'll stop repeating this.
fs/ocfs2/xattr.c
Quoted 5 lines
fs/ocfs2/xattr.c new file +0
-0
This file I like :-) Pretty straightforward and clean.
Joel
Signature · 11 lines
"Too much walking shoes worn thin. Too much trippin' and my soul's worn thin. Time to catch a ride it leaves today Her name is what it means. Too much walking shoes worn thin." Joel Becker Principal Software Developer Oracle E-mail: joel.becker at oracle.com Phone: (650) 506-8127
PATCH 7/15 Add extent tree operation for xattr value.v3
Quoted 17 lines · Joel Becker
Why don't you have an operation for getting the el? Really, the
way I see it, you should have:void ocfs2_fill_extent_tree(*et, *bh, void *object, type) { et->et_type = type; et->et_ops = extent_tree_ops[type]; get_bh(bh); et->et_root_bh = bh; et->et_object = object ? object : (void *)bh->b_data; et->et_root_el = et->et_ops->et_get_root_el(et); }If you pass NULL for *object, it knows to use the start of the bh. This works for dinode, etc. But you can pass a non-NULL object for the
xattr_value_root. Theget_root_eloperations know how to takeet_objectand return the el:
<snip>
Quoted 15 lines
Quoted 15 lines
fs/ocfs2/alloc.c +5
-10-int ocfs2_insert_extent (struct ocfs2_super *osb, -handle_t *handle, -struct inode *inode, -struct buffer_head *root_bh, -u32 cpos, -u64 start_blk, -u32 new_clusters, -u8 flags, -struct ocfs2_alloc_context *meta_ac, -enum ocfs2_extent_tree_type et_type) +static int ocfs2_insert_extent(struct ocfs2_super *osb, + handle_t *handle, + struct inode *inode, + struct buffer_head *root_bh, + u32 cpos,
Quoted 8 lines
fs/ocfs2/alloc.c out: · line 4283
Quoted 5 lines
fs/ocfs2/alloc.c +5
-0+u64 start_blk, + u32 new_clusters, + u8 flags, + struct ocfs2_alloc_context *meta_ac, + struct ocfs2_extent_tree *et)Here you are passing in the et, so you don't need
root_bh
anymore, right
In fact, you don't need
xattr_insert_extent/dinode_insert_extent. Just have the callers of ocfs2_insert_extent create an extent tree (they're almost doing so already) and then pass it to ocfs2_insert_extent.
Conversely, if you really like those wrappers, have them turn
cpos/start_blk/new_clusters into an extent_rec before calling ocfs2_insert_extent. It can be passed straight down into do_insert_extent.
Joel
Signature · 9 lines
"Nearly all men can stand adversity, but if you really want to test a man's character, give him power." - Abraham Lincoln Joel Becker Principal Software Developer Oracle E-mail: joel.becker at oracle.com Phone: (650) 506-8127
PATCH 7/15 Add extent tree operation for xattr value.v3
Quoted 49 lines · Joel Becker
Quoted 18 lines · Joel Becker
Why don't you have an operation for getting the el? Really, the
way I see it, you should have:void ocfs2_fill_extent_tree(*et, *bh, void *object, type) { et->et_type = type; et->et_ops = extent_tree_ops[type]; get_bh(bh); et->et_root_bh = bh; et->et_object = object ? object : (void *)bh->b_data; et->et_root_el = et->et_ops->et_get_root_el(et); }If you pass NULL for *object, it knows to use the start of the bh. This works for dinode, etc. But you can pass a non-NULL object for the
xattr_value_root. Theget_root_eloperations know how to takeet_objectand return the el:<snip>
Quoted 15 lines
Quoted 15 lines
fs/ocfs2/alloc.c +5
-10-int ocfs2_insert_extent (struct ocfs2_super *osb, -handle_t *handle, -struct inode *inode, -struct buffer_head *root_bh, -u32 cpos, -u64 start_blk, -u32 new_clusters, -u8 flags, -struct ocfs2_alloc_context *meta_ac, -enum ocfs2_extent_tree_type et_type) +static int ocfs2_insert_extent(struct ocfs2_super *osb, + handle_t *handle, + struct inode *inode, + struct buffer_head *root_bh, + u32 cpos,Quoted 9 lines
fs/ocfs2/alloc.c out: · line 4283
Quoted 6 lines
fs/ocfs2/alloc.c +5
-0+u64 start_blk, + u32 new_clusters, + u8 flags, + struct ocfs2_alloc_context *meta_ac, + struct ocfs2_extent_tree *et)Here you are passing in the et, so you don't need
root_bh
anymore, right
Quoted 5 lines
In fact, you don't need
xattr_insert_extent/dinode_insert_extent. Just have the callers ofocfs2_insert_extentcreate an extent tree (they're almost doing so already) and then pass it toocfs2_insert_extent.
Actually this is the consensus that Mark and I have made. See
http://oss.oracle.com/pipermail/ocfs2-devel/2008-June/002332.html
Quoted 5 lines
Conversely, if you really like those wrappers, have them turn
cpos/start_blk/new_clusters into anextent_recbefore callingocfs2_insert_extent. It can be passed straight down intodo_insert_extent.
yes, this sounds reasonable since ocfs2_extent_rec will be created in ocfs2_insert_extent also. So we just move it to the 3 different callers. Mark, agree?
Regards,
Tao
PATCH 7/15 Add extent tree operation for xattr value.v3
In reply to Joel BeckerQuoted 20 lines · Joel Becker
Quoted 17 lines · Tao Ma
fs/ocfs2/alloc.c +5
-1@@ -495,7 +541,8 @@ struct ocfs2_merge_ctxt {int ocfs2_num_free_extents (struct ocfs2_super *osb,struct inode *inode,struct buffer_head *root_bh, -enum ocfs2_extent_tree_type type) +enum ocfs2_extent_tree_type type, +void *private) {int retval;struct ocfs2_extent_list *el =NULL ;@@ -517,6 +564,12 @@ int ocfs2_num_free_extents(struct ocfs2_super *osb,if (fe->i_last_eb_blk) last_eb_blk =le64_to_cpu (fe->i_last_eb_blk); el = &fe->id2.i_list; + }else if (type ==OCFS2_XATTR_VALUE_EXTENT ) { +struct ocfs2_xattr_value_root *xv = + (struct ocfs2_xattr_value_root *) private;
Quoted 9 lines
fs/ocfs2/alloc.c int ocfs2_num_free_extents(struct ocfs2_super *osb, · line 574
Quoted 4 lines
fs/ocfs2/alloc.c +3
-0+ + last_eb_blk =le64_to_cpu (xv->xr_last_eb_blk); + el = &xv->xr_list;Again, just to avoid the
extent_treeobject, you are
hand-accessing thelast_eb_blockand el entries. I really think you should be passing an *et into this function.
You have mentioned this in your previous review. Thanks. :)
Quoted 27 lines
Quoted 12 lines
fs/ocfs2/alloc.c +0
-10-int ocfs2_insert_extent (struct ocfs2_super *osb, -handle_t *handle, -struct inode *inode, -struct buffer_head *root_bh, -u32 cpos, -u64 start_blk, -u32 new_clusters, -u8 flags, -struct ocfs2_alloc_context *meta_ac, -enum ocfs2_extent_tree_type et_type)fs/ocfs2/alloc.c out: · line 4283
Quoted 10 lines
fs/ocfs2/alloc.c +10
-0+static int ocfs2_insert_extent (struct ocfs2_super *osb, +handle_t *handle, +struct inode *inode, +struct buffer_head *root_bh, +u32 cpos, +u64 start_blk, +u32 new_clusters, +u8 flags, +struct ocfs2_alloc_context *meta_ac, +struct ocfs2_extent_tree *et)Here you are passing in the et, so you don't need
root_bh
anymore, right
Quoted 12 lines
fs/ocfs2/alloc.c int ocfs2_mark_extent_written(struct inode *inode, struct buffer_head *root_bh, · line 4663
Quoted 8 lines
fs/ocfs2/alloc.c +2
-1@@ -4554,7 +4658,8 @@ int ocfs2_mark_extent_written(struct inode *inode, struct buffer_head *root_bh, handle_t *handle, u32 cpos, u32 len, u32 phys, struct ocfs2_alloc_context *meta_ac, struct ocfs2_cached_dealloc_ctxt *dealloc, - enum ocfs2_extent_tree_type et_type) + enum ocfs2_extent_tree_type et_type, + void *private)Here again you're passing
root_bh,et_type, and private. Why
not just pass in the et?
All these are described in my previous e-mail: I'd like to limit ocfs2_extent_tree only in alloc.c so that other files(suballoc.c,file.c,dir.c,aops.c) don't need to know this structure and only give the parameter and I don't need to touch this structure everywhere. So is it object-oriented or am I misunderstand it? ;)
Quoted 18 lines
fs/ocfs2/suballoc.c int ocfs2_lock_allocators(struct inode *inode, struct buffer_head *root_bh, · line 1911
Quoted 13 lines
fs/ocfs2/suballoc.c +2
-1@@ -1906,7 +1906,8 @@ int ocfs2_lock_allocators(struct inode *inode, struct buffer_head *root_bh,struct ocfs2_extent_list *root_el, u32 clusters_to_add, u32 extents_to_split, struct ocfs2_alloc_context **data_ac, - struct ocfs2_alloc_context **meta_ac) + struct ocfs2_alloc_context **meta_ac, + enum ocfs2_extent_tree_type type, void *private)Pass in a struct
ocfs2_extent_listand you don't have to passroot_bh.root_el, type, or private.
PATCH 10/15 Add xattr tree operations in ocfs2_extent_tree.v3
In reply to Tao MaModification from V2 to V3:
Add one new function ocfs2_xattr_tree_insert_extent so that manipulation of the extent tree will be handled by this one.
ocfs2_xattr_block will use ocfs2_extent_list to store large numbers of EAs. So add a new type in ocfs2_extent_tree_type and add one new ocfs_extent_tree implementation so that it can use b-tree code to handle the storage of many EAs.
--- fs/ocfs2/alloc.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++ fs/ocfs2/alloc.h | 10 ++++++ fs/ocfs2/suballoc.c | 6 ++++ 3 files changed, 99 insertions(+), 0 deletions(-)
fs/ocfs2/alloc.c +83 -0
@@ -177,6 +177,48 @@ static struct ocfs2_extent_tree_operations ocfs2_xattr_et_ops = { .sanity_check = ocfs2_xattr_value_sanity_check, }; +static void ocfs2_xattr_tree_set_last_eb_blk (struct ocfs2_extent_tree *et, +u64 blkno) +{ +struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *) et->root_bh->b_data; +struct ocfs2_xattr_tree_root *xt = &xb->xb_attrs.xb_root; + + xt->xt_last_eb_blk =cpu_to_le64 (blkno); +} + +static u64 ocfs2_xattr_tree_get_last_eb_blk (struct ocfs2_extent_tree *et) +{ +struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *) et->root_bh->b_data; +struct ocfs2_xattr_tree_root *xt = &xb->xb_attrs.xb_root; + +return le64_to_cpu (xt->xt_last_eb_blk); +} + +static void ocfs2_xattr_tree_update_clusters (struct inode *inode, +struct ocfs2_extent_tree *et, +u32 clusters) +{ +struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)et->root_bh->b_data; + +le32_add_cpu (&xb->xb_attrs.xb_root.xt_clusters, clusters); +} + +static int ocfs2_xattr_tree_sanity_check (struct inode *inode, +struct ocfs2_extent_tree *et) +{ +return 0 ; +} + +static struct ocfs2_extent_tree_operations ocfs2_xattr_tree_et_ops = { + .set_last_eb_blk = ocfs2_xattr_tree_set_last_eb_blk, + .get_last_eb_blk = ocfs2_xattr_tree_get_last_eb_blk, + .update_clusters = ocfs2_xattr_tree_update_clusters, + .sanity_check = ocfs2_xattr_tree_sanity_check, +}; +static struct ocfs2_extent_tree * ocfs2_new_extent_tree(struct buffer_head *bh, enum ocfs2_extent_tree_type et_type,
@@ -201,6 +243,11 @@ static struct ocfs2_extent_tree* (struct ocfs2_xattr_value_root *) private; et->root_el = &xv->xr_list; et->eops = &ocfs2_xattr_et_ops; + }else if (et_type ==OCFS2_XATTR_TREE_EXTENT ) { +struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)bh->b_data; + et->root_el = &xb->xb_attrs.xb_root.xt_list; + et->eops = &ocfs2_xattr_tree_et_ops; }return et;
@@ -570,6 +617,12 @@ int ocfs2_num_free_extents(struct ocfs2_super *osb, last_eb_blk =le64_to_cpu (xv->xr_last_eb_blk); el = &xv->xr_list; + }else if (type ==OCFS2_XATTR_TREE_EXTENT ) { +struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)root_bh->b_data; + + last_eb_blk =le64_to_cpu (xb->xb_attrs.xb_root.xt_last_eb_blk); + el = &xb->xb_attrs.xb_root.xt_list; }if (last_eb_blk) {
@@ -4406,6 +4459,36 @@ bail:return status; } +int ocfs2_xattr_tree_insert_extent (struct ocfs2_super *osb, +handle_t *handle, +struct inode *inode, +struct buffer_head *root_bh, +u32 cpos, +u64 start_blk, +u32 new_clusters, +u8 flags, +struct ocfs2_alloc_context *meta_ac) +{ +int status; +struct ocfs2_extent_tree *et =NULL ; + + et =ocfs2_new_extent_tree (root_bh,OCFS2_XATTR_TREE_EXTENT ,NULL ); +if (!et) { + status = -ENOMEM ; +mlog_errno (status); + goto bail; + } + + status =ocfs2_insert_extent (osb, handle, inode, root_bh, + cpos, start_blk, new_clusters, + flags, meta_ac, et); + +if (et) +ocfs2_free_extent_tree (et); +bail: +return status; +} + static void ocfs2_make_right_split_rec(struct super_block *sb, struct ocfs2_extent_rec *split_rec, u32 cpos,
fs/ocfs2/alloc.h +10 -0
@@ -29,6 +29,7 @@enum ocfs2_extent_tree_type {OCFS2_DINODE_EXTENT =0 ,OCFS2_XATTR_VALUE_EXTENT , +OCFS2_XATTR_TREE_EXTENT , };struct ocfs2_alloc_context ;
@@ -51,6 +52,15 @@ int ocfs2_xattr_value_insert_extent(struct ocfs2_super *osb,u8 flags, struct ocfs2_alloc_context *meta_ac, void *private); +int ocfs2_xattr_tree_insert_extent (struct ocfs2_super *osb, +handle_t *handle, +struct inode *inode, +struct buffer_head *root_bh, +u32 cpos, +u64 start_blk, +u32 new_clusters, +u8 flags, +struct ocfs2_alloc_context *meta_ac);struct ocfs2_cached_dealloc_ctxt ; int ocfs2_mark_extent_written(struct inode *inode, struct buffer_head *root_bh, handle_t *handle, u32 cpos, u32 len, u32 phys,
fs/ocfs2/suballoc.c +6 -0
@@ -2069,6 +2069,12 @@ int ocfs2_add_clusters_in_btree(struct ocfs2_super *osb, status =ocfs2_dinode_insert_extent (osb, handle, inode, root_bh, *logical_offset, block, num_bits, flags, meta_ac); + else if (type == OCFS2_XATTR_TREE_EXTENT) + status =ocfs2_xattr_tree_insert_extent (osb, handle, + inode, root_bh, + *logical_offset, + block, num_bits, flags, + meta_ac); else status = ocfs2_xattr_value_insert_extent(osb, handle, inode, root_bh,
Signature · 1 line
1.5.4.GIT
PATCH 11/15 Add xattr bucket iteration for large numbers of EAs.v11
In reply to Tao MaModification from V2 to V3:
1. Add a new abstration of ocfs2_xattr_bucket which records bhs in this bucket and ocfs2_xattr_header for quick refrence.
2. Now ocfs2_iterate_xattr_buckets will only read the bhs of the bucket, creat a ocfs2_xattr_bucket and let the caller do all the rest of the work.
3. According to the discussion with Mark, now all the xe_entry will only exist in the 1st block of the bucket and name/value pair will not spread multiple blocks, so the efficiency will be improved and the code is much easier.
We use bucket in ocfs2 to store large numbers of EAs, and list xattrs will iterate all the buckets and list all the names one by one. This patch add the iteration for the xattr bucket. For how xattr bucket looks like and their disk layout, please see
http://oss.oracle.com/osswiki/OCFS2/DesignDocs/IndexedEATrees.
--- fs/ocfs2/ocfs2_fs.h | 35 +++++++- fs/ocfs2/xattr.c | 255 ++++++++++++++++++++++++++++++++++++++++++++++++++- fs/ocfs2/xattr.h | 9 ++ 3 files changed, 293 insertions(+), 6 deletions(-)
fs/ocfs2/ocfs2_fs.h +33 -2
@@ -749,8 +749,13 @@ struct ocfs2_xattr_header {__le16 xh_count;/* contains the count of how many records are in the local xattr storage. */ -__le16 xh_reserved1; -__le32 xh_reserved2; +__le16 xh_offset;/* current offset for storing +xattr. */ +__le16 xh_name_value_len;/* total length of name/value +length in this bucket. */ +__le16 xh_num_buckets;/* bucket nums in one extent +record, only valid in the +first bucket. */ __le64 xh_csum;struct ocfs2_xattr_entry xh_entries[0 ];/* xattr entry list. */ };
@@ -787,6 +792,10 @@ struct ocfs2_xattr_tree_root {#define OCFS2_XATTR_SIZE (size) (((size) + OCFS2_XATTR_ROUND) & \ ~(OCFS2_XATTR_ROUND)) +#define OCFS2_XATTR_BUCKET_SIZE 4096 +#define OCFS2_XATTR_MAX_BLOCKS_PER_BUCKET (OCFS2_XATTR_BUCKET_SIZE \ + / OCFS2_MIN_BLOCKSIZE) +/* * On disk structure for xattr block. */
@@ -957,6 +966,17 @@ static inline u64 ocfs2_backup_super_blkno(struct super_block *sb, int index)return 0 ; } + +static inline u16 ocfs2_xattr_recs_per_xb (struct super_block *sb) +{ +int size; + + size = sb->s_blocksize - + offsetof(struct ocfs2_xattr_block , + xb_attrs.xb_root.xt_list.l_recs); + +return size /sizeof (struct ocfs2_extent_rec ); +}#else static inline int ocfs2_fast_symlink_chars (int blocksize) {
@@ -1040,6 +1060,17 @@ static inline uint64_t ocfs2_backup_super_blkno(int blocksize, int index)return 0 ; } + +static inline int ocfs2_xattr_recs_per_xb (int blocksize) +{ +int size; + + size = blocksize - + offsetof(struct ocfs2_xattr_block , + xb_attrs.xb_root.xt_list.l_recs); + +return size /sizeof (struct ocfs2_extent_rec ); +}#endif /* __KERNEL__ */
fs/ocfs2/xattr.c +251 -4
289 lines not on this page · raw message
fs/ocfs2/xattr.h +9 -0
@@ -55,4 +55,13 @@ extern int ocfs2_xattr_set(struct inode *, int, const char *, const void *,extern int ocfs2_xattr_remove (struct inode *inode,struct buffer_head *di_bh);extern struct xattr_handler *ocfs2_xattr_handlers[]; +static inline u16 ocfs2_xattr_buckets_per_cluster (struct ocfs2_super *osb) +{ +return (1 << osb->s_clustersize_bits) /OCFS2_XATTR_BUCKET_SIZE ; +} + +static inline u16 ocfs2_blocks_per_xattr_bucket (struct super_block *sb) +{ +return OCFS2_XATTR_BUCKET_SIZE / (1 << sb->s_blocksize_bits); +}#endif /* OCFS2_XATTR_H */
Signature · 1 line
1.5.4.GIT
PATCH 11/15 Add xattr bucket iteration for large numbers of EAs.v11
Quoted 10 lines · Tao Ma
Modification from V2 to V3:
1. Add a new abstration ofocfs2_xattr_bucketwhich records bhs in this bucket andocfs2_xattr_headerfor quick refrence.
2. Nowocfs2_iterate_xattr_bucketswill only read the bhs of the bucket, creat aocfs2_xattr_bucketand let the caller do all the rest of the work.
3. According to the discussion with Mark, now all thexe_entrywill only exist in the 1st block of the bucket and name/value pair will not spread multiple blocks, so the efficiency will be improved and the code is much easier.
Great, I like this new abstraction too.
--Mark
--
Mark Fasheh
PATCH 12/15 Add xattr find process for xattr index btree.v3
In reply to Tao MaModification from V2 to V3:
1. Refactor ocfs2_xattr_get_value_outside so that it now only handles ocfs2_xattr_value_root.
2. Remove the kmallc+memcpy in list/find.
We use bucket in ocfs2 to store large numbers of EAs, so when we want to find a specified xattr, we do like this:
1. Use ocfs2_xattr_get_rec to find the xattr clusters.
2. Find the xattr bucket which may contain this xattr.
3. Iterate the bucket and find whether the xattr exist. In ocfs2_xattr_block_get, we need to recalcuate the block offset and name offset for the right position of name/value.
--- fs/ocfs2/xattr.c | 347 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 files changed, 324 insertions(+), 23 deletions(-)
fs/ocfs2/xattr.c +324 -23
@@ -115,12 +115,25 @@ struct ocfs2_xattr_search {*/ struct buffer_head *xattr_bh;struct ocfs2_xattr_header *header; +struct ocfs2_xattr_bucket bucket;void *base;void *end;struct ocfs2_xattr_entry *here;int not_found; }; +static int ocfs2_xattr_bucket_get_name_value (struct inode *inode, +struct ocfs2_xattr_header *xh, +int index, +int *block_off, +int *new_offset); + +static int ocfs2_xattr_index_block_find (struct inode *inode, +struct buffer_head *root_bh, +int name_index, +const char *name, +struct ocfs2_xattr_search *xs); + static int ocfs2_xattr_tree_list_index_block(struct inode *inode, struct ocfs2_xattr_tree_root *xt, char *buffer,
@@ -620,7 +633,7 @@ static int ocfs2_xattr_find_entry(int name_index, }static int ocfs2_xattr_get_value_outside (struct inode *inode, -struct ocfs2_xattr_search *xs, +struct ocfs2_xattr_value_root *xv,void *buffer,size_t len) {
@@ -629,12 +642,8 @@ static int ocfs2_xattr_get_value_outside(struct inode *inode,int i, ret =0 ;size_t cplen, blocksize;struct buffer_head *bh =NULL ; -struct ocfs2_xattr_value_root *xv;struct ocfs2_extent_list *el; - xv = (struct ocfs2_xattr_value_root *) - (xs->base +le16_to_cpu (xs->here->xe_name_offset) + -OCFS2_XATTR_SIZE (xs->here->xe_name_len)); el = &xv->xr_list; clusters =le32_to_cpu (xv->xr_clusters); bpc =ocfs2_clusters_to_blocks (inode->i_sb,1 );
@@ -684,6 +693,7 @@ static int ocfs2_xattr_ibody_get(struct inode *inode, {struct ocfs2_inode_info *oi =OCFS2_I (inode);struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; +struct ocfs2_xattr_value_root *xv;size_t size;int ret =0 ;
@@ -708,7 +718,11 @@ static int ocfs2_xattr_ibody_get(struct inode *inode,le16_to_cpu (xs->here->xe_name_offset) + OCFS2_XATTR_SIZE(xs->here->xe_name_len), size); }else { - ret =ocfs2_xattr_get_value_outside (inode, xs, + xv = (struct ocfs2_xattr_value_root *) + (xs->base +le16_to_cpu ( + xs->here->xe_name_offset) + +OCFS2_XATTR_SIZE (xs->here->xe_name_len)); + ret =ocfs2_xattr_get_value_outside (inode, xv, buffer, size);if (ret <0 ) {mlog_errno (ret);
@@ -730,12 +744,15 @@ static int ocfs2_xattr_block_get(struct inode *inode,struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data;struct buffer_head *blk_bh =NULL ;struct ocfs2_xattr_block *xb; +struct ocfs2_xattr_value_root *xv;size_t size; -int ret = -ENODATA ; +int ret = -ENODATA , name_offset, name_len, block_off, i;if (!di->i_xattr_loc)return ret; +memset (&xs->bucket,0 ,sizeof (xs->bucket)); + ret =ocfs2_read_block (OCFS2_SB (inode->i_sb),le64_to_cpu (di->i_xattr_loc), &blk_bh,OCFS2_BH_CACHED , inode);
@@ -752,12 +769,19 @@ static int ocfs2_xattr_block_get(struct inode *inode, xs->xattr_bh = blk_bh; xb = (struct ocfs2_xattr_block *)blk_bh->b_data; - xs->header = &xb->xb_attrs.xb_header; - xs->base = (void *)xs->header; - xs->end = (void *)(blk_bh->b_data) + blk_bh->b_size; - xs->here = xs->header->xh_entries; - ret =ocfs2_xattr_find_entry (name_index, name, xs); +if (!(le16_to_cpu (xb->xb_flags) &OCFS2_XATTR_INDEXED )) { + xs->header = &xb->xb_attrs.xb_header; + xs->base = (void *)xs->header; + xs->end = (void *)(blk_bh->b_data) + blk_bh->b_size; + xs->here = xs->header->xh_entries; + + ret =ocfs2_xattr_find_entry (name_index, name, xs); + }else + ret =ocfs2_xattr_index_block_find (inode, blk_bh, + name_index, + name, xs); +if (ret) goto cleanup; size =le64_to_cpu (xs->here->xe_value_size);
@@ -765,12 +789,26 @@ static int ocfs2_xattr_block_get(struct inode *inode, ret = -ERANGE ;if (size > buffer_size) goto cleanup; + + name_offset =le16_to_cpu (xs->here->xe_name_offset); + name_len =OCFS2_XATTR_SIZE (xs->here->xe_name_len); + i = xs->here - xs->header->xh_entries; + +if (le16_to_cpu (xb->xb_flags) &OCFS2_XATTR_INDEXED ) { + ret =ocfs2_xattr_bucket_get_name_value (inode, + xs->bucket.xh, + i, + &block_off, + &name_offset); + xs->base = xs->bucket.bhs[block_off]->b_data; + }if (ocfs2_xattr_is_local (xs->here)) {memcpy (buffer, (void *)xs->base + -le16_to_cpu (xs->here->xe_name_offset) + -OCFS2_XATTR_SIZE (xs->here->xe_name_len), size); + name_offset + name_len, size); }else { - ret =ocfs2_xattr_get_value_outside (inode, xs, + xv = (struct ocfs2_xattr_value_root *) + (xs->base + name_offset + name_len); + ret =ocfs2_xattr_get_value_outside (inode, xv, buffer, size);if (ret <0 ) {mlog_errno (ret);
@@ -780,8 +818,11 @@ static int ocfs2_xattr_block_get(struct inode *inode, } ret = size; cleanup: -brelse (blk_bh); +for (i =0 ; i <OCFS2_XATTR_MAX_BLOCKS_PER_BUCKET ; i++) +brelse (xs->bucket.bhs[i]); +memset (&xs->bucket,0 ,sizeof (xs->bucket)); +brelse (blk_bh);return ret; }
@@ -1695,6 +1736,7 @@ static int ocfs2_xattr_block_find(struct inode *inode, {struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data;struct buffer_head *blk_bh =NULL ; +struct ocfs2_xattr_block *xb;int ret =0 ;if (!di->i_xattr_loc)
@@ -1715,20 +1757,26 @@ static int ocfs2_xattr_block_find(struct inode *inode, } xs->xattr_bh = blk_bh; - xs->header = &((struct ocfs2_xattr_block *)blk_bh->b_data)-> - xb_attrs.xb_header; - xs->base = (void *)xs->header; - xs->end = (void *)(blk_bh->b_data) + blk_bh->b_size; - xs->here = xs->header->xh_entries; + xb = (struct ocfs2_xattr_block *)blk_bh->b_data; + +if (!(le16_to_cpu (xb->xb_flags) &OCFS2_XATTR_INDEXED )) { + xs->header = &xb->xb_attrs.xb_header; + xs->base = (void *)xs->header; + xs->end = (void *)(blk_bh->b_data) + blk_bh->b_size; + xs->here = xs->header->xh_entries; + + ret =ocfs2_xattr_find_entry (name_index, name, xs); + }else + ret =ocfs2_xattr_index_block_find (inode, blk_bh, + name_index, + name, xs); - ret =ocfs2_xattr_find_entry (name_index, name, xs);if (ret && ret != -ENODATA ) { xs->xattr_bh =NULL ; goto cleanup; } xs->not_found = ret;return 0 ; - cleanup:brelse (blk_bh);
@@ -1957,6 +2005,18 @@ cleanup:return ret; } +static inline u32 ocfs2_xattr_hash_by_name (struct inode *inode, +int name_index, +const char *suffix_name) +{ +struct xattr_handler *handler =ocfs2_xattr_handler (name_index); +char *prefix = handler->prefix; +int prefix_len =strlen (handler->prefix); + +return ocfs2_xattr_name_hash (inode, prefix, prefix_len, + (char *)suffix_name,strlen (suffix_name)); +} +/* * Find the xattr extent rec which may contains name_hash. * e_cpos will be the first name hash of the xattr rec.
@@ -2026,6 +2086,247 @@ typedef int (xattr_bucket_func)(struct inode *inode,struct ocfs2_xattr_bucket *bucket, void *para); +static int ocfs2_find_xe_in_bucket (struct inode *inode, +struct buffer_head *header_bh, +int name_index, +const char *name, +u32 name_hash, +u16 *xe_index, +int *found) +{ +int i, ret =0 , cmp =1 , block_off, new_offset; +struct ocfs2_xattr_header *xh = + (struct ocfs2_xattr_header *)header_bh->b_data; +size_t name_len =strlen (name); +struct ocfs2_xattr_entry *xe =NULL ; +struct buffer_head *name_bh =NULL ; +char *xe_name; + +/* +* We don't use binary search in the bucket because there +* may be multiple entries with the same name hash. +*/ +for (i =0 ; i <le16_to_cpu (xh->xh_count); i++) { + xe = &xh->xh_entries[i]; + +if (name_hash >le32_to_cpu (xe->xe_name_hash)) +continue ; +else if (name_hash <le32_to_cpu (xe->xe_name_hash)) +break ; + + cmp = name_index -ocfs2_xattr_get_type (xe); +if (!cmp) + cmp = name_len - xe->xe_name_len; +if (cmp) +continue ; + + ret =ocfs2_xattr_bucket_get_name_value (inode, + xh, + i, + &block_off, + &new_offset); +if (ret) { +mlog_errno (ret); +break ; + } + + ret =ocfs2_read_block (OCFS2_SB (inode->i_sb), + header_bh->b_blocknr + block_off, + &name_bh,OCFS2_BH_CACHED , inode); +if (ret) { +mlog_errno (ret); +break ; + } + xe_name = name_bh->b_data + new_offset; + + cmp =memcmp (name, xe_name, name_len); +brelse (name_bh); + name_bh =NULL ; + +if (cmp ==0 ) { + *xe_index = i; + *found =1 ; + ret =0 ; +break ; + } + } + +return ret; +} + +/* +* Find the specided xattr entry in a series of buckets. +* This series start from p_blkno and last for num_clusters. +* The ocfs2_xattr_header.xh_num_buckets of the first bucket contains +* the num of the valid buckets. +* +* Return the buffer_head this xattr should reside in. And if the xattr's +* hash is in the gap of 2 buckets, return the lower bucket. +*/ +static int ocfs2_xattr_bucket_find (struct inode *inode, +int name_index, +const char *name, +u32 name_hash, +u64 p_blkno, +u32 first_hash, +u32 num_clusters, +struct ocfs2_xattr_search *xs) +{ +int ret, found =0 ; +struct buffer_head *bh =NULL ; +struct buffer_head *lower_bh =NULL ; +struct ocfs2_xattr_header *xh =NULL ; +struct ocfs2_xattr_entry *xe =NULL ; +u16 index =0 ; +u16 blk_per_bucket =ocfs2_blocks_per_xattr_bucket (inode->i_sb); +int low_bucket =0 , bucket, high_bucket; +u32 last_hash; +u64 blkno; + + ret =ocfs2_read_block (OCFS2_SB (inode->i_sb), p_blkno, + &bh,OCFS2_BH_CACHED , inode); +if (ret) { +mlog_errno (ret); + goto out; + } + + xh = (struct ocfs2_xattr_header *)bh->b_data; + high_bucket =le16_to_cpu (xh->xh_num_buckets) -1 ; + +while (low_bucket <= high_bucket) { +brelse (bh); + bh =NULL ; + bucket = (low_bucket + high_bucket) /2 ; + + blkno = p_blkno + bucket * blk_per_bucket; + + ret =ocfs2_read_block (OCFS2_SB (inode->i_sb), blkno, + &bh,OCFS2_BH_CACHED , inode); +if (ret) { +mlog_errno (ret); + goto out; + } + + xh = (struct ocfs2_xattr_header *)bh->b_data; + xe = &xh->xh_entries[0 ]; +if (name_hash <le32_to_cpu (xe->xe_name_hash)) { + high_bucket = bucket -1 ; +continue ; + } + +/* +* Check whether the hash of the last entry in our +* bucket is larger than the search one. +*/ + xe = &xh->xh_entries[le16_to_cpu (xh->xh_count) -1 ]; + last_hash =le32_to_cpu (xe->xe_name_hash); + +/* record lower_bh which may be the insert place. */ +brelse (lower_bh); + lower_bh = bh; + bh =NULL ; + +if (name_hash >le32_to_cpu (xe->xe_name_hash)) { + low_bucket = bucket +1 ; +continue ; + } + +/* the searched xattr should reside in this bucket if exists. */ + ret =ocfs2_find_xe_in_bucket (inode, lower_bh, + name_index, name, name_hash, + &index, &found); +break ; + } + +/* +* Record the bucket we have found. +* When the xattr's hash value is in the gap of 2 buckets, we will +* always set it to the previous bucket. +*/ +if (!lower_bh) { +/* +* We can't find any bucket whose first name_hash is less +* than the find name_hash. +*/ +BUG_ON (bh->b_blocknr != p_blkno); + lower_bh = bh; + bh =NULL ; + } + xs->bucket.bhs[0 ] = lower_bh; + xs->bucket.xh = (struct ocfs2_xattr_header *) + xs->bucket.bhs[0 ]->b_data; + lower_bh =NULL ; + + xs->header = xs->bucket.xh; + xs->base = xs->bucket.bhs[0 ]->b_data; + xs->end = xs->base + inode->i_sb->s_blocksize; + +if (found) { +/* +* If we have found the xattr enty, read all the blocks in +* this bucket. +*/ + ret =ocfs2_read_blocks (OCFS2_SB (inode->i_sb), + xs->bucket.bhs[0 ]->b_blocknr +1 , + blk_per_bucket -1 , &xs->bucket.bhs[1 ], +OCFS2_BH_CACHED , inode); +if (ret) { +mlog_errno (ret); + goto out; + } + + xs->here = &xs->header->xh_entries[index]; +mlog (0 ,"find xattr %s in bucket %llu, entry = %u\n" , name, + (unsigned long long )xs->bucket.bhs[0 ]->b_blocknr, index); + }else + ret = -ENODATA ; + +out: +brelse (bh); +brelse (lower_bh); +return ret; +} + +static int ocfs2_xattr_index_block_find (struct inode *inode, +struct buffer_head *root_bh, +int name_index, +const char *name, +struct ocfs2_xattr_search *xs) +{ +int ret; +struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)root_bh->b_data; +struct ocfs2_xattr_tree_root *xb_root = &xb->xb_attrs.xb_root; +struct ocfs2_extent_list *el = &xb_root->xt_list; +u64 p_blkno =0 ; +u32 first_hash, num_clusters =0 ; +u32 name_hash =ocfs2_xattr_hash_by_name (inode, name_index, name); + +if (le16_to_cpu (el->l_next_free_rec) ==0 ) +return -ENODATA ; + +mlog (0 ,"find xattr %s, hash = %u, index = %d in xattr tree\n" , + name, name_hash, name_index); + + ret =ocfs2_xattr_get_rec (inode, name_hash, &p_blkno, &first_hash, + &num_clusters, el); +if (ret) { +mlog_errno (ret); + goto out; + } + +BUG_ON (p_blkno ==0 || num_clusters ==0 || first_hash > name_hash); + +mlog (0 ,"find xattr extent rec %u clusters from %llu, the first hash " +"in the rec is %u\n" , num_clusters, p_blkno, first_hash); + + ret =ocfs2_xattr_bucket_find (inode, name_index, name, name_hash, + p_blkno, first_hash, num_clusters, xs); + +out: +return ret; +} + static int ocfs2_iterate_xattr_buckets(struct inode *inode, u64 blkno, u32 clusters,
Signature · 1 line
1.5.4.GIT
PATCH 12/15 Add xattr find process for xattr index btree.v3
fs/ocfs2/xattr.c typedef int (xattr_bucket_func)(struct inode *inode, · line 2158
Quoted 3 lines · Tao Ma
fs/ocfs2/xattr.c +3
-0+ +/* +* Find the specided xattr entry in a series of buckets.
"specified"
Quoted 69 lines
+ * This series start from
p_blknoand last fornum_clusters.
+ * The ocfs2_xattr_header.xh_num_buckets of the first bucket contains
+ * the num of the valid buckets.
+ *
+ * Return thebuffer_headthis xattr should reside in. And if the xattr's
+ * hash is in the gap of 2 buckets, return the lower bucket.
+ */fs/ocfs2/xattr.c +62
-0+static int ocfs2_xattr_bucket_find (struct inode *inode, +int name_index, +const char *name, +u32 name_hash, +u64 p_blkno, +u32 first_hash, +u32 num_clusters, +struct ocfs2_xattr_search *xs) +{ +int ret, found =0 ; +struct buffer_head *bh =NULL ; +struct buffer_head *lower_bh =NULL ; +struct ocfs2_xattr_header *xh =NULL ; +struct ocfs2_xattr_entry *xe =NULL ; +u16 index =0 ; +u16 blk_per_bucket =ocfs2_blocks_per_xattr_bucket (inode->i_sb); +int low_bucket =0 , bucket, high_bucket; +u32 last_hash; +u64 blkno; + + ret =ocfs2_read_block (OCFS2_SB (inode->i_sb), p_blkno, + &bh,OCFS2_BH_CACHED , inode); +if (ret) { +mlog_errno (ret); + goto out; + } + + xh = (struct ocfs2_xattr_header *)bh->b_data; + high_bucket =le16_to_cpu (xh->xh_num_buckets) -1 ; + +while (low_bucket <= high_bucket) { +brelse (bh); + bh =NULL ; + bucket = (low_bucket + high_bucket) /2 ; + + blkno = p_blkno + bucket * blk_per_bucket; + + ret =ocfs2_read_block (OCFS2_SB (inode->i_sb), blkno, + &bh,OCFS2_BH_CACHED , inode); +if (ret) { +mlog_errno (ret); + goto out; + } + + xh = (struct ocfs2_xattr_header *)bh->b_data; + xe = &xh->xh_entries[0 ]; +if (name_hash <le32_to_cpu (xe->xe_name_hash)) { + high_bucket = bucket -1 ; +continue ; + } + +/* +* Check whether the hash of the last entry in our +* bucket is larger than the search one. +*/ + xe = &xh->xh_entries[le16_to_cpu (xh->xh_count) -1 ]; + last_hash =le32_to_cpu (xe->xe_name_hash); + +/* record lower_bh which may be the insert place. */ +brelse (lower_bh); + lower_bh = bh; + bh =NULL ;
fs/ocfs2/xattr.c typedef int (xattr_bucket_func)(struct inode *inode, · line 2237
Quoted 10 lines
fs/ocfs2/xattr.c +10
-0+ +if (name_hash >le32_to_cpu (xe->xe_name_hash)) { + low_bucket = bucket +1 ; +continue ; + } + +/* the searched xattr should reside in this bucket if exists. */ + ret =ocfs2_find_xe_in_bucket (inode, lower_bh, + name_index, name, name_hash, + &index, &found);
'ret' isn't checked here, and it's overwritten unconditionally below, so I assume you meant to check it for error at least...
--Mark
--
Mark Fasheh
PATCH 12/15 Add xattr find process for xattr index btree.v3
Hi Mark,
Thanks for your review, here is the revised one.
Modification from V3 to V4:
1. fix a typo of "specided".
2. check the return value of ocfs2_find_xe_in_bucket.
We use bucket in ocfs2 to store large numbers of EAs, so when we want to find a specified xattr, we do like this:
1. Use ocfs2_xattr_get_rec to find the xattr clusters.
2. Find the xattr bucket which may contain this xattr.
3. Iterate the bucket and find whether the xattr exist. In ocfs2_xattr_block_get, we need to recalcuate the block offset and name offset for the right position of name/value.
--- fs/ocfs2/xattr.c | 351 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 files changed, 328 insertions(+), 23 deletions(-)
fs/ocfs2/xattr.c +328 -23
@@ -115,12 +115,25 @@ struct ocfs2_xattr_search {*/ struct buffer_head *xattr_bh;struct ocfs2_xattr_header *header; +struct ocfs2_xattr_bucket bucket;void *base;void *end;struct ocfs2_xattr_entry *here;int not_found; }; +static int ocfs2_xattr_bucket_get_name_value (struct inode *inode, +struct ocfs2_xattr_header *xh, +int index, +int *block_off, +int *new_offset); + +static int ocfs2_xattr_index_block_find (struct inode *inode, +struct buffer_head *root_bh, +int name_index, +const char *name, +struct ocfs2_xattr_search *xs); + static int ocfs2_xattr_tree_list_index_block(struct inode *inode, struct ocfs2_xattr_tree_root *xt, char *buffer,
@@ -620,7 +633,7 @@ static int ocfs2_xattr_find_entry(int name_index, }static int ocfs2_xattr_get_value_outside (struct inode *inode, -struct ocfs2_xattr_search *xs, +struct ocfs2_xattr_value_root *xv,void *buffer,size_t len) {
@@ -629,12 +642,8 @@ static int ocfs2_xattr_get_value_outside(struct inode *inode,int i, ret =0 ;size_t cplen, blocksize;struct buffer_head *bh =NULL ; -struct ocfs2_xattr_value_root *xv;struct ocfs2_extent_list *el; - xv = (struct ocfs2_xattr_value_root *) - (xs->base +le16_to_cpu (xs->here->xe_name_offset) + -OCFS2_XATTR_SIZE (xs->here->xe_name_len)); el = &xv->xr_list; clusters =le32_to_cpu (xv->xr_clusters); bpc =ocfs2_clusters_to_blocks (inode->i_sb,1 );
@@ -684,6 +693,7 @@ static int ocfs2_xattr_ibody_get(struct inode *inode, {struct ocfs2_inode_info *oi =OCFS2_I (inode);struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; +struct ocfs2_xattr_value_root *xv;size_t size;int ret =0 ;
@@ -708,7 +718,11 @@ static int ocfs2_xattr_ibody_get(struct inode *inode,le16_to_cpu (xs->here->xe_name_offset) + OCFS2_XATTR_SIZE(xs->here->xe_name_len), size); }else { - ret =ocfs2_xattr_get_value_outside (inode, xs, + xv = (struct ocfs2_xattr_value_root *) + (xs->base +le16_to_cpu ( + xs->here->xe_name_offset) + +OCFS2_XATTR_SIZE (xs->here->xe_name_len)); + ret =ocfs2_xattr_get_value_outside (inode, xv, buffer, size);if (ret <0 ) {mlog_errno (ret);
@@ -730,12 +744,15 @@ static int ocfs2_xattr_block_get(struct inode *inode,struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data;struct buffer_head *blk_bh =NULL ;struct ocfs2_xattr_block *xb; +struct ocfs2_xattr_value_root *xv;size_t size; -int ret = -ENODATA ; +int ret = -ENODATA , name_offset, name_len, block_off, i;if (!di->i_xattr_loc)return ret; +memset (&xs->bucket,0 ,sizeof (xs->bucket)); + ret =ocfs2_read_block (OCFS2_SB (inode->i_sb),le64_to_cpu (di->i_xattr_loc), &blk_bh,OCFS2_BH_CACHED , inode);
@@ -752,12 +769,19 @@ static int ocfs2_xattr_block_get(struct inode *inode, xs->xattr_bh = blk_bh; xb = (struct ocfs2_xattr_block *)blk_bh->b_data; - xs->header = &xb->xb_attrs.xb_header; - xs->base = (void *)xs->header; - xs->end = (void *)(blk_bh->b_data) + blk_bh->b_size; - xs->here = xs->header->xh_entries; - ret =ocfs2_xattr_find_entry (name_index, name, xs); +if (!(le16_to_cpu (xb->xb_flags) &OCFS2_XATTR_INDEXED )) { + xs->header = &xb->xb_attrs.xb_header; + xs->base = (void *)xs->header; + xs->end = (void *)(blk_bh->b_data) + blk_bh->b_size; + xs->here = xs->header->xh_entries; + + ret =ocfs2_xattr_find_entry (name_index, name, xs); + }else + ret =ocfs2_xattr_index_block_find (inode, blk_bh, + name_index, + name, xs); +if (ret) goto cleanup; size =le64_to_cpu (xs->here->xe_value_size);
@@ -765,12 +789,26 @@ static int ocfs2_xattr_block_get(struct inode *inode, ret = -ERANGE ;if (size > buffer_size) goto cleanup; + + name_offset =le16_to_cpu (xs->here->xe_name_offset); + name_len =OCFS2_XATTR_SIZE (xs->here->xe_name_len); + i = xs->here - xs->header->xh_entries; + +if (le16_to_cpu (xb->xb_flags) &OCFS2_XATTR_INDEXED ) { + ret =ocfs2_xattr_bucket_get_name_value (inode, + xs->bucket.xh, + i, + &block_off, + &name_offset); + xs->base = xs->bucket.bhs[block_off]->b_data; + }if (ocfs2_xattr_is_local (xs->here)) {memcpy (buffer, (void *)xs->base + -le16_to_cpu (xs->here->xe_name_offset) + -OCFS2_XATTR_SIZE (xs->here->xe_name_len), size); + name_offset + name_len, size); }else { - ret =ocfs2_xattr_get_value_outside (inode, xs, + xv = (struct ocfs2_xattr_value_root *) + (xs->base + name_offset + name_len); + ret =ocfs2_xattr_get_value_outside (inode, xv, buffer, size);if (ret <0 ) {mlog_errno (ret);
@@ -780,8 +818,11 @@ static int ocfs2_xattr_block_get(struct inode *inode, } ret = size; cleanup: -brelse (blk_bh); +for (i =0 ; i <OCFS2_XATTR_MAX_BLOCKS_PER_BUCKET ; i++) +brelse (xs->bucket.bhs[i]); +memset (&xs->bucket,0 ,sizeof (xs->bucket)); +brelse (blk_bh);return ret; }
@@ -1695,6 +1736,7 @@ static int ocfs2_xattr_block_find(struct inode *inode, {struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data;struct buffer_head *blk_bh =NULL ; +struct ocfs2_xattr_block *xb;int ret =0 ;if (!di->i_xattr_loc)
@@ -1715,20 +1757,26 @@ static int ocfs2_xattr_block_find(struct inode *inode, } xs->xattr_bh = blk_bh; - xs->header = &((struct ocfs2_xattr_block *)blk_bh->b_data)-> - xb_attrs.xb_header; - xs->base = (void *)xs->header; - xs->end = (void *)(blk_bh->b_data) + blk_bh->b_size; - xs->here = xs->header->xh_entries; + xb = (struct ocfs2_xattr_block *)blk_bh->b_data; + +if (!(le16_to_cpu (xb->xb_flags) &OCFS2_XATTR_INDEXED )) { + xs->header = &xb->xb_attrs.xb_header; + xs->base = (void *)xs->header; + xs->end = (void *)(blk_bh->b_data) + blk_bh->b_size; + xs->here = xs->header->xh_entries; + + ret =ocfs2_xattr_find_entry (name_index, name, xs); + }else + ret =ocfs2_xattr_index_block_find (inode, blk_bh, + name_index, + name, xs); - ret =ocfs2_xattr_find_entry (name_index, name, xs);if (ret && ret != -ENODATA ) { xs->xattr_bh =NULL ; goto cleanup; } xs->not_found = ret;return 0 ; - cleanup:brelse (blk_bh);
@@ -1957,6 +2005,18 @@ cleanup:return ret; } +static inline u32 ocfs2_xattr_hash_by_name (struct inode *inode, +int name_index, +const char *suffix_name) +{ +struct xattr_handler *handler =ocfs2_xattr_handler (name_index); +char *prefix = handler->prefix; +int prefix_len =strlen (handler->prefix); + +return ocfs2_xattr_name_hash (inode, prefix, prefix_len, + (char *)suffix_name,strlen (suffix_name)); +} +/* * Find the xattr extent rec which may contains name_hash. * e_cpos will be the first name hash of the xattr rec.
@@ -2026,6 +2086,251 @@ typedef int (xattr_bucket_func)(struct inode *inode,struct ocfs2_xattr_bucket *bucket, void *para); +static int ocfs2_find_xe_in_bucket (struct inode *inode, +struct buffer_head *header_bh, +int name_index, +const char *name, +u32 name_hash, +u16 *xe_index, +int *found) +{ +int i, ret =0 , cmp =1 , block_off, new_offset; +struct ocfs2_xattr_header *xh = + (struct ocfs2_xattr_header *)header_bh->b_data; +size_t name_len =strlen (name); +struct ocfs2_xattr_entry *xe =NULL ; +struct buffer_head *name_bh =NULL ; +char *xe_name; + +/* +* We don't use binary search in the bucket because there +* may be multiple entries with the same name hash. +*/ +for (i =0 ; i <le16_to_cpu (xh->xh_count); i++) { + xe = &xh->xh_entries[i]; + +if (name_hash >le32_to_cpu (xe->xe_name_hash)) +continue ; +else if (name_hash <le32_to_cpu (xe->xe_name_hash)) +break ; + + cmp = name_index -ocfs2_xattr_get_type (xe); +if (!cmp) + cmp = name_len - xe->xe_name_len; +if (cmp) +continue ; + + ret =ocfs2_xattr_bucket_get_name_value (inode, + xh, + i, + &block_off, + &new_offset); +if (ret) { +mlog_errno (ret); +break ; + } + + ret =ocfs2_read_block (OCFS2_SB (inode->i_sb), + header_bh->b_blocknr + block_off, + &name_bh,OCFS2_BH_CACHED , inode); +if (ret) { +mlog_errno (ret); +break ; + } + xe_name = name_bh->b_data + new_offset; + + cmp =memcmp (name, xe_name, name_len); +brelse (name_bh); + name_bh =NULL ; + +if (cmp ==0 ) { + *xe_index = i; + *found =1 ; + ret =0 ; +break ; + } + } + +return ret; +} + +/* +* Find the specified xattr entry in a series of buckets. +* This series start from p_blkno and last for num_clusters. +* The ocfs2_xattr_header.xh_num_buckets of the first bucket contains +* the num of the valid buckets. +* +* Return the buffer_head this xattr should reside in. And if the xattr's +* hash is in the gap of 2 buckets, return the lower bucket. +*/ +static int ocfs2_xattr_bucket_find (struct inode *inode, +int name_index, +const char *name, +u32 name_hash, +u64 p_blkno, +u32 first_hash, +u32 num_clusters, +struct ocfs2_xattr_search *xs) +{ +int ret, found =0 ; +struct buffer_head *bh =NULL ; +struct buffer_head *lower_bh =NULL ; +struct ocfs2_xattr_header *xh =NULL ; +struct ocfs2_xattr_entry *xe =NULL ; +u16 index =0 ; +u16 blk_per_bucket =ocfs2_blocks_per_xattr_bucket (inode->i_sb); +int low_bucket =0 , bucket, high_bucket; +u32 last_hash; +u64 blkno; + + ret =ocfs2_read_block (OCFS2_SB (inode->i_sb), p_blkno, + &bh,OCFS2_BH_CACHED , inode); +if (ret) { +mlog_errno (ret); + goto out; + } + + xh = (struct ocfs2_xattr_header *)bh->b_data; + high_bucket =le16_to_cpu (xh->xh_num_buckets) -1 ; + +while (low_bucket <= high_bucket) { +brelse (bh); + bh =NULL ; + bucket = (low_bucket + high_bucket) /2 ; + + blkno = p_blkno + bucket * blk_per_bucket; + + ret =ocfs2_read_block (OCFS2_SB (inode->i_sb), blkno, + &bh,OCFS2_BH_CACHED , inode); +if (ret) { +mlog_errno (ret); + goto out; + } + + xh = (struct ocfs2_xattr_header *)bh->b_data; + xe = &xh->xh_entries[0 ]; +if (name_hash <le32_to_cpu (xe->xe_name_hash)) { + high_bucket = bucket -1 ; +continue ; + } + +/* +* Check whether the hash of the last entry in our +* bucket is larger than the search one. +*/ + xe = &xh->xh_entries[le16_to_cpu (xh->xh_count) -1 ]; + last_hash =le32_to_cpu (xe->xe_name_hash); + +/* record lower_bh which may be the insert place. */ +brelse (lower_bh); + lower_bh = bh; + bh =NULL ; + +if (name_hash >le32_to_cpu (xe->xe_name_hash)) { + low_bucket = bucket +1 ; +continue ; + } + +/* the searched xattr should reside in this bucket if exists. */ + ret =ocfs2_find_xe_in_bucket (inode, lower_bh, + name_index, name, name_hash, + &index, &found); +if (!ret) { +mlog_errno (ret); + goto out; + } +break ; + } + +/* +* Record the bucket we have found. +* When the xattr's hash value is in the gap of 2 buckets, we will +* always set it to the previous bucket. +*/ +if (!lower_bh) { +/* +* We can't find any bucket whose first name_hash is less +* than the find name_hash. +*/ +BUG_ON (bh->b_blocknr != p_blkno); + lower_bh = bh; + bh =NULL ; + } + xs->bucket.bhs[0 ] = lower_bh; + xs->bucket.xh = (struct ocfs2_xattr_header *) + xs->bucket.bhs[0 ]->b_data; + lower_bh =NULL ; + + xs->header = xs->bucket.xh; + xs->base = xs->bucket.bhs[0 ]->b_data; + xs->end = xs->base + inode->i_sb->s_blocksize; + +if (found) { +/* +* If we have found the xattr enty, read all the blocks in +* this bucket. +*/ + ret =ocfs2_read_blocks (OCFS2_SB (inode->i_sb), + xs->bucket.bhs[0 ]->b_blocknr +1 , + blk_per_bucket -1 , &xs->bucket.bhs[1 ], +OCFS2_BH_CACHED , inode); +if (ret) { +mlog_errno (ret); + goto out; + } + + xs->here = &xs->header->xh_entries[index]; +mlog (0 ,"find xattr %s in bucket %llu, entry = %u\n" , name, + (unsigned long long )xs->bucket.bhs[0 ]->b_blocknr, index); + }else + ret = -ENODATA ; + +out: +brelse (bh); +brelse (lower_bh); +return ret; +} + +static int ocfs2_xattr_index_block_find (struct inode *inode, +struct buffer_head *root_bh, +int name_index, +const char *name, +struct ocfs2_xattr_search *xs) +{ +int ret; +struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)root_bh->b_data; +struct ocfs2_xattr_tree_root *xb_root = &xb->xb_attrs.xb_root; +struct ocfs2_extent_list *el = &xb_root->xt_list; +u64 p_blkno =0 ; +u32 first_hash, num_clusters =0 ; +u32 name_hash =ocfs2_xattr_hash_by_name (inode, name_index, name); + +if (le16_to_cpu (el->l_next_free_rec) ==0 ) +return -ENODATA ; + +mlog (0 ,"find xattr %s, hash = %u, index = %d in xattr tree\n" , + name, name_hash, name_index); + + ret =ocfs2_xattr_get_rec (inode, name_hash, &p_blkno, &first_hash, + &num_clusters, el); +if (ret) { +mlog_errno (ret); + goto out; + } + +BUG_ON (p_blkno ==0 || num_clusters ==0 || first_hash > name_hash); + +mlog (0 ,"find xattr extent rec %u clusters from %llu, the first hash " +"in the rec is %u\n" , num_clusters, p_blkno, first_hash); + + ret =ocfs2_xattr_bucket_find (inode, name_index, name, name_hash, + p_blkno, first_hash, num_clusters, xs); + +out: +return ret; +} + static int ocfs2_iterate_xattr_buckets(struct inode *inode, u64 blkno, u32 clusters,
Signature · 1 line
1.5.4.1
PATCH 12/15 Add xattr find process for xattr index btree.v3
Quoted 2 lines · Tao Ma
Hi Mark,
Thanks for your review, here is the revised one.
Ok, that looks good, thanks Tao.
--Mark
--
Mark Fasheh
PATCH 13/15 Enable xattr set in index btree. v3
In reply to Tao MaModification from V2 to V3:
1. Remove kmalloc+memcpy in set.
2. Remove xattr entry when we fails to find the enough space for outside storage.
3. Empty the whole block when creating new xattr bucket.
4. Limit xattr entry to be only in 1st block of the bucket and limit xattr name/value pair within the same block.
5. Limit the xattr tree leaf size to be 64K.
The previous 2 patches add the ability of list/get xattr in buckets for ocfs2. This patch enables ocfs2 to store large numbers of EAs.
The original design doc is written by Mark Fasheh, and it can be found in
http://oss.oracle.com/osswiki/OCFS2/DesignDocs/IndexedEATrees. Just some little modifications to it.
First, because the bucket size is 4K, a new field named as xh_offset is added in ocfs2_xattr_header to indicate the next valid name/value offset in abucket. It is used when we store new EA name/value. With this field, we can find the place more quickly and what's more, we don't need to sort the name/value every time to let the last entry indicate the next unused space. Considering when the blocksize is 512, we may have to update 8 blocks for one insertion if we sort name/value like the original in-inode xattr. It is definitely inefficient.
Because of the new xh_offset, another field named as xh_name_value_len is also added in ocfs2_xattr_header. It records the total length of all the name/values in the bucket. We need this so that we can check it and defragment the bucket if the bucket is too much fragmented.
o now the insertion will be like this:
1. xattr_index_block_find: find the right bucket by the name_hash, say bucketA.
2. check whether there is enough space in bucketA. If yes, insert it directly and modify xh_offset and xh_name_value_len accordingly. If no, check xh_name_value_len to see whether we can store this by defragment the bucket. If yes, defragment it and go on insertion.
3. If defragement doesnt' work, check whether there is new empty bucket in the clusters within this extent record. If yes, init the new bucket and move all the buckets after bucketA one by one to the next bucket. Move half of the entries in bucketA to the next bucket and go on insertion.
4. If there is no new bucket, grow the extent tree.(This should be the same as Mark has described in the design doc).
As for xattr deletion, we will delete an xattr bucket when all the xattr in this bucket are removed and move all the buckets after it to the previous one. When all the xattr buckets in an extend record are freed, free this extend records from ocfs2_xattr_tree.
--- fs/ocfs2/xattr.c | 2267 +++++++++++++++++++++++++++++++++++++++++++++++++++++- fs/ocfs2/xattr.h | 10 + 2 files changed, 2275 insertions(+), 2 deletions(-)
fs/ocfs2/xattr.c +2265 -2
@@ -36,6 +36,7 @@#include <linux/mount.h> #include <linux/writeback.h> #include <linux/falloc.h> +#include <linux/sort.h> #define MLOG_MASK_PREFIX ML_XATTR#include <cluster/masklog.h>
@@ -139,6 +140,13 @@ static int ocfs2_xattr_tree_list_index_block(struct inode *inode,char *buffer, size_t buffer_size); +static int ocfs2_xattr_create_index_block (struct inode *inode, +struct ocfs2_xattr_search *xs); + +static int ocfs2_xattr_set_entry_index_block (struct inode *inode, +struct ocfs2_xattr_info *xi, +struct ocfs2_xattr_search *xs); +static inline struct xattr_handler *ocfs2_xattr_handler (int name_index) {struct xattr_handler *handler =NULL ;
@@ -1784,6 +1792,52 @@ cleanup: }/* +* When all the xattrs are deleted from index btree, the ocfs2_xattr_tree +* will be erased and ocfs2_xattr_block will have its ocfs2_xattr_header +* re-initialized. +*/ +static int ocfs2_restore_xattr_block (struct inode *inode, +struct ocfs2_xattr_search *xs) +{ +int ret; +handle_t *handle; +struct ocfs2_super *osb =OCFS2_SB (inode->i_sb); +struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)xs->xattr_bh->b_data; +struct ocfs2_extent_list *el = &xb->xb_attrs.xb_root.xt_list; +u16 xb_flags =le16_to_cpu (xb->xb_flags); + +BUG_ON (!(xb_flags &OCFS2_XATTR_INDEXED ) || +le16_to_cpu (el->l_next_free_rec) !=0 ); + + handle =ocfs2_start_trans (osb,OCFS2_XATTR_BLOCK_UPDATE_CREDITS ); +if (IS_ERR (handle)) { + ret =PTR_ERR (handle); + handle =NULL ; + goto out; + } + + ret =ocfs2_journal_access (handle, inode, xs->xattr_bh, +OCFS2_JOURNAL_ACCESS_WRITE ); +if (ret <0 ) { +mlog_errno (ret); + goto out_commit; + } + +memset (&xb->xb_attrs,0 , inode->i_sb->s_blocksize - + offsetof(struct ocfs2_xattr_block , xb_attrs)); + + xb->xb_flags =cpu_to_le16 (xb_flags & ~OCFS2_XATTR_INDEXED ); + +ocfs2_journal_dirty (handle, xs->xattr_bh); + +out_commit: +ocfs2_commit_trans (osb, handle); +out: +return ret; +} + +/* * ocfs2_xattr_block_set() * * Set, replace or remove an extended attribute into external block.
@@ -1878,10 +1932,25 @@ out:ocfs2_free_alloc_context (meta_ac);if (ret <0 )return ret; + }else + xblk = (struct ocfs2_xattr_block *)xs->xattr_bh->b_data; + +if (!(le16_to_cpu (xblk->xb_flags) &OCFS2_XATTR_INDEXED )) { +/* Set extended attribute into external block */ + ret =ocfs2_xattr_set_entry (inode, xi, xs,OCFS2_HAS_XATTR_FL ); +if (!ret || ret != -ENOSPC ) + goto end; + + ret =ocfs2_xattr_create_index_block (inode, xs); +if (ret) + goto end; } -/* Set extended attribute into external block */ - ret =ocfs2_xattr_set_entry (inode, xi, xs,OCFS2_HAS_XATTR_FL ); + ret =ocfs2_xattr_set_entry_index_block (inode, xi, xs); +if (!ret && xblk->xb_attrs.xb_root.xt_list.l_next_free_rec ==0 ) + ret =ocfs2_restore_xattr_block (inode, xs); + +end:return ret; }
@@ -1903,6 +1972,7 @@ int ocfs2_xattr_set(struct inode *inode,struct buffer_head *di_bh =NULL ;struct ocfs2_dinode *di;int ret; +u16 i, blk_per_bucket =ocfs2_blocks_per_xattr_bucket (inode->i_sb);struct ocfs2_xattr_info xi = { .name_index = name_index,
@@ -2001,6 +2071,8 @@ cleanup:ocfs2_inode_unlock (inode,1 );brelse (di_bh);brelse (xbs.xattr_bh); +for (i =0 ; i < blk_per_bucket; i++) +brelse (xbs.bucket.bhs[i]);return ret; }
@@ -2487,3 +2559,2194 @@ static int ocfs2_xattr_tree_list_index_block(struct inode *inode, out:return ret; } + +static int cmp_xe (const void *a,const void *b) +{ +const struct ocfs2_xattr_entry *l = a, *r = b; +u32 l_hash =le32_to_cpu (l->xe_name_hash); +u32 r_hash =le32_to_cpu (r->xe_name_hash); + +if (l_hash > r_hash) +return 1 ; +if (l_hash < r_hash) +return -1 ; +return 0 ; +} + +static void swap_xe (void *a,void *b,int size) +{ +struct ocfs2_xattr_entry *l = a, *r = b, tmp; + + tmp = *l; +memcpy (l, r,sizeof (struct ocfs2_xattr_entry )); +memcpy (r, &tmp,sizeof (struct ocfs2_xattr_entry )); +} + +/* +* When the ocfs2_xattr_block is filled up, new bucket will be created +* and all the xattr entries will be moved to the new bucket. +* Note: we need to sort the entries since they are not saved in order +* in the ocfs2_xattr_block. +*/ +static void ocfs2_cp_xattr_block_to_bucket (struct inode *inode, +struct buffer_head *xb_bh, +struct buffer_head *xh_bh, +struct buffer_head *data_bh) +{ +int i, blocksize = inode->i_sb->s_blocksize; +u16 offset, size, off_change; +struct ocfs2_xattr_entry *xe; +struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)xb_bh->b_data; +struct ocfs2_xattr_header *xb_xh = &xb->xb_attrs.xb_header; +struct ocfs2_xattr_header *xh = + (struct ocfs2_xattr_header *)xh_bh->b_data; +u16 count =le16_to_cpu (xb_xh->xh_count); +char *target = xh_bh->b_data, *src = xb_bh->b_data; + +mlog (0 ,"cp xattr from block %llu to bucket %llu\n" , + (unsigned long long )xb_bh->b_blocknr, + (unsigned long long )xh_bh->b_blocknr); + +memset (xh_bh->b_data,0 , blocksize); +if (data_bh) +memset (data_bh->b_data,0 , blocksize); +/* +* Since the xe_name_offset is based on ocfs2_xattr_header, +* there is a offset change corresponding to the change of +* ocfs2_xattr_header's position. +*/ + off_change = offsetof(struct ocfs2_xattr_block, xb_attrs.xb_header); + xe = &xb_xh->xh_entries[count -1 ]; + offset =le16_to_cpu (xe->xe_name_offset) + off_change; + size = blocksize - offset; + +/* copy all the names and values. */ +if (data_bh) + target = data_bh->b_data; +memcpy (target + offset, src + offset, size); + +/* Init new header now. */ + xh->xh_count = xb_xh->xh_count; + xh->xh_num_buckets =cpu_to_le16 (1 ); + xh->xh_name_value_len =cpu_to_le16 (size); + xh->xh_offset =cpu_to_le16 (OCFS2_XATTR_BUCKET_SIZE - size); + +/* copy all the entries. */ + target = xh_bh->b_data; + offset = offsetof(struct ocfs2_xattr_header , xh_entries); + size = count *sizeof (struct ocfs2_xattr_entry ); +memcpy (target + offset, (char *)xb_xh + offset, size); + +/* Change the xe offset for all the xe because of the move. */ + off_change =OCFS2_XATTR_BUCKET_SIZE - blocksize + + offsetof(struct ocfs2_xattr_block, xb_attrs.xb_header); +for (i =0 ; i < count; i++) +le16_add_cpu (&xh->xh_entries[i].xe_name_offset, off_change); + +mlog (0 ,"copy entry: start = %u, size = %u, offset_change = %u\n" , + offset, size, off_change); + +sort (target + offset, count,sizeof (struct ocfs2_xattr_entry ), + cmp_xe, swap_xe); +} + +/* +* After we move xattr from block to index btree, we have to +* update ocfs2_xattr_search to the new xe and base. +* +* When the entry is in xattr block, xattr_bh indicates the storage place. +* While if the entry is in index b-tree, "bucket" indicates the +* real place of the xattr. +*/ +static int ocfs2_xattr_update_xattr_search (struct inode *inode, +struct ocfs2_xattr_search *xs, +struct buffer_head *old_bh, +struct buffer_head *new_bh) +{ +int ret =0 ; +char *buf = old_bh->b_data; +struct ocfs2_xattr_block *old_xb = (struct ocfs2_xattr_block *)buf; +struct ocfs2_xattr_header *old_xh = &old_xb->xb_attrs.xb_header; +int i, blocksize = inode->i_sb->s_blocksize; +u16 blk_per_bucket =ocfs2_blocks_per_xattr_bucket (inode->i_sb); + + xs->bucket.bhs[0 ] = new_bh; +get_bh (new_bh); + xs->bucket.xh = (struct ocfs2_xattr_header *)xs->bucket.bhs[0 ]->b_data; + xs->header = xs->bucket.xh; + + xs->base = new_bh->b_data; + xs->end = xs->base + inode->i_sb->s_blocksize; + +if (!xs->not_found) { +if (OCFS2_XATTR_BUCKET_SIZE != blocksize) { + ret =ocfs2_read_blocks (OCFS2_SB (inode->i_sb), + xs->bucket.bhs[0 ]->b_blocknr +1 , + blk_per_bucket -1 , &xs->bucket.bhs[1 ], +OCFS2_BH_CACHED , inode); +if (ret) { +mlog_errno (ret); +return ret; + } + + i = xs->here - old_xh->xh_entries; + xs->here = &xs->header->xh_entries[i]; + } + } + +return ret; +} + +static int ocfs2_xattr_create_index_block (struct inode *inode, +struct ocfs2_xattr_search *xs) +{ +int ret, credits =OCFS2_SUBALLOC_ALLOC ; +u32 bit_off, len; +u64 blkno; +handle_t *handle; +struct ocfs2_super *osb =OCFS2_SB (inode->i_sb); +struct ocfs2_inode_info *oi =OCFS2_I (inode); +struct ocfs2_alloc_context *data_ac; +struct buffer_head *xh_bh =NULL , *data_bh =NULL ; +struct buffer_head *xb_bh = xs->xattr_bh; +struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)xb_bh->b_data; +struct ocfs2_xattr_tree_root *xr; +u16 xb_flags =le16_to_cpu (xb->xb_flags); +u16 bpb =ocfs2_blocks_per_xattr_bucket (inode->i_sb); + +mlog (0 ,"create xattr index block for %llu\n" , + (unsigned long long )xb_bh->b_blocknr); + +BUG_ON (xb_flags &OCFS2_XATTR_INDEXED ); + + ret =ocfs2_reserve_clusters (osb,1 , &data_ac); +if (ret) { +mlog_errno (ret); + goto out; + } + +/* +* XXX: +* We can use this lock for now, and maybe move to a dedicated mutex +* if performance becomes a problem later. +*/ +down_write (&oi->ip_alloc_sem); + +/* +* 3 more credits, one for xattr block update, one for the 1st block +* of the new xattr bucket and one for the value/data. +*/ + credits +=3 ; + handle =ocfs2_start_trans (osb, credits); +if (IS_ERR (handle)) { + ret =PTR_ERR (handle); +mlog_errno (ret); + goto out_sem; + } + + ret =ocfs2_journal_access (handle, inode, xb_bh, +OCFS2_JOURNAL_ACCESS_WRITE ); +if (ret) { +mlog_errno (ret); + goto out_commit; + } + + ret =ocfs2_claim_clusters (osb, handle, data_ac,1 , &bit_off, &len); +if (ret) { +mlog_errno (ret); + goto out_commit; + } + +/* +* The bucket may spread in many blocks, and +* we will only touch the 1st block and the last block +* in the whole bucket(one for entry and one for data). +*/ + blkno =ocfs2_clusters_to_blocks (inode->i_sb, bit_off); + +mlog (0 ,"allocate 1 cluster from %llu to xattr block\n" , blkno); + + xh_bh =sb_getblk (inode->i_sb, blkno); +if (!xh_bh) { + ret = -EIO ; +mlog_errno (ret); + goto out_commit; + } + +ocfs2_set_new_buffer_uptodate (inode, xh_bh); + + ret =ocfs2_journal_access (handle, inode, xh_bh, +OCFS2_JOURNAL_ACCESS_CREATE ); +if (ret) { +mlog_errno (ret); + goto out_commit; + } + +if (bpb >1 ) { + data_bh =sb_getblk (inode->i_sb, blkno + bpb -1 ); +if (!data_bh) { + ret = -EIO ; +mlog_errno (ret); + goto out_commit; + } + +ocfs2_set_new_buffer_uptodate (inode, data_bh); + + ret =ocfs2_journal_access (handle, inode, data_bh, +OCFS2_JOURNAL_ACCESS_CREATE ); +if (ret) { +mlog_errno (ret); + goto out_commit; + } + } + +ocfs2_cp_xattr_block_to_bucket (inode, xb_bh, xh_bh, data_bh); + +ocfs2_journal_dirty (handle, xh_bh); +if (data_bh) +ocfs2_journal_dirty (handle, data_bh); + +ocfs2_xattr_update_xattr_search (inode, xs, xb_bh, xh_bh); + +/* Change from ocfs2_xattr_header to ocfs2_xattr_tree_root */ +memset (&xb->xb_attrs,0 , inode->i_sb->s_blocksize - + offsetof(struct ocfs2_xattr_block , xb_attrs)); + + xr = &xb->xb_attrs.xb_root; + xr->xt_clusters =cpu_to_le32 (1 ); + xr->xt_last_eb_blk =0 ; + xr->xt_list.l_tree_depth =0 ; + xr->xt_list.l_count =cpu_to_le16 (ocfs2_xattr_recs_per_xb (inode->i_sb)); + xr->xt_list.l_next_free_rec =cpu_to_le16 (1 ); + + xr->xt_list.l_recs[0 ].e_cpos =0 ; + xr->xt_list.l_recs[0 ].e_blkno =cpu_to_le64 (blkno); + xr->xt_list.l_recs[0 ].e_leaf_clusters =cpu_to_le16 (1 ); + + xb->xb_flags =cpu_to_le16 (xb_flags |OCFS2_XATTR_INDEXED ); + + ret =ocfs2_journal_dirty (handle, xb_bh); +if (ret) { +mlog_errno (ret); + goto out_commit; + } + +out_commit: +ocfs2_commit_trans (osb, handle); + +out_sem: +up_write (&oi->ip_alloc_sem); + +out: +if (data_ac) +ocfs2_free_alloc_context (data_ac); + +brelse (xh_bh); +brelse (data_bh); + +return ret; +} + +static int cmp_xe_offset (const void *a,const void *b) +{ +const struct ocfs2_xattr_entry *l = a, *r = b; +u32 l_name_offset =le16_to_cpu (l->xe_name_offset); +u32 r_name_offset =le16_to_cpu (r->xe_name_offset); + +if (l_name_offset < r_name_offset) +return 1 ; +if (l_name_offset > r_name_offset) +return -1 ; +return 0 ; +} + +/* +* defrag a xattr bucket if we find that the bucket has some +* holes beteen name/value pairs. +* We will move all the name/value pairs to the end of the bucket +* so that we can spare some space for insertion. +*/ +static int ocfs2_defrag_xattr_bucket (struct inode *inode, +struct ocfs2_xattr_bucket *bucket) +{ +int ret, i; +size_t end, offset, len, value_len; +struct ocfs2_xattr_header *xh; +char *entries, *buf, *bucket_buf =NULL ; +u64 blkno = bucket->bhs[0 ]->b_blocknr; +u16 blk_per_bucket =ocfs2_blocks_per_xattr_bucket (inode->i_sb); +u16 xh_offset; +struct ocfs2_super *osb =OCFS2_SB (inode->i_sb); +size_t blocksize = inode->i_sb->s_blocksize; +handle_t *handle; +struct buffer_head **bhs; +struct ocfs2_xattr_entry *xe; + + bhs =kzalloc (sizeof (struct buffer_head *) * blk_per_bucket, +GFP_NOFS ); +if (!bhs) +return -ENOMEM ; + + ret =ocfs2_read_blocks (osb, blkno, blk_per_bucket, bhs, +OCFS2_BH_CACHED , inode); +if (ret) + goto out; + +/* +* In order to make the operation more efficient and generic, +* we copy all the blocks into a contiguous memory and do the +* defragment there, so if anything is error, we will not touch +* the real block. +*/ + bucket_buf =kmalloc (OCFS2_XATTR_BUCKET_SIZE ,GFP_NOFS ); +if (!bucket_buf) { + ret = -EIO ; + goto out; + } + + buf = bucket_buf; +for (i =0 ; i < blk_per_bucket; i++, buf += blocksize) +memcpy (buf, bhs[i]->b_data, blocksize); + + handle =ocfs2_start_trans ((OCFS2_SB (inode->i_sb)), blk_per_bucket); +if (IS_ERR (handle)) { + ret =PTR_ERR (handle); + handle =NULL ; +mlog_errno (ret); + goto out; + } + +for (i =0 ; i < blk_per_bucket; i++) { + ret =ocfs2_journal_access (handle, inode, bhs[i], +OCFS2_JOURNAL_ACCESS_WRITE ); +if (ret <0 ) { +mlog_errno (ret); + goto commit; + } + } + + xh = (struct ocfs2_xattr_header *)bucket_buf; + entries = (char *)xh->xh_entries; + xh_offset =le16_to_cpu (xh->xh_offset); + +mlog (0 ,"adjust xattr bucket in %llu, count = %u, " +"xh_offset = %u, xh_name_value_len = %u.\n" , + blkno,le16_to_cpu (xh->xh_count), xh_offset, +le16_to_cpu (xh->xh_name_value_len)); + +/* +* sort all the entries by their offset. +* the largest will be the first, so that we can +* move them to the end one by one. +*/ +sort (entries,le16_to_cpu (xh->xh_count), +sizeof (struct ocfs2_xattr_entry ), + cmp_xe_offset, swap_xe); + +/* Move all name/values to the end of the bucket. */ + xe = xh->xh_entries; + end =OCFS2_XATTR_BUCKET_SIZE ; +for (i =0 ; i <le16_to_cpu (xh->xh_count); i++, xe++) { + offset =le16_to_cpu (xe->xe_name_offset); +if (ocfs2_xattr_is_local (xe)) + value_len =OCFS2_XATTR_SIZE ( +le64_to_cpu (xe->xe_value_size)); +else + value_len =OCFS2_XATTR_ROOT_SIZE ; + len =OCFS2_XATTR_SIZE (xe->xe_name_len) + value_len; + +/* +* We must make sure that the name/value pair +* exist in the same block. So adjust end to +* the previous block end if needed. +*/ +if (((end -len) / blocksize != + (end -1 ) / blocksize)) + end = end -end % blocksize; + +if (end > offset + len) { +memmove (bucket_buf + end -len, + bucket_buf + offset, len); + xe->xe_name_offset =cpu_to_le16 (end -len); + } + +mlog_bug_on_msg (end < offset + len,"Defrag check failed for " +"bucket %llu\n" , (unsigned long long )blkno); + + end -= len; + } + +mlog_bug_on_msg (xh_offset > end,"Defrag check failed for " +"bucket %llu\n" , (unsigned long long )blkno); + +if (xh_offset == end) + goto commit; + +memset (bucket_buf + xh_offset,0 , end -xh_offset); + xh->xh_offset =cpu_to_le16 (end); + +/* sort the entries by their name_hash. */ +sort (entries,le16_to_cpu (xh->xh_count), +sizeof (struct ocfs2_xattr_entry ), + cmp_xe, swap_xe); + + buf = bucket_buf; +for (i =0 ; i < blk_per_bucket; i++, buf += blocksize) { +memcpy (bhs[i]->b_data, buf, blocksize); +ocfs2_journal_dirty (handle, bhs[i]); + } + +commit: +ocfs2_commit_trans (OCFS2_SB (inode->i_sb), handle); +out: + +if (bhs) { +for (i =0 ; i < blk_per_bucket; i++) +brelse (bhs[i]); + } +kfree (bhs); + +kfree (bucket_buf); +return ret; +} + +/* +* Move half nums of the xattr bucket in the previous cluster to this new +* cluster. We only touch the last cluster of the previous extend record. +* +* first_bh is the first buffer_head of a series of bucket in the same +* extent rec and header_bh is the header of one bucket in this cluster. +* They will be updated if we move the data header_bh contains to the new +* cluster. first_hash will be set as the 1st xe's name_hash of the new cluster. +*/ +static int ocfs2_mv_xattr_bucket_cross_cluster (struct inode *inode, +handle_t *handle, +struct buffer_head **first_bh, +struct buffer_head **header_bh, +u64 new_blkno, +u64 prev_blkno, +u32 num_clusters, +u32 *first_hash) +{ +int i, ret, credits; +struct ocfs2_super *osb =OCFS2_SB (inode->i_sb); +int bpc =ocfs2_clusters_to_blocks (inode->i_sb,1 ); +int num_buckets =ocfs2_xattr_buckets_per_cluster (osb); +int blocksize = inode->i_sb->s_blocksize; +struct buffer_head *old_bh, *new_bh, *prev_bh, *new_first_bh =NULL ; +struct ocfs2_xattr_header *new_xh; +struct ocfs2_xattr_header *xh = + (struct ocfs2_xattr_header *)((*first_bh)->b_data); + +BUG_ON (le16_to_cpu (xh->xh_num_buckets) < num_buckets); +BUG_ON (OCFS2_XATTR_BUCKET_SIZE == osb->s_clustersize); + + prev_bh = *first_bh; +get_bh (prev_bh); + xh = (struct ocfs2_xattr_header *)prev_bh->b_data; + + prev_blkno += (num_clusters -1 ) * bpc + bpc /2 ; + +mlog (0 ,"move half of xattrs in cluster %llu to %llu\n" , + prev_blkno, new_blkno); + +/* +* We need to update the 1st half of the new cluster and +* 1 more for the update of the 1st bucket of the previous +* extent record. +*/ + credits = bpc /2 +1 ; + ret =ocfs2_extend_trans (handle, credits); +if (ret) { +mlog_errno (ret); + goto out; + } + + ret =ocfs2_journal_access (handle, inode, prev_bh, +OCFS2_JOURNAL_ACCESS_WRITE ); +if (ret) { +mlog_errno (ret); + goto out; + } + +for (i =0 ; i < bpc /2 ; i++, prev_blkno++, new_blkno++) { + old_bh = new_bh =NULL ; + new_bh =sb_getblk (inode->i_sb, new_blkno); +if (!new_bh) { + ret = -EIO ; +mlog_errno (ret); + goto out; + } + +ocfs2_set_new_buffer_uptodate (inode, new_bh); + + ret =ocfs2_journal_access (handle, inode, new_bh, +OCFS2_JOURNAL_ACCESS_CREATE ); +if (ret <0 ) { +mlog_errno (ret); +brelse (new_bh); + goto out; + } + + ret =ocfs2_read_block (osb, prev_blkno, + &old_bh,OCFS2_BH_CACHED , inode); +if (ret <0 ) { +mlog_errno (ret); +brelse (new_bh); + goto out; + } + +memcpy (new_bh->b_data, old_bh->b_data, blocksize); + +if (i ==0 ) { + new_xh = (struct ocfs2_xattr_header *)new_bh->b_data; + new_xh->xh_num_buckets =cpu_to_le16 (num_buckets /2 ); + +if (first_hash) + *first_hash =le32_to_cpu ( + new_xh->xh_entries[0 ].xe_name_hash); + new_first_bh = new_bh; +get_bh (new_first_bh); + } + +ocfs2_journal_dirty (handle, new_bh); + +if (*header_bh == old_bh) { +brelse (*header_bh); + *header_bh = new_bh; +get_bh (*header_bh); + +brelse (*first_bh); + *first_bh = new_first_bh; +get_bh (*first_bh); + } +brelse (new_bh); +brelse (old_bh); + } + +le16_add_cpu (&xh->xh_num_buckets, -(num_buckets /2 )); + +ocfs2_journal_dirty (handle, prev_bh); +out: +brelse (prev_bh); +brelse (new_first_bh); +return ret; +} + +static int ocfs2_read_xattr_bucket (struct inode *inode, +u64 blkno, +struct buffer_head **bhs, +int new) +{ +int ret =0 ; +u16 i, blk_per_bucket =ocfs2_blocks_per_xattr_bucket (inode->i_sb); + +if (!new) +return ocfs2_read_blocks (OCFS2_SB (inode->i_sb), blkno, + blk_per_bucket, bhs, +OCFS2_BH_CACHED , inode); + +for (i =0 ; i < blk_per_bucket; i++) { + bhs[i] =sb_getblk (inode->i_sb, blkno + i); +if (bhs[i] ==NULL ) { + ret = -EIO ; +mlog_errno (ret); +break ; + } +ocfs2_set_new_buffer_uptodate (inode, bhs[i]); + } + +return ret; +} + +/* +* Move half num of the xattrs in old bucket(blk) to new bucket(new_blk). +* first_hash will record the 1st hash of the new bucket. +*/ +static int ocfs2_half_xattr_bucket (struct inode *inode, +handle_t *handle, +u64 blk, +u64 new_blk, +u32 *first_hash, +int new_bucket_head) +{ +int ret, i; +u16 count, start, len, name_value_len, xe_len, name_offset; +u16 blk_per_bucket =ocfs2_blocks_per_xattr_bucket (inode->i_sb); +struct buffer_head **s_bhs, **t_bhs =NULL ; +struct ocfs2_xattr_header *xh; +struct ocfs2_xattr_entry *xe; +int blocksize = inode->i_sb->s_blocksize; + +mlog (0 ,"move half of xattrs from bucket %llu to %llu\n" , + blk, new_blk); + + s_bhs =kcalloc (blk_per_bucket,sizeof (struct buffer_head *),GFP_NOFS ); +if (!s_bhs) +return -ENOMEM ; + + ret =ocfs2_read_xattr_bucket (inode, blk, s_bhs,0 ); +if (ret) { +mlog_errno (ret); + goto out; + } + + ret =ocfs2_journal_access (handle, inode, s_bhs[0 ], +OCFS2_JOURNAL_ACCESS_WRITE ); +if (ret) { +mlog_errno (ret); + goto out; + } + + t_bhs =kcalloc (blk_per_bucket,sizeof (struct buffer_head *),GFP_NOFS ); +if (!t_bhs) { + ret = -ENOMEM ; + goto out; + } + + ret =ocfs2_read_xattr_bucket (inode, new_blk, t_bhs, new_bucket_head); +if (ret) { +mlog_errno (ret); + goto out; + } + +for (i =0 ; i < blk_per_bucket; i++) { + ret =ocfs2_journal_access (handle, inode, t_bhs[i], +OCFS2_JOURNAL_ACCESS_CREATE ); +if (ret) { +mlog_errno (ret); + goto out; + } + } + +/* copy the whole bucket to the new first. */ +for (i =0 ; i < blk_per_bucket; i++) +memcpy (t_bhs[i]->b_data, s_bhs[i]->b_data, blocksize); + +/* update the new bucket. */ + xh = (struct ocfs2_xattr_header *)t_bhs[0 ]->b_data; + count =le16_to_cpu (xh->xh_count); + start = count /2 ; + +/* +* Calculate the total name/value len and xh_offset for +* the old bucket first. +*/ + name_offset =OCFS2_XATTR_BUCKET_SIZE ; + name_value_len =0 ; +for (i =0 ; i < start; i++) { + xe = &xh->xh_entries[i]; + xe_len =OCFS2_XATTR_SIZE (xe->xe_name_len); +if (ocfs2_xattr_is_local (xe)) + xe_len += +OCFS2_XATTR_SIZE (le64_to_cpu (xe->xe_value_size)); +else + xe_len +=OCFS2_XATTR_ROOT_SIZE ; + name_value_len += xe_len; +if (le16_to_cpu (xe->xe_name_offset) < name_offset) + name_offset =le16_to_cpu (xe->xe_name_offset); + } + +/* +* Now begin the modification to the new bucket. +* +* In the new bucket, We just move the xattr entry to the beginning +* and don't touch the name/value. So there will be some holes in the +* bucket, and they will be removed when ocfs2_defrag_xattr_bucket is +* called. +*/ + xe = &xh->xh_entries[start]; + len =sizeof (struct ocfs2_xattr_entry ) * (count - start); +mlog (0 ,"mv xattr entry len %d from %d to %d\n" , len, + (char *)xe - (char *)xh, (char *)xh->xh_entries - (char *)xh); +memmove ((char *)xh->xh_entries, (char *)xe, len); + xe = &xh->xh_entries[count - start]; + len =sizeof (struct ocfs2_xattr_entry ) * start; +memset ((char *)xe,0 , len); + +le16_add_cpu (&xh->xh_count, -start); +le16_add_cpu (&xh->xh_name_value_len, -name_value_len); + +/* Calculate xh_offset for the new bucket. */ + xh->xh_offset =cpu_to_le16 (OCFS2_XATTR_BUCKET_SIZE ); +for (i =0 ; i <le16_to_cpu (xh->xh_count); i++) { + xe = &xh->xh_entries[i]; + xe_len =OCFS2_XATTR_SIZE (xe->xe_name_len); +if (ocfs2_xattr_is_local (xe)) + xe_len += +OCFS2_XATTR_SIZE (le64_to_cpu (xe->xe_value_size)); +else + xe_len +=OCFS2_XATTR_ROOT_SIZE ; +if (le16_to_cpu (xe->xe_name_offset) < +le16_to_cpu (xh->xh_offset)) + xh->xh_offset = xe->xe_name_offset; + } + +/* set xh->xh_num_buckets for the new xh. */ +if (new_bucket_head) + xh->xh_num_buckets =cpu_to_le16 (1 ); +else + xh->xh_num_buckets =0 ; + +for (i =0 ; i < blk_per_bucket; i++) { +ocfs2_journal_dirty (handle, t_bhs[i]); +if (ret) +mlog_errno (ret); + } + +/* store the first_hash of the new bucket. */ +if (first_hash) + *first_hash =le32_to_cpu (xh->xh_entries[0 ].xe_name_hash); + +/* +* Now only update the 1st block of the old bucket. +* Please note that the entry has been sorted already above. +*/ + xh = (struct ocfs2_xattr_header *)s_bhs[0 ]->b_data; +memset (&xh->xh_entries[start],0 , +sizeof (struct ocfs2_xattr_entry ) * (count - start)); + xh->xh_count =cpu_to_le16 (start); + xh->xh_offset =cpu_to_le16 (name_offset); + xh->xh_name_value_len =cpu_to_le16 (name_value_len); + +ocfs2_journal_dirty (handle, s_bhs[0 ]); +if (ret) +mlog_errno (ret); + +out: +if (s_bhs) { +for (i =0 ; i < blk_per_bucket; i++) +brelse (s_bhs[i]); + } +kfree (s_bhs); + +if (t_bhs) { +for (i =0 ; i < blk_per_bucket; i++) +brelse (t_bhs[i]); + } +kfree (t_bhs); + +return ret; +} + +/* +* Copy xattr from one bucket to another bucket. +* +* The caller must make sure that the journal transaction +* has enough space for journaling. +*/ +static int ocfs2_cp_xattr_bucket (struct inode *inode, +handle_t *handle, +u64 s_blkno, +u64 t_blkno, +int t_is_new) +{ +int ret, i; +int blk_per_bucket =ocfs2_blocks_per_xattr_bucket (inode->i_sb); +int blocksize = inode->i_sb->s_blocksize; +struct buffer_head **s_bhs, **t_bhs =NULL ; + +BUG_ON (s_blkno == t_blkno); + +mlog (0 ,"cp bucket %llu to %llu, target is %d\n" , + s_blkno, t_blkno, t_is_new); + + s_bhs =kzalloc (sizeof (struct buffer_head *) * blk_per_bucket, +GFP_NOFS ); +if (!s_bhs) +return -ENOMEM ; + + ret =ocfs2_read_xattr_bucket (inode, s_blkno, s_bhs,0 ); +if (ret) + goto out; + + t_bhs =kzalloc (sizeof (struct buffer_head *) * blk_per_bucket, +GFP_NOFS ); +if (!t_bhs) { + ret = -ENOMEM ; + goto out; + } + + ret =ocfs2_read_xattr_bucket (inode, t_blkno, t_bhs, t_is_new); +if (ret) + goto out; + +for (i =0 ; i < blk_per_bucket; i++) { + ret =ocfs2_journal_access (handle, inode, t_bhs[i], +OCFS2_JOURNAL_ACCESS_WRITE ); +if (ret) + goto out; + } + +for (i =0 ; i < blk_per_bucket; i++) { +memcpy (t_bhs[i]->b_data, s_bhs[i]->b_data, blocksize); +ocfs2_journal_dirty (handle, t_bhs[i]); + } + +out: +if (s_bhs) { +for (i =0 ; i < blk_per_bucket; i++) +brelse (s_bhs[i]); + } +kfree (s_bhs); + +if (t_bhs) { +for (i =0 ; i < blk_per_bucket; i++) +brelse (t_bhs[i]); + } +kfree (t_bhs); + +return ret; +} + +/* +* Copy one xattr cluster from src_blk to to_blk. +* The to_blk will become the first bucket header of the cluster, so its +* xh_num_buckets will be initialized as the bucket num in the cluster. +*/ +static int ocfs2_cp_xattr_cluster (struct inode *inode, +handle_t *handle, +struct buffer_head *first_bh, +u64 src_blk, +u64 to_blk, +u32 *first_hash) +{ +int i, ret, credits; +struct ocfs2_super *osb =OCFS2_SB (inode->i_sb); +int bpc =ocfs2_clusters_to_blocks (inode->i_sb,1 ); +int num_buckets =ocfs2_xattr_buckets_per_cluster (osb); +struct buffer_head *bh =NULL ; +struct ocfs2_xattr_header *xh; +u64 to_blk_start = to_blk; + +mlog (0 ,"cp xattrs from cluster %llu to %llu\n" , src_blk, to_blk); + +/* +* We need to update the new cluster and 1 more for the update of +* the 1st bucket of the previous extent rec. +*/ + credits = bpc +1 ; + ret =ocfs2_extend_trans (handle, credits); +if (ret) { +mlog_errno (ret); + goto out; + } + + ret =ocfs2_journal_access (handle, inode, first_bh, +OCFS2_JOURNAL_ACCESS_WRITE ); +if (ret) { +mlog_errno (ret); + goto out; + } + +for (i =0 ; i < num_buckets; i++) { + ret =ocfs2_cp_xattr_bucket (inode, handle, + src_blk, to_blk,1 ); +if (ret) { +mlog_errno (ret); + goto out; + } + + src_blk +=ocfs2_blocks_per_xattr_bucket (inode->i_sb); + to_blk +=ocfs2_blocks_per_xattr_bucket (inode->i_sb); + } + +/* update the old bucket header. */ + xh = (struct ocfs2_xattr_header *)first_bh->b_data; +le16_add_cpu (&xh->xh_num_buckets, -num_buckets); + +ocfs2_journal_dirty (handle, first_bh); + +/* update the new bucket header. */ + ret =ocfs2_read_block (osb, to_blk_start, &bh,OCFS2_BH_CACHED , inode); +if (ret <0 ) { +mlog_errno (ret); + goto out; + } + + ret =ocfs2_journal_access (handle, inode, bh, +OCFS2_JOURNAL_ACCESS_WRITE ); +if (ret) { +mlog_errno (ret); + goto out; + } + + xh = (struct ocfs2_xattr_header *)bh->b_data; + xh->xh_num_buckets =cpu_to_le16 (num_buckets); + +ocfs2_journal_dirty (handle, bh); + +if (first_hash) + *first_hash =le32_to_cpu (xh->xh_entries[0 ].xe_name_hash); +out: +brelse (bh); +return ret; +} + +/* +* Move half of the xattrs in this cluster to the new cluster. +* This function should only be called when bucket size == cluster size. +* Otherwise ocfs2_mv_xattr_bucket_cross_cluster should be used instead. +*/ +static int ocfs2_half_xattr_cluster (struct inode *inode, +handle_t *handle, +u64 prev_blk, +u64 new_blk, +u32 *first_hash) +{ +u16 blk_per_bucket =ocfs2_blocks_per_xattr_bucket (inode->i_sb); +int ret, credits =2 * blk_per_bucket; + +BUG_ON (OCFS2_XATTR_BUCKET_SIZE <OCFS2_SB (inode->i_sb)->s_clustersize); + + ret =ocfs2_extend_trans (handle, credits); +if (ret) { +mlog_errno (ret); +return ret; + } + +/* Move half of the xattr in start_blk to the next bucket. */ +return ocfs2_half_xattr_bucket (inode, handle, prev_blk, + new_blk, first_hash,1 ); +} + +/* +* Move some xattrs from the old cluster to the new one since they are not +* contiguous in ocfs2 xattr tree. +* +* new_blk starts a new separate cluster, and we will move some xattrs from +* prev_blk to it. v_start will be set as the first name hash value in this +* new cluster so that it can be used as e_cpos during tree insertion and +* don't collide with our original b-tree operations. first_bh and header_bh +* will also be updated since they will be used in ocfs2_extend_xattr_bucket +* to extend the insert bucket. +* +* The problem is how much xattr should we move to the new one and when should +* we update first_bh and header_bh? +* 1. If cluster size > bucket size, that means the previous cluster has more +* than 1 bucket, so just move half nums of bucket into the new cluster and +* update the first_bh and header_bh if the insert bucket has been moved +* to the new cluster. +* 2. If cluster_size == bucket_size: +* a) If the previous extent rec has more than one cluster and the insert +* place isn't in the last cluster, copy the entire last cluster to the +* new one. This time, we don't need to upate the first_bh and header_bh +* since they will not be moved into the new cluster. +* b) Otherwise, move the bottom half of the xattrs in the last cluster into +* the new one. And we set the extend flag to zero if the insert place is +* moved into the new allocated cluster since no extend is needed. +*/ +static int ocfs2_adjust_xattr_cross_cluster (struct inode *inode, +handle_t *handle, +struct buffer_head **first_bh, +struct buffer_head **header_bh, +u64 new_blk, +u64 prev_blk, +u32 prev_clusters, +u32 *v_start, +int *extend) +{ +int ret =0 ; +int bpc =ocfs2_clusters_to_blocks (inode->i_sb,1 ); + +mlog (0 ,"adjust xattrs from cluster %llu len %u to %llu\n" , + prev_blk, prev_clusters, new_blk); + +if (ocfs2_xattr_buckets_per_cluster (OCFS2_SB (inode->i_sb)) >1 ) + ret =ocfs2_mv_xattr_bucket_cross_cluster (inode, + handle, + first_bh, + header_bh, + new_blk, + prev_blk, + prev_clusters, + v_start); +else { +u64 last_blk = prev_blk + bpc * (prev_clusters -1 ); + +if (prev_clusters >1 && (*header_bh)->b_blocknr != last_blk) + ret =ocfs2_cp_xattr_cluster (inode, handle, *first_bh, + last_blk, new_blk, + v_start); +else { + ret =ocfs2_half_xattr_cluster (inode, handle, + last_blk, new_blk, + v_start); + +if ((*header_bh)->b_blocknr == last_blk && extend) + *extend =0 ; + } + } + +return ret; +} + +/* +* Add a new cluster for xattr storage. +* +* If the new cluster is contiguous with the previous one, it will be +* appended to the same extent record, and num_clusters will be updated. +* If not, we will insert a new extent for it and move some xattrs in +* the last cluster into the new allocated one. +* We also need to limit the maximum size of a btree leaf, otherwise we'll +* lose the benefits of hashing because we'll have to search large leaves. +* So now the maximum size is OCFS2_MAX_XATTR_TREE_LEAF_SIZE(or clustersize, +* if it's bigger). +* +* first_bh is the first block of the previous extent rec and header_bh +* indicates the bucket we will insert the new xattrs. They will be updated +* when the header_bh is moved into the new cluster. +*/ +static int ocfs2_add_new_xattr_cluster (struct inode *inode, +struct buffer_head *root_bh, +struct buffer_head **first_bh, +struct buffer_head **header_bh, +u32 *num_clusters, +u32 prev_cpos, +u64 prev_blkno, +int *extend) +{ +int ret, credits; +u16 bpc =ocfs2_clusters_to_blocks (inode->i_sb,1 ); +u32 prev_clusters = *num_clusters; +u32 clusters_to_add =1 , bit_off, num_bits, v_start =0 ; +u64 block; +handle_t *handle =NULL ; +struct ocfs2_alloc_context *data_ac =NULL ; +struct ocfs2_alloc_context *meta_ac =NULL ; +struct ocfs2_super *osb =OCFS2_SB (inode->i_sb); +struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)root_bh->b_data; +struct ocfs2_xattr_tree_root *xb_root = &xb->xb_attrs.xb_root; +struct ocfs2_extent_list *root_el = &xb_root->xt_list; +enum ocfs2_extent_tree_type type =OCFS2_XATTR_TREE_EXTENT ; + +mlog (0 ,"Add new xattr cluster for %llu, previous xattr hash = %u, " +"previous xattr blkno = %llu\n" , + (unsigned long long )OCFS2_I (inode)->ip_blkno, + prev_cpos, prev_blkno); + + ret =ocfs2_lock_allocators (inode, root_bh, root_el, + clusters_to_add,0 , &data_ac, + &meta_ac, type,NULL ); +if (ret) { +mlog_errno (ret); + goto leave; + } + + credits =ocfs2_calc_extend_credits (osb->sb, root_el, clusters_to_add); + handle =ocfs2_start_trans (osb, credits); +if (IS_ERR (handle)) { + ret =PTR_ERR (handle); + handle =NULL ; +mlog_errno (ret); + goto leave; + } + + ret =ocfs2_journal_access (handle, inode, root_bh, +OCFS2_JOURNAL_ACCESS_WRITE ); +if (ret <0 ) { +mlog_errno (ret); + goto leave; + } + + ret =__ocfs2_claim_clusters (osb, handle, data_ac,1 , + clusters_to_add, &bit_off, &num_bits); +if (ret <0 ) { +if (ret != -ENOSPC ) +mlog_errno (ret); + goto leave; + } + +BUG_ON (num_bits > clusters_to_add); + + block =ocfs2_clusters_to_blocks (osb->sb, bit_off); +mlog (0 ,"Allocating %u clusters at block %u for xattr in inode %llu\n" , + num_bits, bit_off, (unsigned long long )OCFS2_I (inode)->ip_blkno); + +if (prev_blkno + prev_clusters * bpc == block && + (prev_clusters + num_bits) << osb->s_clustersize_bits <= +OCFS2_MAX_XATTR_TREE_LEAF_SIZE ) { +/* +* If this cluster is contiguous with the old one and +* adding this new cluster, we don't surpass the limit of +* OCFS2_MAX_XATTR_TREE_LEAF_SIZE, cool. We will let it be +* initialized and used like other buckets in the previous +* cluster. +* So add it as a contiguous one. The caller will handle +* its init process. +*/ + v_start = prev_cpos + prev_clusters; + *num_clusters = prev_clusters + num_bits; +mlog (0 ,"Add contiguous %u clusters to previous extent rec.\n" , + num_bits); + }else { + ret =ocfs2_adjust_xattr_cross_cluster (inode, + handle, + first_bh, + header_bh, + block, + prev_blkno, + prev_clusters, + &v_start, + extend); +if (ret) { +mlog_errno (ret); + goto leave; + } + } + +mlog (0 ,"Insert %u clusters at block %llu for xattr@%u\n" , + num_bits, block, v_start); + ret =ocfs2_xattr_tree_insert_extent (osb, handle, inode, root_bh, + v_start, block, num_bits, +0 , meta_ac); +if (ret <0 ) { +mlog_errno (ret); + goto leave; + } + + ret =ocfs2_journal_dirty (handle, root_bh); +if (ret <0 ) { +mlog_errno (ret); + goto leave; + } + +leave: +if (handle) +ocfs2_commit_trans (osb, handle); +if (data_ac) +ocfs2_free_alloc_context (data_ac); +if (meta_ac) +ocfs2_free_alloc_context (meta_ac); + +return ret; +} + +/* +* Extend a new xattr bucket and move xattrs to the end one by one until +* We meet with start_bh. Only move half of the xattrs to the bucket after it. +*/ +static int ocfs2_extend_xattr_bucket (struct inode *inode, +struct buffer_head *first_bh, +struct buffer_head *start_bh, +u32 num_clusters) +{ +int ret, credits; +struct ocfs2_super *osb =OCFS2_SB (inode->i_sb); +u16 blk_per_bucket =ocfs2_blocks_per_xattr_bucket (inode->i_sb); +u64 start_blk = start_bh->b_blocknr, end_blk; +u32 num_buckets = num_clusters *ocfs2_xattr_buckets_per_cluster (osb); +handle_t *handle; +struct ocfs2_xattr_header *first_xh = + (struct ocfs2_xattr_header *)first_bh->b_data; +u16 bucket =le16_to_cpu (first_xh->xh_num_buckets); + +mlog (0 ,"extend xattr bucket in %llu, xattr extend rec starting " +"from %llu, len = %u\n" , start_blk, + (unsigned long long )first_bh->b_blocknr, num_clusters); + +BUG_ON (bucket >= num_buckets); + + end_blk = first_bh->b_blocknr + (bucket -1 ) * blk_per_bucket; + +/* +* We will touch all the buckets after the start_bh(include it). +* Add one more bucket and modify the first_bh. +*/ + credits = end_blk - start_blk +2 * blk_per_bucket +1 ; + handle =ocfs2_start_trans (osb, credits); +if (IS_ERR (handle)) { + ret =PTR_ERR (handle); + handle =NULL ; +mlog_errno (ret); + goto out; + } + + ret =ocfs2_journal_access (handle, inode, first_bh, +OCFS2_JOURNAL_ACCESS_WRITE ); +if (ret) { +mlog_errno (ret); + goto commit; + } + +while (end_blk != start_blk) { + ret =ocfs2_cp_xattr_bucket (inode, handle, end_blk, + end_blk + blk_per_bucket,0 ); +if (ret) + goto commit; + end_blk -= blk_per_bucket; + } + +/* Move half of the xattr in start_blk to the next bucket. */ + ret =ocfs2_half_xattr_bucket (inode, handle, start_blk, + start_blk + blk_per_bucket,NULL ,0 ); + +le16_add_cpu (&first_xh->xh_num_buckets,1 ); +ocfs2_journal_dirty (handle, first_bh); + +commit: +ocfs2_commit_trans (osb, handle); +out: +return ret; +} + +/* +* Add new xattr bucket in an extent record and adjust the buckets accordingly. +* xb_bh is the ocfs2_xattr_block. +* We will move all the buckets starting from header_bh to the next place. As +* for this one, half num of its xattrs will be moved to the next one. +* +* We will allocate a new cluster if current cluster is full and adjust +* header_bh and first_bh if the insert place is moved to the new cluster. +*/ +static int ocfs2_add_new_xattr_bucket (struct inode *inode, +struct buffer_head *xb_bh, +struct buffer_head *header_bh) +{ +struct ocfs2_xattr_header *first_xh =NULL ; +struct buffer_head *first_bh =NULL ; +struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)xb_bh->b_data; +struct ocfs2_xattr_tree_root *xb_root = &xb->xb_attrs.xb_root; +struct ocfs2_extent_list *el = &xb_root->xt_list; +struct ocfs2_xattr_header *xh = + (struct ocfs2_xattr_header *)header_bh->b_data; +u32 name_hash =le32_to_cpu (xh->xh_entries[0 ].xe_name_hash); +struct super_block *sb = inode->i_sb; +struct ocfs2_super *osb =OCFS2_SB (sb); +int ret, num_buckets, extend =1 ; +u64 p_blkno; +u32 e_cpos, num_clusters; + +mlog (0 ,"Add new xattr bucket starting form %llu\n" , + (unsigned long long )header_bh->b_blocknr); + +/* +* Add refrence for header_bh here because it may be +* changed in ocfs2_add_new_xattr_cluster and we need +* to free it in the end. +*/ +get_bh (header_bh); + + ret =ocfs2_xattr_get_rec (inode, name_hash, &p_blkno, &e_cpos, + &num_clusters, el); +if (ret) { +mlog_errno (ret); + goto out; + } + + ret =ocfs2_read_block (osb, p_blkno, + &first_bh,OCFS2_BH_CACHED , inode); +if (ret) { +mlog_errno (ret); + goto out; + } + + num_buckets =ocfs2_xattr_buckets_per_cluster (osb) * num_clusters; + first_xh = (struct ocfs2_xattr_header *)first_bh->b_data; + +if (num_buckets ==le16_to_cpu (first_xh->xh_num_buckets)) { + ret =ocfs2_add_new_xattr_cluster (inode, + xb_bh, + &first_bh, + &header_bh, + &num_clusters, + e_cpos, + p_blkno, + &extend); +if (ret) { +mlog_errno (ret); + goto out; + } + } + +if (extend) + ret =ocfs2_extend_xattr_bucket (inode, + first_bh, + header_bh, + num_clusters); +if (ret) +mlog_errno (ret); +out: +brelse (first_bh); +brelse (header_bh); +return ret; +} + +static inline char *ocfs2_xattr_bucket_get_val (struct inode *inode, +struct ocfs2_xattr_bucket *bucket, +int offs) +{ +int block_off = offs >> inode->i_sb->s_blocksize_bits; + + offs = offs % inode->i_sb->s_blocksize; +return bucket->bhs[block_off]->b_data + offs; +} + +/* +* Handle the normal xattr set, including replace, delete and new. +* When the bucket is empty, "is_empty" is set and the caller can +* free this bucket. +* +* Note: "local" indicates the real data's locality. So we can't +* just its bucket locality by its length. +*/ +static void ocfs2_xattr_set_entry_normal (struct inode *inode, +struct ocfs2_xattr_info *xi, +struct ocfs2_xattr_search *xs, +u32 name_hash, +int local, +int *is_empty) +{ +struct ocfs2_xattr_entry *last, *xe; +int name_len =strlen (xi->name); +struct ocfs2_xattr_header *xh = xs->header; +u16 count =le16_to_cpu (xh->xh_count), start; +size_t blocksize = inode->i_sb->s_blocksize; +char *val; +size_t offs, size, new_size; + + last = &xh->xh_entries[count]; +if (!xs->not_found) { + xe = xs->here; + offs =le16_to_cpu (xe->xe_name_offset); +if (ocfs2_xattr_is_local (xe)) + size =OCFS2_XATTR_SIZE (name_len) + +OCFS2_XATTR_SIZE (le64_to_cpu (xe->xe_value_size)); +else + size =OCFS2_XATTR_SIZE (name_len) + +OCFS2_XATTR_SIZE (OCFS2_XATTR_ROOT_SIZE ); + +/* +* If the new value will be stored outside, xi->value has been +* initalized as an empty ocfs2_xattr_value_root, and the same +* goes with xi->value_len, so we can set new_size safely here. +* See ocfs2_xattr_set_in_bucket. +*/ + new_size =OCFS2_XATTR_SIZE (name_len) + +OCFS2_XATTR_SIZE (xi->value_len); + +le16_add_cpu (&xh->xh_name_value_len, -size); +if (xi->value) { +if (new_size > size) + goto set_new_name_value; + +/* Now replace the old value with new one. */ +if (local) + xe->xe_value_size =cpu_to_le64 (xi->value_len); +else + xe->xe_value_size =0 ; + + val =ocfs2_xattr_bucket_get_val (inode, + &xs->bucket, offs); +memset (val +OCFS2_XATTR_SIZE (name_len),0 , + size -OCFS2_XATTR_SIZE (name_len)); +if (OCFS2_XATTR_SIZE (xi->value_len) >0 ) +memcpy (val +OCFS2_XATTR_SIZE (name_len), + xi->value, xi->value_len); + +le16_add_cpu (&xh->xh_name_value_len, new_size); +ocfs2_xattr_set_local (xe, local); +return ; + }else { +/* Remove the old entry. */ + last -=1 ; +memmove (xe, xe +1 , + (void *)last - (void *)xe); +memset (last,0 ,sizeof (struct ocfs2_xattr_entry )); +le16_add_cpu (&xh->xh_count,-1 ); +if (xh->xh_count ==0 && is_empty) + *is_empty =1 ; +return ; + } + }else { +/* find a new entry for insert. */ +int low =0 , high = count -1 , tmp; +struct ocfs2_xattr_entry *tmp_xe; + +while (low <= high) { + tmp = (low + high) /2 ; + tmp_xe = &xh->xh_entries[tmp]; + +if (name_hash >le32_to_cpu (tmp_xe->xe_name_hash)) + low = tmp +1 ; +else if (name_hash < +le32_to_cpu (tmp_xe->xe_name_hash)) + high = tmp -1 ; +else +break ; + } + + xe = &xh->xh_entries[low]; +if (low != count) +memmove (xe +1 , xe, (void *)last - (void *)xe); + +le16_add_cpu (&xh->xh_count,1 ); +memset (xe,0 ,sizeof (struct ocfs2_xattr_entry )); + xe->xe_name_hash =cpu_to_le32 (name_hash); + xe->xe_name_len = name_len; +ocfs2_xattr_set_type (xe, xi->name_index); + } + +set_new_name_value: +/* Insert the new name+value. */ + size =OCFS2_XATTR_SIZE (name_len) +OCFS2_XATTR_SIZE (xi->value_len); + +/* +* We must make sure that the name/value pair +* exists in the same block. +*/ + offs =le16_to_cpu (xh->xh_offset); + start = offs - size; + +if (start >> inode->i_sb->s_blocksize_bits != + (offs -1 ) >> inode->i_sb->s_blocksize_bits) { + offs = offs - offs % blocksize; + xh->xh_offset =cpu_to_le16 (offs); + } + + val =ocfs2_xattr_bucket_get_val (inode, + &xs->bucket, offs - size); + xe->xe_name_offset =cpu_to_le16 (offs - size); + +memset (val,0 , size); +memcpy (val, xi->name, name_len); +memcpy (val +OCFS2_XATTR_SIZE (name_len), xi->value, xi->value_len); + + xe->xe_value_size =cpu_to_le64 (xi->value_len); +ocfs2_xattr_set_local (xe, local); + xs->here = xe; +le16_add_cpu (&xh->xh_offset, -size); +le16_add_cpu (&xh->xh_name_value_len, size); + +return ; +} + +static int ocfs2_xattr_bucket_handle_journal (struct inode *inode, +handle_t *handle, +struct ocfs2_xattr_search *xs, +struct buffer_head **bhs, +u16 bh_num) +{ +int ret =0 , off, block_off; +struct ocfs2_xattr_entry *xe = xs->here; + +/* +* First calculate all the blocks we should journal_access +* and journal_dirty. The first block should always be touched. +*/ + ret =ocfs2_journal_dirty (handle, bhs[0 ]); +if (ret) +mlog_errno (ret); + +/* calc the data. */ + off =le16_to_cpu (xe->xe_name_offset); + block_off = off >> inode->i_sb->s_blocksize_bits; + ret =ocfs2_journal_dirty (handle, bhs[block_off]); +if (ret) +mlog_errno (ret); + +return ret; +} + +/* +* Set the xattr entry in the specified bucket. +* The bucket is indicated by xs->bucket and it should have the enough +* space for the xattr insertion. +*/ +static int ocfs2_xattr_set_entry_in_bucket (struct inode *inode, +struct ocfs2_xattr_info *xi, +struct ocfs2_xattr_search *xs, +u32 name_hash, +int local, +int *bucket_empty) +{ +int i, ret; +handle_t *handle =NULL ; +u16 blk_per_bucket =ocfs2_blocks_per_xattr_bucket (inode->i_sb); +struct ocfs2_super *osb =OCFS2_SB (inode->i_sb); + +mlog (0 ,"Set xattr entry len = %d index = %d in bucket %llu\n" , + xi->value_len, xi->name_index, + (unsigned long long )xs->bucket.bhs[0 ]->b_blocknr); + +if (!xs->bucket.bhs[1 ]) { + ret =ocfs2_read_blocks (osb, + xs->bucket.bhs[0 ]->b_blocknr +1 , + blk_per_bucket -1 , &xs->bucket.bhs[1 ], +OCFS2_BH_CACHED , inode); +if (ret) { +mlog_errno (ret); + goto out; + } + } + + handle =ocfs2_start_trans (osb, blk_per_bucket); +if (IS_ERR (handle)) { + ret =PTR_ERR (handle); + handle =NULL ; +mlog_errno (ret); + goto out; + } + +for (i =0 ; i < blk_per_bucket; i++) { + ret =ocfs2_journal_access (handle, inode, xs->bucket.bhs[i], +OCFS2_JOURNAL_ACCESS_WRITE ); +if (ret <0 ) { +mlog_errno (ret); + goto out; + } + } + +ocfs2_xattr_set_entry_normal (inode, xi, xs, name_hash, + local, bucket_empty); + +/*Only dirty the blocks we have touched in set xattr. */ + ret =ocfs2_xattr_bucket_handle_journal (inode, handle, xs, + xs->bucket.bhs, blk_per_bucket); +if (ret) +mlog_errno (ret); +out: +ocfs2_commit_trans (osb, handle); + +return ret; +} + +static int ocfs2_xattr_value_update_size (struct inode *inode, +struct buffer_head *xe_bh, +struct ocfs2_xattr_entry *xe, +u64 new_size) +{ +int ret; +struct ocfs2_super *osb =OCFS2_SB (inode->i_sb); +handle_t *handle =NULL ; + + handle =ocfs2_start_trans (osb,1 ); +if (handle ==NULL ) { + ret = -ENOMEM ; +mlog_errno (ret); + goto out; + } + + ret =ocfs2_journal_access (handle, inode, xe_bh, +OCFS2_JOURNAL_ACCESS_WRITE ); +if (ret <0 ) { +mlog_errno (ret); + goto out_commit; + } + + xe->xe_value_size =cpu_to_le64 (new_size); + + ret =ocfs2_journal_dirty (handle, xe_bh); +if (ret <0 ) +mlog_errno (ret); + +out_commit: +ocfs2_commit_trans (osb, handle); +out: +return ret; +} + +/* +* Truncate the specified xe_off entry in xattr bucket. +* bucket is indicated by header_bh and len is the new length. +* Both the ocfs2_xattr_value_root and the entry will be updated here. +* +* Copy the new updated xe and xe_value_root to new_xe and new_xv if needed. +*/ +static int ocfs2_xattr_bucket_value_truncate (struct inode *inode, +struct buffer_head *header_bh, +int xe_off, +int len) +{ +int ret, offset; +u64 value_blk; +struct buffer_head *value_bh =NULL ; +struct ocfs2_xattr_value_root *xv; +struct ocfs2_xattr_entry *xe; +struct ocfs2_xattr_header *xh = + (struct ocfs2_xattr_header *)header_bh->b_data; +size_t blocksize = inode->i_sb->s_blocksize; + + xe = &xh->xh_entries[xe_off]; + +BUG_ON (!xe ||ocfs2_xattr_is_local (xe)); + + offset =le16_to_cpu (xe->xe_name_offset) + +OCFS2_XATTR_SIZE (xe->xe_name_len); + + value_blk = offset / blocksize; + +/* We don't allow ocfs2_xattr_value to be stored in different block. */ +BUG_ON (value_blk != (offset +OCFS2_XATTR_ROOT_SIZE -1 ) / blocksize); + value_blk += header_bh->b_blocknr; + + ret =ocfs2_read_block (OCFS2_SB (inode->i_sb), value_blk, + &value_bh,OCFS2_BH_CACHED , inode); +if (ret) { +mlog_errno (ret); + goto out; + } + + xv = (struct ocfs2_xattr_value_root *) + (value_bh->b_data + offset % blocksize); + +mlog (0 ,"truncate %u in xattr bucket %llu to %d bytes.\n" , + xe_off, (unsigned long long )header_bh->b_blocknr, len); + ret =ocfs2_xattr_value_truncate (inode, value_bh, xv, len); +if (ret) { +mlog_errno (ret); + goto out; + } + + ret =ocfs2_xattr_value_update_size (inode, header_bh, xe, len); +if (ret) { +mlog_errno (ret); + goto out; + } + +out: +brelse (value_bh); +return ret; +} + +static int ocfs2_xattr_bucket_value_truncate_xs (struct inode *inode, +struct ocfs2_xattr_search *xs, +int len) +{ +int ret, offset; +struct ocfs2_xattr_entry *xe = xs->here; +struct ocfs2_xattr_header *xh = (struct ocfs2_xattr_header *)xs->base; + +BUG_ON (!xs->bucket.bhs[0 ] || !xe ||ocfs2_xattr_is_local (xe)); + + offset = xe - xh->xh_entries; + ret =ocfs2_xattr_bucket_value_truncate (inode, xs->bucket.bhs[0 ], + offset, len); +if (ret) +mlog_errno (ret); + +return ret; +} + +static int ocfs2_xattr_bucket_set_value_outside (struct inode *inode, +struct ocfs2_xattr_search *xs, +char *val, +int value_len) +{ +int offset; +struct ocfs2_xattr_value_root *xv; +struct ocfs2_xattr_entry *xe = xs->here; + +BUG_ON (!xs->base || !xe ||ocfs2_xattr_is_local (xe)); + + offset =le16_to_cpu (xe->xe_name_offset) + +OCFS2_XATTR_SIZE (xe->xe_name_len); + + xv = (struct ocfs2_xattr_value_root *)(xs->base + offset); + +return __ocfs2_xattr_set_value_outside (inode, xv, val, value_len); +} + +/* +* Remove the xattr bucket pointed by bucket_bh. +* All the buckets after it in the same xattr extent rec will be +* move forward one by one. +*/ +static int ocfs2_rm_xattr_bucket (struct inode *inode, +struct buffer_head *first_bh, +struct ocfs2_xattr_bucket *bucket) +{ +int ret =0 , credits; +struct ocfs2_xattr_header *xh = + (struct ocfs2_xattr_header *)first_bh->b_data; +u16 bucket_num =le16_to_cpu (xh->xh_num_buckets); +u64 end, start = bucket->bhs[0 ]->b_blocknr; +struct ocfs2_super *osb =OCFS2_SB (inode->i_sb); +handle_t *handle; +u16 blk_per_bucket =ocfs2_blocks_per_xattr_bucket (inode->i_sb); + + end = first_bh->b_blocknr + (bucket_num -1 ) * blk_per_bucket; + +mlog (0 ,"rm xattr bucket %llu\n" , start); +/* +* We need to update the first xattr_header and all the buckets starting +* from start in this xattr rec. +* +* XXX: Should we empty the old last bucket here? +*/ + credits =1 + end -start; + handle =ocfs2_start_trans (osb, credits); +if (IS_ERR (handle)) { + ret =PTR_ERR (handle); +mlog_errno (ret); +return ret; + } + + ret =ocfs2_journal_access (handle, inode, first_bh, +OCFS2_JOURNAL_ACCESS_WRITE ); +if (ret) { +mlog_errno (ret); + goto out_commit; + } + + +while (start < end) { + ret =ocfs2_cp_xattr_bucket (inode, handle, + start + blk_per_bucket, + start,0 ); +if (ret) { +mlog_errno (ret); + goto out_commit; + } + start += blk_per_bucket; + } + +/* update the first_bh. */ + xh->xh_num_buckets =cpu_to_le16 (bucket_num -1 ); +ocfs2_journal_dirty (handle, first_bh); + +out_commit: +ocfs2_commit_trans (osb, handle); +return ret; +} + +static int ocfs2_rm_xattr_cluster (struct inode *inode, +struct buffer_head *root_bh, +u64 blkno, +u32 cpos, +u32 len) +{ +int ret; +struct ocfs2_super *osb =OCFS2_SB (inode->i_sb); +struct inode *tl_inode = osb->osb_tl_inode; +handle_t *handle; +struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)root_bh->b_data; +struct ocfs2_extent_list *root_el = &xb->xb_attrs.xb_root.xt_list; +struct ocfs2_alloc_context *meta_ac =NULL ; +struct ocfs2_cached_dealloc_ctxt dealloc; + +ocfs2_init_dealloc_ctxt (&dealloc); + +mlog (0 ,"rm xattr extent rec at %u len = %u, start from %llu\n" , + cpos, len, (unsigned long long )blkno); + +ocfs2_remove_xattr_clusters_from_cache (inode, blkno, len); + + ret =ocfs2_lock_allocators (inode, root_bh, root_el, +0 ,1 ,NULL , &meta_ac, +OCFS2_XATTR_TREE_EXTENT ,NULL ); +if (ret) { +mlog_errno (ret); +return ret; + } + +mutex_lock (&tl_inode->i_mutex); + +if (ocfs2_truncate_log_needs_flush (osb)) { + ret =__ocfs2_flush_truncate_log (osb); +if (ret <0 ) { +mlog_errno (ret); + goto out; + } + } + + handle =ocfs2_start_trans (osb,OCFS2_REMOVE_EXTENT_CREDITS ); +if (handle ==NULL ) { + ret = -ENOMEM ; +mlog_errno (ret); + goto out; + } + + ret =ocfs2_journal_access (handle, inode, root_bh, +OCFS2_JOURNAL_ACCESS_WRITE ); +if (ret) { +mlog_errno (ret); + goto out_commit; + } + + ret =ocfs2_remove_extent (inode, root_bh, cpos, len, handle, meta_ac, + &dealloc,OCFS2_XATTR_TREE_EXTENT ,NULL ); +if (ret) { +mlog_errno (ret); + goto out_commit; + } + +le32_add_cpu (&xb->xb_attrs.xb_root.xt_clusters, -len); + + ret =ocfs2_journal_dirty (handle, root_bh); +if (ret) { +mlog_errno (ret); + goto out_commit; + } + + ret =ocfs2_truncate_log_append (osb, handle, blkno, len); +if (ret) +mlog_errno (ret); + +out_commit: +ocfs2_commit_trans (osb, handle); +out: +ocfs2_schedule_truncate_log_flush (osb,1 ); + +mutex_unlock (&tl_inode->i_mutex); + +if (meta_ac) +ocfs2_free_alloc_context (meta_ac); + +ocfs2_run_deallocs (osb, &dealloc); + +return ret; +} + +/* +* Free the xattr bucket indicated by xs->bucket and if all the buckets +* in the clusters is free, free the clusters also. +*/ +static int ocfs2_xattr_bucket_shrink (struct inode *inode, +struct ocfs2_xattr_info *xi, +struct ocfs2_xattr_search *xs, +u32 name_hash) +{ +int ret; +u32 e_cpos, num_clusters; +u64 p_blkno; +struct buffer_head *first_bh =NULL ; +struct ocfs2_xattr_header *first_xh; +struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)xs->xattr_bh->b_data; + +BUG_ON (xs->header->xh_count !=0 ); + + ret =ocfs2_xattr_get_rec (inode, name_hash, &p_blkno, + &e_cpos, &num_clusters, + &xb->xb_attrs.xb_root.xt_list); +if (ret) { +mlog_errno (ret); +return ret; + } + + ret =ocfs2_read_block (OCFS2_SB (inode->i_sb), p_blkno, + &first_bh,OCFS2_BH_CACHED , inode); +if (ret) { +mlog_errno (ret); +return ret; + } + + ret =ocfs2_rm_xattr_bucket (inode, first_bh, &xs->bucket); +if (ret) { +mlog_errno (ret); + goto out; + } + + first_xh = (struct ocfs2_xattr_header *)first_bh->b_data; +if (first_xh->xh_num_buckets ==0 ) + ret =ocfs2_rm_xattr_cluster (inode, xs->xattr_bh, + p_blkno, e_cpos, + num_clusters); + +out: +brelse (first_bh); +return ret; +} + +static void ocfs2_xattr_bucket_remove_xs (struct inode *inode, +struct ocfs2_xattr_search *xs) +{ +handle_t *handle =NULL ; +struct ocfs2_xattr_header *xh = xs->bucket.xh; +struct ocfs2_xattr_entry *last = &xh->xh_entries[ +le16_to_cpu (xh->xh_count) -1 ]; +int ret =0 ; + + handle =ocfs2_start_trans ((OCFS2_SB (inode->i_sb)),1 ); +if (IS_ERR (handle)) { + ret =PTR_ERR (handle); +mlog_errno (ret); +return ; + } + + ret =ocfs2_journal_access (handle, inode, xs->bucket.bhs[0 ], +OCFS2_JOURNAL_ACCESS_WRITE ); +if (ret) { +mlog_errno (ret); + goto out_commit; + } + +/* Remove the old entry. */ +memmove (xs->here, xs->here +1 , + (void *)last - (void *)xs->here); +memset (last,0 ,sizeof (struct ocfs2_xattr_entry )); +le16_add_cpu (&xh->xh_count,-1 ); + + ret =ocfs2_journal_dirty (handle, xs->bucket.bhs[0 ]); +if (ret <0 ) +mlog_errno (ret); +out_commit: +ocfs2_commit_trans (OCFS2_SB (inode->i_sb), handle); +} + +/* +* Set the xattr name/value in the bucket specified in xs. +* +* As the new value in xi may be stored in the bucket or in an outside cluster, +* we divide the whole process into 3 steps: +* 1. insert name/value in the bucket(ocfs2_xattr_set_entry_in_bucket) +* 2. truncate of the outside cluster(ocfs2_xattr_bucket_value_truncate_xs) +* 3. Set the value to the outside cluster(ocfs2_xattr_bucket_set_value_outside) +* 4. If the clusters for the new outside value can't be allocated, we need +* to free the xattr we allocated in set. +*/ +static int ocfs2_xattr_set_in_bucket (struct inode *inode, +struct ocfs2_xattr_info *xi, +struct ocfs2_xattr_search *xs) +{ +int ret, local =1 , bucket_empty =0 ; +size_t value_len; +char *val = (char *)xi->value; +struct ocfs2_xattr_entry *xe = xs->here; +u32 name_hash =ocfs2_xattr_hash_by_name (inode, + xi->name_index, xi->name); + +if (!xs->not_found && !ocfs2_xattr_is_local (xe)) { +/* +* We need to truncate the xattr storage first. +* +* If both the old and new value are stored to +* outside block, we only need to truncate +* the storage and then set the value outside. +* +* If the new value should be stored within block, +* we should free all the outside block first and +* the modification to the xattr block will be done +* by following steps. +*/ +if (xi->value_len >OCFS2_XATTR_INLINE_SIZE ) + value_len = xi->value_len; +else + value_len =0 ; + + ret =ocfs2_xattr_bucket_value_truncate_xs (inode, xs, + value_len); +if (ret) + goto out; + +if (value_len) + goto set_value_outside; + } + + value_len = xi->value_len; +/* So we have to handle the inside block change now. */ +if (value_len >OCFS2_XATTR_INLINE_SIZE ) { +/* +* If the new value will be stored outside of block, +* initalize a new empty value root and insert it first. +*/ + local =0 ; + xi->value = &def_xv; + xi->value_len =OCFS2_XATTR_ROOT_SIZE ; + } + + ret =ocfs2_xattr_set_entry_in_bucket (inode, xi, xs, name_hash, + local, &bucket_empty); +if (ret) { +mlog_errno (ret); + goto out; + } + +if (value_len >OCFS2_XATTR_INLINE_SIZE ) { +/* allocate the space now for the outside block storage. */ + ret =ocfs2_xattr_bucket_value_truncate_xs (inode, xs, + value_len); +if (ret) { +mlog_errno (ret); + +if (xs->not_found) { +/* +* We can't allocate enough clusters for outside +* storage and we have allocated xattr already, +* so need to remove it. +*/ +ocfs2_xattr_bucket_remove_xs (inode, xs); + } + goto out; + } + }else { +if (bucket_empty) + ret =ocfs2_xattr_bucket_shrink (inode, xi, + xs, name_hash); + goto out; + } + +set_value_outside: + ret =ocfs2_xattr_bucket_set_value_outside (inode, xs, val, value_len); +out: +return ret; +} + +/* check whether the xattr bucket is filled up with the same hash value. */ +static int ocfs2_check_xattr_bucket_collision (struct inode *inode, +struct ocfs2_xattr_bucket *bucket) +{ +struct ocfs2_xattr_header *xh = bucket->xh; + +if (xh->xh_entries[le16_to_cpu (xh->xh_count) -1 ].xe_name_hash == + xh->xh_entries[0 ].xe_name_hash) { +mlog (ML_ERROR ,"Too much hash collision in xattr bucket %llu, " +"hash = %u\n" , + (unsigned long long )bucket->bhs[0 ]->b_blocknr, +le32_to_cpu (xh->xh_entries[0 ].xe_name_hash)); +return -ENOSPC ; + } + +return 0 ; +} + +static int ocfs2_xattr_set_entry_index_block (struct inode *inode, +struct ocfs2_xattr_info *xi, +struct ocfs2_xattr_search *xs) +{ +struct ocfs2_xattr_header *xh; +struct ocfs2_xattr_entry *xe; +u16 count, header_size, xh_offset; +int i, free, max_free, need, old; +size_t value_size =0 , name_len =strlen (xi->name); +size_t blocksize = inode->i_sb->s_blocksize; +int ret, allocation =0 ; +u16 blk_per_bucket =ocfs2_blocks_per_xattr_bucket (inode->i_sb); + +mlog_entry ("Set xattr %s in xattr index block\n" , xi->name); + +try_again: + xh = xs->header; + count =le16_to_cpu (xh->xh_count); + xh_offset =le16_to_cpu (xh->xh_offset); + header_size =sizeof (struct ocfs2_xattr_header ) + + count *sizeof (struct ocfs2_xattr_entry ); + max_free =OCFS2_XATTR_BUCKET_SIZE - +le16_to_cpu (xh->xh_name_value_len) - header_size; + +mlog_bug_on_msg (header_size > blocksize,"bucket %llu has header size " +"of %u which exceed block size\n" , + (unsigned long long )xs->bucket.bhs[0 ]->b_blocknr, + header_size); + +if (xi->value && xi->value_len >OCFS2_XATTR_INLINE_SIZE ) + value_size =OCFS2_XATTR_ROOT_SIZE ; +else if (xi->value) + value_size =OCFS2_XATTR_SIZE (xi->value_len); + +if (xs->not_found) + need =sizeof (struct ocfs2_xattr_entry ) + +OCFS2_XATTR_SIZE (name_len) + value_size; +else { + need = value_size +OCFS2_XATTR_SIZE (name_len); + +/* +* We only replace the old value if the new length is smaller +* than the old one. Otherwise we will allocate new space in the +* bucket to store it. +*/ + xe = xs->here; +if (ocfs2_xattr_is_local (xe)) + old =OCFS2_XATTR_SIZE (le64_to_cpu (xe->xe_value_size)); +else + old =OCFS2_XATTR_SIZE (OCFS2_XATTR_ROOT_SIZE ); + +if (old >= value_size) + need =0 ; + } + + free = xh_offset - header_size; +/* +* We need to make sure the new name/value pair +* can exist in the same block. +*/ +if (xh_offset % blocksize < need) + free -= xh_offset % blocksize; + +mlog (0 ,"xs->not_found = %d, in xattr bucket %llu: free = %d, " +"need = %d, max_free = %d, xh_offset = %u, xh_name_value_len = %u" +"\n" , xs->not_found, + (unsigned long long )xs->bucket.bhs[0 ]->b_blocknr, + free, need, max_free,le16_to_cpu (xh->xh_offset), +le16_to_cpu (xh->xh_name_value_len)); + +if (free < need || count ==ocfs2_xattr_max_xe_in_bucket (inode->i_sb)) { +if (need <= max_free && + count <ocfs2_xattr_max_xe_in_bucket (inode->i_sb)) { +/* +* We can create the space by defragment. Since only the +* name/value will be moved, the xe shouldn't be changed +* in xs. +*/ + ret =ocfs2_defrag_xattr_bucket (inode, &xs->bucket); +if (ret) { +mlog_errno (ret); + goto out; + } + + xh_offset =le16_to_cpu (xh->xh_offset); + free = xh_offset - header_size; +if (xh_offset % blocksize < need) + free -= xh_offset % blocksize; + +if (free >= need) + goto xattr_set; + +mlog (0 ,"Can't get enough space for xattr insert by " +"defragment. Need %u bytes, but we have %d, so " +"allocate new bucket for it.\n" , need, free); + } + +/* +* We have to add new buckets or clusters and one +* allocation should leave us enough space for insert. +*/ +BUG_ON (allocation); + +/* +* We do not allow for overlapping ranges between buckets. And +* the maximum number of collisions we will allow for then is +* one bucket's worth, so check it here whether we need to +* add a new bucket for the insert. +*/ + ret =ocfs2_check_xattr_bucket_collision (inode, &xs->bucket); +if (ret) { +mlog_errno (ret); + goto out; + } + + ret =ocfs2_add_new_xattr_bucket (inode, + xs->xattr_bh, + xs->bucket.bhs[0 ]); +if (ret) { +mlog_errno (ret); + goto out; + } + +for (i =0 ; i < blk_per_bucket; i++) +brelse (xs->bucket.bhs[i]); + +memset (&xs->bucket,0 ,sizeof (xs->bucket)); + + ret =ocfs2_xattr_index_block_find (inode, xs->xattr_bh, + xi->name_index, + xi->name, xs); +if (ret && ret != -ENODATA ) + goto out; + xs->not_found = ret; + allocation =1 ; + goto try_again; + } + +xattr_set: + ret =ocfs2_xattr_set_in_bucket (inode, xi, xs); +out: +mlog_exit (ret); +return ret; +}
fs/ocfs2/xattr.h +10 -0
@@ -29,6 +29,8 @@#include <linux/init.h> #include <linux/xattr.h> +#define OCFS2_MAX_XATTR_TREE_LEAF_SIZE 65536 +enum ocfs2_xattr_type {OCFS2_XATTR_INDEX_USER =1 ,OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS ,
@@ -64,4 +66,12 @@ static inline u16 ocfs2_blocks_per_xattr_bucket(struct super_block *sb) {return OCFS2_XATTR_BUCKET_SIZE / (1 << sb->s_blocksize_bits); } + +static inline u16 ocfs2_xattr_max_xe_in_bucket (struct super_block *sb) +{ +u16 len = sb->s_blocksize - + offsetof(struct ocfs2_xattr_header , xh_entries); + +return len /sizeof (struct ocfs2_xattr_entry ); +}#endif /* OCFS2_XATTR_H */
Signature · 1 line
1.5.4.GIT
PATCH 13/15 Enable xattr set in index btree. v3
Quoted 110 lines · Tao Ma
fs/ocfs2/xattr.c +110
-0+/* +* Add a new cluster for xattr storage. +* +* If the new cluster is contiguous with the previous one, it will be +* appended to the same extent record, and num_clusters will be updated. +* If not, we will insert a new extent for it and move some xattrs in +* the last cluster into the new allocated one. +* We also need to limit the maximum size of a btree leaf, otherwise we'll +* lose the benefits of hashing because we'll have to search large leaves. +* So now the maximum size is OCFS2_MAX_XATTR_TREE_LEAF_SIZE(or clustersize, +* if it's bigger). +* +* first_bh is the first block of the previous extent rec and header_bh +* indicates the bucket we will insert the new xattrs. They will be updated +* when the header_bh is moved into the new cluster. +*/ +static int ocfs2_add_new_xattr_cluster (struct inode *inode, +struct buffer_head *root_bh, +struct buffer_head **first_bh, +struct buffer_head **header_bh, +u32 *num_clusters, +u32 prev_cpos, +u64 prev_blkno, +int *extend) +{ +int ret, credits; +u16 bpc =ocfs2_clusters_to_blocks (inode->i_sb,1 ); +u32 prev_clusters = *num_clusters; +u32 clusters_to_add =1 , bit_off, num_bits, v_start =0 ; +u64 block; +handle_t *handle =NULL ; +struct ocfs2_alloc_context *data_ac =NULL ; +struct ocfs2_alloc_context *meta_ac =NULL ; +struct ocfs2_super *osb =OCFS2_SB (inode->i_sb); +struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)root_bh->b_data; +struct ocfs2_xattr_tree_root *xb_root = &xb->xb_attrs.xb_root; +struct ocfs2_extent_list *root_el = &xb_root->xt_list; +enum ocfs2_extent_tree_type type =OCFS2_XATTR_TREE_EXTENT ; + +mlog (0 ,"Add new xattr cluster for %llu, previous xattr hash = %u, " +"previous xattr blkno = %llu\n" , + (unsigned long long )OCFS2_I (inode)->ip_blkno, + prev_cpos, prev_blkno); + + ret =ocfs2_lock_allocators (inode, root_bh, root_el, + clusters_to_add,0 , &data_ac, + &meta_ac, type,NULL ); +if (ret) { +mlog_errno (ret); + goto leave; + } + + credits =ocfs2_calc_extend_credits (osb->sb, root_el, clusters_to_add); + handle =ocfs2_start_trans (osb, credits); +if (IS_ERR (handle)) { + ret =PTR_ERR (handle); + handle =NULL ; +mlog_errno (ret); + goto leave; + } + + ret =ocfs2_journal_access (handle, inode, root_bh, +OCFS2_JOURNAL_ACCESS_WRITE ); +if (ret <0 ) { +mlog_errno (ret); + goto leave; + } + + ret =__ocfs2_claim_clusters (osb, handle, data_ac,1 , + clusters_to_add, &bit_off, &num_bits); +if (ret <0 ) { +if (ret != -ENOSPC ) +mlog_errno (ret); + goto leave; + } + +BUG_ON (num_bits > clusters_to_add); + + block =ocfs2_clusters_to_blocks (osb->sb, bit_off); +mlog (0 ,"Allocating %u clusters at block %u for xattr in inode %llu\n" , + num_bits, bit_off, (unsigned long long )OCFS2_I (inode)->ip_blkno); + +if (prev_blkno + prev_clusters * bpc == block && + (prev_clusters + num_bits) << osb->s_clustersize_bits <= +OCFS2_MAX_XATTR_TREE_LEAF_SIZE ) { +/* +* If this cluster is contiguous with the old one and +* adding this new cluster, we don't surpass the limit of +* OCFS2_MAX_XATTR_TREE_LEAF_SIZE, cool. We will let it be +* initialized and used like other buckets in the previous +* cluster. +* So add it as a contiguous one. The caller will handle +* its init process. +*/ + v_start = prev_cpos + prev_clusters; + *num_clusters = prev_clusters + num_bits; +mlog (0 ,"Add contiguous %u clusters to previous extent rec.\n" , + num_bits); + }else { + ret =ocfs2_adjust_xattr_cross_cluster (inode, + handle, + first_bh, + header_bh, + block, + prev_blkno, + prev_clusters, + &v_start, + extend); +if (ret) {
fs/ocfs2/xattr.c static int ocfs2_xattr_tree_list_index_block(struct inode *inode, · line 3705
Quoted 10 lines
fs/ocfs2/xattr.c +10
-0+mlog_errno (ret); + goto leave; + } + } + +mlog (0 ,"Insert %u clusters at block %llu for xattr at %u\n" , + num_bits, block, v_start); + ret =ocfs2_xattr_tree_insert_extent (osb, handle, inode, root_bh, + v_start, block, num_bits, +0 , meta_ac);
I think we need to do something down in the extent insertion code to prevent merging in the OCFS2_MAX_XATTR_TREE_LEAF_SIZE cases - what's preventing it from appending that allocation to the end of an extent (any extent) in the btree?
--Mark
--
Mark Fasheh
PATCH 13/15 Enable xattr set in index btree. v3
Quoted 118 lines · Mark Fasheh
Quoted 117 lines · Tao Ma
fs/ocfs2/xattr.c +117
-0+/* +* Add a new cluster for xattr storage. +* +* If the new cluster is contiguous with the previous one, it will be +* appended to the same extent record, and num_clusters will be updated. +* If not, we will insert a new extent for it and move some xattrs in +* the last cluster into the new allocated one. +* We also need to limit the maximum size of a btree leaf, otherwise we'll +* lose the benefits of hashing because we'll have to search large leaves. +* So now the maximum size is OCFS2_MAX_XATTR_TREE_LEAF_SIZE(or clustersize, +* if it's bigger). +* +* first_bh is the first block of the previous extent rec and header_bh +* indicates the bucket we will insert the new xattrs. They will be updated +* when the header_bh is moved into the new cluster. +*/ +static int ocfs2_add_new_xattr_cluster (struct inode *inode, +struct buffer_head *root_bh, +struct buffer_head **first_bh, +struct buffer_head **header_bh, +u32 *num_clusters, +u32 prev_cpos, +u64 prev_blkno, +int *extend) +{ +int ret, credits; +u16 bpc =ocfs2_clusters_to_blocks (inode->i_sb,1 ); +u32 prev_clusters = *num_clusters; +u32 clusters_to_add =1 , bit_off, num_bits, v_start =0 ; +u64 block; +handle_t *handle =NULL ; +struct ocfs2_alloc_context *data_ac =NULL ; +struct ocfs2_alloc_context *meta_ac =NULL ; +struct ocfs2_super *osb =OCFS2_SB (inode->i_sb); +struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)root_bh->b_data; +struct ocfs2_xattr_tree_root *xb_root = &xb->xb_attrs.xb_root; +struct ocfs2_extent_list *root_el = &xb_root->xt_list; +enum ocfs2_extent_tree_type type =OCFS2_XATTR_TREE_EXTENT ; + +mlog (0 ,"Add new xattr cluster for %llu, previous xattr hash = %u, " +"previous xattr blkno = %llu\n" , + (unsigned long long )OCFS2_I (inode)->ip_blkno, + prev_cpos, prev_blkno); + + ret =ocfs2_lock_allocators (inode, root_bh, root_el, + clusters_to_add,0 , &data_ac, + &meta_ac, type,NULL ); +if (ret) { +mlog_errno (ret); + goto leave; + } + + credits =ocfs2_calc_extend_credits (osb->sb, root_el, clusters_to_add); + handle =ocfs2_start_trans (osb, credits); +if (IS_ERR (handle)) { + ret =PTR_ERR (handle); + handle =NULL ; +mlog_errno (ret); + goto leave; + } + + ret =ocfs2_journal_access (handle, inode, root_bh, +OCFS2_JOURNAL_ACCESS_WRITE ); +if (ret <0 ) { +mlog_errno (ret); + goto leave; + } + + ret =__ocfs2_claim_clusters (osb, handle, data_ac,1 , + clusters_to_add, &bit_off, &num_bits); +if (ret <0 ) { +if (ret != -ENOSPC ) +mlog_errno (ret); + goto leave; + } + +BUG_ON (num_bits > clusters_to_add); + + block =ocfs2_clusters_to_blocks (osb->sb, bit_off); +mlog (0 ,"Allocating %u clusters at block %u for xattr in inode %llu\n" , + num_bits, bit_off, (unsigned long long )OCFS2_I (inode)->ip_blkno); + +if (prev_blkno + prev_clusters * bpc == block && + (prev_clusters + num_bits) << osb->s_clustersize_bits <= +OCFS2_MAX_XATTR_TREE_LEAF_SIZE ) { +/* +* If this cluster is contiguous with the old one and +* adding this new cluster, we don't surpass the limit of +* OCFS2_MAX_XATTR_TREE_LEAF_SIZE, cool. We will let it be +* initialized and used like other buckets in the previous +* cluster. +* So add it as a contiguous one. The caller will handle +* its init process. +*/ + v_start = prev_cpos + prev_clusters; + *num_clusters = prev_clusters + num_bits; +mlog (0 ,"Add contiguous %u clusters to previous extent rec.\n" , + num_bits); + }else { + ret =ocfs2_adjust_xattr_cross_cluster (inode, + handle, + first_bh, + header_bh, + block, + prev_blkno, + prev_clusters, + &v_start, + extend); +if (ret) { +mlog_errno (ret); + goto leave; + } + } + +mlog (0 ,"Insert %u clusters at block %llu for xattr at %u\n" , + num_bits, block, v_start);
Quoted 8 lines
fs/ocfs2/xattr.c static int ocfs2_xattr_tree_list_index_block(struct inode *inode, · line 3705
Quoted 3 lines
fs/ocfs2/xattr.c +3
-0+ ret =ocfs2_xattr_tree_insert_extent (osb, handle, inode, root_bh, + v_start, block, num_bits, +0 , meta_ac);I think we need to do something down in the extent insertion code to prevent merging in the
OCFS2_MAX_XATTR_TREE_LEAF_SIZEcases - what's preventing it from appending that allocation to the end of an extent (any extent) in the btree?
We don't need to do that. You see, in ocfs2_adjust_xattr_corss_cluster I have adjusted v_start(v_start now indicates the first name hash of the xattrs in the new cluster), so now they will not be contiguous now. ;)
We only allow xattr hashes to be the same in one bucket(4K) size, so with 64K as our leaf size, even if the cluster is contiguous, we will not have contiguous name hash. so it is safe to say here v_start can never be old_v_start+1.
Regards,
Tao
PATCH 13/15 Enable xattr set in index btree. v3
Quoted 138 lines · Tao Ma
Quoted 127 lines · Mark Fasheh
Quoted 126 lines · Tao Ma
+/*
+ * Add a new cluster for xattr storage.
+ *
+ * If the new cluster is contiguous with the previous one, it will be
+ * appended to the same extent record, andnum_clusterswill be updated.
+ * If not, we will insert a new extent for it and move some xattrs in
+ * the last cluster into the new allocated one.
+ * We also need to limit the maximum size of a btree leaf, otherwise we'll
+ * lose the benefits of hashing because we'll have to search large leaves.
+ * So now the maximum size is OCFS2_MAX_XATTR_TREE_LEAF_SIZE(or clustersize,
+ * if it's bigger).
+ *
+ *first_bhis the first block of the previous extent rec andheader_bh
+ * indicates the bucket we will insert the new xattrs. They will be updated
+ * when theheader_bhis moved into the new cluster.
+ */fs/ocfs2/xattr.c +38
-0+static int ocfs2_add_new_xattr_cluster (struct inode *inode, +struct buffer_head *root_bh, +struct buffer_head **first_bh, +struct buffer_head **header_bh, +u32 *num_clusters, +u32 prev_cpos, +u64 prev_blkno, +int *extend) +{ +int ret, credits; +u16 bpc =ocfs2_clusters_to_blocks (inode->i_sb,1 ); +u32 prev_clusters = *num_clusters; +u32 clusters_to_add =1 , bit_off, num_bits, v_start =0 ; +u64 block; +handle_t *handle =NULL ; +struct ocfs2_alloc_context *data_ac =NULL ; +struct ocfs2_alloc_context *meta_ac =NULL ; +struct ocfs2_super *osb =OCFS2_SB (inode->i_sb); +struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)root_bh->b_data; +struct ocfs2_xattr_tree_root *xb_root = &xb->xb_attrs.xb_root; +struct ocfs2_extent_list *root_el = &xb_root->xt_list; +enum ocfs2_extent_tree_type type =OCFS2_XATTR_TREE_EXTENT ; + +mlog (0 ,"Add new xattr cluster for %llu, previous xattr hash = %u, " +"previous xattr blkno = %llu\n" , + (unsigned long long )OCFS2_I (inode)->ip_blkno, + prev_cpos, prev_blkno); + + ret =ocfs2_lock_allocators (inode, root_bh, root_el, + clusters_to_add,0 , &data_ac, + &meta_ac, type,NULL ); +if (ret) { +mlog_errno (ret); + goto leave; + } + + credits = ocfs2_calc_extend_credits(osb->sb, root_el,
clusters_to_add);fs/ocfs2/xattr.c +27
-0+ handle =ocfs2_start_trans (osb, credits); +if (IS_ERR (handle)) { + ret =PTR_ERR (handle); + handle =NULL ; +mlog_errno (ret); + goto leave; + } + + ret =ocfs2_journal_access (handle, inode, root_bh, +OCFS2_JOURNAL_ACCESS_WRITE ); +if (ret <0 ) { +mlog_errno (ret); + goto leave; + } + + ret =__ocfs2_claim_clusters (osb, handle, data_ac,1 , + clusters_to_add, &bit_off, &num_bits); +if (ret <0 ) { +if (ret != -ENOSPC ) +mlog_errno (ret); + goto leave; + } + +BUG_ON (num_bits > clusters_to_add); + + block =ocfs2_clusters_to_blocks (osb->sb, bit_off); + mlog(0, "Allocating %u clusters at block %u for xattr in inode%llu\n",
(quoted diff) +1
-0+ num_bits, bit_off, (unsigned longlong)OCFS2_I(inode)->ip_blkno);
fs/ocfs2/xattr.c +16
-0+ +if (prev_blkno + prev_clusters * bpc == block && + (prev_clusters + num_bits) << osb->s_clustersize_bits <= +OCFS2_MAX_XATTR_TREE_LEAF_SIZE ) { +/* +* If this cluster is contiguous with the old one and +* adding this new cluster, we don't surpass the limit of +* OCFS2_MAX_XATTR_TREE_LEAF_SIZE, cool. We will let it be +* initialized and used like other buckets in the previous +* cluster. +* So add it as a contiguous one. The caller will handle +* its init process. +*/ + v_start = prev_cpos + prev_clusters; + *num_clusters = prev_clusters + num_bits; + mlog(0, "Add contiguous %u clusters to previous extentrec.\n",
fs/ocfs2/xattr.c +20
-0+ num_bits); + }else { + ret =ocfs2_adjust_xattr_cross_cluster (inode, + handle, + first_bh, + header_bh, + block, + prev_blkno, + prev_clusters, + &v_start, + extend); +if (ret) { +mlog_errno (ret); + goto leave; + } + } + +mlog (0 ,"Insert %u clusters at block %llu for xattr at %u\n" , + num_bits, block, v_start); + ret = ocfs2_xattr_tree_insert_extent(osb, handle, inode, root_bh,Quoted 8 lines
fs/ocfs2/xattr.c static int ocfs2_xattr_tree_list_index_block(struct inode *inode, · line 3705
Quoted 2 lines
fs/ocfs2/xattr.c +2
-0+ v_start, block, num_bits, + 0, meta_ac);I think we need to do something down in the extent insertion code to prevent
merging in theOCFS2_MAX_XATTR_TREE_LEAF_SIZEcases - what's preventing it from appending that allocation to the end of an extent (any extent) in the btree?
Quoted 8 lines
We don't need to do that. You see, in
ocfs2_adjust_xattr_corss_clusterI have adjustedv_start(v_start now indicates the first name hash of the xattrs in the new cluster), so now they will not be contiguous now. ;)We only allow xattr hashes to be the same in one bucket(4K) size, so with 64K as our leaf size, even if the cluster is contiguous, we will not have contiguous name hash. so it is safe to say here
v_startcan never be old_v_start+1.
Hmm, ok, and there can never be anything in between v_start and old_v_start to begin with, since v_start is determined from the values within the old bucket. Clever :)
One question though - what's your *guarantee* that v_start can never be old_v_start + 1.... What if we *only* have hashes with the values old_v_start and old_v_start+1 in the bucket?
Btw, it might really just have been easier for you to pass something to the merge code which says "leave as seperate extents this time" :)
--Mark
--
Mark Fasheh
PATCH 1/1 Refactor ocfs2_insert_extent for not-merging.
From: taoma <[email protected]>
Hi Mark,
Here is the patch we discussed this morning.
In xattr bucket, we want to limit the maximum size of a btree leaf, otherwise we'll lose the benefits of hashing because we'll have to search large leaves.
So add a new flag in ocfs2_extent_tree which will prevent ocfs2_insert_extent from merging the leaf record even if they are contiguous.
--- fs/ocfs2/alloc.c | 23 ++++++++++++++++++++--- 1 files changed, 20 insertions(+), 3 deletions(-)
fs/ocfs2/alloc.c +20 -3
@@ -73,12 +73,18 @@ struct ocfs2_extent_tree_operations {int (*sanity_check) (struct inode *inode,struct ocfs2_extent_tree *et); }; +/* +* flags is used to adjust ocfs2_insert_extent behaviour +* for some specific reason. +*/ +#define OCFS2_EXTENT_TREE_NOT_MERGE_LEAF 0x1struct ocfs2_extent_tree {enum ocfs2_extent_tree_type type;struct ocfs2_extent_tree_operations *eops;struct buffer_head *root_bh;struct ocfs2_extent_list *root_el;void *private; +u16 flags; }; static void ocfs2_dinode_set_last_eb_blk(struct ocfs2_extent_tree *et,
@@ -248,6 +254,7 @@ static struct ocfs2_extent_tree* (struct ocfs2_xattr_block *)bh->b_data; et->root_el = &xb->xb_attrs.xb_root.xt_list; et->eops = &ocfs2_xattr_tree_et_ops; + et->flags =OCFS2_EXTENT_TREE_NOT_MERGE_LEAF ; }return et;
@@ -4118,13 +4125,23 @@ out: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) +struct ocfs2_extent_rec *insert_rec, +struct ocfs2_extent_tree *et) {int i;enum ocfs2_contig_type contig_type =CONTIG_NONE ;BUG_ON (le16_to_cpu (el->l_tree_depth) !=0 ); +/* +* We don't calculate whether they are contiguous if the caller don't +* want us to do it. +*/ +if (et->flags &OCFS2_EXTENT_TREE_NOT_MERGE_LEAF ) { + insert->ins_contig =CONTIG_NONE ; +return ; + } +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);
@@ -4241,7 +4258,7 @@ static int ocfs2_figure_insert_type(struct inode *inode,le16_to_cpu (el->l_next_free_rec);if (!insert->ins_tree_depth) { -ocfs2_figure_contig_type (inode, insert, el, insert_rec); +ocfs2_figure_contig_type (inode, insert, el, insert_rec, et);ocfs2_figure_appending_type (insert, el, insert_rec);return 0 ; }
@@ -4275,7 +4292,7 @@ static int ocfs2_figure_insert_type(struct inode *inode,* into two types of appends: simple record append, or a * rotate inside the tail leaf. */ -ocfs2_figure_contig_type (inode, insert, el, insert_rec); +ocfs2_figure_contig_type (inode, insert, el, insert_rec, et);/* * The insert code isn't quite ready to deal with all cases of
Signature · 1 line
1.5.4.1
PATCH 1/1 Refactor ocfs2_insert_extent for not-merging.
Quoted 11 lines · Tao Ma
From: taoma <[email protected]>
Hi Mark,
Here is the patch we discussed this morning.In xattr bucket, we want to limit the maximum size of a btree leaf, otherwise we'll lose the benefits of hashing because we'll have to search large leaves.
So add a new flag in
ocfs2_extent_treewhich will preventocfs2_insert_extentfrom merging the leaf record even if they are contiguous.
Ok, this is close... The problem I see here though is that we've gone from always merging when possible to *never* merging.
What we want is "don't merge if the resulting extent is larger than a given size".
So, instead of a flags field, how about "unsigned int max_extent_clusters". If it's zero, we don't limit the extent. Otherwise, we honor the value it is set to. A (cleaner) alternative would be to set it to the max value a u16 can have by default so we never have to do an explicit "is this zero" check.
Inside of ocfs2_figure_contig_type():
... ocfs2_figure_contig_type(...) {
...
unsigned int len = le16_to_cpu(ext_rec->e_leaf_clusters) + le16_to_cpu(insert_rec->e_leaf_clusters);
/*
* Caller might want us to limit the size of extents, don't calculate
* contiguousness if we might exceed that limit.
*/
if (et->max_extent_clusters && len > et->max_extent_clusters) {
insert->ins_contig = CONTIG_NONE;
return;
}
...
}Thanks,
--Mark
--
Mark Fasheh
PATCH 1/1 Refactor ocfs2_insert_extent for not-merging.
Quoted 15 lines · Mark Fasheh
Quoted 5 lines · Tao Ma
From: taoma <[email protected]>
Hi Mark,
Here is the patch we discussed this morning.Quoted 7 lines
In xattr bucket, we want to limit the maximum size of a btree leaf, otherwise we'll lose the benefits of hashing because we'll have to search large leaves.
So add a new flag in
ocfs2_extent_treewhich will preventocfs2_insert_extentfrom merging the leaf record even if they are contiguous.
Quoted 6 lines
Ok, this is close... The problem I see here though is that we've gone from always merging when possible to *never* merging.
What we want is "don't merge if the resulting extent is larger than a given size".
I leave this judgement to the caller itself(the flag) and I think the caller should be responsible for telling ocfs2_insert_extent whether the new added cluster should be merged or not.
So if the caller say loudly that "please don't merge", we will not merge. As for xattr extent tree, I have already judge the situation in the caller(ocfs2_add_new_xattr_cluster), so it will not be a problem. There is also another benefit. In future, other extent tree type may have another type of things which will ask ocfs2_insert_extent to "not merge", but not because of the leaf size. And we can also handle this. Make sense?
Regards,
Tao
PATCH 1/1 Refactor ocfs2_insert_extent for not-merging.
Quoted 20 lines · Tao Ma
Quoted 19 lines · Mark Fasheh
Quoted 5 lines · Tao Ma
From: taoma <[email protected]>
Hi Mark,
Here is the patch we discussed this morning.Quoted 8 lines
In xattr bucket, we want to limit the maximum size of a btree leaf, otherwise we'll lose the benefits of hashing because we'll have to search large leaves.
So add a new flag in
ocfs2_extent_treewhich will preventocfs2_insert_extent
from merging the leaf record even if they are contiguous.Ok, this is close... The problem I see here though is that we've gone from always merging when possible to *never* merging.
Quoted 6 lines
Quoted 3 lines
What we want is "don't merge if the resulting extent is larger than a given size".
I leave this judgement to the caller itself(the flag) and I think the caller should be responsible for telling
ocfs2_insert_extentwhether the new added cluster should be merged or not.
Right.
Quoted 2 lines
So if the caller say loudly that "please don't merge", we will not merge.
Well, my point is it's better if the caller says (loudly) "don't exceed this limit!"... That way, it doesn't have to look at the extents to the right and left of the record being inserted and figure out whether a merge would be a bad thing. Basically, it leaves the specifics of where extent records are to the btree code...
Quoted 2 lines
As for xattr extent tree, I have already judge the situation in the
caller(ocfs2_add_new_xattr_cluster), so it will not be a problem.
Ok, I didn't see that patch. Is it actually checking records on either side of the insert?
Quoted 4 lines
There is also another benefit. In future, other extent tree type may have another type of things which will ask
ocfs2_insert_extentto "not merge", but not because of the leaf size. And we can also handle this. Make sense?
Yeah, I think we're about 90% in agreement. I also want this for future use
- indexed directories will need to do the same thing. I'd like them to just just pass down a max size however, and never have to worry about the details of extent merging.
--Mark
--
Mark Fasheh
PATCH 1/1 Refactor ocfs2_insert_extent for not-merging. Revised
Hi Mark,
Your explanation is clear enough. So here is the revised one. Thanks.
In xattr bucket, we want to limit the maximum size of a btree leaf, otherwise we'll lose the benefits of hashing because we'll have to search large leaves.
So add a new field in ocfs2_extent_tree which indicates the max leaf cluster size we want so that we can prevent ocfs2_insert_extent from merging the leaf record even if they are contiguous.
--- fs/ocfs2/alloc.c | 38 +++++++++++++++++++++++++++++--------- fs/ocfs2/alloc.h | 5 +++++ 2 files changed, 34 insertions(+), 9 deletions(-)
fs/ocfs2/alloc.c +29 -9
@@ -79,6 +79,7 @@ struct ocfs2_extent_tree {struct buffer_head *root_bh;struct ocfs2_extent_list *root_el;void *private; +unsigned int max_leaf_clusters; }; static void ocfs2_dinode_set_last_eb_blk(struct ocfs2_extent_tree *et,
@@ -220,7 +221,8 @@ static struct ocfs2_extent_tree_operations ocfs2_xattr_tree_et_ops = { };static struct ocfs2_extent_tree * -ocfs2_new_extent_tree (struct buffer_head *bh, +ocfs2_new_extent_tree (struct inode *inode, +struct buffer_head *bh,enum ocfs2_extent_tree_type et_type,void *private) {
@@ -248,6 +250,8 @@ static struct ocfs2_extent_tree* (struct ocfs2_xattr_block *)bh->b_data; et->root_el = &xb->xb_attrs.xb_root.xt_list; et->eops = &ocfs2_xattr_tree_et_ops; + et->max_leaf_clusters =ocfs2_clusters_for_bytes (inode->i_sb, +OCFS2_MAX_XATTR_TREE_LEAF_SIZE ); }return et;
@@ -4118,7 +4122,8 @@ out: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) +struct ocfs2_extent_rec *insert_rec, +struct ocfs2_extent_tree *et) {int i;enum ocfs2_contig_type contig_type =CONTIG_NONE ;
@@ -4134,6 +4139,20 @@ static void ocfs2_figure_contig_type(struct inode *inode, } } insert->ins_contig = contig_type; + +if (insert->ins_contig !=CONTIG_NONE ) { +struct ocfs2_extent_rec *rec = + &el->l_recs[insert->ins_contig_index]; +unsigned int len =le16_to_cpu (rec->e_leaf_clusters) + +le16_to_cpu (insert_rec->e_leaf_clusters); + +/* +* Caller might want us to limit the size of extents, don't +* calculate contiguousness if we might exceed that limit. +*/ +if (et->max_leaf_clusters && len > et->max_leaf_clusters) + insert->ins_contig =CONTIG_NONE ; + } }/*
@@ -4241,7 +4260,7 @@ static int ocfs2_figure_insert_type(struct inode *inode,le16_to_cpu (el->l_next_free_rec);if (!insert->ins_tree_depth) { -ocfs2_figure_contig_type (inode, insert, el, insert_rec); +ocfs2_figure_contig_type (inode, insert, el, insert_rec, et);ocfs2_figure_appending_type (insert, el, insert_rec);return 0 ; }
@@ -4275,7 +4294,7 @@ static int ocfs2_figure_insert_type(struct inode *inode,* into two types of appends: simple record append, or a * rotate inside the tail leaf. */ -ocfs2_figure_contig_type (inode, insert, el, insert_rec); +ocfs2_figure_contig_type (inode, insert, el, insert_rec, et);/* * The insert code isn't quite ready to deal with all cases of
@@ -4411,7 +4430,7 @@ int ocfs2_dinode_insert_extent(struct ocfs2_super *osb,int status;struct ocfs2_extent_tree *et =NULL ; - et =ocfs2_new_extent_tree (root_bh,OCFS2_DINODE_EXTENT ,NULL ); + et =ocfs2_new_extent_tree (inode, root_bh,OCFS2_DINODE_EXTENT ,NULL );if (!et) { status = -ENOMEM ;mlog_errno (status);
@@ -4442,7 +4461,7 @@ int ocfs2_xattr_value_insert_extent(struct ocfs2_super *osb,int status;struct ocfs2_extent_tree *et =NULL ; - et =ocfs2_new_extent_tree (root_bh,OCFS2_XATTR_VALUE_EXTENT , private); + et =ocfs2_new_extent_tree (inode, root_bh,OCFS2_XATTR_VALUE_EXTENT , private);if (!et) { status = -ENOMEM ;mlog_errno (status);
@@ -4472,7 +4491,8 @@ int ocfs2_xattr_tree_insert_extent(struct ocfs2_super *osb,int status;struct ocfs2_extent_tree *et =NULL ; - et =ocfs2_new_extent_tree (root_bh,OCFS2_XATTR_TREE_EXTENT ,NULL ); + et =ocfs2_new_extent_tree (inode, root_bh,OCFS2_XATTR_TREE_EXTENT , +NULL );if (!et) { status = -ENOMEM ;mlog_errno (status);
@@ -4763,7 +4783,7 @@ int ocfs2_mark_extent_written(struct inode *inode, struct buffer_head *root_bh, goto out; } - et =ocfs2_new_extent_tree (root_bh, et_type, private); + et =ocfs2_new_extent_tree (inode, root_bh, et_type, private);if (!et) { ret = -ENOMEM ;mlog_errno (ret);
@@ -5061,7 +5081,7 @@ int ocfs2_remove_extent(struct inode *inode, struct buffer_head *root_bh,struct ocfs2_path *path =NULL ;struct ocfs2_extent_tree *et =NULL ; - et =ocfs2_new_extent_tree (root_bh, et_type, private); + et =ocfs2_new_extent_tree (inode, root_bh, et_type, private);if (!et) { ret = -ENOMEM ;mlog_errno (ret);
fs/ocfs2/alloc.h +5 -0
@@ -32,6 +32,11 @@ enum ocfs2_extent_tree_type { OCFS2_XATTR_TREE_EXTENT, }; +/* +* For xattr tree leaf, we limit the leaf byte size to be 64K. +*/ +#define OCFS2_MAX_XATTR_TREE_LEAF_SIZE 65536 +struct ocfs2_alloc_context ; int ocfs2_dinode_insert_extent(struct ocfs2_super *osb, handle_t *handle,
Signature · 1 line
1.5.4.1
PATCH 1/1 Refactor ocfs2_insert_extent for not-merging. Revised
Quoted 2 lines · Tao Ma
Hi Mark,
Your explanation is clear enough. So here is the revised one. Thanks.
Great, thanks Tao.
Quoted 8 lines
fs/ocfs2/alloc.c +4
-0@@ -4134,6 +4139,20 @@ static void ocfs2_figure_contig_type(struct inode *inode, } } insert->ins_contig = contig_type; + +if (insert->ins_contig !=CONTIG_NONE ) { +struct ocfs2_extent_rec *rec = + &el->l_recs[insert->ins_contig_index];
fs/ocfs2/alloc.c static void ocfs2_figure_contig_type(struct inode *inode, · line 4155
Quoted 10 lines
fs/ocfs2/alloc.c +10
-0+unsigned int len =le16_to_cpu (rec->e_leaf_clusters) + +le16_to_cpu (insert_rec->e_leaf_clusters); + +/* +* Caller might want us to limit the size of extents, don't +* calculate contiguousness if we might exceed that limit. +*/ +if (et->max_leaf_clusters && len > et->max_leaf_clusters) + insert->ins_contig =CONTIG_NONE ; + }
I would have just compared len and max_leaf_clusters at the top of the function, but either way works for me. The rest of the patch looks great.
Do we have to do anything similar for when you remove clusters from the ea index tree?
--Mark
--
Mark Fasheh
PATCH 13/15 Enable xattr set in index btree. v3
In reply to Tao MaQuoted 8 lines · Tao Ma
Modification from V2 to V3:
1. Remove kmalloc+memcpy in set.
2. Remove xattr entry when we fails to find the enough space for outside storage.
3. Empty the whole block when creating new xattr bucket.
4. Limit xattr entry to be only in 1st block of the bucket and limit xattr name/value pair within the same block.
5. Limit the xattr tree leaf size to be 64K.
Ok, this is looking good Tao.
I think I figured out what xh_offset is. Basically, ea name/value pairs grow from the end of the bucket forward, and you're just using it to mark the 'top' of that stack so that insertion doesn't have to search the entire bucket. You're relying on a (hopefully rare) defrag of the bucket to eliminate holes created by ea removal. I guess we'll only have to worry about defragging when the bucket is full.
Is this all correct? If so, there might be a more descriptive variable name we can use...
--Mark
--
Mark Fasheh
PATCH 13/15 Enable xattr set in index btree. v3
Mark Fasheh Wrote:
Quoted 12 lines · Tao Ma
Quoted 10 lines
Modification from V2 to V3:
1. Remove kmalloc+memcpy in set.
2. Remove xattr entry when we fails to find the enough space for outside storage.
3. Empty the whole block when creating new xattr bucket.
4. Limit xattr entry to be only in 1st block of the bucket and limit xattr name/value pair within the same block.
5. Limit the xattr tree leaf size to be 64K.
Quoted 9 lines
Ok, this is looking good Tao.
I think I figured out what
xh_offsetis. Basically, ea name/value pairs grow from the end of the bucket forward, and you're just using it to mark the 'top' of that stack so that insertion doesn't have to search the entire bucket. You're relying on a (hopefully rare) defrag of the bucket to eliminate holes created by ea removal. I guess we'll only have to worry about defragging when the bucket is full.
yes, you are right. As for defragging, please see ocfs2_xattr_set_entry_index_block, when we found it is full, we will creat a new bucket and move half of the xattr to the new bucket.
Quoted 3 lines
Is this all correct? If so, there might be a more descriptive variable name we can use...
so how about xh_store_end? Sorry for my poor English. Or maybe you can give me a good name. ;)
Regards,
Tao
PATCH 13/15 Enable xattr set in index btree. v3
Quoted 23 lines · Tao Ma
Mark Fasheh Wrote:
Quoted 14 lines · Tao Ma
Quoted 10 lines
Modification from V2 to V3:
1. Remove kmalloc+memcpy in set.
2. Remove xattr entry when we fails to find the enough space for outside storage.
3. Empty the whole block when creating new xattr bucket.
4. Limit xattr entry to be only in 1st block of the bucket and limit xattr name/value pair within the same block.
5. Limit the xattr tree leaf size to be 64K.Ok, this is looking good Tao.
Quoted 8 lines
I think I figured out what
xh_offsetis. Basically, ea name/value pairs grow
from the end of the bucket forward, and you're just using it to mark the 'top' of that stack so that insertion doesn't have to search the entire bucket. You're relying on a (hopefully rare) defrag of the bucket to eliminate holes created by ea removal. I guess we'll only have to worry about defragging when the bucket is full.
Quoted 3 lines
yes, you are right. As for defragging, please see
ocfs2_xattr_set_entry_index_block, when we found it is full, we will creat a new bucket and move half of the xattr to the new bucket.
Right.
Quoted 5 lines
Quoted 3 lines
Is this all correct? If so, there might be a more descriptive variable name we can use...
so how about
xh_store_end? Sorry for my poor English. Or maybe you can give me a good name. ;)
xh_free_start? xh_val_stack? I'm not too good at naming either :(
--Mark
--
Mark Fasheh
PATCH 14/15 Delete all xattr buckets in inode removal.v3
In reply to Tao MaIn inode removal, we need to iterate all the buckets, remove all the outside value storage for individual xattr and delete the xattr buckets.
--- fs/ocfs2/xattr.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 80 insertions(+), 4 deletions(-)
fs/ocfs2/xattr.c +80 -4
@@ -147,6 +147,9 @@ static int ocfs2_xattr_set_entry_index_block(struct inode *inode,struct ocfs2_xattr_info *xi, struct ocfs2_xattr_search *xs); +static int ocfs2_delete_xattr_index_block (struct inode *inode, +struct buffer_head *xb_bh); +static inline struct xattr_handler *ocfs2_xattr_handler (int name_index) {struct xattr_handler *handler =NULL ;
@@ -1527,13 +1530,14 @@ static int ocfs2_xattr_block_remove(struct inode *inode, struct buffer_head *blk_bh) {struct ocfs2_xattr_block *xb; -struct ocfs2_xattr_header *header;int ret =0 ; xb = (struct ocfs2_xattr_block *)blk_bh->b_data; - header = &(xb->xb_attrs.xb_header); - - ret =ocfs2_remove_value_outside (inode, blk_bh, header); +if (!(le16_to_cpu (xb->xb_flags) &OCFS2_XATTR_INDEXED )) { +struct ocfs2_xattr_header *header = &(xb->xb_attrs.xb_header); + ret =ocfs2_remove_value_outside (inode, blk_bh, header); + }else + ret =ocfs2_delete_xattr_index_block (inode, blk_bh);return ret; }
@@ -4750,3 +4754,75 @@ out:mlog_exit (ret);return ret; } + +static int ocfs2_delete_xattr_in_bucket (struct inode *inode, +struct ocfs2_xattr_bucket *bucket, +void *para) +{ +int ret =0 ; +struct ocfs2_xattr_header *xh = bucket->xh; +u16 i; +struct ocfs2_xattr_entry *xe; + +for (i =0 ; i <le16_to_cpu (xh->xh_count); i++) { + xe = &xh->xh_entries[i]; +if (ocfs2_xattr_is_local (xe)) +continue ; + + ret =ocfs2_xattr_bucket_value_truncate (inode, + bucket->bhs[0 ], + i,0 ); +if (ret) { +mlog_errno (ret); +break ; + } + } + +return ret; +} + +static int ocfs2_delete_xattr_index_block (struct inode *inode, +struct buffer_head *xb_bh) +{ +struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)xb_bh->b_data; +struct ocfs2_extent_list *el = &xb->xb_attrs.xb_root.xt_list; +int ret =0 ; +u32 name_hash =UINT_MAX , e_cpos, num_clusters; +u64 p_blkno; + +if (le16_to_cpu (el->l_next_free_rec) ==0 ) +return 0 ; + +while (name_hash >0 ) { + ret =ocfs2_xattr_get_rec (inode, name_hash, &p_blkno, + &e_cpos, &num_clusters, el); +if (ret) { +mlog_errno (ret); + goto out; + } + + ret =ocfs2_iterate_xattr_buckets (inode, p_blkno, num_clusters, + ocfs2_delete_xattr_in_bucket, +NULL ); +if (ret) { +mlog_errno (ret); + goto out; + } + + ret =ocfs2_rm_xattr_cluster (inode, xb_bh, + p_blkno, e_cpos, num_clusters); +if (ret) { +mlog_errno (ret); +break ; + } + +if (e_cpos ==0 ) +break ; + + name_hash = e_cpos -1 ; + } + +out: +return ret; +}
Signature · 1 line
1.5.4.GIT
PATCH 8/15 ocfs2: reserve inline space for extended attribute
In reply to Tao MaThis patch reserve some space in inode block for extended attribute. That space used to store extent list or inline data.
--- fs/ocfs2/alloc.c | 22 ++++++++++++++++------ fs/ocfs2/ocfs2.h | 1 + fs/ocfs2/ocfs2_fs.h | 43 ++++++++++++++++++++++++++++++++++++++++--- fs/ocfs2/super.c | 2 ++ 4 files changed, 59 insertions(+), 9 deletions(-)
fs/ocfs2/alloc.c +16 -6
@@ -6467,20 +6467,29 @@ out:return ret; } -static void ocfs2_zero_dinode_id2 (struct inode *inode,struct ocfs2_dinode *di) +static void ocfs2_zero_dinode_id2_with_xattr (struct inode *inode, +struct ocfs2_dinode *di) {unsigned int blocksize =1 << inode->i_sb->s_blocksize_bits; +unsigned int xattrsize =le16_to_cpu (di->i_xattr_inline_size); -memset (&di->id2,0 , blocksize - offsetof(struct ocfs2_dinode , id2)); +if (le16_to_cpu (di->i_dyn_features) &OCFS2_INLINE_XATTR_FL ) +memset (&di->id2,0 , blocksize - + offsetof(struct ocfs2_dinode , id2) - + xattrsize); +else +memset (&di->id2,0 , blocksize - + offsetof(struct ocfs2_dinode , id2)); }void ocfs2_dinode_new_extent_list (struct inode *inode,struct ocfs2_dinode *di) { -ocfs2_zero_dinode_id2 (inode, di); +ocfs2_zero_dinode_id2_with_xattr (inode, di); di->id2.i_list.l_tree_depth =0 ; di->id2.i_list.l_next_free_rec =0 ; - di->id2.i_list.l_count =cpu_to_le16 (ocfs2_extent_recs_per_inode (inode->i_sb)); + di->id2.i_list.l_count =cpu_to_le16 ( +ocfs2_extent_recs_per_inode_with_xattr (inode->i_sb, di)); }void ocfs2_set_inode_data_inline (struct inode *inode,struct ocfs2_dinode *di)
@@ -6497,9 +6506,10 @@ void ocfs2_set_inode_data_inline(struct inode *inode, struct ocfs2_dinode *di)* We clear the entire i_data structure here so that all * fields can be properly initialized. */ -ocfs2_zero_dinode_id2 (inode, di); +ocfs2_zero_dinode_id2_with_xattr (inode, di); - idata->id_count =cpu_to_le16 (ocfs2_max_inline_data (inode->i_sb)); + idata->id_count =cpu_to_le16 ( +ocfs2_max_inline_data_with_xattr (inode->i_sb, di)); } int ocfs2_convert_inline_data_to_extents(struct inode *inode,
fs/ocfs2/ocfs2.h +1 -0
@@ -227,6 +227,7 @@ struct ocfs2_superint s_sectsize_bits;int s_clustersize;int s_clustersize_bits; +unsigned int s_xattr_inline_size;atomic_t vol_state;struct mutex recovery_lock;
fs/ocfs2/ocfs2_fs.h +40 -3
76 lines not on this page · raw message
fs/ocfs2/super.c +2 -0
@@ -1421,6 +1421,8 @@ static int ocfs2_initialize_super(struct super_block *sb, osb->slot_num =OCFS2_INVALID_SLOT ; + osb->s_xattr_inline_size =OCFS2_MIN_XATTR_INLINE_SIZE ; + osb->local_alloc_state =OCFS2_LA_UNUSED ; osb->local_alloc_bh =NULL ;
Signature · 1 line
1.5.4.4
PATCH 8/15 ocfs2: reserve inline space for extended attribute
Quoted 2 lines · Tiger Yang
This patch reserve some space in inode block for extended attribute. That space used to store extent list or inline data.
This patch looks much better, thanks. One small note below.
Quoted 102 lines
fs/ocfs2/ocfs2.h +1
-0@@ -227,6 +227,7 @@ struct ocfs2_superint s_sectsize_bits;int s_clustersize;int s_clustersize_bits; +unsigned int s_xattr_inline_size;atomic_t vol_state;struct mutex recovery_lock;fs/ocfs2/ocfs2_fs.h +40
-3@@ -300,6 +300,9 @@ struct ocfs2_new_group_input {*/ #define OCFS2_DEFAULT_LOCAL_ALLOC_SIZE 8 +/* Inline extended attribute size (in bytes) */ +#define OCFS2_MIN_XATTR_INLINE_SIZE 256 +struct ocfs2_system_inode_info {char *si_name;int si_iflags;@@ -622,7 +625,8 @@ struct ocfs2_dinode {belongs to */ __le16 i_suballoc_bit;/* Bit offset in suballocator block group */ -/*10*/ __le32 i_reserved0; +/*10*/ __le16 i_reserved0; +__le16 i_xattr_inline_size;__le32 i_clusters;/* Cluster count */ __le32 i_uid;/* Owner UID */ __le32 i_gid;/* Owning GID */ @@ -641,11 +645,12 @@ struct ocfs2_dinode {__le32 i_atime_nsec;__le32 i_ctime_nsec;__le32 i_mtime_nsec; -__le32 i_attr; +/*70*/ __le32 i_attr;__le16 i_orphaned_slot;/* Only valid when OCFS2_ORPHANED_FL was set in i_flags */ __le16 i_dyn_features; -/*70*/ __le64 i_reserved2[8 ]; +__le64 i_xattr_loc; +/*80*/ __le64 i_reserved2[7 ];/*B8*/ union {__le64 i_pad1;/* Generic way to refer to this 64bit union */ @@ -843,6 +848,20 @@ static inline int ocfs2_max_inline_data(struct super_block *sb) offsetof(struct ocfs2_dinode, id2.i_data.id_data); } +static inline int ocfs2_max_inline_data_with_xattr (struct super_block *sb, +struct ocfs2_dinode *di) +{ +unsigned int xattrsize =le16_to_cpu (di->i_xattr_inline_size); + +if (le16_to_cpu (di->i_dyn_features) &OCFS2_INLINE_XATTR_FL ) +return sb->s_blocksize - + offsetof(struct ocfs2_dinode, id2.i_data.id_data) - + xattrsize; +else +return sb->s_blocksize - + offsetof(struct ocfs2_dinode, id2.i_data.id_data); +} +static inline int ocfs2_extent_recs_per_inode (struct super_block *sb) {int size;@@ -853,6 +872,24 @@ static inline int ocfs2_extent_recs_per_inode(struct super_block *sb)return size /sizeof (struct ocfs2_extent_rec ); } +static inline int ocfs2_extent_recs_per_inode_with_xattr ( +struct super_block *sb, +struct ocfs2_dinode *di) +{ +int size; +unsigned int xattrsize =le16_to_cpu (di->i_xattr_inline_size); + +if (le16_to_cpu (di->i_dyn_features) &OCFS2_INLINE_XATTR_FL ) + size = sb->s_blocksize - + offsetof(struct ocfs2_dinode, id2.i_list.l_recs) - + xattrsize; +else + size = sb->s_blocksize - + offsetof(struct ocfs2_dinode, id2.i_list.l_recs); + +return size /sizeof (struct ocfs2_extent_rec ); +} +static inline int ocfs2_chain_recs_per_inode (struct super_block *sb) {int size;fs/ocfs2/super.c +0
-0
fs/ocfs2/super.c static int ocfs2_initialize_super(struct super_block *sb, · line 1424
Quoted 4 lines
fs/ocfs2/super.c +1
-0@@ -1421,6 +1421,8 @@ static int ocfs2_initialize_super(struct super_block *sb, osb->slot_num =OCFS2_INVALID_SLOT ; + osb->s_xattr_inline_size =OCFS2_MIN_XATTR_INLINE_SIZE ;
Why not use the value of i_xattr_inline_size off the super-block inode to tell the module how large xattrs should be? That way we could allow users to change it in the future.
By the way, regarding sizes. I just realized that the sizes chosen for xattrs need to be aligned to 16 byte boundaries (sizeof(struct ocfs2_extent_rec)) so we don't waste space in the extent list above them.
Does that make sense to you? I think we're safe - you chose the value of OCFS2_MIN_XATTR_INLINE_SIZE well. A comment above it noting this might be nice though so that we don't make a mistake in the future.
--Mark
--
Mark Fasheh
PATCH 8/15 ocfs2: reserve inline space for extended attribute
Quoted 9 lines · Mark Fasheh
fs/ocfs2/super.c static int ocfs2_initialize_super(struct super_block *sb, · line 1424
Quoted 5 lines
fs/ocfs2/super.c +1
-0@@ -1421,6 +1421,8 @@ static int ocfs2_initialize_super(struct super_block *sb, osb->slot_num =OCFS2_INVALID_SLOT ; + osb->s_xattr_inline_size =OCFS2_MIN_XATTR_INLINE_SIZE ;Why not use the value of
i_xattr_inline_sizeoff the super-block inode to tell the module how large xattrs should be? That way we could allow users to change it in the future.
I change it in the fifth patch.
- osb->s_xattr_inline_size = OCFS2_MIN_XATTR_INLINE_SIZE;
+ osb->s_xattr_inline_size = le16_to_cpu(
+ di->id2.i_super.s_xattr_inline_size);
This is easy to test patches at the very beginning, since the fifth patch need new mkfs.ocfs2 support.
Quoted 7 lines
By the way, regarding sizes. I just realized that the sizes chosen for xattrs need to be aligned to 16 byte boundaries (
sizeof(struct ocfs2_extent_rec)) so we don't waste space in the extent list above them.Does that make sense to you? I think we're safe - you chose the value of
OCFS2_MIN_XATTR_INLINE_SIZEwell. A comment above it noting this might be nice though so that we don't make a mistake in the future.
yes, I knew it. At first I reserve space for xattr just reduce l_count. I will add comment for this later.
Best regards,
tiger
PATCH 9/15 ocfs2: Add extended attribute support v3
In reply to Tao MaThis patch implement storing extended attributes both in inode or external block. We only set EAs in inode when blocksize > 512 or that inode block has free space for it. When EAs value size larger than 80 bytes, we will store the value via b-tree outside inode or block.
--- fs/ocfs2/Makefile | 2 + fs/ocfs2/file.c | 5 + fs/ocfs2/inode.c | 8 + fs/ocfs2/inode.h | 3 + fs/ocfs2/journal.h | 10 + fs/ocfs2/namei.c | 5 + fs/ocfs2/ocfs2.h | 2 + fs/ocfs2/ocfs2_fs.h | 8 +- fs/ocfs2/suballoc.c | 17 +- fs/ocfs2/suballoc.h | 3 + fs/ocfs2/super.c | 14 + fs/ocfs2/symlink.c | 9 + fs/ocfs2/xattr.c | 1636 ++++++++++++++++++++++++++++++++++++++++++++++ fs/ocfs2/xattr.h | 58 ++ fs/ocfs2/xattr_trusted.c | 82 +++ fs/ocfs2/xattr_user.c | 94 +++ 16 files changed, 1950 insertions(+), 6 deletions(-) create mode 100644 fs/ocfs2/xattr.h create mode 100644 fs/ocfs2/xattr_trusted.c create mode 100644 fs/ocfs2/xattr_user.c
fs/ocfs2/Makefile +2 -0
@@ -36,6 +36,8 @@ ocfs2-objs := \ uptodate.o \ ver.o \ xattr.o \ + xattr_user.o \ + xattr_trusted.o ocfs2_stackglue-objs := stackglue.o ocfs2_stack_o2cb-objs := stack_o2cb.o
fs/ocfs2/file.c +5 -0
@@ -55,6 +55,7 @@#include "mmap.h" #include "suballoc.h" #include "super.h" +#include "xattr.h" #include "buffer_head_io.h"
@@ -2070,6 +2071,10 @@ const struct inode_operations ocfs2_file_iops = { .setattr = ocfs2_setattr, .getattr = ocfs2_getattr, .permission = ocfs2_permission, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, + .listxattr = ocfs2_listxattr, + .removexattr = generic_removexattr, .fallocate = ocfs2_fallocate, };
fs/ocfs2/inode.c +8 -0
@@ -49,6 +49,7 @@#include "symlink.h" #include "sysfile.h" #include "uptodate.h" +#include "xattr.h" #include "buffer_head_io.h"
@@ -730,6 +731,13 @@ static int ocfs2_wipe_inode(struct inode *inode, goto bail_unlock_dir; } +/*Free extended attribute resources associated with this inode.*/ + status =ocfs2_xattr_remove (inode, di_bh); +if (status <0 ) { +mlog_errno (status); + goto bail_unlock_dir; + } + status =ocfs2_remove_inode (inode, di_bh, orphan_dir_inode, orphan_dir_bh);if (status <0 )
fs/ocfs2/inode.h +3 -0
@@ -40,6 +40,9 @@ struct ocfs2_inode_info/* protects allocation changes on this inode. */ struct rw_semaphore ip_alloc_sem; +/* protects extended attribute changes on this inode */ +struct rw_semaphore xattr_sem; +/* These fields are protected by ip_lock */ spinlock_t ip_lock;u32 ip_open_count;
fs/ocfs2/journal.h +10 -0
@@ -282,6 +282,9 @@ int ocfs2_journal_dirty_data(handle_t *handle,/* simple file updates like chmod, etc. */ #define OCFS2_INODE_UPDATE_CREDITS 1 +/* extended attribute block update */ +#define OCFS2_XATTR_BLOCK_UPDATE_CREDITS 1 +/* group extend. inode update and last group update. */ #define OCFS2_GROUP_EXTEND_CREDITS (OCFS2_INODE_UPDATE_CREDITS + 1)
@@ -339,6 +342,13 @@ int ocfs2_journal_dirty_data(handle_t *handle,#define OCFS2_RENAME_CREDITS (3 * OCFS2_INODE_UPDATE_CREDITS + 3 \ + OCFS2_UNLINK_CREDITS) +/* global bitmap dinode, group desc., relinked group, +* suballocator dinode, group desc., relinked group, +* dinode, xattr block */ +#define OCFS2_XATTR_BLOCK_CREATE_CREDITS (OCFS2_SUBALLOC_ALLOC * 2 + \ + + OCFS2_INODE_UPDATE_CREDITS \ + + OCFS2_XATTR_BLOCK_UPDATE_CREDITS) +/* * Please note that the caller must make sure that root_el is the root * of extent tree. So for an inode, it should be &fe->id2.i_list. Otherwise
fs/ocfs2/namei.c +5 -0
@@ -60,6 +60,7 @@#include "symlink.h" #include "sysfile.h" #include "uptodate.h" +#include "xattr.h" #include "buffer_head_io.h"
@@ -1918,4 +1919,8 @@ const struct inode_operations ocfs2_dir_iops = { .setattr = ocfs2_setattr, .getattr = ocfs2_getattr, .permission = ocfs2_permission, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, + .listxattr = ocfs2_listxattr, + .removexattr = generic_removexattr, };
fs/ocfs2/ocfs2.h +2 -0
@@ -172,6 +172,7 @@ enum ocfs2_mount_optionsOCFS2_MOUNT_ERRORS_PANIC =1 <<3 ,/* Panic on errors */ OCFS2_MOUNT_DATA_WRITEBACK =1 <<4 ,/* No data ordering */ OCFS2_MOUNT_LOCALFLOCKS =1 <<5 ,/* No cluster aware user file locks */ + OCFS2_MOUNT_NOUSERXATTR = 1 << 6, /* No user xattr */ };#define OCFS2_OSB_SOFT_RO 0x0001
@@ -200,6 +201,7 @@ struct ocfs2_superu32 bitmap_cpg;u8 *uuid;char *uuid_str; +u32 uuid_hash;u8 *vol_label;u64 first_cluster_group_blkno;u32 fs_generation;
fs/ocfs2/ocfs2_fs.h +6 -2
@@ -567,7 +567,7 @@ struct ocfs2_super_block {/*40*/ __le16 s_max_slots;/* Max number of simultaneous mounts before tunefs required */ __le16 s_tunefs_flag; -__le32 s_reserved1; +__le32 s_uuid_hash;/* hash value of uuid */ __le64 s_first_cluster_group;/* Block offset of 1st cluster * group header */ /*50*/ __u8 s_label[OCFS2_MAX_VOL_LABEL_LEN ];/* Label for mounting, etc. */
@@ -781,7 +781,11 @@ struct ocfs2_xattr_tree_root {/*10*/ struct ocfs2_extent_list xt_list;/* Extent record list */ }; -#define OCFS2_XATTR_INDEXED 0x1 +#define OCFS2_XATTR_INDEXED 0x1 +#define OCFS2_HASH_SHIFT 5 +#define OCFS2_XATTR_ROUND 3 +#define OCFS2_XATTR_SIZE (size) (((size) + OCFS2_XATTR_ROUND) & \ + ~(OCFS2_XATTR_ROUND))/* * On disk structure for xattr block.
fs/ocfs2/suballoc.c +13 -4
@@ -493,9 +493,9 @@ bail:return status; } -int ocfs2_reserve_new_metadata (struct ocfs2_super *osb, -struct ocfs2_extent_list *root_el, -struct ocfs2_alloc_context **ac) +int ocfs2_reserve_new_metadata_blocks (struct ocfs2_super *osb, +int blocks, +struct ocfs2_alloc_context **ac) {int status;u32 slot;
@@ -507,7 +507,7 @@ int ocfs2_reserve_new_metadata(struct ocfs2_super *osb, goto bail; } - (*ac)->ac_bits_wanted =ocfs2_extend_meta_needed (root_el); + (*ac)->ac_bits_wanted = blocks; (*ac)->ac_which =OCFS2_AC_USE_META ; slot = osb->slot_num; (*ac)->ac_group_search = ocfs2_block_group_search;
@@ -532,6 +532,15 @@ bail:return status; } +int ocfs2_reserve_new_metadata (struct ocfs2_super *osb, +struct ocfs2_extent_list *root_el, +struct ocfs2_alloc_context **ac) +{ +return ocfs2_reserve_new_metadata_blocks (osb, +ocfs2_extend_meta_needed (root_el), + ac); +} +static int ocfs2_steal_inode_from_other_nodes (struct ocfs2_super *osb,struct ocfs2_alloc_context *ac) {
fs/ocfs2/suballoc.h +3 -0
@@ -67,6 +67,9 @@ static inline int ocfs2_alloc_context_bits_left(struct ocfs2_alloc_context *ac)int ocfs2_reserve_new_metadata (struct ocfs2_super *osb,struct ocfs2_extent_list *root_el,struct ocfs2_alloc_context **ac); +int ocfs2_reserve_new_metadata_blocks (struct ocfs2_super *osb, +int blocks, +struct ocfs2_alloc_context **ac);int ocfs2_reserve_new_inode (struct ocfs2_super *osb,struct ocfs2_alloc_context **ac); int ocfs2_reserve_clusters(struct ocfs2_super *osb,
fs/ocfs2/super.c +14 -0
@@ -64,6 +64,7 @@#include "sysfile.h" #include "uptodate.h" #include "ver.h" +#include "xattr.h" #include "buffer_head_io.h"
@@ -154,6 +155,8 @@ enum { Opt_localalloc, Opt_localflocks, Opt_stack, + Opt_user_xattr, + Opt_nouser_xattr, Opt_err, };
@@ -173,6 +176,8 @@ static match_table_t tokens = { {Opt_localalloc, "localalloc=%d"}, {Opt_localflocks, "localflocks"}, {Opt_stack, "cluster_stack=%s"}, + {Opt_user_xattr, "user_xattr"}, + {Opt_nouser_xattr, "nouser_xattr"}, {Opt_err, NULL} };
@@ -847,6 +852,12 @@ static int ocfs2_parse_options(struct super_block *sb,case Opt_data_writeback: mopt->mount_opt |=OCFS2_MOUNT_DATA_WRITEBACK ;break ; +case Opt_user_xattr: + mopt->mount_opt &= ~OCFS2_MOUNT_NOUSERXATTR ; +break ; +case Opt_nouser_xattr: + mopt->mount_opt |=OCFS2_MOUNT_NOUSERXATTR ; +break ;case Opt_atime_quantum:if (match_int (&args[0 ], &option)) { status =0 ;
@@ -1132,6 +1143,7 @@ static void ocfs2_inode_init_once(struct kmem_cache *cachep, void *data) oi->ip_dir_start_lookup =0 ;init_rwsem (&oi->ip_alloc_sem); +init_rwsem (&oi->xattr_sem);mutex_init (&oi->ip_io_mutex); oi->ip_blkno =0ULL ;
@@ -1375,6 +1387,7 @@ static int ocfs2_initialize_super(struct super_block *sb, sb->s_fs_info = osb; sb->s_op = &ocfs2_sops; sb->s_export_op = &ocfs2_export_ops; + sb->s_xattr = ocfs2_xattr_handlers; sb->s_time_gran =1 ; sb->s_flags |=MS_NOATIME ;/* this is needed to support O_LARGEFILE */
@@ -1561,6 +1574,7 @@ static int ocfs2_initialize_super(struct super_block *sb, osb->first_cluster_group_blkno =le64_to_cpu (di->id2.i_super.s_first_cluster_group); osb->fs_generation =le32_to_cpu (di->i_fs_generation); + osb->uuid_hash =le32_to_cpu (di->id2.i_super.s_uuid_hash);mlog (0 ,"vol_label: %s\n" , osb->vol_label);mlog (0 ,"uuid: %s\n" , osb->uuid_str); mlog(0, "root_blkno=%llu, system_dir_blkno=%llu\n",
fs/ocfs2/symlink.c +9 -0
@@ -50,6 +50,7 @@#include "inode.h" #include "journal.h" #include "symlink.h" +#include "xattr.h" #include "buffer_head_io.h"
@@ -168,10 +169,18 @@ const struct inode_operations ocfs2_symlink_inode_operations = { .follow_link = ocfs2_follow_link, .getattr = ocfs2_getattr, .setattr = ocfs2_setattr, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, + .listxattr = ocfs2_listxattr, + .removexattr = generic_removexattr, };const struct inode_operations ocfs2_fast_symlink_inode_operations = { .readlink = ocfs2_readlink, .follow_link = ocfs2_follow_link, .getattr = ocfs2_getattr, .setattr = ocfs2_setattr, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, + .listxattr = ocfs2_listxattr, + .removexattr = generic_removexattr, };
fs/ocfs2/xattr.c +1636 -0
1,663 lines not on this page · raw message
fs/ocfs2/xattr.h new file +58 -0
58 lines not on this page · raw message
fs/ocfs2/xattr_trusted.c new file +82 -0
82 lines not on this page · raw message
fs/ocfs2/xattr_user.c new file +94 -0
94 lines not on this page · raw message
Signature · 1 line
1.5.4.4
PATCH 9/15 ocfs2: Add extended attribute support v3
fs/ocfs2/inode.h struct ocfs2_inode_info · line 45
Quoted 11 lines · Tiger Yang
fs/ocfs2/inode.h +3
-0@@ -40,6 +40,9 @@ struct ocfs2_inode_info/* protects allocation changes on this inode. */ struct rw_semaphore ip_alloc_sem; +/* protects extended attribute changes on this inode */ +struct rw_semaphore xattr_sem; +
ip_xattr_sem please. Every other field in that structure is similarly
prefixed. Xattrs don't get to be special in that regard ;)
--Mark
--
Mark Fasheh
PATCH 15/15 ocfs2: Add incompatible flag for extended attribute v3
In reply to Tao MaThis patch add incompatible flag for extended attribute support. This flag could protect EAs not destroyed by previous ocfs2-tools or kernel modules.
--- fs/ocfs2/ocfs2.h | 7 +++++++ fs/ocfs2/ocfs2_fs.h | 19 +++++++++++++------ fs/ocfs2/super.c | 3 ++- fs/ocfs2/xattr.c | 12 ++++++++++++ 4 files changed, 34 insertions(+), 7 deletions(-)
fs/ocfs2/ocfs2.h +7 -0
@@ -329,6 +329,13 @@ static inline int ocfs2_supports_inline_data(struct ocfs2_super *osb)return 0 ; } +static inline int ocfs2_supports_xattr (struct ocfs2_super *osb) +{ +if (osb->s_feature_incompat &OCFS2_FEATURE_INCOMPAT_XATTR ) +return 1 ; +return 0 ; +} +/* set / clear functions because cluster events can make these happen * in parallel so we want the transitions to be atomic. this also * means that any future flags osb_flags must be protected by spinlock
fs/ocfs2/ocfs2_fs.h +13 -6
@@ -91,7 +91,8 @@ | OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC \ | OCFS2_FEATURE_INCOMPAT_INLINE_DATA \ | OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP \ - | OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK) + | OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK \ + | OCFS2_FEATURE_INCOMPAT_XATTR)#define OCFS2_FEATURE_RO_COMPAT_SUPP OCFS2_FEATURE_RO_COMPAT_UNWRITTEN/*
@@ -128,10 +129,6 @@/* Support for data packed into inode blocks */ #define OCFS2_FEATURE_INCOMPAT_INLINE_DATA 0x0040 -/* Support for the extended slot map */ -#define OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP 0x100 - -/* * Support for alternate, userspace cluster stacks. If set, the superblock * field s_cluster_info contains a tag for the alternate stack in use as
@@ -143,6 +140,12 @@*/ #define OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK 0x0080 +/* Support for the extended slot map */ +#define OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP 0x100 + +/* Support for extended attributes */ +#define OCFS2_FEATURE_INCOMPAT_XATTR 0x0200 +/* * backup superblock flag is used to indicate that this volume * has backup superblocks.
@@ -575,7 +578,11 @@ struct ocfs2_super_block {/*A0*/ struct ocfs2_cluster_info s_cluster_info;/* Selected userspace stack. Only valid with INCOMPAT flag. */ -/*B8*/ __le64 s_reserved2[17 ];/* Fill out superblock */ +/*B8*/ __le16 s_xattr_inline_size;/* extended attribute inline size +for this fs*/ +__le16 s_reserved0; +__le32 s_reserved1; +/*C0*/ __le64 s_reserved2[16 ];/* Fill out superblock */ /*140*/ /*
fs/ocfs2/super.c +2 -1
@@ -1434,7 +1434,8 @@ static int ocfs2_initialize_super(struct super_block *sb, osb->slot_num =OCFS2_INVALID_SLOT ; - osb->s_xattr_inline_size =OCFS2_MIN_XATTR_INLINE_SIZE ; + osb->s_xattr_inline_size =le16_to_cpu ( + di->id2.i_super.s_xattr_inline_size); osb->local_alloc_state =OCFS2_LA_UNUSED ; osb->local_alloc_bh =NULL ;
fs/ocfs2/xattr.c +12 -0
@@ -580,6 +580,9 @@ ssize_t ocfs2_listxattr(struct dentry *dentry,struct ocfs2_dinode *di =NULL ;struct ocfs2_inode_info *oi =OCFS2_I (dentry->d_inode); +if (!ocfs2_supports_xattr (OCFS2_SB (dentry->d_sb))) +return -EOPNOTSUPP ; +if (!(oi->ip_dyn_features &OCFS2_HAS_XATTR_FL ))return ret;
@@ -859,6 +862,9 @@ int ocfs2_xattr_get(struct inode *inode, .not_found = -ENODATA, }; +if (!ocfs2_supports_xattr (OCFS2_SB (inode->i_sb))) +return -EOPNOTSUPP ; +if (!(oi->ip_dyn_features &OCFS2_HAS_XATTR_FL )) ret = -ENODATA ;
@@ -1557,6 +1563,9 @@ int ocfs2_xattr_remove(struct inode *inode, struct buffer_head *di_bh)handle_t *handle;int ret; +if (!ocfs2_supports_xattr (OCFS2_SB (inode->i_sb))) +return 0 ; +if (!(oi->ip_dyn_features &OCFS2_HAS_XATTR_FL ))return 0 ;
@@ -1993,6 +2002,9 @@ int ocfs2_xattr_set(struct inode *inode, .not_found = -ENODATA, }; +if (!ocfs2_supports_xattr (OCFS2_SB (inode->i_sb))) +return -EOPNOTSUPP ; + ret =ocfs2_inode_lock (inode, &di_bh,1 );if (ret <0 ) {mlog_errno (ret);
Signature · 1 line
1.5.4.4