ocfs2: flush truncate log when main bitmap run out of space
PATCH 1/1 ocfs2: flush truncate log when main bitmap run out of space
Truncate log problem has been described in
commit 50308d813bf2 ("ocfs2: Try to free truncate log when meeting ENOSPC in write.") and
commit 2070ad1aebff ("ocfs2: retry on ENOSPC if sufficient space in truncate log"), but the following situations cannot be solved:
case 1:
Main bitmap has been updated, but the transaction of the deleted blocks has not been committed completely. In this case, function ocfs2_reserve_cluster_bitmap_bits() returns success while function __ocfs2_claim_clusters return ENOSPC because we cannot reuse the deleted blocks before the transaction committed(test by function ocfs2_test_bg_bit_allocatable()). One can reproduce this by following steps:
a, prepare a file which size is 50G, and volume still have 30G free space b, open the file with O_TRUNC flag
c, sleep 5 seconds
e, fallocate a 50G file.
case 2:
Main bitmap doesn't have enough free bits, so does truncate log. But main bitmap plus truncate log has enough free bits. We are not gonging to try flush truncate log in this case, which is not reasonable.
So force commit the transaction when flushing the truncate log for case 1. For case 2, the value of osb->truncated_clusters doesn't seem to make sense, do the flush whenever we run out of space seems to be more reasonable.
--- fs/ocfs2/alloc.c | 43 +++---------------------------------------- fs/ocfs2/alloc.h | 2 -- fs/ocfs2/aops.c | 4 ++-- fs/ocfs2/ocfs2.h | 5 ----- fs/ocfs2/suballoc.c | 6 +++--- 5 files changed, 8 insertions(+), 52 deletions(-)
fs/ocfs2/alloc.c +3 -40
@@ -5921,7 +5921,6 @@ int ocfs2_truncate_log_append(struct ocfs2_super *osb,ocfs2_journal_dirty (handle, tl_bh); - osb->truncated_clusters += num_clusters; bail:return status; }
@@ -5940,6 +5939,7 @@ static int ocfs2_replay_truncate_records(struct ocfs2_super *osb,struct inode *tl_inode = osb->osb_tl_inode;struct buffer_head *tl_bh = osb->osb_tl_bh;handle_t *handle; +tid_t target; di = (struct ocfs2_dinode *) tl_bh->b_data; tl = &di->id2.i_dealloc;
@@ -5989,8 +5989,8 @@ static int ocfs2_replay_truncate_records(struct ocfs2_super *osb,ocfs2_commit_trans (osb, handle); i--; } - - osb->truncated_clusters =0 ; +if (jbd2_journal_start_commit (osb->journal->j_journal, &target)) +jbd2_log_wait_commit (osb->journal->j_journal, target); bail:return status;
@@ -6102,43 +6102,6 @@ void ocfs2_schedule_truncate_log_flush(struct ocfs2_super *osb, } } -/* -* Try to flush truncate logs if we can free enough clusters from it. -* As for return value, "< 0" means error, "0" no space and "1" means -* we have freed enough spaces and let the caller try to allocate again. -*/ -int ocfs2_try_to_free_truncate_log (struct ocfs2_super *osb, -unsigned int needed) -{ -tid_t target; -int ret =0 ; -unsigned int truncated_clusters; - -inode_lock (osb->osb_tl_inode); - truncated_clusters = osb->truncated_clusters; -inode_unlock (osb->osb_tl_inode); - -/* -* Check whether we can succeed in allocating if we free -* the truncate log. -*/ -if (truncated_clusters < needed) - goto out; - - ret =ocfs2_flush_truncate_log (osb); -if (ret) { -mlog_errno (ret); - goto out; - } - -if (jbd2_journal_start_commit (osb->journal->j_journal, &target)) { -jbd2_log_wait_commit (osb->journal->j_journal, target); - ret =1 ; - } -out: -return ret; -} - static int ocfs2_get_truncate_log_info(struct ocfs2_super *osb, int slot_num, struct inode **tl_inode,
fs/ocfs2/alloc.h +0 -2
@@ -188,8 +188,6 @@ int ocfs2_truncate_log_append(struct ocfs2_super *osb,u64 start_blk, unsigned int num_clusters);int __ocfs2_flush_truncate_log (struct ocfs2_super *osb); -int ocfs2_try_to_free_truncate_log (struct ocfs2_super *osb, -unsigned int needed);/* * Process local structure which describes the block unlinks done
fs/ocfs2/aops.c +2 -2
@@ -1893,8 +1893,8 @@ int ocfs2_write_begin_nolock(struct address_space *mapping,*/ try_free =0 ; - ret1 =ocfs2_try_to_free_truncate_log (osb, clusters_need); -if (ret1 ==1 ) + ret1 =ocfs2_flush_truncate_log (osb); +if (!ret1) goto try_again;if (ret1 <0 )
fs/ocfs2/ocfs2.h +0 -5
@@ -440,11 +440,6 @@ struct ocfs2_superstruct buffer_head *osb_tl_bh;struct delayed_work osb_truncate_log_wq;atomic_t osb_tl_disable; -/* -* How many clusters in our truncate log. -* It must be protected by osb_tl_inode->i_mutex. -*/ -unsigned int truncated_clusters;struct ocfs2_node_map osb_recovering_orphan_dirs;unsigned int *osb_orphan_wipes;
fs/ocfs2/suballoc.c +3 -3
@@ -1187,14 +1187,14 @@ static int ocfs2_reserve_clusters_with_limit(struct ocfs2_super *osb,if (status == -ENOSPC ) { retry: status =ocfs2_reserve_cluster_bitmap_bits (osb, *ac); -/* Retry if there is sufficient space cached in truncate log */ +/* Retry after flush truncate log */ if (status == -ENOSPC && !retried) { retried =1 ;ocfs2_inode_unlock ((*ac)->ac_inode,1 );inode_unlock ((*ac)->ac_inode); - ret =ocfs2_try_to_free_truncate_log (osb, bits_wanted); -if (ret ==1 ) { + ret =ocfs2_flush_truncate_log (osb); +if (!ret) {iput ((*ac)->ac_inode); (*ac)->ac_inode =NULL ; goto retry;
PATCH 1/1 ocfs2: flush truncate log when main bitmap run out of space
Quoted 24 lines · Jia Guo
Truncate log problem has been described in
commit50308d813bf2("ocfs2: Try to free truncate log when meeting ENOSPC in write.") and
commit2070ad1aebff("ocfs2: retry on ENOSPC if sufficient space in truncate log"), but the following situations cannot be solved:case 1:
Main bitmap has been updated, but the transaction of the deleted blocks has not been committed completely. In this case, functionocfs2_reserve_cluster_bitmap_bits()returns success while function__ocfs2_claim_clustersreturn ENOSPC because we cannot reuse the deleted blocks before the transactioncommitted(test by function ocfs2_test_bg_bit_allocatable()). One can reproduce this by following steps:
a, prepare a file which size is 50G, and volume still have 30G free space b, open the file withO_TRUNCflag
c, sleep 5 seconds
e, fallocate a 50G file.case 2:
Main bitmap doesn't have enough free bits, so does truncate log. But main bitmap plus truncate log has enough free bits. We are not gonging to try flush truncate log in this case, which is not reasonable.
Quoted 6 lines
So force commit the transaction when flushing the truncate log for case 1. For case 2, the value of
osb->truncated_clustersdoesn't seem to make sense, do the flush whenever we run out of space seems to be more reasonable.Signed-off-by Jia Guo <[email protected]>
Quoted 150 lines
--- fs/ocfs2/alloc.c | 43 +++---------------------------------------- fs/ocfs2/alloc.h | 2 -- fs/ocfs2/aops.c | 4 ++-- fs/ocfs2/ocfs2.h | 5 ----- fs/ocfs2/suballoc.c | 6 +++--- 5 files changed, 8 insertions(+), 52 deletions(-)fs/ocfs2/alloc.c +3
-40@@ -5921,7 +5921,6 @@ int ocfs2_truncate_log_append(struct ocfs2_super *osb,ocfs2_journal_dirty (handle, tl_bh); - osb->truncated_clusters += num_clusters; bail:return status; }@@ -5940,6 +5939,7 @@ static int ocfs2_replay_truncate_records(struct ocfs2_super *osb,struct inode *tl_inode = osb->osb_tl_inode;struct buffer_head *tl_bh = osb->osb_tl_bh;handle_t *handle; +tid_t target; di = (struct ocfs2_dinode *) tl_bh->b_data; tl = &di->id2.i_dealloc;@@ -5989,8 +5989,8 @@ static int ocfs2_replay_truncate_records(struct ocfs2_super *osb,ocfs2_commit_trans (osb, handle); i--; } - - osb->truncated_clusters =0 ; +if (jbd2_journal_start_commit (osb->journal->j_journal, &target)) +jbd2_log_wait_commit (osb->journal->j_journal, target); bail:return status;@@ -6102,43 +6102,6 @@ void ocfs2_schedule_truncate_log_flush(struct ocfs2_super *osb, } } -/* -* Try to flush truncate logs if we can free enough clusters from it. -* As for return value, "< 0" means error, "0" no space and "1" means -* we have freed enough spaces and let the caller try to allocate again. -*/ -int ocfs2_try_to_free_truncate_log (struct ocfs2_super *osb, -unsigned int needed) -{ -tid_t target; -int ret =0 ; -unsigned int truncated_clusters; - -inode_lock (osb->osb_tl_inode); - truncated_clusters = osb->truncated_clusters; -inode_unlock (osb->osb_tl_inode); - -/* -* Check whether we can succeed in allocating if we free -* the truncate log. -*/ -if (truncated_clusters < needed) - goto out; - - ret =ocfs2_flush_truncate_log (osb); -if (ret) { -mlog_errno (ret); - goto out; - } - -if (jbd2_journal_start_commit (osb->journal->j_journal, &target)) { -jbd2_log_wait_commit (osb->journal->j_journal, target); - ret =1 ; - } -out: -return ret; -} - static int ocfs2_get_truncate_log_info(struct ocfs2_super *osb, int slot_num, struct inode **tl_inode,fs/ocfs2/alloc.h +0
-2@@ -188,8 +188,6 @@ int ocfs2_truncate_log_append(struct ocfs2_super *osb,u64 start_blk, unsigned int num_clusters);int __ocfs2_flush_truncate_log (struct ocfs2_super *osb); -int ocfs2_try_to_free_truncate_log (struct ocfs2_super *osb, -unsigned int needed);/* * Process local structure which describes the block unlinks done fs/ocfs2/aops.c +2
-2@@ -1893,8 +1893,8 @@ int ocfs2_write_begin_nolock(struct address_space *mapping,*/ try_free =0 ; - ret1 =ocfs2_try_to_free_truncate_log (osb, clusters_need); -if (ret1 ==1 ) + ret1 =ocfs2_flush_truncate_log (osb); +if (!ret1) goto try_again;if (ret1 <0 )fs/ocfs2/ocfs2.h +0
-5@@ -440,11 +440,6 @@ struct ocfs2_superstruct buffer_head *osb_tl_bh;struct delayed_work osb_truncate_log_wq;atomic_t osb_tl_disable; -/* -* How many clusters in our truncate log. -* It must be protected by osb_tl_inode->i_mutex. -*/ -unsigned int truncated_clusters;struct ocfs2_node_map osb_recovering_orphan_dirs;unsigned int *osb_orphan_wipes;fs/ocfs2/suballoc.c +3
-3@@ -1187,14 +1187,14 @@ static int ocfs2_reserve_clusters_with_limit(struct ocfs2_super *osb,if (status == -ENOSPC ) { retry: status =ocfs2_reserve_cluster_bitmap_bits (osb, *ac); -/* Retry if there is sufficient space cached in truncate log */ +/* Retry after flush truncate log */ if (status == -ENOSPC && !retried) { retried =1 ;ocfs2_inode_unlock ((*ac)->ac_inode,1 );inode_unlock ((*ac)->ac_inode); - ret =ocfs2_try_to_free_truncate_log (osb, bits_wanted); -if (ret ==1 ) { + ret =ocfs2_flush_truncate_log (osb); +if (!ret) {iput ((*ac)->ac_inode); (*ac)->ac_inode =NULL ; goto retry;
PATCH 1/1 ocfs2: flush truncate log when main bitmap run out of space
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
On 2018/12/28 at 9:30, in message <[email protected]>, jiangyiwen
Quoted 25 lines · <[email protected]> wrote:
Quoted 24 lines · Jia Guo
Truncate log problem has been described in
commit50308d813bf2("ocfs2: Try to free truncate log when meeting ENOSPC in write.") and
commit2070ad1aebff("ocfs2: retry on ENOSPC if sufficient space in truncate log"), but the following situations cannot be solved:case 1:
Main bitmap has been updated, but the transaction of the deleted blocks has not been committed completely. In this case, functionocfs2_reserve_cluster_bitmap_bits()returns success while function__ocfs2_claim_clustersreturn ENOSPC because we cannot reuse the deleted blocks before the transactioncommitted(test by function ocfs2_test_bg_bit_allocatable()). One can reproduce this by following steps:
a, prepare a file which size is 50G, and volume still have 30G free space b, open the file withO_TRUNCflag
c, sleep 5 seconds
e, fallocate a 50G file.case 2:
Main bitmap doesn't have enough free bits, so does truncate log. But main bitmap plus truncate log has enough free bits. We are not gonging to try flush truncate log in this case, which is not reasonable.
Quoted 8 lines
Quoted 6 lines
So force commit the transaction when flushing the truncate log for case 1. For case 2, the value of
osb->truncated_clustersdoesn't seem to make sense, do the flush whenever we run out of space seems to be more reasonable.Signed-off-by Jia Guo <[email protected]>Reviewed-by Yiwen Jiang <[email protected]>
By the way, maybe we also can take the other nodes' truncate log back to main BM file.
Thanks
Gang
Quoted 163 lines
fs/ocfs2/alloc.c static int ocfs2_replay_truncate_records(struct
Quoted 22 lines
--- fs/ocfs2/alloc.c | 43 +++---------------------------------------- fs/ocfs2/alloc.h | 2 -- fs/ocfs2/aops.c | 4 ++-- fs/ocfs2/ocfs2.h | 5 ----- fs/ocfs2/suballoc.c | 6 +++--- 5 files changed, 8 insertions(+), 52 deletions(-)fs/ocfs2/alloc.c +0
-1@@ -5921,7 +5921,6 @@ int ocfs2_truncate_log_append(struct ocfs2_super *osb,ocfs2_journal_dirty (handle, tl_bh); - osb->truncated_clusters += num_clusters; bail:return status; }@@ -5940,6 +5939,7 @@ static int ocfs2_replay_truncate_records(struct
ocfs2_super*osb,fs/ocfs2/alloc.c static int ocfs2_replay_truncate_records(struct
Quoted 8 lines
fs/ocfs2/alloc.c +1
-0struct inode *tl_inode = osb->osb_tl_inode;struct buffer_head *tl_bh = osb->osb_tl_bh;handle_t *handle; +tid_t target; di = (struct ocfs2_dinode *) tl_bh->b_data; tl = &di->id2.i_dealloc;@@ -5989,8 +5989,8 @@ static int ocfs2_replay_truncate_records(struct
ocfs2_super*osb,fs/ocfs2/alloc.c void ocfs2_schedule_truncate_log_flush(struct
Quoted 11 lines
fs/ocfs2/alloc.c +2
-2ocfs2_commit_trans (osb, handle); i--; } - - osb->truncated_clusters =0 ; +if (jbd2_journal_start_commit (osb->journal->j_journal, &target)) +jbd2_log_wait_commit (osb->journal->j_journal, target); bail:return status;@@ -6102,43 +6102,6 @@ void ocfs2_schedule_truncate_log_flush(struct
ocfs2_super*osb,fs/ocfs2/aops.c int ocfs2_write_begin_nolock(struct address_space
Quoted 61 lines
fs/ocfs2/alloc.c +0
-37} } -/* -* Try to flush truncate logs if we can free enough clusters from it. -* As for return value, "< 0" means error, "0" no space and "1" means -* we have freed enough spaces and let the caller try to allocate again. -*/ -int ocfs2_try_to_free_truncate_log (struct ocfs2_super *osb, -unsigned int needed) -{ -tid_t target; -int ret =0 ; -unsigned int truncated_clusters; - -inode_lock (osb->osb_tl_inode); - truncated_clusters = osb->truncated_clusters; -inode_unlock (osb->osb_tl_inode); - -/* -* Check whether we can succeed in allocating if we free -* the truncate log. -*/ -if (truncated_clusters < needed) - goto out; - - ret =ocfs2_flush_truncate_log (osb); -if (ret) { -mlog_errno (ret); - goto out; - } - -if (jbd2_journal_start_commit (osb->journal->j_journal, &target)) { -jbd2_log_wait_commit (osb->journal->j_journal, target); - ret =1 ; - } -out: -return ret; -} - static int ocfs2_get_truncate_log_info(struct ocfs2_super *osb, int slot_num, struct inode **tl_inode,fs/ocfs2/alloc.h +0
-2@@ -188,8 +188,6 @@ int ocfs2_truncate_log_append(struct ocfs2_super *osb,u64 start_blk, unsigned int num_clusters);int __ocfs2_flush_truncate_log (struct ocfs2_super *osb); -int ocfs2_try_to_free_truncate_log (struct ocfs2_super *osb, -unsigned int needed);/* * Process local structure which describes the block unlinks done fs/ocfs2/aops.c +0
-0@@ -1893,8 +1893,8 @@ int ocfs2_write_begin_nolock(struct address_space*mapping,
fs/ocfs2/suballoc.c static int ocfs2_reserve_clusters_with_limit(struct
Quoted 31 lines
fs/ocfs2/aops.c +2
-2*/ try_free =0 ; - ret1 =ocfs2_try_to_free_truncate_log (osb, clusters_need); -if (ret1 ==1 ) + ret1 =ocfs2_flush_truncate_log (osb); +if (!ret1) goto try_again;if (ret1 <0 )fs/ocfs2/ocfs2.h +0
-5@@ -440,11 +440,6 @@ struct ocfs2_superstruct buffer_head *osb_tl_bh;struct delayed_work osb_truncate_log_wq;atomic_t osb_tl_disable; -/* -* How many clusters in our truncate log. -* It must be protected by osb_tl_inode->i_mutex. -*/ -unsigned int truncated_clusters;struct ocfs2_node_map osb_recovering_orphan_dirs;unsigned int *osb_orphan_wipes;fs/ocfs2/suballoc.c +0
-0@@ -1187,14 +1187,14 @@ static int ocfs2_reserve_clusters_with_limit(struct
ocfs2_super*osb,Quoted 8 lines
fs/ocfs2/suballoc.c +1
-1if (status == -ENOSPC ) { retry: status =ocfs2_reserve_cluster_bitmap_bits (osb, *ac); -/* Retry if there is sufficient space cached in truncate log */ +/* Retry after flush truncate log */ if (status == -ENOSPC && !retried) { retried =1 ;fs/ocfs2/suballoc.c static int ocfs2_reserve_clusters_with_limit(struct ocfs2_super *osb, · line 1200
Quoted 10 lines
fs/ocfs2/suballoc.c +2
-2ocfs2_inode_unlock ((*ac)->ac_inode,1 );inode_unlock ((*ac)->ac_inode); - ret =ocfs2_try_to_free_truncate_log (osb, bits_wanted); -if (ret ==1 ) { + ret =ocfs2_flush_truncate_log (osb); +if (!ret) {iput ((*ac)->ac_inode); (*ac)->ac_inode =NULL ; goto retry;_______________________________________________
Ocfs2-devel mailing list
Ocfs2-devel at oss.oracle.com
https://oss.oracle.com/mailman/listinfo/ocfs2-devel
PATCH 1/1 ocfs2: flush truncate log when main bitmap run out of space
In reply to Jia GuoOverall, this patch makes senses to me.
But it still have something to improve. Please see my comments below.
Quoted 6 lines · Jia Guo
Truncate log problem has been described in
commit50308d813bf2("ocfs2: Try to free truncate log when meeting ENOSPC in write.") and
commit2070ad1aebff("ocfs2: retry on ENOSPC if sufficient space in truncate log"), but the following situations cannot be solved:
Quoted 12 lines
case 1:
Main bitmap has been updated, but the transaction of the deleted blocks has not been committed completely. In this case, functionocfs2_reserve_cluster_bitmap_bits()returns success while function__ocfs2_claim_clustersreturn ENOSPC because we cannot reuse the deleted blocks before the transactioncommitted(test by function ocfs2_test_bg_bit_allocatable()). One can reproduce this by following steps:
a, prepare a file which size is 50G, and volume still have 30G free space b, open the file withO_TRUNCflag
c, sleep 5 seconds
e, fallocate a 50G file.
What's the step d?
Quoted 113 lines
case 2:
Main bitmap doesn't have enough free bits, so does truncate log. But main bitmap plus truncate log has enough free bits. We are not gonging to try flush truncate log in this case, which is not reasonable.So force commit the transaction when flushing the truncate log for case 1. For case 2, the value of
osb->truncated_clustersdoesn't seem to make sense, do the flush whenever we run out of space seems to be more reasonable.Signed-off-by Jia Guo <[email protected]>--- fs/ocfs2/alloc.c | 43 +++---------------------------------------- fs/ocfs2/alloc.h | 2 -- fs/ocfs2/aops.c | 4 ++-- fs/ocfs2/ocfs2.h | 5 ----- fs/ocfs2/suballoc.c | 6 +++--- 5 files changed, 8 insertions(+), 52 deletions(-)fs/ocfs2/alloc.c +3
-40@@ -5921,7 +5921,6 @@ int ocfs2_truncate_log_append(struct ocfs2_super *osb,ocfs2_journal_dirty (handle, tl_bh); - osb->truncated_clusters += num_clusters; bail:return status; }@@ -5940,6 +5939,7 @@ static int ocfs2_replay_truncate_records(struct ocfs2_super *osb,struct inode *tl_inode = osb->osb_tl_inode;struct buffer_head *tl_bh = osb->osb_tl_bh;handle_t *handle; +tid_t target; di = (struct ocfs2_dinode *) tl_bh->b_data; tl = &di->id2.i_dealloc;@@ -5989,8 +5989,8 @@ static int ocfs2_replay_truncate_records(struct ocfs2_super *osb,ocfs2_commit_trans (osb, handle); i--; } - - osb->truncated_clusters =0 ; +if (jbd2_journal_start_commit (osb->journal->j_journal, &target)) +jbd2_log_wait_commit (osb->journal->j_journal, target); bail:return status;@@ -6102,43 +6102,6 @@ void ocfs2_schedule_truncate_log_flush(struct ocfs2_super *osb, } } -/* -* Try to flush truncate logs if we can free enough clusters from it. -* As for return value, "< 0" means error, "0" no space and "1" means -* we have freed enough spaces and let the caller try to allocate again. -*/ -int ocfs2_try_to_free_truncate_log (struct ocfs2_super *osb, -unsigned int needed) -{ -tid_t target; -int ret =0 ; -unsigned int truncated_clusters; - -inode_lock (osb->osb_tl_inode); - truncated_clusters = osb->truncated_clusters; -inode_unlock (osb->osb_tl_inode); - -/* -* Check whether we can succeed in allocating if we free -* the truncate log. -*/ -if (truncated_clusters < needed) - goto out; - - ret =ocfs2_flush_truncate_log (osb); -if (ret) { -mlog_errno (ret); - goto out; - } - -if (jbd2_journal_start_commit (osb->journal->j_journal, &target)) { -jbd2_log_wait_commit (osb->journal->j_journal, target); - ret =1 ; - } -out: -return ret; -} - static int ocfs2_get_truncate_log_info(struct ocfs2_super *osb, int slot_num, struct inode **tl_inode,fs/ocfs2/alloc.h +0
-2@@ -188,8 +188,6 @@ int ocfs2_truncate_log_append(struct ocfs2_super *osb,u64 start_blk, unsigned int num_clusters);int __ocfs2_flush_truncate_log (struct ocfs2_super *osb); -int ocfs2_try_to_free_truncate_log (struct ocfs2_super *osb, -unsigned int needed);/* * Process local structure which describes the block unlinks done fs/ocfs2/aops.c +0
-0
fs/ocfs2/aops.c int ocfs2_write_begin_nolock(struct address_space *mapping, · line 1897
Quoted 7 lines
fs/ocfs2/aops.c +2
-2@@ -1893,8 +1893,8 @@ int ocfs2_write_begin_nolock(struct address_space *mapping,*/ try_free =0 ; - ret1 =ocfs2_try_to_free_truncate_log (osb, clusters_need); -if (ret1 ==1 ) + ret1 =ocfs2_flush_truncate_log (osb); +if (!ret1)
clusters_need is no longer used, so please clean it up as well.
Thanks,
Joseph
Quoted 42 lines
fs/ocfs2/aops.c +0
-0goto try_again;if (ret1 <0 )fs/ocfs2/ocfs2.h +0
-5@@ -440,11 +440,6 @@ struct ocfs2_superstruct buffer_head *osb_tl_bh;struct delayed_work osb_truncate_log_wq;atomic_t osb_tl_disable; -/* -* How many clusters in our truncate log. -* It must be protected by osb_tl_inode->i_mutex. -*/ -unsigned int truncated_clusters;struct ocfs2_node_map osb_recovering_orphan_dirs;unsigned int *osb_orphan_wipes;fs/ocfs2/suballoc.c +3
-3@@ -1187,14 +1187,14 @@ static int ocfs2_reserve_clusters_with_limit(struct ocfs2_super *osb,if (status == -ENOSPC ) { retry: status =ocfs2_reserve_cluster_bitmap_bits (osb, *ac); -/* Retry if there is sufficient space cached in truncate log */ +/* Retry after flush truncate log */ if (status == -ENOSPC && !retried) { retried =1 ;ocfs2_inode_unlock ((*ac)->ac_inode,1 );inode_unlock ((*ac)->ac_inode); - ret =ocfs2_try_to_free_truncate_log (osb, bits_wanted); -if (ret ==1 ) { + ret =ocfs2_flush_truncate_log (osb); +if (!ret) {iput ((*ac)->ac_inode); (*ac)->ac_inode =NULL ; goto retry;
PATCH 1/1 ocfs2: flush truncate log when main bitmap run out of space
Quoted 22 lines · Joseph Qi
Overall, this patch makes senses to me.
But it still have something to improve. Please see my comments below.Quoted 6 lines · Jia Guo
Truncate log problem has been described in
commit50308d813bf2("ocfs2: Try to free truncate log when meeting ENOSPC in write.") and
commit2070ad1aebff("ocfs2: retry on ENOSPC if sufficient space in truncate log"), but the following situations cannot be solved:Quoted 12 lines
case 1:
Main bitmap has been updated, but the transaction of the deleted blocks has not been committed completely. In this case, functionocfs2_reserve_cluster_bitmap_bits()returns success while function__ocfs2_claim_clustersreturn ENOSPC because we cannot reuse the deleted blocks before the transactioncommitted(test by function ocfs2_test_bg_bit_allocatable()). One can reproduce this by following steps:
a, prepare a file which size is 50G, and volume still have 30G free space b, open the file withO_TRUNCflag
c, sleep 5 seconds
e, fallocate a 50G file.
Quoted 1 line
What's the step d?
It is a mistake, only 4 steps and fallocate will fail. Fix it in patch v2. :) :)
Quoted 117 lines
Quoted 117 lines
case 2:
Main bitmap doesn't have enough free bits, so does truncate log. But main bitmap plus truncate log has enough free bits. We are not gonging to try flush truncate log in this case, which is not reasonable.So force commit the transaction when flushing the truncate log for case 1. For case 2, the value of
osb->truncated_clustersdoesn't seem to make sense, do the flush whenever we run out of space seems to be more reasonable.Signed-off-by Jia Guo <[email protected]>--- fs/ocfs2/alloc.c | 43 +++---------------------------------------- fs/ocfs2/alloc.h | 2 -- fs/ocfs2/aops.c | 4 ++-- fs/ocfs2/ocfs2.h | 5 ----- fs/ocfs2/suballoc.c | 6 +++--- 5 files changed, 8 insertions(+), 52 deletions(-)fs/ocfs2/alloc.c +3
-40@@ -5921,7 +5921,6 @@ int ocfs2_truncate_log_append(struct ocfs2_super *osb,ocfs2_journal_dirty (handle, tl_bh); - osb->truncated_clusters += num_clusters; bail:return status; }@@ -5940,6 +5939,7 @@ static int ocfs2_replay_truncate_records(struct ocfs2_super *osb,struct inode *tl_inode = osb->osb_tl_inode;struct buffer_head *tl_bh = osb->osb_tl_bh;handle_t *handle; +tid_t target; di = (struct ocfs2_dinode *) tl_bh->b_data; tl = &di->id2.i_dealloc;@@ -5989,8 +5989,8 @@ static int ocfs2_replay_truncate_records(struct ocfs2_super *osb,ocfs2_commit_trans (osb, handle); i--; } - - osb->truncated_clusters =0 ; +if (jbd2_journal_start_commit (osb->journal->j_journal, &target)) +jbd2_log_wait_commit (osb->journal->j_journal, target); bail:return status;@@ -6102,43 +6102,6 @@ void ocfs2_schedule_truncate_log_flush(struct ocfs2_super *osb, } } -/* -* Try to flush truncate logs if we can free enough clusters from it. -* As for return value, "< 0" means error, "0" no space and "1" means -* we have freed enough spaces and let the caller try to allocate again. -*/ -int ocfs2_try_to_free_truncate_log (struct ocfs2_super *osb, -unsigned int needed) -{ -tid_t target; -int ret =0 ; -unsigned int truncated_clusters; - -inode_lock (osb->osb_tl_inode); - truncated_clusters = osb->truncated_clusters; -inode_unlock (osb->osb_tl_inode); - -/* -* Check whether we can succeed in allocating if we free -* the truncate log. -*/ -if (truncated_clusters < needed) - goto out; - - ret =ocfs2_flush_truncate_log (osb); -if (ret) { -mlog_errno (ret); - goto out; - } - -if (jbd2_journal_start_commit (osb->journal->j_journal, &target)) { -jbd2_log_wait_commit (osb->journal->j_journal, target); - ret =1 ; - } -out: -return ret; -} - static int ocfs2_get_truncate_log_info(struct ocfs2_super *osb, int slot_num, struct inode **tl_inode,fs/ocfs2/alloc.h +0
-2@@ -188,8 +188,6 @@ int ocfs2_truncate_log_append(struct ocfs2_super *osb,u64 start_blk, unsigned int num_clusters);int __ocfs2_flush_truncate_log (struct ocfs2_super *osb); -int ocfs2_try_to_free_truncate_log (struct ocfs2_super *osb, -unsigned int needed);/* * Process local structure which describes the block unlinks done fs/ocfs2/aops.c +0
-0@@ -1893,8 +1893,8 @@ int ocfs2_write_begin_nolock(struct address_space *mapping,*/ try_free =0 ;
Quoted 9 lines
fs/ocfs2/aops.c int ocfs2_write_begin_nolock(struct address_space *mapping, · line 1897
Quoted 4 lines
fs/ocfs2/aops.c +2
-2- ret1 =ocfs2_try_to_free_truncate_log (osb, clusters_need); -if (ret1 ==1 ) + ret1 =ocfs2_flush_truncate_log (osb); +if (!ret1)
clusters_needis no longer used, so please clean it up as well.Thanks,
Joseph
OK, I will fix it in patch v2.
Thanks,
Jia
Quoted 44 lines
Quoted 32 lines
fs/ocfs2/aops.c +0
-0goto try_again;if (ret1 <0 )fs/ocfs2/ocfs2.h +0
-5@@ -440,11 +440,6 @@ struct ocfs2_superstruct buffer_head *osb_tl_bh;struct delayed_work osb_truncate_log_wq;atomic_t osb_tl_disable; -/* -* How many clusters in our truncate log. -* It must be protected by osb_tl_inode->i_mutex. -*/ -unsigned int truncated_clusters;struct ocfs2_node_map osb_recovering_orphan_dirs;unsigned int *osb_orphan_wipes;fs/ocfs2/suballoc.c +1
-1@@ -1187,14 +1187,14 @@ static int ocfs2_reserve_clusters_with_limit(struct ocfs2_super *osb,if (status == -ENOSPC ) { retry: status =ocfs2_reserve_cluster_bitmap_bits (osb, *ac); -/* Retry if there is sufficient space cached in truncate log */ +/* Retry after flush truncate log */ if (status == -ENOSPC && !retried) { retried =1 ;fs/ocfs2/suballoc.c static int ocfs2_reserve_clusters_with_limit(struct ocfs2_super *osb, · line 1200
Quoted 10 lines
fs/ocfs2/suballoc.c +2
-2ocfs2_inode_unlock ((*ac)->ac_inode,1 );inode_unlock ((*ac)->ac_inode); - ret =ocfs2_try_to_free_truncate_log (osb, bits_wanted); -if (ret ==1 ) { + ret =ocfs2_flush_truncate_log (osb); +if (!ret) {iput ((*ac)->ac_inode); (*ac)->ac_inode =NULL ; goto retry;.