Skip to content

Commit 29f083c

Browse files
committed
selftests/namespace: first threaded active reference count test
Test that namespace becomes inactive after thread exits. This verifies active reference counting works with threads, not just processes. Link: https://patch.msgid.link/20251029-work-namespace-nstree-listns-v4-67-2e6f823ebdc0@kernel.org Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent c89d100 commit 29f083c

1 file changed

Lines changed: 138 additions & 0 deletions

File tree

tools/testing/selftests/namespaces/ns_active_ref_test.c

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@
1212
#include <sys/stat.h>
1313
#include <sys/types.h>
1414
#include <sys/wait.h>
15+
#include <sys/syscall.h>
1516
#include <unistd.h>
17+
#include <pthread.h>
1618
#include "../kselftest_harness.h"
1719
#include "../filesystems/utils.h"
20+
#include "wrappers.h"
1821

1922
#ifndef FD_NSFS_ROOT
2023
#define FD_NSFS_ROOT -10003 /* Root of the nsfs filesystem */
@@ -2113,4 +2116,139 @@ TEST(ns_mixed_types_same_owner)
21132116
ASSERT_LT(u_fd, 0);
21142117
}
21152118

2119+
/* Thread test helpers and structures */
2120+
struct thread_ns_info {
2121+
__u64 ns_id;
2122+
int pipefd;
2123+
int syncfd_read;
2124+
int syncfd_write;
2125+
int exit_code;
2126+
};
2127+
2128+
static void *thread_create_namespace(void *arg)
2129+
{
2130+
struct thread_ns_info *info = (struct thread_ns_info *)arg;
2131+
int ret;
2132+
2133+
/* Create new network namespace */
2134+
ret = unshare(CLONE_NEWNET);
2135+
if (ret < 0) {
2136+
info->exit_code = 1;
2137+
return NULL;
2138+
}
2139+
2140+
/* Get namespace ID */
2141+
int fd = open("/proc/thread-self/ns/net", O_RDONLY);
2142+
if (fd < 0) {
2143+
info->exit_code = 2;
2144+
return NULL;
2145+
}
2146+
2147+
ret = ioctl(fd, NS_GET_ID, &info->ns_id);
2148+
close(fd);
2149+
if (ret < 0) {
2150+
info->exit_code = 3;
2151+
return NULL;
2152+
}
2153+
2154+
/* Send namespace ID to main thread */
2155+
if (write(info->pipefd, &info->ns_id, sizeof(info->ns_id)) != sizeof(info->ns_id)) {
2156+
info->exit_code = 4;
2157+
return NULL;
2158+
}
2159+
2160+
/* Wait for signal to exit */
2161+
char sync_byte;
2162+
if (read(info->syncfd_read, &sync_byte, 1) != 1) {
2163+
info->exit_code = 5;
2164+
return NULL;
2165+
}
2166+
2167+
info->exit_code = 0;
2168+
return NULL;
2169+
}
2170+
2171+
/*
2172+
* Test that namespace becomes inactive after thread exits.
2173+
* This verifies active reference counting works with threads, not just processes.
2174+
*/
2175+
TEST(thread_ns_inactive_after_exit)
2176+
{
2177+
pthread_t thread;
2178+
struct thread_ns_info info;
2179+
struct file_handle *handle;
2180+
int pipefd[2];
2181+
int syncpipe[2];
2182+
int ret;
2183+
char sync_byte;
2184+
char buf[sizeof(*handle) + MAX_HANDLE_SZ];
2185+
2186+
ASSERT_EQ(pipe(pipefd), 0);
2187+
ASSERT_EQ(pipe(syncpipe), 0);
2188+
2189+
info.pipefd = pipefd[1];
2190+
info.syncfd_read = syncpipe[0];
2191+
info.syncfd_write = -1;
2192+
info.exit_code = -1;
2193+
2194+
/* Create thread that will create a namespace */
2195+
ret = pthread_create(&thread, NULL, thread_create_namespace, &info);
2196+
ASSERT_EQ(ret, 0);
2197+
2198+
/* Read namespace ID from thread */
2199+
__u64 ns_id;
2200+
ret = read(pipefd[0], &ns_id, sizeof(ns_id));
2201+
if (ret != sizeof(ns_id)) {
2202+
sync_byte = 'X';
2203+
write(syncpipe[1], &sync_byte, 1);
2204+
pthread_join(thread, NULL);
2205+
close(pipefd[0]);
2206+
close(pipefd[1]);
2207+
close(syncpipe[0]);
2208+
close(syncpipe[1]);
2209+
SKIP(return, "Failed to read namespace ID from thread");
2210+
}
2211+
2212+
TH_LOG("Thread created namespace with ID %llu", (unsigned long long)ns_id);
2213+
2214+
/* Construct file handle */
2215+
handle = (struct file_handle *)buf;
2216+
handle->handle_bytes = sizeof(struct nsfs_file_handle);
2217+
handle->handle_type = FILEID_NSFS;
2218+
struct nsfs_file_handle *fh = (struct nsfs_file_handle *)handle->f_handle;
2219+
fh->ns_id = ns_id;
2220+
fh->ns_type = 0;
2221+
fh->ns_inum = 0;
2222+
2223+
/* Namespace should be active while thread is alive */
2224+
TH_LOG("Attempting to open namespace while thread is alive (should succeed)");
2225+
int nsfd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
2226+
ASSERT_GE(nsfd, 0);
2227+
close(nsfd);
2228+
2229+
/* Signal thread to exit */
2230+
TH_LOG("Signaling thread to exit");
2231+
sync_byte = 'X';
2232+
ASSERT_EQ(write(syncpipe[1], &sync_byte, 1), 1);
2233+
close(syncpipe[1]);
2234+
2235+
/* Wait for thread to exit */
2236+
ASSERT_EQ(pthread_join(thread, NULL), 0);
2237+
close(pipefd[0]);
2238+
close(pipefd[1]);
2239+
close(syncpipe[0]);
2240+
2241+
if (info.exit_code != 0)
2242+
SKIP(return, "Thread failed to create namespace");
2243+
2244+
TH_LOG("Thread exited, namespace should be inactive");
2245+
2246+
/* Namespace should now be inactive */
2247+
nsfd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
2248+
ASSERT_LT(nsfd, 0);
2249+
/* Should fail with ENOENT (inactive) or ESTALE (gone) */
2250+
TH_LOG("Namespace inactive as expected: %s (errno=%d)", strerror(errno), errno);
2251+
ASSERT_TRUE(errno == ENOENT || errno == ESTALE);
2252+
}
2253+
21162254
TEST_HARNESS_MAIN

0 commit comments

Comments
 (0)