[PATCH][cr-tests]: eclone-1: Test basic functionality

Sukadev Bhattiprolu sukadev at linux.vnet.ibm.com
Tue Feb 2 11:35:08 PST 2010


From: Sukadev Bhattiprolu <sukadev at linux.vnet.ibm.com>
Date: Sat, 30 Jan 2010 12:49:30 -0800
Subject: [PATCH 1/6] eclone-1: Test basic functionality

Verify that a child process gets the expected pid and arguments on stack
when it is created with eclone() system call.

NOTE:	The myclone() function in eclone-lib.c supports just x86 for now.
	Needs to be ported to other architectures.

Signed-off-by: Sukadev Bhattiprolu <sukadev at linux.vnet.ibm.com>
---
 Makefile            |    2 +-
 eclone/Makefile     |   16 +++++++
 eclone/clone_args.h |   37 ++++++++++++++++
 eclone/eclone-1.c   |  121 +++++++++++++++++++++++++++++++++++++++++++++++++++
 eclone/eclone-lib.c |   85 ++++++++++++++++++++++++++++++++++++
 5 files changed, 260 insertions(+), 1 deletions(-)
 create mode 100644 eclone/Makefile
 create mode 100644 eclone/clone_args.h
 create mode 100644 eclone/eclone-1.c
 create mode 100644 eclone/eclone-lib.c

diff --git a/Makefile b/Makefile
index 95f8b6e..1d412b9 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
 SUBDIRS = libcrtest counterloop fileio simple userns ipc sleep \
-	  process-tree futex epoll taskfs
+	  process-tree futex epoll taskfs eclone
 
 targets = ns_exec mysu
 
diff --git a/eclone/Makefile b/eclone/Makefile
new file mode 100644
index 0000000..43cef65
--- /dev/null
+++ b/eclone/Makefile
@@ -0,0 +1,16 @@
+
+CFLAGS = -Wall
+
+LDFLAGS = 
+
+PROGS = eclone-1
+
+all: $(PROGS)
+
+eclone-lib.o: eclone-lib.c clone_args.h
+
+$(PROGS): %: %.c eclone-lib.o
+	$(CC) $(CFLAGS) -o $@ $< eclone-lib.o $(LDFLAGS)
+
+clean:
+	rm -f $(PROGS) eclone-lib.o
diff --git a/eclone/clone_args.h b/eclone/clone_args.h
new file mode 100644
index 0000000..393f139
--- /dev/null
+++ b/eclone/clone_args.h
@@ -0,0 +1,37 @@
+#include <errno.h>
+
+#define __NR_clone_with_pids 	337
+#define __NR_clone3		337
+#define __NR_eclone		337
+#define __NR_clone 		120
+#define __NR_exit 		1
+#define __NR_getpid 		20
+
+#define CLONE_NEWPID            0x20000000
+#define CLONE_CHILD_SETTID      0x01000000
+#define CLONE_PARENT_SETTID     0x00100000
+#define CLONE_UNUSED		0x00001000
+
+#define STACKSIZE	8192
+
+typedef unsigned long long u64;
+typedef unsigned int u32;
+typedef int pid_t;
+struct clone_args {
+        u64 clone_flags_high;
+
+        u64 child_stack_base;
+        u64 child_stack_size;
+
+        u64 parent_tid_ptr;
+        u64 child_tid_ptr;
+
+        u32 nr_pids;
+
+        u32 reserved0;
+};
+
+void *setup_stack(int (*child_fn)(void *), void *child_arg, int stack_size);
+int eclone(int flags_low, struct clone_args *clone_args, int args_size,
+		int *pids);
+int gettid();
diff --git a/eclone/eclone-1.c b/eclone/eclone-1.c
new file mode 100644
index 0000000..06a10f9
--- /dev/null
+++ b/eclone/eclone-1.c
@@ -0,0 +1,121 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/syscall.h>
+#include "clone_args.h"
+
+int verbose = 0;
+int child_tid, parent_tid;
+
+#define CHILD_TID1	377
+#define	CHILD_TID2	399
+#define	CHILD_ARG	(void *)0x979797
+
+pid_t pids_list[] = { CHILD_TID1, CHILD_TID2 };
+
+int do_child(void *arg)
+{
+	sleep(3);
+
+	if (arg != CHILD_ARG) {
+		printf("ERROR: Expected arg %p, actual %p\n", CHILD_ARG, arg);
+		exit(1);
+	}
+
+	if (gettid() != CHILD_TID1) {
+		printf("FAIL: Child expected pid %d, actual %d\n", CHILD_TID1,
+					getpid());
+		exit(2);
+	} else {
+		printf("PASS: Child got expected pid %d\n", CHILD_TID1);
+		exit(0);
+	}
+
+}
+
+static int myclone(int (*child_fn)(void *), void *child_arg, 
+		unsigned int flags_low, int nr_pids, pid_t *pids_list)
+{
+	int rc;
+	void *stack;
+	struct clone_args ca;
+	int args_size;
+
+	stack = setup_stack(child_fn, child_arg, STACKSIZE);
+	if (!stack) {
+		printf("ERROR: setup_stack returns NULL for size %d\n",
+				STACKSIZE);
+		exit(1);
+	}
+
+	memset(&ca, 0, sizeof(ca));
+	ca.child_stack_base = (u64)(int)stack;
+	ca.child_stack_size = (u64)0;
+	ca.parent_tid_ptr = (u64)((int)&parent_tid);
+	ca.child_tid_ptr = (u64)((int)&child_tid);
+	ca.nr_pids = nr_pids;
+
+	if (verbose) {
+		printf("[%d, %d]: Parent:\n\t child_stack 0x%p, ptidp %llx, "
+				"ctidp %llx, pids %p\n", getpid(), gettid(),
+				stack, ca.parent_tid_ptr, ca.child_tid_ptr,
+				pids_list);
+	}
+
+	args_size = sizeof(struct clone_args);
+	rc = eclone(flags_low, &ca, args_size, pids_list);
+
+	if (verbose) {
+		printf("[%d, %d]: eclone() returned %d, error %d\n", getpid(),
+				gettid(), rc, errno);
+		fflush(stdout);
+	}
+
+	if (rc < 0) {
+		printf("ERROR: ");
+		exit(1);
+	}
+
+	return rc;
+}
+
+int main()
+{
+	int rc, pid, status;
+	unsigned long flags; 
+	int nr_pids = 1;
+
+	flags = SIGCHLD|CLONE_PARENT_SETTID|CLONE_CHILD_SETTID;
+
+	pid = myclone(do_child, (void *)CHILD_ARG, flags, nr_pids, pids_list);
+
+	if (verbose) {
+		printf("[%d, %d]: Parent waiting for %d\n", getpid(),
+					gettid(), pid);
+	}
+
+	rc = waitpid(pid, &status, __WALL);
+	if (rc < 0) {
+		printf("ERROR: ");
+		verbose = 1;
+	}
+
+	if (verbose) {
+		printf("\twaitpid(): child %d, rc %d, error %d, status 0x%x\n",
+				getpid(), rc, errno, status);
+		if (rc >=0) {
+			if (WIFEXITED(status)) {
+				printf("\t EXITED, %d\n", WEXITSTATUS(status));
+			} else if (WIFSIGNALED(status)) {
+				printf("\t SIGNALED, %d\n", WTERMSIG(status));
+			} 
+		}
+	}
+
+	return 0;
+}
diff --git a/eclone/eclone-lib.c b/eclone/eclone-lib.c
new file mode 100644
index 0000000..f547945
--- /dev/null
+++ b/eclone/eclone-lib.c
@@ -0,0 +1,85 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include "clone_args.h"
+
+/* From Oren Laadan */
+
+#if defined(__i386__) && defined(__NR_eclone)
+/*
+ * libc doesn't support eclone() yet...
+ * (see: http://lkml.indiana.edu/hypermail/linux/kernel/9604.3/0204.html)
+ */
+
+int eclone(int flags_low, struct clone_args *clone_args, int args_size,
+		int *pids)
+{
+	long retval;
+
+	__asm__ __volatile__(
+		 "movl %3, %%ebx\n\t"	/* flags_low -> 1st (ebx)  */
+		 "movl %4, %%ecx\n\t"	/* clone_args -> 2nd (ecx) */
+		 "movl %5, %%edx\n\t"	/* args_size -> 3rd (edx) */
+		 "movl %6, %%esi\n\t"	/* pids -> 4th (esi) */
+
+		 "pushl %%ebp\n\t"	/* save value of ebp */
+		 "int $0x80\n\t"	/* Linux/i386 system call */
+		 "testl %0,%0\n\t"	/* check return value */
+		 "jne 1f\n\t"		/* jump if parent */
+		 "popl %%esi\n\t"	/* get subthread function */
+		 "call *%%esi\n\t"	/* start subthread function */
+		 "movl %2,%0\n\t"
+		 "int $0x80\n"		/* exit system call: exit subthread */
+		 "1:\n\t"
+		 "popl %%ebp\t"		/* restore parent's ebp */
+
+		:"=a" (retval)
+
+		:"0" (__NR_clone3),
+		 "i" (__NR_exit),
+		 "b" (flags_low),
+		 "c" (clone_args),
+		 "d" (args_size),
+		 "D" (pids)
+	);
+
+	if (retval < 0) {
+		errno = -retval;
+		retval = -1;
+	}
+	return retval;
+}
+
+void *setup_stack(int (*child_fn)(void *), void *child_arg, int size)
+{
+	void *stack_base;
+	void **stack_top;
+	
+	stack_base = malloc(size);
+	if (!stack_base)
+		return NULL;
+
+	stack_top = (void **)((char *)stack_base + (size - 4));
+	*--stack_top = child_arg;
+	*--stack_top = child_fn;
+
+	return stack_top;
+}
+
+#endif
+
+/* gettid() is a bit more useful than getpid() when messing with clone() */
+int gettid()
+{
+	int rc;
+
+	rc = syscall(__NR_gettid, 0, 0, 0);
+	if (rc < 0) {
+		printf("rc %d, errno %d\n", rc, errno);
+		exit(1);
+	}
+	return rc;
+}
-- 
1.6.6.1



More information about the Containers mailing list