linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
To: linux-kernel@vger.kernel.org, stable@vger.kernel.org
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	alan@lxorguk.ukuu.org.uk, Josef Bacik <jbacik@fusionio.com>,
	Jan Kara <jack@suse.cz>
Subject: [ 31/37] jbd: Fix assertion failure in commit code due to lacking transaction credits
Date: Thu, 18 Oct 2012 20:16:54 -0700	[thread overview]
Message-ID: <20121019031307.852350678@linuxfoundation.org> (raw)
In-Reply-To: <20121019031302.789593147@linuxfoundation.org>

3.0-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Jan Kara <jack@suse.cz>

commit 09e05d4805e6c524c1af74e524e5d0528bb3fef3 upstream.

ext3 users of data=journal mode with blocksize < pagesize were occasionally
hitting assertion failure in journal_commit_transaction() checking whether the
transaction has at least as many credits reserved as buffers attached.  The
core of the problem is that when a file gets truncated, buffers that still need
checkpointing or that are attached to the committing transaction are left with
buffer_mapped set. When this happens to buffers beyond i_size attached to a
page stradding i_size, subsequent write extending the file will see these
buffers and as they are mapped (but underlying blocks were freed) things go
awry from here.

The assertion failure just coincidentally (and in this case luckily as we would
start corrupting filesystem) triggers due to journal_head not being properly
cleaned up as well.

Under some rare circumstances this bug could even hit data=ordered mode users.
There the assertion won't trigger and we would end up corrupting the
filesystem.

We fix the problem by unmapping buffers if possible (in lots of cases we just
need a buffer attached to a transaction as a place holder but it must not be
written out anyway). And in one case, we just have to bite the bullet and wait
for transaction commit to finish.

Reviewed-by: Josef Bacik <jbacik@fusionio.com>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

---
 fs/jbd/commit.c      |   45 +++++++++++++++++++++++++++--------
 fs/jbd/transaction.c |   64 +++++++++++++++++++++++++++++++++++----------------
 2 files changed, 78 insertions(+), 31 deletions(-)

