Received: by 2002:a6b:fb09:0:0:0:0:0 with SMTP id h9csp588116iog; Mon, 13 Jun 2022 08:40:28 -0700 (PDT) X-Google-Smtp-Source: AGRyM1su7ejI/S6BWwT67qP64tpR10ZlI+DKvOXR3Jr5nxnMLG1OF1R6vtKG3eZnKhlGmfqfjwlp X-Received: by 2002:a17:907:60d4:b0:708:850:bc91 with SMTP id hv20-20020a17090760d400b007080850bc91mr436268ejc.102.1655134828243; Mon, 13 Jun 2022 08:40:28 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1655134828; cv=none; d=google.com; s=arc-20160816; b=WcdvTr47B7cDHmQWyG8idUoY8/MfVBQFP+P8DIC12sCflX1K00uHp/4abhqFhrWqZ8 cgW3ES6W+CH1kJiATO1EqaaRcTLTre6Hh2gRldretuQ+0JvDeohYfrW7qAYa3aI1UYkX 1YDPybA7ckEfzZjwJA3ruuyOu04eFGFKIxd24l2EAw9al3weZDCKXBdW2go4s1s/yYlY I5BRJXYNTeAqvHmrUb0AuNlH2bP+eBX33o1nA3Ago0Bo8Iprw/sCCDCcdGrxHQQE1O9J W8hEhZIGFlcCgmKp2gHC4nFO0k1ZRBkb48IRPXsdhZGoCs2HauCJM6s902yORyVJnUhH sdmw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=9AP9+ftd0ThDiAyYv6V2k3i4Qhiz1j21TQB7DJI0ux8=; b=scJNAw/LE9u+ZnfOvn1iCp+icdjIMCezyoUlGP3P0MSlY4nu6fOjX4NLlL/lMpPnTP tJkv5Kl7LJXk5QDk4alsjx2FqO8kJfWVqnUGZJhNR+k4yhqxbnG/9vpqX3LjWR6Udk6j s7C0+xE26rsCpWMlFQVclM7Zc/SJ/gQSO4eyYWVqWMisL5hGAzhYvEzE3JDUydNEWh3l UWywgGQ2h/9Z1g5PEXJn+zrngsNcYHyXyJjmUs9D0nRiSCSyZ1Q1DkJL6Dm9w/ksORaV i5+GUeoIPbIB3q5SbST301l/FECFcGUFMXjzweqUMnrOqVXhsC1WuBXqtF44f0L9cG+Z CMhA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linuxfoundation.org header.s=korg header.b="OXkx/Cx+"; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id j30-20020a170906535e00b00715785bb696si6056062ejo.447.2022.06.13.08.40.03; Mon, 13 Jun 2022 08:40:28 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@linuxfoundation.org header.s=korg header.b="OXkx/Cx+"; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1376529AbiFMNXV (ORCPT + 99 others); Mon, 13 Jun 2022 09:23:21 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58172 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1377303AbiFMNUO (ORCPT ); Mon, 13 Jun 2022 09:20:14 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 302456A41B; Mon, 13 Jun 2022 04:23:27 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id EC29260B6E; Mon, 13 Jun 2022 11:23:22 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id F3987C34114; Mon, 13 Jun 2022 11:23:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=linuxfoundation.org; s=korg; t=1655119402; bh=XGs5xbRuZEFEYbmbCHJHaN8WX/rs0lVDODu8zAdt2rw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=OXkx/Cx+KQSeVbpoKUKRrPWrnopkATFS6lvhlky6Dy+NK1gl0nbBVdu44ttZCcu6a CdVmrkLpUXk6o4sHT5os1JHa2IkFmoxGwYThyMewZbwSwsiatIkvF38EG1FFudcsLU ZInhK3nXOm1TJyitZZLsUF2JPnY40WSJQhp/3SRI= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Jchao Sun , Jan Kara Subject: [PATCH 5.15 226/247] writeback: Fix inode->i_io_list not be protected by inode->i_lock error Date: Mon, 13 Jun 2022 12:12:08 +0200 Message-Id: <20220613094929.801024476@linuxfoundation.org> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220613094922.843438024@linuxfoundation.org> References: <20220613094922.843438024@linuxfoundation.org> User-Agent: quilt/0.66 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-8.3 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_HI, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Jchao Sun commit 10e14073107dd0b6d97d9516a02845a8e501c2c9 upstream. Commit b35250c0816c ("writeback: Protect inode->i_io_list with inode->i_lock") made inode->i_io_list not only protected by wb->list_lock but also inode->i_lock, but inode_io_list_move_locked() was missed. Add lock there and also update comment describing things protected by inode->i_lock. This also fixes a race where __mark_inode_dirty() could move inode under flush worker's hands and thus sync(2) could miss writing some inodes. Fixes: b35250c0816c ("writeback: Protect inode->i_io_list with inode->i_lock") Link: https://lore.kernel.org/r/20220524150540.12552-1-sunjunchao2870@gmail.com CC: stable@vger.kernel.org Signed-off-by: Jchao Sun Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/fs-writeback.c | 37 ++++++++++++++++++++++++++++--------- fs/inode.c | 2 +- 2 files changed, 29 insertions(+), 10 deletions(-) --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -120,6 +120,7 @@ static bool inode_io_list_move_locked(st struct list_head *head) { assert_spin_locked(&wb->list_lock); + assert_spin_locked(&inode->i_lock); list_move(&inode->i_io_list, head); @@ -1400,9 +1401,9 @@ static int move_expired_inodes(struct li inode = wb_inode(delaying_queue->prev); if (inode_dirtied_after(inode, dirtied_before)) break; + spin_lock(&inode->i_lock); list_move(&inode->i_io_list, &tmp); moved++; - spin_lock(&inode->i_lock); inode->i_state |= I_SYNC_QUEUED; spin_unlock(&inode->i_lock); if (sb_is_blkdev_sb(inode->i_sb)) @@ -1418,7 +1419,12 @@ static int move_expired_inodes(struct li goto out; } - /* Move inodes from one superblock together */ + /* + * Although inode's i_io_list is moved from 'tmp' to 'dispatch_queue', + * we don't take inode->i_lock here because it is just a pointless overhead. + * Inode is already marked as I_SYNC_QUEUED so writeback list handling is + * fully under our control. + */ while (!list_empty(&tmp)) { sb = wb_inode(tmp.prev)->i_sb; list_for_each_prev_safe(pos, node, &tmp) { @@ -1853,8 +1859,8 @@ static long writeback_sb_inodes(struct s * We'll have another go at writing back this inode * when we completed a full scan of b_io. */ - spin_unlock(&inode->i_lock); requeue_io(inode, wb); + spin_unlock(&inode->i_lock); trace_writeback_sb_inodes_requeue(inode); continue; } @@ -2389,6 +2395,7 @@ void __mark_inode_dirty(struct inode *in { struct super_block *sb = inode->i_sb; int dirtytime = 0; + struct bdi_writeback *wb = NULL; trace_writeback_mark_inode_dirty(inode, flags); @@ -2441,13 +2448,24 @@ void __mark_inode_dirty(struct inode *in inode->i_state |= flags; /* + * Grab inode's wb early because it requires dropping i_lock and we + * need to make sure following checks happen atomically with dirty + * list handling so that we don't move inodes under flush worker's + * hands. + */ + if (!was_dirty) { + wb = locked_inode_to_wb_and_lock_list(inode); + spin_lock(&inode->i_lock); + } + + /* * If the inode is queued for writeback by flush worker, just * update its dirty state. Once the flush worker is done with * the inode it will place it on the appropriate superblock * list, based upon its state. */ if (inode->i_state & I_SYNC_QUEUED) - goto out_unlock_inode; + goto out_unlock; /* * Only add valid (hashed) inodes to the superblock's @@ -2455,22 +2473,19 @@ void __mark_inode_dirty(struct inode *in */ if (!S_ISBLK(inode->i_mode)) { if (inode_unhashed(inode)) - goto out_unlock_inode; + goto out_unlock; } if (inode->i_state & I_FREEING) - goto out_unlock_inode; + goto out_unlock; /* * If the inode was already on b_dirty/b_io/b_more_io, don't * reposition it (that would break b_dirty time-ordering). */ if (!was_dirty) { - struct bdi_writeback *wb; struct list_head *dirty_list; bool wakeup_bdi = false; - wb = locked_inode_to_wb_and_lock_list(inode); - inode->dirtied_when = jiffies; if (dirtytime) inode->dirtied_time_when = jiffies; @@ -2484,6 +2499,7 @@ void __mark_inode_dirty(struct inode *in dirty_list); spin_unlock(&wb->list_lock); + spin_unlock(&inode->i_lock); trace_writeback_dirty_inode_enqueue(inode); /* @@ -2498,6 +2514,9 @@ void __mark_inode_dirty(struct inode *in return; } } +out_unlock: + if (wb) + spin_unlock(&wb->list_lock); out_unlock_inode: spin_unlock(&inode->i_lock); } --- a/fs/inode.c +++ b/fs/inode.c @@ -27,7 +27,7 @@ * Inode locking rules: * * inode->i_lock protects: - * inode->i_state, inode->i_hash, __iget() + * inode->i_state, inode->i_hash, __iget(), inode->i_io_list * Inode LRU list locks protect: * inode->i_sb->s_inode_lru, inode->i_lru * inode->i_sb->s_inode_list_lock protects: