From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from outbound.st.icloud.com (p-east2-cluster5-host8-snip4-6.eps.apple.com [57.103.79.39]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 55B0B314B93 for ; Sun, 5 Apr 2026 16:54:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=57.103.79.39 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775408050; cv=none; b=pX8rNQB5l9kMf1hzpABrBoiyP4sIPFAaYjYCtbqbuenLPVGPhpcoi+bW0hpIPJhe9uXMMehJXQ0MsAbluymYP8UZ04RBkyo2clbLStC1sSY+G80ygxQjQzi/KOW5hJCyfEBu5zPvhUxbzpYH2O9XKlSpLvGSD65ciTDk8rxA9l0= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775408050; c=relaxed/simple; bh=dpzep7CuJUyQ4oOXK2u+mXps1e2CVZNuzYgiGwnuiho=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=JRmP1LodPFcGEnb7ckDhk8Phh4T1JEPZ80qw8TFFbhylcZPwckFhtIVHSdOPEvaWoxX8by4XFkTgI0ZAaLFl/56wpf+SHZfG48EBktgdVuE+Ws52h0R2XxMcPGBI60z9SQsvINZm37KqyTu+pgvwEHcJufzzXaZ9ceqtATCsnVg= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=y-koj.net; spf=pass smtp.mailfrom=y-koj.net; dkim=fail (0-bit key) header.d=y-koj.net header.i=@y-koj.net header.b=WBB4N//0 reason="key not found in DNS"; arc=none smtp.client-ip=57.103.79.39 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=y-koj.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=y-koj.net Authentication-Results: smtp.subspace.kernel.org; dkim=fail reason="key not found in DNS" (0-bit key) header.d=y-koj.net header.i=@y-koj.net header.b="WBB4N//0" Received: from outbound.st.icloud.com (unknown [127.0.0.2]) by p00-icloudmta-asmtp-us-east-1a-100-percent-6 (Postfix) with ESMTPS id 3F1111800B86; Sun, 05 Apr 2026 16:54:08 +0000 (UTC) Dkim-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=y-koj.net; s=sig1; t=1775408048; x=1778000048; bh=mu9GPhEjV1sXVPEctTosQ4rZ0Uc8Riw++0yPOtB7lm4=; h=From:To:Subject:Date:Message-ID:MIME-Version:x-icloud-hme; b=WBB4N//0ZriRZdZNC4xj1q9yHYgr9FKpKgvMuoNxHGlnYVsOTPmE329VXA6ofvua9kI2pTj9hSCgY4Yrff6+g3Y5ckAolyK1kSTmzUEMba4zn0J/VSsK+AxYxssd+2z/ZYDXV29oVWx0kiKLRVfyPGL0/KmZCzdAp4u+9+jJKp80EwZa57pRI9Y3OwmeP25esyouUI5upuaAqCLboQZ1jbvJ4efOkzQj76ky3XdCXzuBOFzPqxB5Z/NndO9b90HaJ+5WiORG6Yj3YsHnQRQqik+px577cTzK4QV8vGFpstdo+AF03CufHXCs2DDbhFPktyZWzXwu5UxOEJxc/xs6Wg== mail-alias-created-date: 1719758601013 Received: from desktop.y-koj.net (unknown [17.42.251.67]) by p00-icloudmta-asmtp-us-east-1a-100-percent-6 (Postfix) with ESMTPSA id 8F7651800FFD; Sun, 05 Apr 2026 16:52:29 +0000 (UTC) From: Yohei Kojima To: Christian Brauner Cc: Yohei Kojima , linux-kernel@vger.kernel.org Subject: [PATCH 3/4] nstree: Fix spurious ENOENT in listns pagination during grace period Date: Mon, 6 Apr 2026 01:50:39 +0900 Message-ID: X-Mailer: git-send-email 2.52.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Proofpoint-ORIG-GUID: 1Y80KsAwImS3q7iiZxsVt5YtuOqW2o4G X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNDA1MDE3NiBTYWx0ZWRfX9flNpJOR3oel fs9Qs1x33AIuxOFwzcHaaQivvXXZNE2S6FW79Iop52bF2cNqvPICvErzdB8gbRSeBDpt0UbLdTD tyGnJcm5LxdbyjfB8DubLMYId0OBDQc+uxQrI3mqJ1C1UdAXunWfjfoJUcJL78sAAhadxTjcXSL QrqDvTIHocuQyo3P6rCQTXBxFHbejQ/Nbmocn1uIaLZdGTZV2gXngU0pgp5IbGwB/kovjsApgLL y6920hv4YfMSVHdi6ONB/Yb3fgtHcHJpG0SnZCw7jsdIYuh/Btjq8xxBAA4cV5BSZ7M0IGW2R5l MyY+Vfd4LjfldzaVTNf4A1d70gtMlG60L2PIhX7k8vPVxczFA5ni1HDmqFgTZg= X-Authority-Info-Out: v=2.4 cv=ArnjHe9P c=1 sm=1 tr=0 ts=69d293b0 cx=c_apl:c_pps:t_out a=YrL12D//S6tul8v/L+6tKg==:117 a=YrL12D//S6tul8v/L+6tKg==:17 a=A5OVakUREuEA:10 a=VkNPw1HP01LnGYTKEx00:22 a=jnl5ZKOAAAAA:8 a=ilVEDnY-_uOBHNOHIFwA:9 a=RNrZ5ZR47oNZP8zBN2PD:22 X-Proofpoint-GUID: 1Y80KsAwImS3q7iiZxsVt5YtuOqW2o4G X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.51,FMLib:17.12.100.49 definitions=2026-04-05_05,2026-04-03_01,2025-10-01_01 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=0 phishscore=0 clxscore=1030 malwarescore=0 lowpriorityscore=0 mlxlogscore=781 bulkscore=0 adultscore=0 mlxscore=0 spamscore=0 classifier=spam authscore=0 adjust=0 reason=mlx scancount=1 engine=8.22.0-2601150000 definitions=main-2604050176 Fix false ENOENT returned from listns when (1) pagination is used (req.ns_id != 0) and (2) listns tries to start enumeration from a destroyed or inactive namespace. The cause was that lookup_ns_id_at(kls->last_ns_id + 1, ...) returned NULL if the first namespace after ns_id was destroyed or inactivated like below: (Note that we can take nstree as a list as it is an rbtree sorted by ns id.) A: active namespace D: destroyed (or inactive) namespace +-----+-----+-----+-----+-----+-----+-----+-----+ state: | A | A | A | D | D | A | A | A | ns_id: | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | +-----+-----+-----+-----+-----+-----+-----+-----+ | | | +-- (kls->last_ns_id + 1) +-- req.ns_id = 3 In this case, lookup_ns_id_at() returns NULL, which results in -ENOENT returned from do_listns() although three namespaces remains in the nstree. The bug is fixed by iterating over the nstree's internal list until it reaches the first active namespace. Fixes: 76b6f5dfb3fd ("nstree: add listns()") Signed-off-by: Yohei Kojima --- kernel/nstree.c | 68 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/kernel/nstree.c b/kernel/nstree.c index 6d12e5900ac0..476d22203ee0 100644 --- a/kernel/nstree.c +++ b/kernel/nstree.c @@ -618,14 +618,32 @@ static ssize_t do_listns_userns(struct klistns *kls) return ret; } +static inline struct ns_common *next_ns_common(struct ns_common *ns, + struct ns_tree_root *ns_tree) +{ + if (ns_tree) + return list_entry_rcu(ns->ns_tree_node.ns_list_entry.next, struct ns_common, ns_tree_node.ns_list_entry); + return list_entry_rcu(ns->ns_unified_node.ns_list_entry.next, struct ns_common, ns_unified_node.ns_list_entry); +} + +static inline bool ns_common_is_head(struct ns_common *ns, + const struct list_head *head, + struct ns_tree_root *ns_tree) +{ + if (ns_tree) + return &ns->ns_tree_node.ns_list_entry == head; + return &ns->ns_unified_node.ns_list_entry == head; +} + /* * Lookup a namespace with id >= ns_id in either the unified tree or a type-specific tree. * Returns the namespace with the smallest id that is >= ns_id. */ static struct ns_common *lookup_ns_id_at(u64 ns_id, int ns_type) { - struct ns_common *ret = NULL; + struct ns_common *min = NULL, *ret = NULL; struct ns_tree_root *ns_tree = NULL; + struct list_head *head; struct rb_node *node; if (ns_type) { @@ -651,9 +669,9 @@ static struct ns_common *lookup_ns_id_at(u64 ns_id, int ns_type) if (ns_id <= ns->ns_id) { if (ns_type) - ret = node_to_ns(node); + min = node_to_ns(node); else - ret = node_to_ns_unified(node); + min = node_to_ns_unified(node); if (ns_id == ns->ns_id) break; node = node->rb_left; @@ -662,8 +680,31 @@ static struct ns_common *lookup_ns_id_at(u64 ns_id, int ns_type) } } - if (ret) - ret = ns_get_unless_inactive(ret); + if (!min) + return NULL; + /* + * Now min->ns_id is the minimum id where min->ns_id >= ns_id holds, + * but min could be inactive or destroyed here, therefore + * ns_get_unless_inactive(min) could return NULL. + * + * To handle this case, try acquiring the next ns until it reaches the + * first valid ns. + */ + if (ns_tree) + head = &ns_tree->ns_list_head; + else + head = &ns_unified_root.ns_list_head; + + while (!ns_common_is_head(min, head, ns_tree)) { + ret = ns_get_unless_inactive(min); + if (ret) + break; + + rcu_read_lock(); + min = next_ns_common(min, ns_tree); + rcu_read_unlock(); + } + return ret; } @@ -675,23 +716,6 @@ static inline struct ns_common *first_ns_common(const struct list_head *head, return list_entry_rcu(head->next, struct ns_common, ns_unified_node.ns_list_entry); } -static inline struct ns_common *next_ns_common(struct ns_common *ns, - struct ns_tree_root *ns_tree) -{ - if (ns_tree) - return list_entry_rcu(ns->ns_tree_node.ns_list_entry.next, struct ns_common, ns_tree_node.ns_list_entry); - return list_entry_rcu(ns->ns_unified_node.ns_list_entry.next, struct ns_common, ns_unified_node.ns_list_entry); -} - -static inline bool ns_common_is_head(struct ns_common *ns, - const struct list_head *head, - struct ns_tree_root *ns_tree) -{ - if (ns_tree) - return &ns->ns_tree_node.ns_list_entry == head; - return &ns->ns_unified_node.ns_list_entry == head; -} - static ssize_t do_listns(struct klistns *kls) { u64 __user *ns_ids = kls->uns_ids; -- 2.52.0