Skip to contentSkip to conversation

clone testcases in LTP

DISCUSSION Nathan Straz 5 participants 12 messages Jul 9, 2003 – Jul 11, 2003

lore
Share & tools
lore
#0 lore raw

Could someone in the Linux ia64 community look at the clone system call tests that the LTP has under ltp/testcases/kernel/syscalls/clone? I know that ia64 uses clone2() instead of clone() but I don't know where the differences are documented. I would like to get these test cases, or some variation or them, compiling and running on ia64.

Thanks,

Signature · 3 lines
Nate Straz                                              [email protected]
sgi, inc                                           http://www.sgi.com/
Linux Test Project                                  http://ltp.sf.net/
#1 lore raw
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line · Nathan Straz

Nathan> Could someone in the Linux ia64 community look at the clone Nathan> system call tests that the LTP has under
Nathan> ltp/testcases/kernel/syscalls/clone? I know that ia64 uses Nathan> clone2() instead of clone() but I don't know where the Nathan> differences are documented. I would like to get these test Nathan> cases, or some variation or them, compiling and running on Nathan> ia64.

The only difference between clone() and clone2() is the explicit stack-size argument. clone2() is discussed in detail in my book, but if you're already familiar with clone(), it's easy to understand what's going on. Here is the pseudo prototype (from glibc):

/* int __clone2(int (*fn) (void *arg), void *child_stack_base, */
/* size_t child_stack_size, int flags, void *arg, */
/* pid_t *parent_tid, void *tls, pid_t *child_tid) */

	--david
#2 lore raw
In reply to Nathan Straz
Quoted 8 lines · David Mosberger

The only difference between clone() and clone2() is the explicit stack-size argument. clone2() is discussed in detail in my book, but if you're already familiar with clone(), it's easy to understand what's going on. Here is the pseudo prototype (from glibc):

/* int __clone2(int (*fn) (void *arg), void *child_stack_base, */
/* size_t child_stack_size, int flags, void *arg, */
/* pid_t *parent_tid, void *tls, pid_t *child_tid) */

Hmm, that prototype seems to have a lot more args than the clone man page states:

   #include <sched.h>
   int clone(int (*fn)(void *), void *child_stack, int flags, void *arg);
   _syscall2(int, clone, int, flags, void *, child_stack);

What are the parameters parent_tid, tls, and child_tid supposed to be?

Signature · 3 lines
Nate Straz                                              [email protected]
sgi, inc                                           http://www.sgi.com/
Linux Test Project                                  http://ltp.sf.net/
#3 lore raw
In reply to Nathan Straz
Quoted 5 lines
Quoted 5 lines
Quoted 1 line
Quoted 1 line
Quoted 1 line · Nathan Straz

/* int __clone2(int (*fn) (void *arg), void *child_stack_base, */ /* size_t child_stack_size, int flags, void *arg, */ /* pid_t *parent_tid, void *tls, pid_t *child_tid) */

Nathan> Hmm, that prototype seems to have a lot more args than the Nathan> clone man page states:

Nathan> #include <sched.h> int clone(int (*fn)(void *), void
Nathan> *child_stack, int flags, void *arg); _syscall2(int, clone, Nathan> int, flags, void *, child_stack);

Nathan> What are the parameters parent_tid, tls, and child_tid Nathan> supposed to be?

Hint 1: search google for "NPTL".
Hint 2: it's nothing ia64-specific.
Hint 3: the arguments are ignored unless you specify certain CLONE* flags,
so the interface remains backwards-compatible.

Hope this helps.

	--david
#4 lore raw
In reply to Nathan Straz
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 3 lines
Quoted 3 lines

"David" = David Mosberger <[email protected]> writes:

On Thu, 10 Jul 2003 11:18:38 -0500, Nathan Straz <[email protected]> said:

/* int __clone2(int (*fn) (void *arg), void *child_stack_base, */ /* size_t child_stack_size, int flags, void *arg, */ /* pid_t *parent_tid, void *tls, pid_t *child_tid) */

