forked from mirrors/linux
		
	jbd2: fix fsync() tid wraparound bug
If an application program does not make any changes to the indirect blocks or extent tree, i_datasync_tid will not get updated. If there are enough commits (i.e., 2**31) such that tid_geq()'s calculations wrap, and there isn't a currently active transaction at the time of the fdatasync() call, this can end up triggering a BUG_ON in fs/jbd2/commit.c: J_ASSERT(journal->j_running_transaction != NULL); It's pretty rare that this can happen, since it requires the use of fdatasync() plus *very* frequent and excessive use of fsync(). But with the right workload, it can. We fix this by replacing the use of tid_geq() with an equality test, since there's only one valid transaction id that we is valid for us to wait until it is commited: namely, the currently running transaction (if it exists). Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
This commit is contained in:
		
							parent
							
								
									59802db074
								
							
						
					
					
						commit
						deeeaf13b2
					
				
					 1 changed files with 13 additions and 3 deletions
				
			
		|  | @ -479,9 +479,12 @@ int __jbd2_log_space_left(journal_t *journal) | |||
| int __jbd2_log_start_commit(journal_t *journal, tid_t target) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * Are we already doing a recent enough commit? | ||||
| 	 * The only transaction we can possibly wait upon is the | ||||
| 	 * currently running transaction (if it exists).  Otherwise, | ||||
| 	 * the target tid must be an old one. | ||||
| 	 */ | ||||
| 	if (!tid_geq(journal->j_commit_request, target)) { | ||||
| 	if (journal->j_running_transaction && | ||||
| 	    journal->j_running_transaction->t_tid == target) { | ||||
| 		/*
 | ||||
| 		 * We want a new commit: OK, mark the request and wakeup the | ||||
| 		 * commit thread.  We do _not_ do the commit ourselves. | ||||
|  | @ -493,7 +496,14 @@ int __jbd2_log_start_commit(journal_t *journal, tid_t target) | |||
| 			  journal->j_commit_sequence); | ||||
| 		wake_up(&journal->j_wait_commit); | ||||
| 		return 1; | ||||
| 	} | ||||
| 	} else if (!tid_geq(journal->j_commit_request, target)) | ||||
| 		/* This should never happen, but if it does, preserve
 | ||||
| 		   the evidence before kjournald goes into a loop and | ||||
| 		   increments j_commit_sequence beyond all recognition. */ | ||||
| 		WARN(1, "jbd: bad log_start_commit: %u %u %u %u\n", | ||||
| 		     journal->j_commit_request, journal->j_commit_sequence, | ||||
| 		     target, journal->j_running_transaction ?  | ||||
| 		     journal->j_running_transaction->t_tid : 0); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Theodore Ts'o
						Theodore Ts'o