diff options
author | Jan Kara <jack@suse.cz> | 2008-07-11 19:27:31 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2008-07-11 19:27:31 -0400 |
commit | c851ed540173736e60d48b53b91a16ea5c903896 (patch) | |
tree | 828fe0d71b7f18dc170090dbb2fb5ac9deae4ee0 /fs/jbd2/transaction.c | |
parent | f4c0a0fdfae708f7aa438c27a380ed4071294e11 (diff) |
jbd2: Implement data=ordered mode handling via inodes
This patch adds necessary framework into JBD2 to be able to track inodes
with each transaction and write-out their dirty data during transaction
commit time.
This new ordered mode brings all sorts of advantages such as possibility
to get rid of journal heads and buffer heads for data buffers in ordered
mode, better ordering of writes on transaction commit, simplification of
some JBD code, no more anonymous pages when truncate of data being
committed happens. Also with this new ordered mode, delayed allocation
on ordered mode is much simpler.
Signed-off-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs/jbd2/transaction.c')
-rw-r--r-- | fs/jbd2/transaction.c | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index ba620c4493d2..98b596d23705 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c | |||
@@ -51,6 +51,7 @@ jbd2_get_transaction(journal_t *journal, transaction_t *transaction) | |||
51 | transaction->t_tid = journal->j_transaction_sequence++; | 51 | transaction->t_tid = journal->j_transaction_sequence++; |
52 | transaction->t_expires = jiffies + journal->j_commit_interval; | 52 | transaction->t_expires = jiffies + journal->j_commit_interval; |
53 | spin_lock_init(&transaction->t_handle_lock); | 53 | spin_lock_init(&transaction->t_handle_lock); |
54 | INIT_LIST_HEAD(&transaction->t_inode_list); | ||
54 | 55 | ||
55 | /* Set up the commit timer for the new transaction. */ | 56 | /* Set up the commit timer for the new transaction. */ |
56 | journal->j_commit_timer.expires = round_jiffies(transaction->t_expires); | 57 | journal->j_commit_timer.expires = round_jiffies(transaction->t_expires); |
@@ -2195,3 +2196,88 @@ void jbd2_journal_refile_buffer(journal_t *journal, struct journal_head *jh) | |||
2195 | spin_unlock(&journal->j_list_lock); | 2196 | spin_unlock(&journal->j_list_lock); |
2196 | __brelse(bh); | 2197 | __brelse(bh); |
2197 | } | 2198 | } |
2199 | |||
2200 | /* | ||
2201 | * File inode in the inode list of the handle's transaction | ||
2202 | */ | ||
2203 | int jbd2_journal_file_inode(handle_t *handle, struct jbd2_inode *jinode) | ||
2204 | { | ||
2205 | transaction_t *transaction = handle->h_transaction; | ||
2206 | journal_t *journal = transaction->t_journal; | ||
2207 | |||
2208 | if (is_handle_aborted(handle)) | ||
2209 | return -EIO; | ||
2210 | |||
2211 | jbd_debug(4, "Adding inode %lu, tid:%d\n", jinode->i_vfs_inode->i_ino, | ||
2212 | transaction->t_tid); | ||
2213 | |||
2214 | /* | ||
2215 | * First check whether inode isn't already on the transaction's | ||
2216 | * lists without taking the lock. Note that this check is safe | ||
2217 | * without the lock as we cannot race with somebody removing inode | ||
2218 | * from the transaction. The reason is that we remove inode from the | ||
2219 | * transaction only in journal_release_jbd_inode() and when we commit | ||
2220 | * the transaction. We are guarded from the first case by holding | ||
2221 | * a reference to the inode. We are safe against the second case | ||
2222 | * because if jinode->i_transaction == transaction, commit code | ||
2223 | * cannot touch the transaction because we hold reference to it, | ||
2224 | * and if jinode->i_next_transaction == transaction, commit code | ||
2225 | * will only file the inode where we want it. | ||
2226 | */ | ||
2227 | if (jinode->i_transaction == transaction || | ||
2228 | jinode->i_next_transaction == transaction) | ||
2229 | return 0; | ||
2230 | |||
2231 | spin_lock(&journal->j_list_lock); | ||
2232 | |||
2233 | if (jinode->i_transaction == transaction || | ||
2234 | jinode->i_next_transaction == transaction) | ||
2235 | goto done; | ||
2236 | |||
2237 | /* On some different transaction's list - should be | ||
2238 | * the committing one */ | ||
2239 | if (jinode->i_transaction) { | ||
2240 | J_ASSERT(jinode->i_next_transaction == NULL); | ||
2241 | J_ASSERT(jinode->i_transaction == | ||
2242 | journal->j_committing_transaction); | ||
2243 | jinode->i_next_transaction = transaction; | ||
2244 | goto done; | ||
2245 | } | ||
2246 | /* Not on any transaction list... */ | ||
2247 | J_ASSERT(!jinode->i_next_transaction); | ||
2248 | jinode->i_transaction = transaction; | ||
2249 | list_add(&jinode->i_list, &transaction->t_inode_list); | ||
2250 | done: | ||
2251 | spin_unlock(&journal->j_list_lock); | ||
2252 | |||
2253 | return 0; | ||
2254 | } | ||
2255 | |||
2256 | /* | ||
2257 | * This function must be called when inode is journaled in ordered mode | ||
2258 | * before truncation happens. It starts writeout of truncated part in | ||
2259 | * case it is in the committing transaction so that we stand to ordered | ||
2260 | * mode consistency guarantees. | ||
2261 | */ | ||
2262 | int jbd2_journal_begin_ordered_truncate(struct jbd2_inode *inode, | ||
2263 | loff_t new_size) | ||
2264 | { | ||
2265 | journal_t *journal; | ||
2266 | transaction_t *commit_trans; | ||
2267 | int ret = 0; | ||
2268 | |||
2269 | if (!inode->i_transaction && !inode->i_next_transaction) | ||
2270 | goto out; | ||
2271 | journal = inode->i_transaction->t_journal; | ||
2272 | spin_lock(&journal->j_state_lock); | ||
2273 | commit_trans = journal->j_committing_transaction; | ||
2274 | spin_unlock(&journal->j_state_lock); | ||
2275 | if (inode->i_transaction == commit_trans) { | ||
2276 | ret = filemap_fdatawrite_range(inode->i_vfs_inode->i_mapping, | ||
2277 | new_size, LLONG_MAX); | ||
2278 | if (ret) | ||
2279 | jbd2_journal_abort(journal, ret); | ||
2280 | } | ||
2281 | out: | ||
2282 | return ret; | ||
2283 | } | ||