From nobody Sat Jun 13 14:50:14 2026 Received: from mail-wr1-f43.google.com (mail-wr1-f43.google.com [209.85.221.43]) (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 3FE7B373C1E for ; Tue, 21 Apr 2026 18:25:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776795978; cv=none; b=gj6+0fkgaVitLH0nNLkwm4HvpT26hOKIG0oqlpt+/A5F8lIdN4y/GEB/Z4pn37u2a95NBXdmkOSuuLZBbgJ+gVWQP7gP7blt2XBDfGtbHoEaXpxNt5j89QLaujVuT/cKwV9msvrEDi1xh4xMbRi/cKvas7zR489w/Z2TbAtdsBU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776795978; c=relaxed/simple; bh=XDOLfamNpMiS8ohhmHXjcuP7Y/+SfT+yBUF9Gbw2JrQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=NmDpENHkY808rLyfoRxNX827PWYmuq5ufh5d4sQuUhh+0CL5iKfAi81/5Z4WjBXze+BFLvh70cJsnyA4YFzGtIue6i34+6k18RJHCNIlfIib3eiBtXAdYywatb1bfK04VXknpwmuModvkmdTpsHl74zQ3qZYeS01fXE8MI8svFk= 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=VdPWWtzW; arc=none smtp.client-ip=209.85.221.43 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="VdPWWtzW" Received: by mail-wr1-f43.google.com with SMTP id ffacd0b85a97d-43d73352cf2so3832247f8f.1 for ; Tue, 21 Apr 2026 11:25:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1776795954; x=1777400754; 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=zpreA5Lv6NS64q4BhjgYRMLorI88lJFA+slHotBbvSo=; b=VdPWWtzW7wKZA8GfKr8Dze5sp/RcpEIXH4dlfGxzeEVQRJqmwUGThyhL4tJfqxbnBq E+qUJ7gTL1BBKnq4pO5odUgjaCHOEVz8T2Ui28OCuoiBA9aVvnOhFFgEGlen2E2DlVps kSBFMUSB4n91W/XBPRpyfXnyegWyA9XuW0CLWVxRT5pD1H/0YYjm4c0mwyA1pnyyHUlV BjmDKWcXkgyugSOadxpzaFaphup+n3y71+G7IrJr27pthlylusOHx4OXnD7dxZhoE1/r hMUzWlyB9Lq03HdfNYRnrpLpO9hhiYjH8yxRFtYgfB3U9VU+Ihj16SjfpTyH8CBcluyO 5XvA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776795954; x=1777400754; 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=zpreA5Lv6NS64q4BhjgYRMLorI88lJFA+slHotBbvSo=; b=k/21R2Li8tNEKidxlBWkMd11dWiaLg4LXAbXdbD2JS+YscLTr1ECCNc1+Mq24TSo1Y EKWxRSXvKzmo8WNcL0mMBqssuRSCmJZDCDbYDn+3wmebPRltOcQlHm68UdnALiwQcfqi c9Y5aa9eF3niGA6s5WKpdxmXtoiumOqiyFwO9kEm7bfEyTV38Y4hPQRcopW5tb7LOXOp /Mz6dg1lYe7LqEOFxWzNoTVBKT4T8eNuNBoBoXuTExOEP2CP+HeE5un37Olt7cAtZW5x 6jwAYl+7wUuASiHxwhN6MtbpznWfGGB5d5S9QDrbPkT0GqGyyidOqUoV8Sh7zBtieRip 1wsQ== X-Forwarded-Encrypted: i=1; AFNElJ8hLJun57hjIlOohYLJz17veovYbUmUERDHx/KzVJbCdId8r7cEicuSz3BczkyZCiyPTiLN0XK2XxxNX8U=@vger.kernel.org X-Gm-Message-State: AOJu0YwZ3gXMJxWV9oM3rkb275bddrryUYM+or//sbY3UoZLi+cFOLIx JDaVRuU/hUasxKBEVMpKVz3VkAzcCAfTcgvhlqzWDdJwWDoJN4hM6fxG X-Gm-Gg: AeBDieuDAYiQdTXcUdg0CFDufuDSvIoveL2qfXP5625nd3l2igtlHoGUNJ//nS5Yd3O DpDgJJYD5PuO05hfKOK3NtTnyjFmO6KY8J8n3O3U79MfnLx5zkP7pCcFF1MK3zO3SfcBw0j69HP bavexptC7gz4IWBtcS/8GZbhQJhtXm7CajMuz545Tp0lA/idH5/1EfNRVAjOHmDCGOwmmdpU/Dk l/nTfeZauFLulZQK2aA+QhlfXX78HeqlCCsUOzmVbDuFlMYCVxvutHX5sev5XerY61OwDLzDUk3 TmyzDgjxBFZjlbvnIrhHolV3vA6OM7eehbSzPba6d8hlKIwdZFmPRXYMLPx0YLidWJJU1QWPoaA q3gAOB01ZBArTl3pS6855Fhjg7yqTfZGNShPg1lgrDt4IqCttkIrkulaT5Nbvm5Z7NG5VVywvZu sEuZJ3bUTHE9Prua1MMc0HOihyZPTj+9qptOxGue4l/XNbo6aDVPVOS6CpA1q7RDnVZ86pVQ3At 6A0illU3g== X-Received: by 2002:a05:6000:22c5:b0:43c:ef4f:79dc with SMTP id ffacd0b85a97d-43fe3db9b0emr29468488f8f.8.1776795953480; Tue, 21 Apr 2026 11:25:53 -0700 (PDT) Received: from f.. (cst-prg-4-152.cust.vodafone.cz. [46.135.4.152]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43fe4cb1405sm38694053f8f.4.2026.04.21.11.25.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Apr 2026 11:25:52 -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 v6 1/3] fs: add icount_read_once() and stop open-coding ->i_count loads Date: Tue, 21 Apr 2026 20:25:36 +0200 Message-ID: <20260421182538.1215894-2-mjguzik@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260421182538.1215894-1-mjguzik@gmail.com> References: <20260421182538.1215894-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 Reviewed-by: Jan Kara --- 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 40474014c03f..7dc1b8168278 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4745,7 +4745,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 69e219f0cfcb..e57873136093 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -902,7 +902,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); @@ -1920,7 +1920,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); @@ -1939,7 +1939,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); @@ -1983,7 +1983,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; @@ -2017,7 +2017,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); } @@ -3040,7 +3040,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 24040909d184..0b66cd0fa2ff 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -2843,7 +2843,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 1c098cfc5c00..f87c738d84b2 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -1158,7 +1158,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 b5b01bb22d12..a046ae84a227 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2217,8 +2217,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 Sat Jun 13 14:50:14 2026 Received: from mail-wr1-f51.google.com (mail-wr1-f51.google.com [209.85.221.51]) (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 A50F6371D07 for ; Tue, 21 Apr 2026 18:26:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776795975; cv=none; b=pgxtbat7W07V8bzsQhQZJI5VFqTZ1j0oDsCBgLwZjwiRZdTsl5YtAJufj5YyYLFr5TkUhTDNrpyT3DqPKH828Xi+08t/L4AunXnhMdLx+lD3109GNAeZML9WMrmgBIHBgNRR+Ot9pi1eBlQByJ9PLGSgSdzehRR6pnRkHdUWo8A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776795975; c=relaxed/simple; bh=z5eXgCfkQiBbuzBvjsC/+er1CqGE0uZNle1S/+mRCoY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rUSAFUbExL+V70ZxwqBjTe61Y0Z0luW0jBi/R46+60nWkPCXvtLqd+pRn98NqfeJ2RBVgsmCbZTHTwLDYIcLbn3N5vOT4Q4VV5cHRPPANanIx3XcXAT+q2N1aP7wTBX7c+cUMu/m208qNJ+6of9fNUiEwjvndOO8h/qSKr5PJb4= 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=tGV+7Jwk; arc=none smtp.client-ip=209.85.221.51 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="tGV+7Jwk" Received: by mail-wr1-f51.google.com with SMTP id ffacd0b85a97d-43cf8d550bdso4089327f8f.0 for ; Tue, 21 Apr 2026 11:26:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1776795957; x=1777400757; 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=YnjJZ3XipIhaoF0C2hZTVVvUz0isi+klnbzN+QxytNU=; b=tGV+7JwkO2D3t9YbIUzZFc5MMo1JpjpRyjQjwh3TplvH+LRdLPDOOy+18FX2wtltAm 7b1PVG2jPwql/KJUP3IDCF3gXqssoZMumhw28VkbulIauuJw9v4xXEGx9RMXNOs6bcZm /NYCQnTEZKTXbJYUhppr67PvIwWjT6KEvQ3/CmyyiE1gyY6lot6m7P9u7RXER+WQMW0z fo6v3s4rv2sukkq9kiRDZ0bbikdkSvTaJsaOUcrAX3+hWZ+xM2TBT5DxaZH+6t4hpXN0 Be5LhmZHjgzq5d4QEZMnkDkaTVsPNmltCwKxMKf/1KMCORzFd0kBfI7Dz3d1e4KTG96C YENA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776795957; x=1777400757; 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=YnjJZ3XipIhaoF0C2hZTVVvUz0isi+klnbzN+QxytNU=; b=FSEnuwFGWNSLZaEmbGqCYzLVfwtzp+k/S+l3L0KGH28Yn0NyztczoX6bPVLnZYu7G0 zsNTaisTJ9qq6YthVXHoXwM7YIsVInSIt/x+nieaMoXnoSOiKBCxdl4iZ2kgGtg0MbkE 2cdGr7I0pKRCuqFSzcAAdk5KoeCQfa71Rgd5QOdq2QgGWxzmm0Bm9l1DjNrdJ2hiQG15 NCwxKyazmMIcPW2onyD1ZML+tdm3D/KMVDDjchSFtUJV3GkiUd4MyioryMej8hP9xD+/ 19v2r2K3zUTdSh0kwMaUd3gqnslj0hssD6V/ltsOdWVhOTCn7crF78hiVs2M4B8ceYwm 5Vlg== X-Forwarded-Encrypted: i=1; AFNElJ99BPzzv6re+vTOu6Nn6zP9m7hkmzypibUkNi1zqKH2ebqB490PThD5T2ebB4v98zxTjO5P81PtRsmegA8=@vger.kernel.org X-Gm-Message-State: AOJu0Yyuryczmgh3nLysRZooS6y8uFmB8xwETghZgFoxBKGyqCNrzEnW bFejAn6vfcNS10rz+8lE4qZmanBbP5TctIR+rzrU+Tv6Okw3mX+GWjiD X-Gm-Gg: AeBDievrKTB4U0EA7fOOfo1RqCbi/BjJqyn5G3LIykRQBednSKgMJFKffrpFNZdnrpB kS9G9KfzEHl08w+TxL8RBBfblzMRLbVxyk81SWre9ZETk07ZVhJwlQTc9EkYPCNGNza+n8yAtEo 8LGemLOOych68bjXeu31wIo86+l9i5tVhfoXcUWvdPvdFP8gLS3pBHs56W9a9UVglDuXq1llf4K eXGUlH4NYUJWirs6ZDq1aa3PUHEVfHaNqbQQ/KjTCXV93oMfw/BRYYsQl5jAJhzB6XW4xDnXaAj BEMrYbqSom/LUIa/efG2b84aRgrLHRiaz2dhjrHUI6thec3TJS5yN7pQQl0pUyRwiGPPI74w47R jMcRZ307/vO/0HmaKAqJsq+mAhypZYu+/oIcNqRLIpkMGhSP6iV15P6tExP/dmjF4Zjva6QlRLJ LjCPFibb2vJ2F6AqmGSd4ikK8jvrU11p9n8L8kwifMTWrmJ8HI6LcoFehtZd6PWdSrorjfb5xx4 ag+LWMB6w== X-Received: by 2002:a05:6000:24c3:b0:43d:dd:8ca4 with SMTP id ffacd0b85a97d-43fe3dd33d4mr30768726f8f.14.1776795956948; Tue, 21 Apr 2026 11:25:56 -0700 (PDT) Received: from f.. (cst-prg-4-152.cust.vodafone.cz. [46.135.4.152]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43fe4cb1405sm38694053f8f.4.2026.04.21.11.25.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Apr 2026 11:25:56 -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 v6 2/3] fs: relocate and tidy up ihold() Date: Tue, 21 Apr 2026 20:25:37 +0200 Message-ID: <20260421182538.1215894-3-mjguzik@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260421182538.1215894-1-mjguzik@gmail.com> References: <20260421182538.1215894-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 Reviewed-by: Jan Kara --- fs/inode.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/fs/inode.c b/fs/inode.c index e57873136093..17f0804b429c 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -518,15 +518,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) { @@ -1572,6 +1563,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 Sat Jun 13 14:50:14 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 B4C6F36A02F for ; Tue, 21 Apr 2026 18:26:04 +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=1776795978; cv=none; b=nMl3cfKGQB+tmN9n4EuAm1iUiKnZzzsiVCloPrJ9YsrTP6Wsx1M34rPLltmboj0k6BR/WYoyUSxsGCHZLD9ZWccLlQFRo/gtaw3bh7VhZ4l4IpxejtSPdkwDELj0Dl4IuZAhAmY5H0cbdW9mkevs/orNLjCjwqZUkCnCM8t/9RM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776795978; c=relaxed/simple; bh=um8mWqo+ysNTDuv/IAnEQP3YwsMRzVln2eIJwdryl48=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=EM4YVpuNT7mjQoUoQjOoihMSDknEiaKxY8OIGn7TawCrxI45sfQfu+7E5tvu6IWnfLf5T0I1+sPVQZYR+Ow1l0KRoWapx5kM4U+ioYigy3Js6WGCKv/T7qVANXSxOi0sIpQ7HU1D4L+BExU6XQ3+4Y85c8fgBhTWgWrkhRsPJ3w= 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=HnUg66xV; 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="HnUg66xV" Received: by mail-wm1-f44.google.com with SMTP id 5b1f17b1804b1-4838c15e3cbso42394075e9.3 for ; Tue, 21 Apr 2026 11:26:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1776795959; x=1777400759; 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=Y+IKYoxJ9Wc1k2pNKDIE5E+KfU0dVLGiKfgsx7sEWKQ=; b=HnUg66xV4caG7iMHOpspaua4N4dM135L1sxtdyLQFQNgDNcPPcc+DLe2A9mUbXK4tj WKP0keuRydmDSwIjhX9ahCXjieQi0dNSjzmBa+8qVDX7iPjbM/87MWC7HxDcVMvWZesS L1AXksXsoQt1o17UAUDyO2rtWuRjz0J9LGggCY+sKUYOMyIt2Ri/DMuOzJVDaTfPjTuB 84bX60uuNS+6DwPyEe2fuawYMb7yLqIcpnxW0vDgMzEEcju1HlJiQRiksE0FtjTumwae LaIfWScoYo/mvpJ0E65wc39hdUJ7HG21uHzq4qM5j9ZYHK4G0egfUwfCWvJPp3Q04ILq ne+Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776795959; x=1777400759; 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=Y+IKYoxJ9Wc1k2pNKDIE5E+KfU0dVLGiKfgsx7sEWKQ=; b=nwJaD51+d9mwzb4l8sIIcKXlvesuUPu7uXmanA/qLCNwvT6gqA2PfmEXKEynbFbsPj adxF74uaKcLMpsQIqJtYZTlVH4dijtEoREGgeldlpeaz1GghmKdIGI80V6k5Zn44Tda/ yKWF10gaYBzl4JhK9R7XBKjNGZmlfGbhrSYd2si86SKD4e7KJA/RZjTwigHXiM1VBSbF QdQBPsdMdwk/FHBOJvRtfwMM378fECIZAU7LaXqeKA1xJfHnwn+jEOadBnrlbf7bm3vu QmXycSpuf7SWU9pkKpqxS56yJdASRWP6dpivTIYPGPQi96HbhXuV8mUV/0W9jyEilt7G VZgQ== X-Forwarded-Encrypted: i=1; AFNElJ+5VfzkJ+A7RSWcDTsWOvKlZ/rpdTpYNiFpaYE6PYEsBco+5Fn8atWbcgE6A5xshQvhUp6QjlKomkTqyX8=@vger.kernel.org X-Gm-Message-State: AOJu0YxTJLotMu7Dd3tfXE07QfqlwREohd3e3PGOxH6JBXXxXqCVaHeL 0tp0rCpNXo3nDPXw+dIuO00oRBhw08100q62qxc59gJFAZenmCORmZp3 X-Gm-Gg: AeBDieuPaiAwdSs3E5KQqgijAYhcdm4Th4gUkFaqGKWllN3C+/wfFUlE0aqsSKL/n3h TCalIsqKHt4o6DZ1WJR8cebowcjAtOukPSOWActYRpbqfR0LYUpJdaeuCWYiWy16Yxoh6xcka2L /Gz9ISkmXcdzv3GDf7LrGZX8jNqSzyqzCVSyWPAbrWiMC0rAriqC8Og96FG5687o+8RQD0p9MFp pRGHeBvT+ni23DRbxtxrv/Qe2Kbojq+/8a3jdqQPHv/gZC5IKqx5d73UqhurvDEIZ4N6fQQS772 hdilYaIsCx69zObJonNUQy0AFK3dUIcv8Cb1Ox4W/igcFPD6NJbQHnbvWMp+a3lH/sU9YbWNwEp 1vpHT9m5CUP+p6ssMKF9L9+QjTCZYLWpqS+TVfxk+QU5HUXwi+bBM28ttVv8l3iMUn4qAmjINBI Dg9sc9ZB6oMoMyxiyNGXNVgFe5NcBFn9JhA3JxvytUCeLF1xOFEBteNaKw5QGPUIj25KmMTDVBb 0WL5Jv+LF128A82tt8H X-Received: by 2002:a05:600c:a106:b0:48a:58ae:9933 with SMTP id 5b1f17b1804b1-48a58ae9fbdmr19378515e9.18.1776795958687; Tue, 21 Apr 2026 11:25:58 -0700 (PDT) Received: from f.. (cst-prg-4-152.cust.vodafone.cz. [46.135.4.152]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43fe4cb1405sm38694053f8f.4.2026.04.21.11.25.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Apr 2026 11:25:58 -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 v6 3/3] fs: allow lockless ->i_count bumps as long as it does not transition 0->1 Date: Tue, 21 Apr 2026 20:25:38 +0200 Message-ID: <20260421182538.1215894-4-mjguzik@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260421182538.1215894-1-mjguzik@gmail.com> References: <20260421182538.1215894-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 Reviewed-by: Jan Kara --- fs/dcache.c | 4 +++ fs/inode.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/fs.h | 4 +-- 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index df11bbba0342..13c81a6bb5e1 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_from_hash() + */ + 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 17f0804b429c..39cb22e63d5b 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1023,6 +1023,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_from_hash(struct inode *inode); =20 /* * Called with the inode lock held. @@ -1047,6 +1048,11 @@ static struct inode *find_inode(struct super_block *= sb, continue; if (!test(inode, data)) continue; + if (igrab_from_hash(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); @@ -1089,6 +1095,11 @@ static struct inode *find_inode_fast(struct super_bl= ock *sb, continue; if (inode->i_sb !=3D sb) continue; + if (igrab_from_hash(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); @@ -1206,6 +1217,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_from_hash() + */ + smp_wmb(); inode_state_clear(inode, I_NEW | I_CREATING); inode_wake_up_bit(inode, __I_NEW); spin_unlock(&inode->i_lock); @@ -1217,6 +1232,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_from_hash() + */ + smp_wmb(); inode_state_clear(inode, I_NEW); inode_wake_up_bit(inode, __I_NEW); spin_unlock(&inode->i_lock); @@ -1576,6 +1595,14 @@ EXPORT_SYMBOL(ihold); =20 struct inode *igrab(struct inode *inode) { + /* + * Read commentary above igrab_from_hash() for an explanation why this wo= rks. + */ + 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); @@ -1593,6 +1620,43 @@ struct inode *igrab(struct inode *inode) } EXPORT_SYMBOL(igrab); =20 +/* + * igrab_from_hash - special inode refcount acquire primitive for the inod= e hash + * + * 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 replace a non-0 value with a bigger one, we have a guarantee= 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_from_hash(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 a046ae84a227..bbe179b02234 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2226,8 +2226,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