--- a/fs/jbd/commit.c
+++ b/fs/jbd/commit.c
@@ -85,7 +85,12 @@ nope:
 static void release_data_buffer(struct buffer_head *bh)
 {
 	if (buffer_freed(bh)) {
+		WARN_ON_ONCE(buffer_dirty(bh));
 		clear_buffer_freed(bh);
+		clear_buffer_mapped(bh);
+		clear_buffer_new(bh);
+		clear_buffer_req(bh);
+		bh->b_bdev = NULL;
 		release_buffer_page(bh);
 	} else
 		put_bh(bh);
@@ -840,17 +845,35 @@ restart_loop:
 		 * there's no point in keeping a checkpoint record for
 		 * it. */
 
-		/* A buffer which has been freed while still being
-		 * journaled by a previous transaction may end up still
-		 * being dirty here, but we want to avoid writing back
-		 * that buffer in the future after the "add to orphan"
-		 * operation been committed,  That's not only a performance
-		 * gain, it also stops aliasing problems if the buffer is
-		 * left behind for writeback and gets reallocated for another
-		 * use in a different page. */
-		if (buffer_freed(bh) && !jh->b_next_transaction) {
-			clear_buffer_freed(bh);
-			clear_buffer_jbddirty(bh);
+		/*
+		 * A buffer which has been freed while still being journaled by
+		 * a previous transaction.
+		 */
+		if (buffer_freed(bh)) {
+			/*
+			 * If the running transaction is the one containing
+			 * "add to orphan" operation (b_next_transaction !=
+			 * NULL), we have to wait for that transaction to
+			 * commit before we can really get rid of the buffer.
+			 * So just clear b_modified to not confuse transaction
+			 * credit accounting and refile the buffer to
+			 * BJ_Forget of the running transaction. If the just
+			 * committed transaction contains "add to orphan"
+			 * operation, we can completely invalidate the buffer
+			 * now. We are rather throughout in that since the
+			 * buffer may be still accessible when blocksize <
+			 * pagesize and it is attached to the last partial
+			 * page.
+			 */
+			jh->b_modified = 0;
+			if (!jh->b_next_transaction) {
+				clear_buffer_freed(bh);
+				clear_buffer_jbddirty(bh);
+				clear_buffer_mapped(bh);
+				clear_buffer_new(bh);
+				clear_buffer_req(bh);
+				bh->b_bdev = NULL;
+			}
 		}
 
 		if (buffer_jbddirty(bh)) {
--- a/fs/jbd/transaction.c
+++ b/fs/jbd/transaction.c
@@ -1837,15 +1837,16 @@ static int __dispose_buffer(struct journ
  * We're outside-transaction here.  Either or both of j_running_transaction
  * and j_committing_transaction may be NULL.
  */
-static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh)
+static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh,
+				int partial_page)
 {
 	transaction_t *transaction;
 	struct journal_head *jh;
 	int may_free = 1;
-	int ret;
 
 	BUFFER_TRACE(bh, "entry");
 
+retry:
 	/*
 	 * It is safe to proceed here without the j_list_lock because the
 	 * buffers cannot be stolen by try_to_free_buffers as long as we are
@@ -1873,10 +1874,18 @@ static int journal_unmap_buffer(journal_
 	 * clear the buffer dirty bit at latest at the moment when the
 	 * transaction marking the buffer as freed in the filesystem
 	 * structures is committed because from that moment on the
-	 * buffer can be reallocated and used by a different page.
+	 * block can be reallocated and used by a different page.
 	 * Since the block hasn't been freed yet but the inode has
 	 * already been added to orphan list, it is safe for us to add
 	 * the buffer to BJ_Forget list of the newest transaction.
+	 *
+	 * Also we have to clear buffer_mapped flag of a truncated buffer
+	 * because the buffer_head may be attached to the page straddling
+	 * i_size (can happen only when blocksize < pagesize) and thus the
+	 * buffer_head can be reused when the file is extended again. So we end
+	 * up keeping around invalidated buffers attached to transactions'
+	 * BJ_Forget list just to stop checkpointing code from cleaning up
+	 * the transaction this buffer was modified in.
 	 */
 	transaction = jh->b_transaction;
 	if (transaction == NULL) {
@@ -1903,13 +1912,9 @@ static int journal_unmap_buffer(journal_
 			 * committed, the buffer won't be needed any
 			 * longer. */
 			JBUFFER_TRACE(jh, "checkpointed: add to BJ_Forget");
-			ret = __dispose_buffer(jh,
+			may_free = __dispose_buffer(jh,
 					journal->j_running_transaction);
-			journal_put_journal_head(jh);
-			spin_unlock(&journal->j_list_lock);
-			jbd_unlock_bh_state(bh);
-			spin_unlock(&journal->j_state_lock);
-			return ret;
+			goto zap_buffer;
 		} else {
 			/* There is no currently-running transaction. So the
 			 * orphan record which we wrote for this file must have
@@ -1917,13 +1922,9 @@ static int journal_unmap_buffer(journal_
 			 * the committing transaction, if it exists. */
 			if (journal->j_committing_transaction) {
 				JBUFFER_TRACE(jh, "give to committing trans");
-				ret = __dispose_buffer(jh,
+				may_free = __dispose_buffer(jh,
 					journal->j_committing_transaction);
-				journal_put_journal_head(jh);
-				spin_unlock(&journal->j_list_lock);
-				jbd_unlock_bh_state(bh);
-				spin_unlock(&journal->j_state_lock);
-				return ret;
+				goto zap_buffer;
 			} else {
 				/* The orphan record's transaction has
 				 * committed.  We can cleanse this buffer */
@@ -1944,10 +1945,24 @@ static int journal_unmap_buffer(journal_
 		}
 		/*
 		 * The buffer is committing, we simply cannot touch
-		 * it. So we just set j_next_transaction to the
-		 * running transaction (if there is one) and mark
-		 * buffer as freed so that commit code knows it should
-		 * clear dirty bits when it is done with the buffer.
+		 * it. If the page is straddling i_size we have to wait
+		 * for commit and try again.
+		 */
+		if (partial_page) {
+			tid_t tid = journal->j_committing_transaction->t_tid;
+
+			journal_put_journal_head(jh);
+			spin_unlock(&journal->j_list_lock);
+			jbd_unlock_bh_state(bh);
+			spin_unlock(&journal->j_state_lock);
+			log_wait_commit(journal, tid);
+			goto retry;
+		}
+		/*
+		 * OK, buffer won't be reachable after truncate. We just set
+		 * j_next_transaction to the running transaction (if there is
+		 * one) and mark buffer as freed so that commit code knows it
+		 * should clear dirty bits when it is done with the buffer.
 		 */
 		set_buffer_freed(bh);
 		if (journal->j_running_transaction && buffer_jbddirty(bh))
@@ -1970,6 +1985,14 @@ static int journal_unmap_buffer(journal_
 	}
 
 zap_buffer:
+	/*
+	 * This is tricky. Although the buffer is truncated, it may be reused
+	 * if blocksize < pagesize and it is attached to the page straddling
+	 * EOF. Since the buffer might have been added to BJ_Forget list of the
+	 * running transaction, journal_get_write_access() won't clear
+	 * b_modified and credit accounting gets confused. So clear b_modified
+	 * here. */
+	jh->b_modified = 0;
 	journal_put_journal_head(jh);
 zap_buffer_no_jh:
 	spin_unlock(&journal->j_list_lock);
@@ -2018,7 +2041,8 @@ void journal_invalidatepage(journal_t *j
 		if (offset <= curr_off) {
 			/* This block is wholly outside the truncation point */
 			lock_buffer(bh);
-			may_free &= journal_unmap_buffer(journal, bh);
+			may_free &= journal_unmap_buffer(journal, bh,
+							 offset > 0);
 			unlock_buffer(bh);
 		}
 		curr_off = next_off;



  parent reply	other threads:[~2012-10-19  3:19 UTC|newest]

Thread overview: 47+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-10-19  3:16 [ 00/37] 3.0.47-stable review Greg Kroah-Hartman
2012-10-19  3:16 ` [ 01/37] ARM: vfp: fix saving d16-d31 vfp registers on v6+ kernels Greg Kroah-Hartman
2012-10-19  3:16 ` [ 02/37] lockd: use rpc clients cl_nodename for id encoding Greg Kroah-Hartman
2012-10-19 23:15   ` Ben Hutchings
2012-10-21 16:26     ` Greg Kroah-Hartman
2012-10-22 17:02       ` Myklebust, Trond
2012-10-23  1:36         ` Ben Hutchings
2012-10-24 15:44         ` Greg Kroah-Hartman
2012-10-19  3:16 ` [ 03/37] ACPI: EC: Make the GPE storm threshold a module parameter Greg Kroah-Hartman
2012-10-19  3:16 ` [ 04/37] ACPI: EC: Add a quirk for CLEVO M720T/M730T laptop Greg Kroah-Hartman
2012-10-19  3:16 ` [ 05/37] mips,kgdb: fix recursive page fault with CONFIG_KPROBES Greg Kroah-Hartman
2012-10-19  3:16 ` [ 06/37] tmpfs,ceph,gfs2,isofs,reiserfs,xfs: fix fh_len checking Greg Kroah-Hartman
2012-10-19  3:16 ` [ 07/37] ARM: 7541/1: Add ARM ERRATA 775420 workaround Greg Kroah-Hartman
2012-10-19  3:16 ` [ 08/37] firewire: cdev: fix user memory corruption (i386 userland on amd64 kernel) Greg Kroah-Hartman
2012-10-19  3:16 ` [ 09/37] SUNRPC: Ensure that the TCP socket is closed when in CLOSE_WAIT Greg Kroah-Hartman
2012-10-19  3:16 ` [ 10/37] xen/bootup: allow {read|write}_cr8 pvops call Greg Kroah-Hartman
2012-10-19  3:16 ` [ 11/37] xen/bootup: allow read_tscp call for Xen PV guests Greg Kroah-Hartman
2012-10-19  3:16 ` [ 12/37] block: fix request_queue->flags initialization Greg Kroah-Hartman
2012-10-19 23:16   ` Ben Hutchings
2012-10-21 16:25     ` Greg Kroah-Hartman
2012-10-21 18:56       ` Tejun Heo
2012-10-22 15:36         ` Greg Kroah-Hartman
2012-10-19  3:16 ` [ 13/37] autofs4 - fix reset pending flag on mount fail Greg Kroah-Hartman
2012-10-19  3:16 ` [ 14/37] module: taint kernel when lve module is loaded Greg Kroah-Hartman
2012-10-19  3:16 ` [ 15/37] video/udlfb: fix line counting in fb_write Greg Kroah-Hartman
2012-10-19  3:16 ` [ 16/37] viafb: dont touch clock state on OLPC XO-1.5 Greg Kroah-Hartman
2012-10-19  3:16 ` [ 17/37] timers: Fix endless looping between cascade() and internal_add_timer() Greg Kroah-Hartman
2012-10-19  3:16 ` [ 18/37] pktgen: fix crash when generating IPv6 packets Greg Kroah-Hartman
2012-10-19  3:16 ` [ 19/37] tg3: Apply short DMA frag workaround to 5906 Greg Kroah-Hartman
2012-10-19  3:16 ` [ 20/37] ipvs: fix oops in ip_vs_dst_event on rmmod Greg Kroah-Hartman
2012-10-19  3:16 ` [ 21/37] netfilter: nf_conntrack: fix racy timer handling with reliable events Greg Kroah-Hartman
2012-10-19  3:16 ` [ 22/37] netfilter: nf_ct_ipv4: packets with wrong ihl are invalid Greg Kroah-Hartman
2012-10-19  3:16 ` [ 23/37] netfilter: nf_nat_sip: fix incorrect handling of EBUSY for RTCP expectation Greg Kroah-Hartman
2012-10-19  3:16 ` [ 24/37] ipvs: fix oops on NAT reply in br_nf context Greg Kroah-Hartman
2012-10-19  3:16 ` [ 25/37] netfilter: nf_nat_sip: fix via header translation with multiple parameters Greg Kroah-Hartman
2012-10-19  3:16 ` [ 26/37] netfilter: nf_ct_expect: fix possible access to uninitialized timer Greg Kroah-Hartman
2012-10-19  3:16 ` [ 27/37] netfilter: limit, hashlimit: avoid duplicated inline Greg Kroah-Hartman
2012-10-19  3:16 ` [ 28/37] netfilter: xt_limit: have r->cost != 0 case work Greg Kroah-Hartman
2012-10-19  3:16 ` [ 29/37] Add CDC-ACM support for the CX93010-2x UCMxx USB Modem Greg Kroah-Hartman
2012-10-19  3:16 ` [ 30/37] drm/radeon: Dont destroy I2C Bus Rec in radeon_ext_tmds_enc_destroy() Greg Kroah-Hartman
2012-10-19  3:16 ` Greg Kroah-Hartman [this message]
2012-10-19  3:16 ` [ 32/37] x86, random: Architectural inlines to get random integers with RDRAND Greg Kroah-Hartman
2012-10-19  3:16 ` [ 33/37] x86, random: Verify RDRAND functionality and allow it to be disabled Greg Kroah-Hartman
2012-10-19  3:16 ` [ 34/37] tpm: Propagate error from tpm_transmit to fix a timeout hang Greg Kroah-Hartman
2012-10-19  3:16 ` [ 35/37] udf: fix retun value on error path in udf_load_logicalvol Greg Kroah-Hartman
2012-10-19  3:16 ` [ 36/37] ALSA: ac97 - Fix missing NULL check in snd_ac97_cvol_new() Greg Kroah-Hartman
2012-10-19  3:17 ` [ 37/37] ALSA: emu10k1: add chip details for E-mu 1010 PCIe card Greg Kroah-Hartman

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20121019031307.852350678@linuxfoundation.org \
    --to=gregkh@linuxfoundation.org \
    --cc=alan@lxorguk.ukuu.org.uk \
    --cc=jack@suse.cz \
    --cc=jbacik@fusionio.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=stable@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).