diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext4/ext4.h | 3 | ||||
-rw-r--r-- | fs/ext4/orphan.c | 77 |
2 files changed, 53 insertions, 27 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index ae79797b9d60..98758e8ea7fc 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1489,7 +1489,7 @@ static inline int ext4_inodes_per_orphan_block(struct super_block *sb) } struct ext4_orphan_block { - int ob_free_entries; /* Number of free orphan entries in block */ + atomic_t ob_free_entries; /* Number of free orphan entries in block */ struct buffer_head *ob_bh; /* Buffer for orphan block */ }; @@ -1497,7 +1497,6 @@ struct ext4_orphan_block { * Info about orphan file. */ struct ext4_orphan_info { - spinlock_t of_lock; int of_blocks; /* Number of orphan blocks in a file */ __u32 of_csum_seed; /* Checksum seed for orphan file */ struct ext4_orphan_block *of_binfo; /* Array with info about orphan diff --git a/fs/ext4/orphan.c b/fs/ext4/orphan.c index ac928f60234a..53adc8f570a3 100644 --- a/fs/ext4/orphan.c +++ b/fs/ext4/orphan.c @@ -10,16 +10,31 @@ static int ext4_orphan_file_add(handle_t *handle, struct inode *inode) { - int i, j; + int i, j, start; struct ext4_orphan_info *oi = &EXT4_SB(inode->i_sb)->s_orphan_info; int ret = 0; + bool found = false; __le32 *bdata; int inodes_per_ob = ext4_inodes_per_orphan_block(inode->i_sb); + int looped = 0; + + /* + * Find block with free orphan entry. Use CPU number for a naive hash + * for a search start in the orphan file + */ + start = raw_smp_processor_id()*13 % oi->of_blocks; + i = start; + do { + if (atomic_dec_if_positive(&oi->of_binfo[i].ob_free_entries) + >= 0) { + found = true; + break; + } + if (++i >= oi->of_blocks) + i = 0; + } while (i != start); - spin_lock(&oi->of_lock); - for (i = 0; i < oi->of_blocks && !oi->of_binfo[i].ob_free_entries; i++); - if (i == oi->of_blocks) { - spin_unlock(&oi->of_lock); + if (!found) { /* * For now we don't grow or shrink orphan file. We just use * whatever was allocated at mke2fs time. The additional @@ -28,28 +43,43 @@ static int ext4_orphan_file_add(handle_t *handle, struct inode *inode) */ return -ENOSPC; } - oi->of_binfo[i].ob_free_entries--; - spin_unlock(&oi->of_lock); - /* - * Get access to orphan block. We have dropped of_lock but since we - * have decremented number of free entries we are guaranteed free entry - * in our block. - */ ret = ext4_journal_get_write_access(handle, inode->i_sb, oi->of_binfo[i].ob_bh, EXT4_JTR_ORPHAN_FILE); - if (ret) + if (ret) { + atomic_inc(&oi->of_binfo[i].ob_free_entries); return ret; + } bdata = (__le32 *)(oi->of_binfo[i].ob_bh->b_data); - spin_lock(&oi->of_lock); /* Find empty slot in a block */ - for (j = 0; j < inodes_per_ob && bdata[j]; j++); - BUG_ON(j == inodes_per_ob); - bdata[j] = cpu_to_le32(inode->i_ino); + j = 0; + do { + if (looped) { + /* + * Did we walk through the block several times without + * finding free entry? It is theoretically possible + * if entries get constantly allocated and freed or + * if the block is corrupted. Avoid indefinite looping + * and bail. We'll use orphan list instead. + */ + if (looped > 3) { + atomic_inc(&oi->of_binfo[i].ob_free_entries); + return -ENOSPC; + } + cond_resched(); + } + while (bdata[j]) { + if (++j >= inodes_per_ob) { + j = 0; + looped++; + } + } + } while (cmpxchg(&bdata[j], (__le32)0, cpu_to_le32(inode->i_ino)) != + (__le32)0); + EXT4_I(inode)->i_orphan_idx = i * inodes_per_ob + j; ext4_set_inode_state(inode, EXT4_STATE_ORPHAN_FILE); - spin_unlock(&oi->of_lock); return ext4_handle_dirty_metadata(handle, NULL, oi->of_binfo[i].ob_bh); } @@ -180,10 +210,8 @@ static int ext4_orphan_file_del(handle_t *handle, struct inode *inode) goto out; bdata = (__le32 *)(oi->of_binfo[blk].ob_bh->b_data); - spin_lock(&oi->of_lock); bdata[off] = 0; - oi->of_binfo[blk].ob_free_entries++; - spin_unlock(&oi->of_lock); + atomic_inc(&oi->of_binfo[blk].ob_free_entries); ret = ext4_handle_dirty_metadata(handle, NULL, oi->of_binfo[blk].ob_bh); out: ext4_clear_inode_state(inode, EXT4_STATE_ORPHAN_FILE); @@ -552,8 +580,6 @@ int ext4_init_orphan_info(struct super_block *sb) struct ext4_orphan_block_tail *ot; ino_t orphan_ino = le32_to_cpu(EXT4_SB(sb)->s_es->s_orphan_file_inum); - spin_lock_init(&oi->of_lock); - if (!ext4_has_feature_orphan_file(sb)) return 0; @@ -597,7 +623,7 @@ int ext4_init_orphan_info(struct super_block *sb) for (j = 0; j < inodes_per_ob; j++) if (bdata[j] == 0) free++; - oi->of_binfo[i].ob_free_entries = free; + atomic_set(&oi->of_binfo[i].ob_free_entries, free); } iput(inode); return 0; @@ -619,7 +645,8 @@ int ext4_orphan_file_empty(struct super_block *sb) if (!ext4_has_feature_orphan_file(sb)) return 1; for (i = 0; i < oi->of_blocks; i++) - if (oi->of_binfo[i].ob_free_entries != inodes_per_ob) + if (atomic_read(&oi->of_binfo[i].ob_free_entries) != + inodes_per_ob) return 0; return 1; } |