diff options
Diffstat (limited to 'fs/jbd/commit.c')
| -rw-r--r-- | fs/jbd/commit.c | 182 |
1 files changed, 113 insertions, 69 deletions
diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c index 42da60784311..32a8caf0c41e 100644 --- a/fs/jbd/commit.c +++ b/fs/jbd/commit.c | |||
| @@ -160,6 +160,117 @@ static int journal_write_commit_record(journal_t *journal, | |||
| 160 | return (ret == -EIO); | 160 | return (ret == -EIO); |
| 161 | } | 161 | } |
| 162 | 162 | ||
| 163 | static void journal_do_submit_data(struct buffer_head **wbuf, int bufs) | ||
| 164 | { | ||
| 165 | int i; | ||
| 166 | |||
| 167 | for (i = 0; i < bufs; i++) { | ||
| 168 | wbuf[i]->b_end_io = end_buffer_write_sync; | ||
| 169 | /* We use-up our safety reference in submit_bh() */ | ||
| 170 | submit_bh(WRITE, wbuf[i]); | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | /* | ||
| 175 | * Submit all the data buffers to disk | ||
| 176 | */ | ||
| 177 | static void journal_submit_data_buffers(journal_t *journal, | ||
| 178 | transaction_t *commit_transaction) | ||
| 179 | { | ||
| 180 | struct journal_head *jh; | ||
| 181 | struct buffer_head *bh; | ||
| 182 | int locked; | ||
| 183 | int bufs = 0; | ||
| 184 | struct buffer_head **wbuf = journal->j_wbuf; | ||
| 185 | |||
| 186 | /* | ||
| 187 | * Whenever we unlock the journal and sleep, things can get added | ||
| 188 | * onto ->t_sync_datalist, so we have to keep looping back to | ||
| 189 | * write_out_data until we *know* that the list is empty. | ||
| 190 | * | ||
| 191 | * Cleanup any flushed data buffers from the data list. Even in | ||
| 192 | * abort mode, we want to flush this out as soon as possible. | ||
| 193 | */ | ||
| 194 | write_out_data: | ||
| 195 | cond_resched(); | ||
| 196 | spin_lock(&journal->j_list_lock); | ||
| 197 | |||
| 198 | while (commit_transaction->t_sync_datalist) { | ||
| 199 | jh = commit_transaction->t_sync_datalist; | ||
| 200 | bh = jh2bh(jh); | ||
| 201 | locked = 0; | ||
| 202 | |||
| 203 | /* Get reference just to make sure buffer does not disappear | ||
| 204 | * when we are forced to drop various locks */ | ||
| 205 | get_bh(bh); | ||
| 206 | /* If the buffer is dirty, we need to submit IO and hence | ||
| 207 | * we need the buffer lock. We try to lock the buffer without | ||
| 208 | * blocking. If we fail, we need to drop j_list_lock and do | ||
| 209 | * blocking lock_buffer(). | ||
| 210 | */ | ||
| 211 | if (buffer_dirty(bh)) { | ||
| 212 | if (test_set_buffer_locked(bh)) { | ||
| 213 | BUFFER_TRACE(bh, "needs blocking lock"); | ||
| 214 | spin_unlock(&journal->j_list_lock); | ||
| 215 | /* Write out all data to prevent deadlocks */ | ||
| 216 | journal_do_submit_data(wbuf, bufs); | ||
| 217 | bufs = 0; | ||
| 218 | lock_buffer(bh); | ||
| 219 | spin_lock(&journal->j_list_lock); | ||
| 220 | } | ||
| 221 | locked = 1; | ||
| 222 | } | ||
| 223 | /* We have to get bh_state lock. Again out of order, sigh. */ | ||
| 224 | if (!inverted_lock(journal, bh)) { | ||
| 225 | jbd_lock_bh_state(bh); | ||
| 226 | spin_lock(&journal->j_list_lock); | ||
| 227 | } | ||
| 228 | /* Someone already cleaned up the buffer? */ | ||
| 229 | if (!buffer_jbd(bh) | ||
| 230 | || jh->b_transaction != commit_transaction | ||
| 231 | || jh->b_jlist != BJ_SyncData) { | ||
| 232 | jbd_unlock_bh_state(bh); | ||
| 233 | if (locked) | ||
| 234 | unlock_buffer(bh); | ||
| 235 | BUFFER_TRACE(bh, "already cleaned up"); | ||
| 236 | put_bh(bh); | ||
| 237 | continue; | ||
| 238 | } | ||
| 239 | if (locked && test_clear_buffer_dirty(bh)) { | ||
| 240 | BUFFER_TRACE(bh, "needs writeout, adding to array"); | ||
| 241 | wbuf[bufs++] = bh; | ||
| 242 | __journal_file_buffer(jh, commit_transaction, | ||
| 243 | BJ_Locked); | ||
| 244 | jbd_unlock_bh_state(bh); | ||
| 245 | if (bufs == journal->j_wbufsize) { | ||
| 246 | spin_unlock(&journal->j_list_lock); | ||
| 247 | journal_do_submit_data(wbuf, bufs); | ||
| 248 | bufs = 0; | ||
| 249 | goto write_out_data; | ||
| 250 | } | ||
| 251 | } | ||
| 252 | else { | ||
| 253 | BUFFER_TRACE(bh, "writeout complete: unfile"); | ||
| 254 | __journal_unfile_buffer(jh); | ||
| 255 | jbd_unlock_bh_state(bh); | ||
| 256 | if (locked) | ||
| 257 | unlock_buffer(bh); | ||
| 258 | journal_remove_journal_head(bh); | ||
| 259 | /* Once for our safety reference, once for | ||
| 260 | * journal_remove_journal_head() */ | ||
| 261 | put_bh(bh); | ||
| 262 | put_bh(bh); | ||
| 263 | } | ||
| 264 | |||
| 265 | if (lock_need_resched(&journal->j_list_lock)) { | ||
| 266 | spin_unlock(&journal->j_list_lock); | ||
| 267 | goto write_out_data; | ||
| 268 | } | ||
| 269 | } | ||
| 270 | spin_unlock(&journal->j_list_lock); | ||
| 271 | journal_do_submit_data(wbuf, bufs); | ||
| 272 | } | ||
| 273 | |||
| 163 | /* | 274 | /* |
| 164 | * journal_commit_transaction | 275 | * journal_commit_transaction |
| 165 | * | 276 | * |
| @@ -313,80 +424,13 @@ void journal_commit_transaction(journal_t *journal) | |||
| 313 | * Now start flushing things to disk, in the order they appear | 424 | * Now start flushing things to disk, in the order they appear |
| 314 | * on the transaction lists. Data blocks go first. | 425 | * on the transaction lists. Data blocks go first. |
| 315 | */ | 426 | */ |
| 316 | |||
| 317 | err = 0; | 427 | err = 0; |
| 318 | /* | 428 | journal_submit_data_buffers(journal, commit_transaction); |
| 319 | * Whenever we unlock the journal and sleep, things can get added | ||
| 320 | * onto ->t_sync_datalist, so we have to keep looping back to | ||
| 321 | * write_out_data until we *know* that the list is empty. | ||
| 322 | */ | ||
| 323 | bufs = 0; | ||
| 324 | /* | ||
| 325 | * Cleanup any flushed data buffers from the data list. Even in | ||
| 326 | * abort mode, we want to flush this out as soon as possible. | ||
| 327 | */ | ||
| 328 | write_out_data: | ||
| 329 | cond_resched(); | ||
| 330 | spin_lock(&journal->j_list_lock); | ||
| 331 | |||
| 332 | while (commit_transaction->t_sync_datalist) { | ||
| 333 | struct buffer_head *bh; | ||
| 334 | |||
| 335 | jh = commit_transaction->t_sync_datalist; | ||
| 336 | commit_transaction->t_sync_datalist = jh->b_tnext; | ||
| 337 | bh = jh2bh(jh); | ||
| 338 | if (buffer_locked(bh)) { | ||
| 339 | BUFFER_TRACE(bh, "locked"); | ||
| 340 | if (!inverted_lock(journal, bh)) | ||
| 341 | goto write_out_data; | ||
| 342 | __journal_temp_unlink_buffer(jh); | ||
| 343 | __journal_file_buffer(jh, commit_transaction, | ||
| 344 | BJ_Locked); | ||
| 345 | jbd_unlock_bh_state(bh); | ||
| 346 | if (lock_need_resched(&journal->j_list_lock)) { | ||
| 347 | spin_unlock(&journal->j_list_lock); | ||
| 348 | goto write_out_data; | ||
| 349 | } | ||
| 350 | } else { | ||
| 351 | if (buffer_dirty(bh)) { | ||
| 352 | BUFFER_TRACE(bh, "start journal writeout"); | ||
| 353 | get_bh(bh); | ||
| 354 | wbuf[bufs++] = bh; | ||
| 355 | if (bufs == journal->j_wbufsize) { | ||
| 356 | jbd_debug(2, "submit %d writes\n", | ||
| 357 | bufs); | ||
| 358 | spin_unlock(&journal->j_list_lock); | ||
| 359 | ll_rw_block(SWRITE, bufs, wbuf); | ||
| 360 | journal_brelse_array(wbuf, bufs); | ||
| 361 | bufs = 0; | ||
| 362 | goto write_out_data; | ||
| 363 | } | ||
| 364 | } else { | ||
| 365 | BUFFER_TRACE(bh, "writeout complete: unfile"); | ||
| 366 | if (!inverted_lock(journal, bh)) | ||
| 367 | goto write_out_data; | ||
| 368 | __journal_unfile_buffer(jh); | ||
| 369 | jbd_unlock_bh_state(bh); | ||
| 370 | journal_remove_journal_head(bh); | ||
| 371 | put_bh(bh); | ||
| 372 | if (lock_need_resched(&journal->j_list_lock)) { | ||
| 373 | spin_unlock(&journal->j_list_lock); | ||
| 374 | goto write_out_data; | ||
| 375 | } | ||
| 376 | } | ||
| 377 | } | ||
| 378 | } | ||
| 379 | |||
| 380 | if (bufs) { | ||
| 381 | spin_unlock(&journal->j_list_lock); | ||
| 382 | ll_rw_block(SWRITE, bufs, wbuf); | ||
| 383 | journal_brelse_array(wbuf, bufs); | ||
| 384 | spin_lock(&journal->j_list_lock); | ||
| 385 | } | ||
| 386 | 429 | ||
| 387 | /* | 430 | /* |
| 388 | * Wait for all previously submitted IO to complete. | 431 | * Wait for all previously submitted IO to complete. |
| 389 | */ | 432 | */ |
| 433 | spin_lock(&journal->j_list_lock); | ||
| 390 | while (commit_transaction->t_locked_list) { | 434 | while (commit_transaction->t_locked_list) { |
| 391 | struct buffer_head *bh; | 435 | struct buffer_head *bh; |
| 392 | 436 | ||
