From nobody Thu Apr 2 02:40:52 2026 Received: from mail-wm1-f53.google.com (mail-wm1-f53.google.com [209.85.128.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9553F3C8702 for ; Mon, 30 Mar 2026 12:26:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774873574; cv=none; b=KS8YwHS18Gb5pF1UwyHMHSgck2SPlyRG7l9jEGw/qEmXlEMiRPForWMRZEg3KBazhubm50qJNJLhw6v0h7jD6taA1znZg5FEu7Okkt0RQxQZKwhfGjTUB+tugh6v7hi8c/5JTyVAJkLdGjSiztagEXTrzHDy9bdLHgWQUm83bSM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774873574; c=relaxed/simple; bh=L1RFcgDnQ31NWDGIxtybwFx8VaMwRcRUNwVLv3wONAI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=VgTKOh5zhZZngcYLOmBuPdEGt6qT3oWIQsuBxC4D9HRwrEVk5cZiW3uw36JtSFjLyUdUMHpomzJQKANREDLFPpMoDOLqoJdKJU4GDd6wNfUrKaXhOxZ62c3TELAFhxDdBE8/pR5Yrg8SQnBYS91E5X2liJAhbMQbn8eZVeFWSsQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=O7Ao8wxi; arc=none smtp.client-ip=209.85.128.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="O7Ao8wxi" Received: by mail-wm1-f53.google.com with SMTP id 5b1f17b1804b1-487012ce896so26875005e9.0 for ; Mon, 30 Mar 2026 05:26:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1774873571; x=1775478371; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=tNGkc6mI/lF/b7D/ZXMwhX1tSYEj50reD2Mab1GCQ2M=; b=O7Ao8wxiqpcaxoBMt9/yIr1cfJWkx2oBX7J1OUQvRIgz891zv4CZY/ANO5q/foGtxa lRXslpVEIAK6xabrEi4upgg6+jsW+TH+mcFXQBQ7ucAig0tywsCV5rKC0ax5SJJdvDaJ /nJaLQBbuYjdwW0ptAJYtmle44It7uH7lM66VlHj/+XIHIwiTUaDhl7o0fXJ/ICmrUFa HuZxHgAKf/RmnYqJ9aR5SlOt8FMh9C5pS0/Lzw62IFHKjgMcU+kMrOTMfHcakZcg/Bb5 Nr+dkZV2dJo9fbSFlMFZGQ3jZuc+9OMOLu0+UOZatzEMLQXdXu4fhAIHrten74KIky0p 5yZA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774873571; x=1775478371; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=tNGkc6mI/lF/b7D/ZXMwhX1tSYEj50reD2Mab1GCQ2M=; b=EMXsaxf6DW8uuJ5bHZBd3RdP4VZSQV5Ya6fPOoA0jUP1yScSLA4RIUpuBQ5uFNSdr1 jLYpG4a0x6x1VxAv9+Jp7ytvrLRVJBWIQCQHzvZWrPW5Kzn3Nef9CX6Nz2ZYV9dC23Jo Wf2FviYlJgeWMHlBLyAus8ejYRq5qVn7fxYBKOoJBPr6wLataE8pygGgRIBVkF7x2CtS H7hhPnqreQ8W2vVfk7cJ7QCBJyFSGC+H/rZzQD0KoaeCMQYpLOK0WaQv7q9LjYP6tEu5 aSL/arh4mizcDR39KDupnUHz8MhWDbY5XILu5zb+STHYObDWJmbICb1Zks64neHdhr0o 1Qzw== X-Forwarded-Encrypted: i=1; AJvYcCWukk+bzSnjF7+Vw/Lj2qHP7JKDqLSWpCUHrp1heVSO7Ybbbqark+qhB+tWtwraqsKRkjsSOtIF4+PkesA=@vger.kernel.org X-Gm-Message-State: AOJu0YwDdYq4cuSmDQFHcrbpHqVs4x8B9CYStodGezBVmdSKyH4gFHeM YOTQ6tsLRgX5kp3k6YkPOFtK8yri7MJZw7gNnNhpXJxYeO3I7IEZjaei X-Gm-Gg: ATEYQzw+OZbgIa++V0PQlJmV/MWpSQ9LPRpFWLDsyP6xJfIS90eXcURwD4gdt3VBOVQ uTBRNjlq0POEP6yoTlpp4WIfFTr9mrvt58o13IMTQp8qlJXcxFUpA9vz1mjYID1nnB47g6c9PuH jspZM/BRaRkqBJLy2DQBpJJkNfP8+/dOvDqqRnQgIBW2or7lX75v5vs+AtkS9nXwg/dP1m6uSKc WxMhvlUx+VFnyvUAu9Yc/hSiszvkcEquaVwo9lcl648oML2zS9opxouHcj/8YQ+s2oeyPZ3tn5Q pScBB1/dq+ARQLbQv08j2dwx15eK7Wfh+b5ogy1TeMn18pX+97XDpBddeL15ihSQFeu0pr6urXp ieQQI2XKlhmiSHo1nhd45eaEGecAk+CVRs32psw6nqqcfuzixqfH1614RD6HoLz0z2z7CLGst7Z 02BQFR6Ryw11jEXkszZykf2DMIWhNEubQIZx7vDfwTr7pSXwUqIz0QuCZ5hpsRpFrZLgyvQRslP Q== X-Received: by 2002:a05:600c:c178:b0:487:59c:2bb8 with SMTP id 5b1f17b1804b1-48727ef16bamr208759835e9.27.1774873570772; Mon, 30 Mar 2026 05:26:10 -0700 (PDT) Received: from f.. (cst-prg-89-171.cust.vodafone.cz. [46.135.89.171]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48725da0333sm113541525e9.2.2026.03.30.05.26.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 30 Mar 2026 05:26:10 -0700 (PDT) From: Mateusz Guzik To: brauner@kernel.org Cc: viro@zeniv.linux.org.uk, jack@suse.cz, linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, Mateusz Guzik Subject: [PATCH v4 1/4] fs: add icount_read_once() and stop open-coding ->i_count loads Date: Mon, 30 Mar 2026 14:25:59 +0200 Message-ID: <20260330122602.3659417-2-mjguzik@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260330122602.3659417-1-mjguzik@gmail.com> References: <20260330122602.3659417-1-mjguzik@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Similarly to inode_state_read_once(), it makes the caller spell out they acknowledge instability of the returned value. Signed-off-by: Mateusz Guzik --- arch/powerpc/platforms/cell/spufs/file.c | 2 +- fs/btrfs/inode.c | 2 +- fs/ceph/mds_client.c | 2 +- fs/ext4/ialloc.c | 4 ++-- fs/hpfs/inode.c | 2 +- fs/inode.c | 12 ++++++------ fs/nfs/inode.c | 4 ++-- fs/smb/client/inode.c | 2 +- fs/ubifs/super.c | 2 +- fs/xfs/xfs_inode.c | 2 +- fs/xfs/xfs_trace.h | 2 +- include/linux/fs.h | 13 +++++++++++++ include/trace/events/filelock.h | 2 +- security/landlock/fs.c | 2 +- 14 files changed, 33 insertions(+), 20 deletions(-) diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platfo= rms/cell/spufs/file.c index 10fa9b844fcc..f6de8c1169d5 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -1430,7 +1430,7 @@ static int spufs_mfc_open(struct inode *inode, struct= file *file) if (ctx->owner !=3D current->mm) return -EINVAL; =20 - if (icount_read(inode) !=3D 1) + if (icount_read_once(inode) !=3D 1) return -EBUSY; =20 mutex_lock(&ctx->mapping_lock); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 8d97a8ad3858..f36c49e83c04 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4741,7 +4741,7 @@ static void btrfs_prune_dentries(struct btrfs_root *r= oot) =20 inode =3D btrfs_find_first_inode(root, min_ino); while (inode) { - if (icount_read(&inode->vfs_inode) > 1) + if (icount_read_once(&inode->vfs_inode) > 1) d_prune_aliases(&inode->vfs_inode); =20 min_ino =3D btrfs_ino(inode) + 1; diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index b1746273f186..2cb3c919d40d 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -2223,7 +2223,7 @@ static int trim_caps_cb(struct inode *inode, int mds,= void *arg) int count; dput(dentry); d_prune_aliases(inode); - count =3D icount_read(inode); + count =3D icount_read_once(inode); if (count =3D=3D 1) (*remaining)--; doutc(cl, "%p %llx.%llx cap %p pruned, count now %d\n", diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 3fd8f0099852..8c80d5087516 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -252,10 +252,10 @@ void ext4_free_inode(handle_t *handle, struct inode *= inode) "nonexistent device\n", __func__, __LINE__); return; } - if (icount_read(inode) > 1) { + if (icount_read_once(inode) > 1) { ext4_msg(sb, KERN_ERR, "%s:%d: inode #%llu: count=3D%d", __func__, __LINE__, inode->i_ino, - icount_read(inode)); + icount_read_once(inode)); return; } if (inode->i_nlink) { diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c index 0e932cc8be1b..1b4fcf760aad 100644 --- a/fs/hpfs/inode.c +++ b/fs/hpfs/inode.c @@ -184,7 +184,7 @@ void hpfs_write_inode(struct inode *i) struct hpfs_inode_info *hpfs_inode =3D hpfs_i(i); struct inode *parent; if (i->i_ino =3D=3D hpfs_sb(i->i_sb)->sb_root) return; - if (hpfs_inode->i_rddir_off && !icount_read(i)) { + if (hpfs_inode->i_rddir_off && !icount_read_once(i)) { if (*hpfs_inode->i_rddir_off) pr_err("write_inode: some position still there\n"); kfree(hpfs_inode->i_rddir_off); diff --git a/fs/inode.c b/fs/inode.c index 5ad169d51728..1f5a383ccf27 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -907,7 +907,7 @@ void evict_inodes(struct super_block *sb) again: spin_lock(&sb->s_inode_list_lock); list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { - if (icount_read(inode)) + if (icount_read_once(inode)) continue; =20 spin_lock(&inode->i_lock); @@ -1926,7 +1926,7 @@ static void iput_final(struct inode *inode) int drop; =20 WARN_ON(inode_state_read(inode) & I_NEW); - VFS_BUG_ON_INODE(atomic_read(&inode->i_count) !=3D 0, inode); + VFS_BUG_ON_INODE(icount_read(inode) !=3D 0, inode); =20 if (op->drop_inode) drop =3D op->drop_inode(inode); @@ -1945,7 +1945,7 @@ static void iput_final(struct inode *inode) * Re-check ->i_count in case the ->drop_inode() hooks played games. * Note we only execute this if the verdict was to drop the inode. */ - VFS_BUG_ON_INODE(atomic_read(&inode->i_count) !=3D 0, inode); + VFS_BUG_ON_INODE(icount_read(inode) !=3D 0, inode); =20 if (drop) { inode_state_set(inode, I_FREEING); @@ -1989,7 +1989,7 @@ void iput(struct inode *inode) * equal to one, then two CPUs racing to further drop it can both * conclude it's fine. */ - VFS_BUG_ON_INODE(atomic_read(&inode->i_count) < 1, inode); + VFS_BUG_ON_INODE(icount_read_once(inode) < 1, inode); =20 if (atomic_add_unless(&inode->i_count, -1, 1)) return; @@ -2023,7 +2023,7 @@ EXPORT_SYMBOL(iput); void iput_not_last(struct inode *inode) { VFS_BUG_ON_INODE(inode_state_read_once(inode) & (I_FREEING | I_CLEAR), in= ode); - VFS_BUG_ON_INODE(atomic_read(&inode->i_count) < 2, inode); + VFS_BUG_ON_INODE(icount_read_once(inode) < 2, inode); =20 WARN_ON(atomic_sub_return(1, &inode->i_count) =3D=3D 0); } @@ -3046,7 +3046,7 @@ void dump_inode(struct inode *inode, const char *reas= on) } =20 state =3D inode_state_read_once(inode); - count =3D atomic_read(&inode->i_count); + count =3D icount_read_once(inode); =20 if (!sb || get_kernel_nofault(s_type, &sb->s_type) || !s_type || diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 98a8f0de1199..22834eddd5b1 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -608,7 +608,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, st= ruct nfs_fattr *fattr) inode->i_sb->s_id, (unsigned long long)NFS_FILEID(inode), nfs_display_fhandle_hash(fh), - icount_read(inode)); + icount_read_once(inode)); =20 out: return inode; @@ -2261,7 +2261,7 @@ static int nfs_update_inode(struct inode *inode, stru= ct nfs_fattr *fattr) dfprintk(VFS, "NFS: %s(%s/%llu fh_crc=3D0x%08x ct=3D%d info=3D0x%llx)\n", __func__, inode->i_sb->s_id, inode->i_ino, nfs_display_fhandle_hash(NFS_FH(inode)), - icount_read(inode), fattr->valid); + icount_read_once(inode), fattr->valid); =20 if (!(fattr->valid & NFS_ATTR_FATTR_FILEID)) { /* Only a mounted-on-fileid? Just exit */ diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 888f9e35f14b..ab35e35b16d7 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -2842,7 +2842,7 @@ int cifs_revalidate_dentry_attr(struct dentry *dentry) } =20 cifs_dbg(FYI, "Update attributes: %s inode 0x%p count %d dentry: 0x%p d_t= ime %ld jiffies %ld\n", - full_path, inode, icount_read(inode), + full_path, inode, icount_read_once(inode), dentry, cifs_get_time(dentry), jiffies); =20 again: diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 9a77d8b64ffa..38972786817e 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -358,7 +358,7 @@ static void ubifs_evict_inode(struct inode *inode) goto out; =20 dbg_gen("inode %llu, mode %#x", inode->i_ino, (int)inode->i_mode); - ubifs_assert(c, !icount_read(inode)); + ubifs_assert(c, !icount_read_once(inode)); =20 truncate_inode_pages_final(&inode->i_data); =20 diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index beaa26ec62da..4f659eba6ae5 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -1046,7 +1046,7 @@ xfs_itruncate_extents_flags( int error =3D 0; =20 xfs_assert_ilocked(ip, XFS_ILOCK_EXCL); - if (icount_read(VFS_I(ip))) + if (icount_read_once(VFS_I(ip))) xfs_assert_ilocked(ip, XFS_IOLOCK_EXCL); if (whichfork =3D=3D XFS_DATA_FORK) ASSERT(new_size <=3D XFS_ISIZE(ip)); diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 5e8190fe2be9..cbdec40826b3 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -1156,7 +1156,7 @@ DECLARE_EVENT_CLASS(xfs_iref_class, TP_fast_assign( __entry->dev =3D VFS_I(ip)->i_sb->s_dev; __entry->ino =3D ip->i_ino; - __entry->count =3D icount_read(VFS_I(ip)); + __entry->count =3D icount_read_once(VFS_I(ip)); __entry->pincount =3D atomic_read(&ip->i_pincount); __entry->iflags =3D ip->i_flags; __entry->caller_ip =3D caller_ip; diff --git a/include/linux/fs.h b/include/linux/fs.h index 8afbe2ef2686..07363fce4406 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2225,8 +2225,21 @@ static inline void mark_inode_dirty_sync(struct inod= e *inode) __mark_inode_dirty(inode, I_DIRTY_SYNC); } =20 +/* + * returns the refcount on the inode. it can change arbitrarily. + */ +static inline int icount_read_once(const struct inode *inode) +{ + return atomic_read(&inode->i_count); +} + +/* + * returns the refcount on the inode. The lock guarantees no new references + * are added, but references can be dropped as long as the result is > 0. + */ static inline int icount_read(const struct inode *inode) { + lockdep_assert_held(&inode->i_lock); return atomic_read(&inode->i_count); } =20 diff --git a/include/trace/events/filelock.h b/include/trace/events/fileloc= k.h index 116774886244..c8c8847bb6f6 100644 --- a/include/trace/events/filelock.h +++ b/include/trace/events/filelock.h @@ -190,7 +190,7 @@ TRACE_EVENT(generic_add_lease, __entry->i_ino =3D inode->i_ino; __entry->wcount =3D atomic_read(&inode->i_writecount); __entry->rcount =3D atomic_read(&inode->i_readcount); - __entry->icount =3D icount_read(inode); + __entry->icount =3D icount_read_once(inode); __entry->owner =3D fl->c.flc_owner; __entry->flags =3D fl->c.flc_flags; __entry->type =3D fl->c.flc_type; diff --git a/security/landlock/fs.c b/security/landlock/fs.c index c1ecfe239032..32d560f12dbd 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -1278,7 +1278,7 @@ static void hook_sb_delete(struct super_block *const = sb) struct landlock_object *object; =20 /* Only handles referenced inodes. */ - if (!icount_read(inode)) + if (!icount_read_once(inode)) continue; =20 /* --=20 2.48.1 From nobody Thu Apr 2 02:40:52 2026 Received: from mail-wm1-f42.google.com (mail-wm1-f42.google.com [209.85.128.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B66043CBE69 for ; Mon, 30 Mar 2026 12:26:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774873575; cv=none; b=uP3NRU1Hcrd7BCzdalVuuuZ2HhkVBq8bPk0RFr+2Q5K2WvOzHbBR1QqPm5FXFFrEAE5NLRpjPvsWThOry/Y8ZvJ532W6lsKB20rfYvVI8tPcDwgW/NuyvoajMEFJ+qCAqVHRc0ILf7fgV1ZgocnId+OILOLCo6EAonz9qhkaXok= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774873575; c=relaxed/simple; bh=COKwH1VH8BuYCRynamMlTkaMzJ4AGwGGF/sBUjJNvDg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=dZIi+POb/ikXkL0LiYgHMky27Nx1pD58/t2MqqdlDBnbFj/Icje+V3BrOAMP3Sc04CLByQ2SQ8tz5DI+sYxHvTVQZBgwTsST1KJkYM/ppUEi3zbH+UBiS/CheGYwCi4uVm0ULIXaMWrLmajpCck4PwpBWA0YAEtpajDs85FzKhY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=HSRcTdML; arc=none smtp.client-ip=209.85.128.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="HSRcTdML" Received: by mail-wm1-f42.google.com with SMTP id 5b1f17b1804b1-4852e9ca034so42378385e9.2 for ; Mon, 30 Mar 2026 05:26:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1774873572; x=1775478372; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=jBIfgeXR6ZzaVZZprJJKEW+34xpz6VJfBbaNi9NqWK8=; b=HSRcTdMLvmqF/ibNsUK6wJquEZjZLD8VZwhzYzeY5MDFT6UQjz6l30yjqkzumdTKnJ ANONMBfO5pTRTVsmXZa4Z3k2gSKX77XW4daTICfYLi0uznuEATOsEkNvHmC1HkYSiWii 7/aLUGmuYpxLaMXhMqjZ6jqeAQFiFapsTynbZfc3VNfr/FCd6J3r4H+Zl2cvaHsQDXXj R8jA/zaCyZHyhWm3cDBclSazdOgMf+HEnHcPR00LwhjWP4rUm6SnFO/QhxfquZz9jA/O RJfhDcWzfwdRvSK0lygWRsLApP9KvYDNIh6R3VYkNpIHSwok5q0wXlfVdR5jns7SXoQI mggA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774873572; x=1775478372; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=jBIfgeXR6ZzaVZZprJJKEW+34xpz6VJfBbaNi9NqWK8=; b=q8uQ69SZRGZ+3abwyVXymz/p0ZNLckPjipZB+1tbrUxIcufNXhkUM4wL6ImcYj9WV1 RpC5yFa/PvPu5oNepW4cbbcFK5wIjcdVRISSfZkEaUUfdLwfoxNeGV7DX9mjqko8yo/Y NC/bBEz9AvyEqKktybwlh2WFkdy70X5LS0Gzv+q8M7wmUU4Hu1GJB+G45olb6VDs5TYP jnFZHqBEGzV1XnlNb7wDdvqRJKFySqjVG442HnQ21y/Y+zTrM8yMTNEkvhSGy/dqkS8G O6ZR5pBxixavr+rV6oCTZX1cw3uX4DTUxniQeEyJ/ubLJ/NDFBLtufZqvXBhHfi66ANc Kezw== X-Forwarded-Encrypted: i=1; AJvYcCXjTephDDEYjSqfjv9JduggwDbddbdsMDltfdPtU213FbP39NA2bb/+RuHHoRpYmsUSZaaItklb5i5h+PY=@vger.kernel.org X-Gm-Message-State: AOJu0YxsLJR1xMVTHQqi9D0mRJYP/rLgY69x+moXYfGUeJAjN1Zv2OVE S3l+pXQO66t/STSt1ztF6879CR+4lD1H6WVFZUWP0/0PQIh2ai25o6Vh X-Gm-Gg: ATEYQzwlCVSy1aAiKiameo0OLQ0LoOzq5b4JcuznVfaY5tYOdXoOeyX36fRGePyl3M3 How1WogyRxoj5Hj87y+W6oPE00DOjTOLQtv5uTood3e3anwMM331uJRR1dEXCjQNgHAAk0RhsFz i9qZ7/QjC23PYmOFY5hCetEuYLzwa8rAvH4hrYaZmFHgPgiZWqDZQQoaoe3PNCcJ1wfMnhsA0kY dpihLXOuAOmU54yiGzZ7b6eCg7A2QHF79Ivwp5cK4KcR+bUxW1u7vxMd7Hqpa1NPIU53+1gX1v9 xxNahVvxrH4kABk0+d8Kdo3laMm2uZbAP0ULlSReotl/T/J5PHKcvIGw6BeHgUOjL/YEXFGMK8I GzhSAp2R2FMinzKkYGW5S297nf3KCHSFiQ8HX9ch8sGf8YpKZE0qkqdC2Kk6BsvcGlN12a7aTLj /1k1j6V7+h6B763t9rlt/9vhQRbiO/lFIx814WzFHUNvUuOqSNaCGRBm3afVA7QBi3IQIiWiiLp A== X-Received: by 2002:a05:600c:3e88:b0:487:243f:dc3e with SMTP id 5b1f17b1804b1-48727ee98a2mr194363335e9.6.1774873571860; Mon, 30 Mar 2026 05:26:11 -0700 (PDT) Received: from f.. (cst-prg-89-171.cust.vodafone.cz. [46.135.89.171]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48725da0333sm113541525e9.2.2026.03.30.05.26.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 30 Mar 2026 05:26:11 -0700 (PDT) From: Mateusz Guzik To: brauner@kernel.org Cc: viro@zeniv.linux.org.uk, jack@suse.cz, linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, Mateusz Guzik Subject: [PATCH v4 2/4] fs: relocate and tidy up ihold() Date: Mon, 30 Mar 2026 14:26:00 +0200 Message-ID: <20260330122602.3659417-3-mjguzik@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260330122602.3659417-1-mjguzik@gmail.com> References: <20260330122602.3659417-1-mjguzik@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The placement was illogical, move it next to igrab(). Take this opportunity to add docs and an assert on the refcount. While its modification remains gated with a WARN_ON, the new assert will also dump the inode state which might aid debugging. No functional changes. Signed-off-by: Mateusz Guzik --- fs/inode.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/fs/inode.c b/fs/inode.c index 1f5a383ccf27..e397a4b56671 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -522,15 +522,6 @@ static void init_once(void *foo) inode_init_once(inode); } =20 -/* - * get additional reference to inode; caller must already hold one. - */ -void ihold(struct inode *inode) -{ - WARN_ON(atomic_inc_return(&inode->i_count) < 2); -} -EXPORT_SYMBOL(ihold); - struct wait_queue_head *inode_bit_waitqueue(struct wait_bit_queue_entry *w= qe, struct inode *inode, u32 bit) { @@ -1578,6 +1569,17 @@ ino_t iunique(struct super_block *sb, ino_t max_rese= rved) } EXPORT_SYMBOL(iunique); =20 +/** + * ihold - get a reference on the inode, provided you already have one + * @inode: inode to operate on + */ +void ihold(struct inode *inode) +{ + VFS_BUG_ON_INODE(icount_read_once(inode) < 1, inode); + WARN_ON(atomic_inc_return(&inode->i_count) < 2); +} +EXPORT_SYMBOL(ihold); + struct inode *igrab(struct inode *inode) { spin_lock(&inode->i_lock); --=20 2.48.1 From nobody Thu Apr 2 02:40:52 2026 Received: from mail-wm1-f44.google.com (mail-wm1-f44.google.com [209.85.128.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 79F263CCA1A for ; Mon, 30 Mar 2026 12:26:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774873577; cv=none; b=HVgGgHSvOUDURIYToYuEbOkefi2rER3KfM7dpiZ07KPXYfU+p3abH24GBRFPwUAh8D6BMqj9gDc9MaTl2n9PJZ7WUpFC3qkOLNEDdk+90si+EH0+ftXclKtd9aKl/I+5WkN5ogcawduXTxZ6x3Qrx+Z3yROnQVZe9C4p05w5ms8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774873577; c=relaxed/simple; bh=HHQdlPXWfQ0bdc5Tr17fd+OYag4Slbu5vp5M9ZuZwb8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=eJEidowZWU7CG9VwbyuTpyLaE95qohAcEBndMMWSl3/x85NRJr9RvbnUXpCAv8QYWK6mgmuefnAtGpRS57bCzcsll4PhhnnMpsCarYdE63y4UiP2KkDeE6lNqd+MtyAlQH3h5Ov+HWbAWQWTB9ZXzerqSny3hji/pxlRMwvW0EQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=FvxNZGG/; arc=none smtp.client-ip=209.85.128.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="FvxNZGG/" Received: by mail-wm1-f44.google.com with SMTP id 5b1f17b1804b1-4852b81c73aso37587115e9.3 for ; Mon, 30 Mar 2026 05:26:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1774873574; x=1775478374; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=FScv3i6uAosiDBVQ3BCLq5X7rIHAG+u+NDcPUFmbj9Q=; b=FvxNZGG/yIbXKzYYDidIcHzHENx5nmzfMD3FdZ6OBA3GW1x2VIKx+XDmA0SP8dJGRA u4iXClCkqpOkFeo5gqHC/jrehzFUZ32XLsUpSm0OM47yghO16qMiSGqQoMUnZ03Wj9oq jdPOJ3s2fPUO1AAhGjTBQAMISxy+WjQ6I24oxK/cst6B8SSSaOCrlxI4vIj9OjrMbHpa jlVNSBL4w+jvpf2wK8gWHdHaH2q5kz4q0eOGp92XGN6JJoSPgxl14T1+gjrFqq3T5eCJ 1UD0/k6WXtEmmhi6TiBhOIwEnt8hLkXIITwiPQUF/VKSVFIN6liojrAHRkVd59Ee6/t9 ZmMA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774873574; x=1775478374; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=FScv3i6uAosiDBVQ3BCLq5X7rIHAG+u+NDcPUFmbj9Q=; b=kNYjsJuPFLWM36aZ1+NDDQo4B5GuAcsB/OhmGk7HSpXsQRuVKGgc/bdoxTUrYP/iGR JCmdyFU0rNKtPKS2U/UfXLWZtJtXLuHQADPLodiHg0Sx8kwnWeE53lejD6u1Ls3gHeUn 6GEPeDwuYikJke1FF3IBl9eIt40zrK3QZ2w1VluBepUYNQVXIadBwGB63XJ+Tyod7EXy owJtJRcHafWBfwRlH5fAfsBqb5T/p4vqs0n4t5wvFXPFD8TLL4Qcr6WKht9CzyuXxOwJ N/CabcdQ/GbSa5fPcVZUtEB3cnLaXDOJqhIA6Sl8Qi0vLn+VDXxhZftmRqZuHgSBJG2M EMkg== X-Forwarded-Encrypted: i=1; AJvYcCVqZCQPDfZhEEoMV+C9udXR5KKE/msCZ2SLngfVuSYeoZjZd1aQ3DTa1iGNgx+A6NGbcOiFIc4MaEj5Epc=@vger.kernel.org X-Gm-Message-State: AOJu0YyRmUc8+y6hKjFexq1/JTr6oa12V00prQrG+gjsyDWDgM55Xaq8 vpMqY7tvHQF5e+hq1VYg4qKZ5+1wuzlpjLXy+3GMzyPPxiqgD6/H/mc1 X-Gm-Gg: ATEYQzwZWHUO9btM/bYNfYPtlgxzoZBIgz8hbtXc2emNk8bnhmV+GWFoMPJWxwnsgfH eFAIuw4xz28rAMlQ6NCs3CAlmjz+Kc/osj+q0qlHLl6Xxl1BiAdSqbPkECuWro3eXkT8OpYjxTR lq7VfyxqNokeGCEDpBGD75dBZoIsb8KMEorrHMfiPCX94G++uZciysaht/w5QJY/v4kWdSAk1Pq I/6K078xjaTjiJBZvJnJNgciIZX32JFMwsohB7BdnOBluXynKCVcDe/DF6J9CH67JeQv/Hh9oJD 4K/mJtdnO3z4uDz3kMA0u3OhK+l48pwY075YoiQYKt/h/FMM+pNNPJhebmKSVzEBD9f4qTA4hIY 7VNbBjf+xm4+ARrPWt2BaKmhq2ce6NZm79DUg2TdpvtQ9782GMX3QX0i+PEKQIROIZ9tvYR3j1a oDdwP5FRd/k7Hi3sOXCvn/fIVgBLWlYAHMi/LHmjmL0hvDKpkFzj2RkqbztKHzx3Hoozw4BY9Xd g== X-Received: by 2002:a05:600c:1d15:b0:485:ae14:8191 with SMTP id 5b1f17b1804b1-48727ee99f6mr200832315e9.5.1774873573523; Mon, 30 Mar 2026 05:26:13 -0700 (PDT) Received: from f.. (cst-prg-89-171.cust.vodafone.cz. [46.135.89.171]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48725da0333sm113541525e9.2.2026.03.30.05.26.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 30 Mar 2026 05:26:12 -0700 (PDT) From: Mateusz Guzik To: brauner@kernel.org Cc: viro@zeniv.linux.org.uk, jack@suse.cz, linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, Mateusz Guzik Subject: [PATCH v4 3/4] fs: handle potential filesystems which use I_DONTCACHE and drop the lock in ->drop_inode Date: Mon, 30 Mar 2026 14:26:01 +0200 Message-ID: <20260330122602.3659417-4-mjguzik@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260330122602.3659417-1-mjguzik@gmail.com> References: <20260330122602.3659417-1-mjguzik@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" f2fs and ntfs play games where they transitioning the refcount 0->1 and rel= ease the inode spinlock, allowing other threads to grab a ref of their own. They also return 0 in that case, making this problem harmless. However, should they start using the I_DONTCACHE machinery down the road while retaining the above, iput_final() will get a race where it can proceed to teardown an inode with references. The easiest way out for the time being is to future-proof it by predicating caching on the count. Developing better ->drop_inode semantics and sanitizing all users is left as en exercise for the reader. Signed-off-by: Mateusz Guzik --- fs/inode.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/fs/inode.c b/fs/inode.c index e397a4b56671..a06edd557e84 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1935,20 +1935,29 @@ static void iput_final(struct inode *inode) else drop =3D inode_generic_drop(inode); =20 - if (!drop && - !(inode_state_read(inode) & I_DONTCACHE) && - (sb->s_flags & SB_ACTIVE)) { + /* + * XXXCRAP: there are ->drop_inode hooks playing nasty games releasing the + * spinlock and temporarily grabbing refs. This opens a possibility someo= ne + * else will sneak in and grab a ref while it happens. + * + * If such a hook returns 0 (=3D=3D don't drop) this happens to be harmle= ss as long + * as the inode is not marked with I_DONTCACHE. Otherwise we are proceedi= ng with + * teardown despite references being present. + * + * Damage-control the problem by including the count in the decision. How= ever, + * assert no refs showed up if the hook decided to drop the inode. + */ + if (drop) + VFS_BUG_ON_INODE(icount_read(inode) !=3D 0, inode); + + if (icount_read(inode) > 0 || + (!drop && !(inode_state_read(inode) & I_DONTCACHE) && + (sb->s_flags & SB_ACTIVE))) { __inode_lru_list_add(inode, true); spin_unlock(&inode->i_lock); return; } =20 - /* - * Re-check ->i_count in case the ->drop_inode() hooks played games. - * Note we only execute this if the verdict was to drop the inode. - */ - VFS_BUG_ON_INODE(icount_read(inode) !=3D 0, inode); - if (drop) { inode_state_set(inode, I_FREEING); } else { --=20 2.48.1 From nobody Thu Apr 2 02:40:52 2026 Received: from mail-wm1-f42.google.com (mail-wm1-f42.google.com [209.85.128.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D93BA3CCFCC for ; Mon, 30 Mar 2026 12:26:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774873578; cv=none; b=B0xVp24ahHLX+8L4pOqdLdq0/nF1NLFDi6J7EDx81CfL4S6gwbzhF/gw0r3hP4tUMsqD1te1PTGrObybs2XAbJlPKaWNHsoWXqi+0R7ZEDbxPR/0OEwJwCnN+MBm0+J4jGYZaAoyULjFCd7hfCh8S/cjwnZFjYYAvYoL1ubZ6j4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774873578; c=relaxed/simple; bh=2CjUp93JkPBxpWSqmVHo2It4km/TFL7MtJebAJ8A1RQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=UEinHnJJU6RTQwzzuasrNori86yg3e8js+I0yiFoE5N+pKPrIwi1SY1hCWNap6LcRxyKF+/qcj7f9B4yy9j2cunaui6TDT1YYbqj1DukQHUtvjG0eHp1ff8cH6f/gZOglpTo1H6pCkjwEcXGAWayEEt/9koPuGOFxoVCqLO0n9c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=oxOME1yu; arc=none smtp.client-ip=209.85.128.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="oxOME1yu" Received: by mail-wm1-f42.google.com with SMTP id 5b1f17b1804b1-4853e1ce427so55296065e9.3 for ; Mon, 30 Mar 2026 05:26:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1774873575; x=1775478375; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=QXuXEDvrDtfw8BayhkvLcB6SfckV4kYiA2bRPrVX/L0=; b=oxOME1yuaZ2WdHB7/YU7Gr6HkfYWNrmPPsvnyWosU55KJmzqGUQNuVMJptzToMTzMp eTpK6KxR/SEQpUocntarl64cmCnW7C0NpRPuf6amr5SC7vbC2Tbp5voONjGArGKMe+TI SqqxWIsRhmUpfh7KSpavuARSaIx6TawRHbgvj8efgwHDH05qNboOSO+awNlgqO0FvCEk ee2sCdTFiB1AbeifV5L02pEqn7PNrz3jNBSVMLsSHOtheWL/y+uZyuXVAHJ1oDDbNEMX wy+znJek35ueQ0ecjIWezz1lxbEWs/4CGps7wXq9KV5hJqsNXwf9ehzLTvBA4RlCxnhf zw1w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774873575; x=1775478375; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=QXuXEDvrDtfw8BayhkvLcB6SfckV4kYiA2bRPrVX/L0=; b=FLvFdn4E2RD9n5pijIyUNJj90ypuqSKqil8cBGIJG82CzCEZPcTakfEiiXS80C0j/m 0mU5eHq/ShS+oPBp+Qtrt3Uuo8nMMLyNgS9YZ2PgB/opbrocQSvkcfvWCv1CwGrDmb4h KXjo9NsB1AznCKrCxeOrXrnRvybYaThK93nm/XBmlAA9JZwTLJ2VfpuuzyhcmGgJuX2y bXdD6W3cPC4M0MoKxNJmnphgabW3qPXdPio7nduXzl2HouZ7fOim7SOmOejWhJljd+XQ LTC1Vjr+rq10JhnH82bqdr2cA2wOJERvb1g2Ydi5RuCoqPXH8bVfrimG4OxbVNFu1ABm 5m2Q== X-Forwarded-Encrypted: i=1; AJvYcCVUNNN/miBBrs7j3wgnKnpZFMvbpM/ImuB+Yu+S07l/htp/d9w96Ph7O1tgDYXo2G/0AE/GxYp/PX3P80o=@vger.kernel.org X-Gm-Message-State: AOJu0YxqGiYg6PkS/41mQWNfTZl9un+0kaqwGs2IzroRtXha/lDqeVsZ avWHuhPlur9iO3CxmSmNLqaHM3tDdj9JNk6gXeh69/OVu6uo9MBAAMnu X-Gm-Gg: ATEYQzyiYrbu0APLXmYzG1JuUPPU7miZuumna2IoK2uvIqWoVwH9G2lbRgghhoMd2pe cr6nr5oosEwYckzEELt8gGRhzeoHets7JINT/fDuRCx/KWkNijL6JoxfMTtw0Jh5Gp2BxuIF1q1 UP679BDd3G7U7mjXcAA2IvwuvUrL19UrAwHs5BPKqvuHQ5q92KjvPswaxPw2NWeiwz63a8fuS/J phr3/ftKLloYwpP7ckPvUw/xztmOIRKjHzviXtUucff1mx7FhhmeffDQ0/a5gt3vRnfSL1qlogS 8tluE4SgRcrWytf3uov1ZCVeT9M0hCyHi/6H5kw9yYoPJFzXuz4HVzU87aflXppURMKIi+EqQMP yfWz7rjI/HFXqfzSgGowtsLhAq59JH4aMwpN9fObjRuVqVwUTC6egY8Uge/HQGR2pzbOPVVcyrH uazTssPtZYAGoOLtvj6R+93iqQoZq5ROfiqi/VxNmyLrofD61y2BFnT1tfhLrLiLYb/n+vVXkll Q== X-Received: by 2002:a05:600c:1e2a:b0:485:1878:7b8c with SMTP id 5b1f17b1804b1-48727ee9f5bmr233570175e9.18.1774873575199; Mon, 30 Mar 2026 05:26:15 -0700 (PDT) Received: from f.. (cst-prg-89-171.cust.vodafone.cz. [46.135.89.171]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48725da0333sm113541525e9.2.2026.03.30.05.26.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 30 Mar 2026 05:26:14 -0700 (PDT) From: Mateusz Guzik To: brauner@kernel.org Cc: viro@zeniv.linux.org.uk, jack@suse.cz, linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, Mateusz Guzik Subject: [PATCH v4 4/4] fs: allow lockless ->i_count bumps as long as it does not transition 0->1 Date: Mon, 30 Mar 2026 14:26:02 +0200 Message-ID: <20260330122602.3659417-5-mjguzik@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260330122602.3659417-1-mjguzik@gmail.com> References: <20260330122602.3659417-1-mjguzik@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" With this change only 0->1 and 1->0 transitions need the lock. I verified all places which look at the refcount either only care about it staying 0 (and have the lock enforce it) or don't hold the inode lock to begin with (making the above change irrelevant to their correcness or lack thereof). I also confirmed nfs and btrfs like to call into these a lot and now avoid the lock in the common case, shaving off some atomics. Signed-off-by: Mateusz Guzik --- fs/dcache.c | 4 +++ fs/inode.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/fs.h | 4 +-- 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 9ceab142896f..b63450ebb85c 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2033,6 +2033,10 @@ void d_instantiate_new(struct dentry *entry, struct = inode *inode) __d_instantiate(entry, inode); spin_unlock(&entry->d_lock); WARN_ON(!(inode_state_read(inode) & I_NEW)); + /* + * Paired with igrab_try_lockless() + */ + smp_wmb(); inode_state_clear(inode, I_NEW | I_CREATING); inode_wake_up_bit(inode, __I_NEW); spin_unlock(&inode->i_lock); diff --git a/fs/inode.c b/fs/inode.c index a06edd557e84..5e5cddd76a9d 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1029,6 +1029,7 @@ long prune_icache_sb(struct super_block *sb, struct s= hrink_control *sc) } =20 static void __wait_on_freeing_inode(struct inode *inode, bool hash_locked,= bool rcu_locked); +static bool igrab_try_lockless(struct inode *inode); =20 /* * Called with the inode lock held. @@ -1053,6 +1054,11 @@ static struct inode *find_inode(struct super_block *= sb, continue; if (!test(inode, data)) continue; + if (igrab_try_lockless(inode)) { + rcu_read_unlock(); + *isnew =3D false; + return inode; + } spin_lock(&inode->i_lock); if (inode_state_read(inode) & (I_FREEING | I_WILL_FREE)) { __wait_on_freeing_inode(inode, hash_locked, true); @@ -1095,6 +1101,11 @@ static struct inode *find_inode_fast(struct super_bl= ock *sb, continue; if (inode->i_sb !=3D sb) continue; + if (igrab_try_lockless(inode)) { + rcu_read_unlock(); + *isnew =3D false; + return inode; + } spin_lock(&inode->i_lock); if (inode_state_read(inode) & (I_FREEING | I_WILL_FREE)) { __wait_on_freeing_inode(inode, hash_locked, true); @@ -1212,6 +1223,10 @@ void unlock_new_inode(struct inode *inode) lockdep_annotate_inode_mutex_key(inode); spin_lock(&inode->i_lock); WARN_ON(!(inode_state_read(inode) & I_NEW)); + /* + * Paired with igrab_try_lockless() + */ + smp_wmb(); inode_state_clear(inode, I_NEW | I_CREATING); inode_wake_up_bit(inode, __I_NEW); spin_unlock(&inode->i_lock); @@ -1223,6 +1238,10 @@ void discard_new_inode(struct inode *inode) lockdep_annotate_inode_mutex_key(inode); spin_lock(&inode->i_lock); WARN_ON(!(inode_state_read(inode) & I_NEW)); + /* + * Paired with igrab_try_lockless() + */ + smp_wmb(); inode_state_clear(inode, I_NEW); inode_wake_up_bit(inode, __I_NEW); spin_unlock(&inode->i_lock); @@ -1582,6 +1601,14 @@ EXPORT_SYMBOL(ihold); =20 struct inode *igrab(struct inode *inode) { + /* + * Read commentary above igrab_try_lockless() for an explanation why this= works. + */ + if (atomic_add_unless(&inode->i_count, 1, 0)) { + VFS_BUG_ON_INODE(inode_state_read_once(inode) & (I_FREEING | I_WILL_FREE= ), inode); + return inode; + } + spin_lock(&inode->i_lock); if (!(inode_state_read(inode) & (I_FREEING | I_WILL_FREE))) { __iget(inode); @@ -1599,6 +1626,44 @@ struct inode *igrab(struct inode *inode) } EXPORT_SYMBOL(igrab); =20 +/* + * igrab_try_lockless - special inode refcount acquire primitive for the i= node hash + * (don't use elsewhere!) + * + * It provides lockless refcount acquire in the common case of no problema= tic + * flags being set and the count being > 0. + * + * There are 4 state flags to worry about and the routine makes sure to no= t bump the + * ref if any of them is present. + * + * I_NEW and I_CREATING can only legally get set *before* the inode become= s visible + * during lookup. Thus if the flags are not spotted, they are guaranteed t= o not be + * a factor. However, we need an acquire fence before returning the inode = just + * in case we raced against clearing the state to make sure our consumer p= icks up + * any other changes made prior. atomic_add_unless provides a full fence, = which + * takes care of it. + * + * I_FREEING and I_WILL_FREE can only legally get set if ->i_count =3D=3D = 0 and it is + * illegal to bump the ref if either is present. Consequently if atomic_ad= d_unless + * managed to replaced a non-0 value with a bigger one, we have a guarante= e neither + * of these flags is set. Note this means explicitly checking of these fla= gs below + * is not necessary, it is only done because it does not cost anything on = top of the + * load which already needs to be done to handle the other flags. + */ +static bool igrab_try_lockless(struct inode *inode) +{ + if (inode_state_read_once(inode) & (I_NEW | I_CREATING | I_FREEING | I_WI= LL_FREE)) + return false; + /* + * Paired with routines clearing I_NEW + */ + if (atomic_add_unless(&inode->i_count, 1, 0)) { + VFS_BUG_ON_INODE(inode_state_read_once(inode) & (I_FREEING | I_WILL_FREE= ), inode); + return true; + } + return false; +} + /** * ilookup5_nowait - search for an inode in the inode cache * @sb: super block of file system to search diff --git a/include/linux/fs.h b/include/linux/fs.h index 07363fce4406..119e0a3d2f42 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2234,8 +2234,8 @@ static inline int icount_read_once(const struct inode= *inode) } =20 /* - * returns the refcount on the inode. The lock guarantees no new references - * are added, but references can be dropped as long as the result is > 0. + * returns the refcount on the inode. The lock guarantees no 0->1 or 1->0 = transitions + * of the count are going to take place, otherwise it changes arbitrarily. */ static inline int icount_read(const struct inode *inode) { --=20 2.48.1