I'm not finding that clone2() works -- We get a segfault in the cloned process when it returns.

This is the program:

#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

int func(void *arg) { 
    fprintf(stderr, "In child\n"); 
    return 0;
} 

int main(void)

{ 
        int pagesize = sysconf(_SC_PAGESIZE); 
        char *stack;
	int status;
        pid_t kidpid; 
#define STACKSIZE (pagesize*8) 
        stack = valloc(STACKSIZE); 
        if ((kidpid = __clone2(func, stack, STACKSIZE, SIGCHLD, NULL,
		    NULL,  NULL, NULL)) = -1) 
                perror("clone"); 
        else { 
                waitpid(kidpid, &status, __WALL);
                if (status) 
                   fprintf(stderr, "child failed: status %x\n", 
				   status); 
	} 
        return 0; 
}

I see:

In child
child failed: status b

where b translates to segfault.

Peter c

#5 lore raw
In reply to Nathan Straz
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line · Peter Chubb

Peter> I'm not finding that clone2() works -- We get a segfault in the cloned Peter> process when it returns.

Peter> __clone2(func, stack, STACKSIZE, SIGCHLD, NULL, NULL, NULL, NULL))

Umh, you're creating a new process with a different initial function. I doubt this has ever been tested. It probably should work, but I don't know of any app doing this for real (either you fork, or you create a thread).

	--david
#6 lore raw
In reply to Nathan Straz
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

"David" = David Mosberger <[email protected]> writes:

On Fri, 11 Jul 2003 09:22:42 +1000, Peter Chubb <[email protected]> said:

Peter> I'm not finding that clone2() works -- We get a segfault in the Peter> cloned process when it returns.

Peter> __clone2(func, stack, STACKSIZE, SIGCHLD, NULL, NULL, NULL, Peter> NULL))

David> Umh, you're creating a new process with a different initial David> function. I doubt this has ever been tested. It probably David> should work, but I don't know of any app doing this for real David> (either you fork, or you create a thread).

The library __clone2() call should cope with this. It does, essentially,

switch(kidpid = sys_clone2(flags, stackbase, stacksize, ptidp, tlsp, tidp)) { case -1:

	goto __syscall_error;
   case 0:
	exit(fn(arg));
   default:
	return kidpid;

}

Peter C

#7 lore raw
In reply to Nathan Straz
Quoted 4 lines · David Mosberger

Nathan> Could someone in the Linux ia64 community look at the clone Nathan> system call tests that the LTP has under
The only difference between clone() and clone2() is the explicit stack-size argument.

And that you pass the bottom of the stack, not the top? Also, are there explicit alignment requirements? I thought it needed to be aligned to page size (or maybe it was 16 bytes). And should the stack size be at a minimum ~8K, to allow enough room for register stack?

I have noticed a problem from the LTP tests. Either I'm doing something wrong (likely related to my assumptions above) or there is something wrong. See clone2.c below. gdb doesn't catch the new thread, but if you watch with strace you see the new thread gets a SEGV somewhere in the dynamic loader on exit, and doesn't set the return value properly. The LTP tests pick this up.

This doesn't happen if thread() explicitly calls _exit(), rather than just return (which should just call _exit). This makes me think it's something to do with __clone2 in in glibc. It looks OK to me by inspection, but unfortunately I am going away for some weeks today so can't look into this much further.

Am I misunderstanding something? Should my example work?

(I attached what I did anyway. BTW the files have DOS line feeds, which seems wrong)

-i
[email protected]
http://www.gelato.unsw.edu.au

--- clone2.c ---
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sched.h>
#include <sys/wait.h>
#include <sys/types.h>

#define MB (1024*1024)

int thread(void *nothing)

{
        printf("HELLO\n");
        return 100;
}

int main(void)
{

    int ret, status;
 
    char *child_stack_base;
    child_stack_base = valloc( MB );
    printf("I see the stack @ %lx\n", child_stack_base);
 
    ret = __clone2(&thread, child_stack_base , MB, SIGCHLD);
 
    wait(&status);
    printf("Child exited (%d)\n", WEXITSTATUS(status));
     
    exit(0);

}

