mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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)
 | 
					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
 | 
							 * We want a new commit: OK, mark the request and wakeup the
 | 
				
			||||||
		 * commit thread.  We do _not_ do the commit ourselves.
 | 
							 * 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);
 | 
								  journal->j_commit_sequence);
 | 
				
			||||||
		wake_up(&journal->j_wait_commit);
 | 
							wake_up(&journal->j_wait_commit);
 | 
				
			||||||
		return 1;
 | 
							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;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue