mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-12-27 13:30:45 -05:00
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>
This commit is contained in:
@@ -12,9 +12,12 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include "../kselftest_harness.h"
|
||||
#include "../filesystems/utils.h"
|
||||
#include "wrappers.h"
|
||||
|
||||
#ifndef FD_NSFS_ROOT
|
||||
#define FD_NSFS_ROOT -10003 /* Root of the nsfs filesystem */
|
||||
@@ -2113,4 +2116,139 @@ TEST(ns_mixed_types_same_owner)
|
||||
ASSERT_LT(u_fd, 0);
|
||||
}
|
||||
|
||||
/* Thread test helpers and structures */
|
||||
struct thread_ns_info {
|
||||
__u64 ns_id;
|
||||
int pipefd;
|
||||
int syncfd_read;
|
||||
int syncfd_write;
|
||||
int exit_code;
|
||||
};
|
||||
|
||||
static void *thread_create_namespace(void *arg)
|
||||
{
|
||||
struct thread_ns_info *info = (struct thread_ns_info *)arg;
|
||||
int ret;
|
||||
|
||||
/* Create new network namespace */
|
||||
ret = unshare(CLONE_NEWNET);
|
||||
if (ret < 0) {
|
||||
info->exit_code = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get namespace ID */
|
||||
int fd = open("/proc/thread-self/ns/net", O_RDONLY);
|
||||
if (fd < 0) {
|
||||
info->exit_code = 2;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = ioctl(fd, NS_GET_ID, &info->ns_id);
|
||||
close(fd);
|
||||
if (ret < 0) {
|
||||
info->exit_code = 3;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Send namespace ID to main thread */
|
||||
if (write(info->pipefd, &info->ns_id, sizeof(info->ns_id)) != sizeof(info->ns_id)) {
|
||||
info->exit_code = 4;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Wait for signal to exit */
|
||||
char sync_byte;
|
||||
if (read(info->syncfd_read, &sync_byte, 1) != 1) {
|
||||
info->exit_code = 5;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
info->exit_code = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test that namespace becomes inactive after thread exits.
|
||||
* This verifies active reference counting works with threads, not just processes.
|
||||
*/
|
||||
TEST(thread_ns_inactive_after_exit)
|
||||
{
|
||||
pthread_t thread;
|
||||
struct thread_ns_info info;
|
||||
struct file_handle *handle;
|
||||
int pipefd[2];
|
||||
int syncpipe[2];
|
||||
int ret;
|
||||
char sync_byte;
|
||||
char buf[sizeof(*handle) + MAX_HANDLE_SZ];
|
||||
|
||||
ASSERT_EQ(pipe(pipefd), 0);
|
||||
ASSERT_EQ(pipe(syncpipe), 0);
|
||||
|
||||
info.pipefd = pipefd[1];
|
||||
info.syncfd_read = syncpipe[0];
|
||||
info.syncfd_write = -1;
|
||||
info.exit_code = -1;
|
||||
|
||||
/* Create thread that will create a namespace */
|
||||
ret = pthread_create(&thread, NULL, thread_create_namespace, &info);
|
||||
ASSERT_EQ(ret, 0);
|
||||
|
||||
/* Read namespace ID from thread */
|
||||
__u64 ns_id;
|
||||
ret = read(pipefd[0], &ns_id, sizeof(ns_id));
|
||||
if (ret != sizeof(ns_id)) {
|
||||
sync_byte = 'X';
|
||||
write(syncpipe[1], &sync_byte, 1);
|
||||
pthread_join(thread, NULL);
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
close(syncpipe[0]);
|
||||
close(syncpipe[1]);
|
||||
SKIP(return, "Failed to read namespace ID from thread");
|
||||
}
|
||||
|
||||
TH_LOG("Thread created namespace with ID %llu", (unsigned long long)ns_id);
|
||||
|
||||
/* Construct file handle */
|
||||
handle = (struct file_handle *)buf;
|
||||
handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
||||
handle->handle_type = FILEID_NSFS;
|
||||
struct nsfs_file_handle *fh = (struct nsfs_file_handle *)handle->f_handle;
|
||||
fh->ns_id = ns_id;
|
||||
fh->ns_type = 0;
|
||||
fh->ns_inum = 0;
|
||||
|
||||
/* Namespace should be active while thread is alive */
|
||||
TH_LOG("Attempting to open namespace while thread is alive (should succeed)");
|
||||
int nsfd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
|
||||
ASSERT_GE(nsfd, 0);
|
||||
close(nsfd);
|
||||
|
||||
/* Signal thread to exit */
|
||||
TH_LOG("Signaling thread to exit");
|
||||
sync_byte = 'X';
|
||||
ASSERT_EQ(write(syncpipe[1], &sync_byte, 1), 1);
|
||||
close(syncpipe[1]);
|
||||
|
||||
/* Wait for thread to exit */
|
||||
ASSERT_EQ(pthread_join(thread, NULL), 0);
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
close(syncpipe[0]);
|
||||
|
||||
if (info.exit_code != 0)
|
||||
SKIP(return, "Thread failed to create namespace");
|
||||
|
||||
TH_LOG("Thread exited, namespace should be inactive");
|
||||
|
||||
/* Namespace should now be inactive */
|
||||
nsfd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
|
||||
ASSERT_LT(nsfd, 0);
|
||||
/* Should fail with ENOENT (inactive) or ESTALE (gone) */
|
||||
TH_LOG("Namespace inactive as expected: %s (errno=%d)", strerror(errno), errno);
|
||||
ASSERT_TRUE(errno == ENOENT || errno == ESTALE);
|
||||
}
|
||||
|
||||
TEST_HARNESS_MAIN
|
||||
|
||||
Reference in New Issue
Block a user