Only in ltp-full-20030606-newgen/testcases/kernel/syscalls/clone/: .nfs0000000000c943670000009d

testcases/kernel/syscalls/clone/clone01.c +13 -1
diff -u ltp-full-20030606/testcases/kernel/syscalls/clone/clone01.c ltp-full-20030606-newgen/testcases/kernel/syscalls/clone/clone01.c
@@ -75,13 +75,23 @@
 #include "test.h"
 #include "usctest.h"
 
+#if defined (__ia64__)
+#define CHILD_STACK_SIZE 65536
+#else
 #define CHILD_STACK_SIZE 1024
+#endif
 
 #if defined (__s390__) || (__s390x__)
 #define clone __clone
 extern int __clone(int(void*),void*,int,void*);
 #endif
 
+#if defined (__ia64__)
+#define clone2 __clone2
+extern int  __clone2(int(void*),void*,size_t,int,void*);
+#endif
+
+
 static void setup();
 static void cleanup();
 static int do_child();
@@ -109,7 +119,7 @@
 	setup();
 
 	/* Allocate stack for child */
-	if((child_stack = (void *) malloc(CHILD_STACK_SIZE)) == NULL) {
+	if((child_stack = (void *) valloc(CHILD_STACK_SIZE)) == NULL) {
 		tst_brkm(TBROK, cleanup, "Cannot allocate stack for child");
 	}
 
@@ -124,6 +134,8 @@
 		 */
 #ifdef __hppa__
 		TEST(clone(do_child, child_stack, SIGCHLD, NULL));
+#elif defined (__ia64__)
+		TEST(clone2(do_child, child_stack, CHILD_STACK_SIZE, SIGCHLD, NULL));
 #else
 		TEST(clone(do_child, child_stack + CHILD_STACK_SIZE, SIGCHLD, NULL));
 #endif	
testcases/kernel/syscalls/clone/clone02.c +14 -2
diff -u ltp-full-20030606/testcases/kernel/syscalls/clone/clone02.c ltp-full-20030606-newgen/testcases/kernel/syscalls/clone/clone02.c
@@ -94,7 +94,12 @@
 #include "test.h"
 #include "usctest.h"
 
+#if defined (__ia64__)
+#define CHILD_STACK_SIZE 65536
+#else
 #define CHILD_STACK_SIZE 1024
+#endif
+
 #define FLAG_ALL CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD
 #define FLAG_NONE SIGCHLD
 #define PARENT_VALUE 1
@@ -107,6 +112,10 @@
 extern int __clone(int(void*),void*,int,void*);
 #endif
 
+#if defined (__ia64__)
+#define clone2 __clone2
+extern int  __clone2(int(void*),void*,size_t,int,void*);
+#endif
 
 static void setup();
 static int test_setup();
@@ -165,7 +174,7 @@
 	setup();
 
 	/* Allocate stack for child */
-	if((child_stack = (void *) malloc(CHILD_STACK_SIZE)) == NULL) {
+	if((child_stack = (void *) valloc(CHILD_STACK_SIZE)) == NULL) {
 		tst_brkm(TBROK, cleanup, "Cannot allocate stack for child");
 	}
 
@@ -188,6 +197,9 @@
 #ifdef __hppa__
 			TEST(clone(child_fn, child_stack,
 				   test_cases[i].flags, NULL));
+#elif (defined __ia64__)
+			TEST(clone2(child_fn, child_stack, 
+				    CHILD_STACK_SIZE, test_cases[i].flags, NULL));
 #else
 			TEST(clone(child_fn, child_stack + CHILD_STACK_SIZE,
 				   test_cases[i].flags, NULL));
@@ -217,6 +229,7 @@
 			 * parent function. If both functions returned
 			 * successfully, test passed, else failed
 			 */	
+			
 			if ((WIFEXITED(status)) && (WEXITSTATUS(status)) &&
 			    (test_cases[i].parent_fn())) {
 				tst_resm(TPASS, "Test Passed");
@@ -358,7 +371,6 @@
 
 	/* save child pid */
 	child_pid = getpid();
-
 	if (test_VM() && test_FILES() && test_FS() && test_SIG()) {
 		return 1;
 	}
testcases/kernel/syscalls/clone/clone03.c +12 -1
diff -u ltp-full-20030606/testcases/kernel/syscalls/clone/clone03.c ltp-full-20030606-newgen/testcases/kernel/syscalls/clone/clone03.c
@@ -72,13 +72,21 @@
 #include "test.h"
 #include "usctest.h"
 
+#if defined (__ia64__)
+#define CHILD_STACK_SIZE 65536
+#else
 #define CHILD_STACK_SIZE 1024
+#endif
 
 #if defined (__s390__) || (__s390x__)
 #define clone __clone
 extern int __clone(int(void*),void*,int,void*);
 #endif
 
+#if defined (__ia64__)
+#define clone2 __clone2
+extern int  __clone2(int(void*),void*,size_t,int,void*);
+#endif
 
 static void setup();
 static void cleanup();
@@ -109,7 +117,7 @@
 	setup();
 
 	/* Allocate stack for child */
-	if((child_stack = (void *) malloc(CHILD_STACK_SIZE)) == NULL) {
+	if((child_stack = (void *) valloc(CHILD_STACK_SIZE)) == NULL) {
 		tst_brkm(TBROK, cleanup, "Cannot allocate stack for child");
 	}
 
@@ -129,6 +137,8 @@
 		 */
 #ifdef __hppa__
 		TEST(clone(child_fn, child_stack, (int)NULL, NULL));
+#elif (defined __ia64__)
+		TEST(clone2(child_fn, child_stack, CHILD_STACK_SIZE, (int)NULL, NULL));
 #else
 		TEST(clone(child_fn, child_stack + CHILD_STACK_SIZE, (int)NULL, NULL));
 #endif
@@ -234,6 +244,7 @@
 	if ((close(pfd[1])) == -1) {
 		tst_resm(TWARN, "close(pfd[1]) failed");
 	}
+
 	return 1;
 }
 
testcases/kernel/syscalls/clone/clone04.c +19 -3
diff -u ltp-full-20030606/testcases/kernel/syscalls/clone/clone04.c ltp-full-20030606-newgen/testcases/kernel/syscalls/clone/clone04.c
@@ -73,13 +73,24 @@
 #include "test.h"
 #include "usctest.h"
 
+
+#if defined (__ia64__)
+#define CHILD_STACK_SIZE 65536
+#else
 #define CHILD_STACK_SIZE 1024
+#endif
 
 #if defined (__s390__) || (__s390x__)
 #define clone __clone
 extern int __clone(int(void*),void*,int,void*);
 #endif
 
+
+#if defined (__ia64__)
+#define clone2 __clone2
+extern int  __clone2(int(void*),void*,size_t,int,void*);
+#endif
+
 static void cleanup(void);
 static void setup(void);
 static int child_fn();
@@ -117,7 +128,7 @@
 	setup();			/* global setup */
 
 	/* Allocate stack for child */
-	child_stack = (void *) malloc(CHILD_STACK_SIZE);
+	child_stack = (void *) valloc(CHILD_STACK_SIZE);
 
 	/* The following loop checks looping state if -i option given */
 
@@ -132,7 +143,7 @@
 						 "child, skipping test case");
 					continue;
 				}
-#ifdef __hppa__				
+#if (defined  __hppa__) || (defined __ia64)
 				test_stack = child_stack;
 #else
 				test_stack = child_stack + CHILD_STACK_SIZE;
@@ -144,8 +155,13 @@
 			/*
 			 * call the system call with the TEST() macro
 		 	 */
+#ifdef __ia64__
+			TEST(clone2(test_cases[ind].child_fn, test_stack,
+				    CHILD_STACK_SIZE, (int)NULL, NULL));
+#else
 			TEST(clone(test_cases[ind].child_fn, test_stack,
-				   (int)NULL, NULL));
+				    CHILD_STACK_SIZE, (int)NULL, NULL));
+#endif
 	
 			if ((TEST_RETURN == -1) &&
 			    (TEST_ERRNO == test_cases[ind].exp_errno)) {
testcases/kernel/syscalls/clone/clone05.c +13 -1
diff -u ltp-full-20030606/testcases/kernel/syscalls/clone/clone05.c ltp-full-20030606-newgen/testcases/kernel/syscalls/clone/clone05.c
@@ -76,8 +76,18 @@
 extern int __clone(int(void*),void*,int,void*);
 #endif
 
+#if defined (__ia64__)
+#define clone2 __clone2
+extern int  __clone2(int(void*),void*,size_t,int,void*);
+#endif
 
+#if defined (__ia64__)
+#define CHILD_STACK_SIZE 65536
+#else
 #define CHILD_STACK_SIZE 1024
+#endif
+
+#define MAX_LINE_LENGTH 256
 #define FLAG CLONE_VM | CLONE_VFORK
 
 static void setup();
@@ -108,7 +118,7 @@
 	setup();
 
 	/* Allocate stack for child */
-	if((child_stack = (void *) malloc(CHILD_STACK_SIZE)) == NULL) {
+	if((child_stack = (void *) valloc(CHILD_STACK_SIZE)) == NULL) {
 		tst_brkm(TBROK, cleanup, "Cannot allocate stack for child");
 	}
 
@@ -123,6 +133,8 @@
 		 */
 #ifdef __hppa__
 		TEST(clone(child_fn, child_stack, FLAG, NULL));
+#elif (defined __ia64__)
+		TEST(clone2(child_fn, child_stack, CHILD_STACK_SIZE, FLAG, NULL));
 #else
 		TEST(clone(child_fn, child_stack + CHILD_STACK_SIZE, FLAG, NULL));
 #endif
testcases/kernel/syscalls/clone/clone06.c +17 -5
diff -u ltp-full-20030606/testcases/kernel/syscalls/clone/clone06.c ltp-full-20030606-newgen/testcases/kernel/syscalls/clone/clone06.c
@@ -82,8 +82,17 @@
 extern int __clone(int(void*),void*,int,void*);
 #endif
 
+#if defined (__ia64__)
+#define clone2 __clone2
+extern int  __clone2(int(void*),void*,size_t,int,void*);
+#endif
 
+#if defined (__ia64__)
+#define CHILD_STACK_SIZE 65536
+#else
 #define CHILD_STACK_SIZE 1024
+#endif
+
 #define MAX_LINE_LENGTH 256
 
 static void setup();
@@ -116,7 +125,7 @@
 	setup();
 
 	/* Allocate stack for child */
-	if((child_stack = (void *) malloc(CHILD_STACK_SIZE)) == NULL) {
+	if((child_stack = (void *) valloc(CHILD_STACK_SIZE)) == NULL) {
 		tst_brkm(TBROK, cleanup, "Cannot allocate stack for child");
 	}
 
@@ -136,6 +145,8 @@
 		 */
 #ifdef __hppa__
 		TEST(clone(child_environ, child_stack, (int)NULL, NULL));
+#elif (defined __ia64__)
+  		TEST(clone2(child_environ, child_stack, CHILD_STACK_SIZE, (int)NULL, NULL));
 #else
 		TEST(clone(child_environ, child_stack + CHILD_STACK_SIZE, (int)NULL, NULL));
 #endif
@@ -225,10 +236,11 @@
 	if ((close(pfd[0])) == -1) {
 		tst_brkm(TBROK, cleanup, "close(pfd[0]) failed");
 	}
-	
-	if ((sprintf(var, getenv("TERM"))) <= 0){
-		tst_resm(TWARN, "sprintf() failed");
+
+	if ( getenv("TERM") == NULL ) {
+		tst_resm(TWARN, "can't get TERM value");
 	}
+	strncpy(var, getenv("TERM"), sizeof(getenv("TERM")));
 
 	if ((write(pfd[1], var, MAX_LINE_LENGTH)) == -1) {
 		tst_resm(TWARN, "write to pipe failed");
@@ -238,7 +250,7 @@
 	if ((close(pfd[1])) == -1) {
 		tst_resm(TWARN, "close(pfd[1]) failed");
 	}
-	
+
 	return 0;
 }
 
#8 lore raw
In reply to Nathan Straz
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line · Ian Wienand

Ian> And that you pass the bottom of the stack, not the top?

Yes.

Ian> Also, are there explicit alignment requirements?

Not from the kernel per se.

Ian> I thought it needed to be aligned to page size (or maybe it was Ian> 16 bytes).

It the memory stack pointer has to be 16-byte aligned and the register-backing store pointer has to be 8-byte aligned, as per architecture/software conventions.

Ian> And should the stack size be at a minimum ~8K, to allow enough Ian> room for register stack?

The kernel doesn't care about the stack size. If you can live with 4KB, fine.

Ian> I have noticed a problem from the LTP tests. Either I'm doing Ian> something wrong (likely related to my assumptions above) or there is Ian> something wrong.

It's entirely possible something is wrong (that's almost always true).

Ian> See clone2.c below.

Time to clarify my role (once again): I'm currently playing maintainer of the ia64 linux tree. That doesn't mean I fix or investigate all bugs that come along. If something is broken or not working as expected then someone who cares should investigate.

It should be trivial for anyone who cares to reproduce this with the Ski simulator. This is exactly the type of questions that can be solved very easily with a simulator.

	--david
#9 lore raw
In reply to Nathan Straz

Peter Chubb <[email protected]> writes:

|> >>>>> "David" = David Mosberger <[email protected]> writes:
|> 
|> >>>>> On Thu, 10 Jul 2003 11:18:38 -0500, Nathan Straz <[email protected]> said:
|> >>> /* int __clone2(int (*fn) (void *arg), void *child_stack_base, */
|> >>> /* size_t child_stack_size, int flags, void *arg, */ /* pid_t
|> >>> *parent_tid, void *tls, pid_t *child_tid) */
|> 
|> I'm not finding that clone2() works -- We get a segfault in the cloned
|> process when it returns.
|> 
|> This is the program:
|> 
|> #include <stdio.h>
|> #include <sys/wait.h>
|> #include <unistd.h>
|> #include <sys/types.h>
|> #include <stdlib.h>
|> 
|> int func(void *arg) { 
|>     fprintf(stderr, "In child\n"); 
|>     return 0;
|> } 

You can't return from the function, only exit() or _exit() works.

Andreas.

Signature · 4 lines
Andreas Schwab, SuSE Labs, [email protected]
SuSE Linux AG, Deutschherrnstr. 15-19, D-90429 Nürnberg
Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."
#10 lore raw
In reply to Nathan Straz
Quoted 5 lines · Andreas Schwab

Peter Chubb <[email protected]> writes:
|> I'm not finding that clone2() works -- We get a segfault in the cloned |> process when it returns.

You can't return from the function, only exit() or _exit() works.

That seems really odd that you can't return from the function. The clone man page (dated 2001-06-26) says either way is an option. I did test it with the explicit exit() call and that does clean up the SIGSEGV.

Signature · 3 lines
Nate Straz                                              [email protected]
sgi, inc                                           http://www.sgi.com/
Linux Test Project                                  http://ltp.sf.net/
#11 lore raw
In reply to Nathan Straz

Nathan Straz <[email protected]> writes:

|> On Fri, Jul 11, 2003 at 11:57:30AM +0200, Andreas Schwab wrote:
|> > Peter Chubb <[email protected]> writes:
|> > |> I'm not finding that clone2() works -- We get a segfault in the cloned
|> > |> process when it returns.
|> > 
|> > You can't return from the function, only exit() or _exit() works.
|> 
|> That seems really odd that you can't return from the function.

Please forget what I wrote, I've now realized that this is supposed to work.

Andreas.

Signature · 4 lines
Andreas Schwab, SuSE Labs, [email protected]
SuSE Linux AG, Deutschherrnstr. 15-19, D-90429 Nürnberg
Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."