]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
util-lib: Add sparc64 support for process creation (#3348)
authorMichael Karcher <github@mkarcher.dialup.fu-berlin.de>
Mon, 30 May 2016 00:03:51 +0000 (02:03 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Mon, 30 May 2016 00:03:51 +0000 (20:03 -0400)
The current raw_clone function takes two arguments, the cloning flags and
a pointer to the stack for the cloned child. The raw cloning without
passing a "thread main" function does not make sense if a new stack is
specified, as it returns in both the parent and the child, which will fail
in the child as the stack is virgin. All uses of raw_clone indeed pass NULL
for the stack pointer which indicates that both processes should share the
stack address (so you better don't pass CLONE_VM).

This commit refactors the code to not require the caller to pass the stack
address, as NULL is the only sensible option. It also adds the magic code
needed to make raw_clone work on sparc64, which does not return 0 in %o0
for the child, but indicates the child process by setting %o1 to non-zero.
This refactoring is not plain aesthetic, because non-NULL stack addresses
need to get mangled before being passed to the clone syscall (you have to
apply STACK_BIAS), whereas NULL must not be mangled. Implementing the
conditional mangling of the stack address would needlessly complicate the
code.

raw_clone is moved to a separete header, because the burden of including
the assert machinery and sched.h shouldn't be applied to every user of
missing_syscalls.h

Makefile.am
src/basic/missing_syscall.h
src/basic/process-util.c
src/basic/raw-clone.h [new file with mode: 0644]
src/core/main.c
src/nspawn/nspawn.c
src/test/test-util.c

index f8e1fac9677f46797de25b1f2bdfe3b65107cc2d..c31c30c05121f22f26a6da22667160b17a372a15 100644 (file)
@@ -746,6 +746,7 @@ noinst_LTLIBRARIES += \
 libbasic_la_SOURCES = \
        src/basic/missing.h \
        src/basic/missing_syscall.h \
+       src/basic/raw-clone.h \
        src/basic/capability-util.c \
        src/basic/capability-util.h \
        src/basic/conf-files.c \
index d502d3b9cad87209c22341373c79b256f8c5dcc4..e102083684329e1381e2fcb0109991d675475b35 100644 (file)
@@ -178,18 +178,6 @@ static inline int setns(int fd, int nstype) {
 
 /* ======================================================================= */
 
-static inline int raw_clone(unsigned long flags, void *child_stack) {
-#if defined(__s390__) || defined(__CRIS__)
-        /* On s390 and cris the order of the first and second arguments
-         * of the raw clone() system call is reversed. */
-        return (int) syscall(__NR_clone, child_stack, flags);
-#else
-        return (int) syscall(__NR_clone, flags, child_stack);
-#endif
-}
-
-/* ======================================================================= */
-
 static inline pid_t raw_getpid(void) {
 #if defined(__alpha__)
         return (pid_t) syscall(__NR_getxpid);
index 1ad88162061173aba4a4fa3e109524ac1441f764..b991e7c6ba567eb525697566ac8769dabaf4b9b1 100644 (file)
@@ -48,6 +48,7 @@
 #include "macro.h"
 #include "missing.h"
 #include "process-util.h"
+#include "raw-clone.h"
 #include "signal-util.h"
 #include "stat-util.h"
 #include "string-table.h"
@@ -726,7 +727,7 @@ void valgrind_summary_hack(void) {
 #ifdef HAVE_VALGRIND_VALGRIND_H
         if (getpid() == 1 && RUNNING_ON_VALGRIND) {
                 pid_t pid;
-                pid = raw_clone(SIGCHLD, NULL);
+                pid = raw_clone(SIGCHLD);
                 if (pid < 0)
                         log_emergency_errno(errno, "Failed to fork off valgrind helper: %m");
                 else if (pid == 0)
diff --git a/src/basic/raw-clone.h b/src/basic/raw-clone.h
new file mode 100644 (file)
index 0000000..d473828
--- /dev/null
@@ -0,0 +1,81 @@
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+  Copyright 2016 Michael Karcher
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sched.h>
+#include <sys/syscall.h>
+
+#include "log.h"
+#include "macro.h"
+
+/**
+ * raw_clone() - uses clone to create a new process with clone flags
+ * @flags: Flags to pass to the clone system call
+ *
+ * Uses the clone system call to create a new process with the cloning
+ * flags and termination signal passed in the flags parameter. Opposed
+ * to glibc's clone funtion, using this function does not set up a
+ * separate stack for the child, but relies on copy-on-write semantics
+ * on the one stack at a common virtual address, just as fork does.
+ *
+ * To obtain copy-on-write semantics, flags must not contain CLONE_VM,
+ * and thus CLONE_THREAD and CLONE_SIGHAND (which require CLONE_VM) are
+ * not usabale.
+ * Additionally, as this function does not pass the ptid, newtls and ctid
+ * parameters to the kernel, flags must not contain CLONE_PARENT_SETTID,
+ * CLONE_CHILD_SETTID, CLONE_CHILD_CLEARTID or CLONE_SETTLS.
+ *
+ * Returns: 0 in the child process and the child process id in the parent.
+ */
+static inline int raw_clone(unsigned long flags) {
+        assert((flags & (CLONE_VM|CLONE_PARENT_SETTID|CLONE_CHILD_SETTID|
+                         CLONE_CHILD_CLEARTID|CLONE_SETTLS)) == 0);
+#if defined(__s390__) || defined(__CRIS__)
+        /* On s390 and cris the order of the first and second arguments
+         * of the raw clone() system call is reversed. */
+        return (int) syscall(__NR_clone, NULL, flags);
+#elif defined(__sparc__) && defined(__arch64__)
+        {
+                /**
+                 * sparc64 always returns the other process id in %o0, and
+                 * a boolean flag whether this is the child or the parent in
+                 * %o1. Inline assembly is needed to get the flag returned
+                 * in %o1.
+                 */
+                int in_child;
+                int child_pid;
+                asm volatile("mov %2, %%g1\n\t"
+                             "mov %3, %%o0\n\t"
+                             "mov 0 , %%o1\n\t"
+                             "t 0x6d\n\t"
+                             "mov %%o1, %0\n\t"
+                             "mov %%o0, %1" :
+                             "=r"(in_child), "=r"(child_pid) :
+                             "i"(__NR_clone), "r"(flags) :
+                             "%o1", "%o0", "%g1" );
+                if (in_child)
+                        return 0;
+                else
+                        return child_pid;
+        }
+#else
+        return (int) syscall(__NR_clone, flags, NULL);
+#endif
+}
index 6397aadc73e5f82d525e8475e3cc6bb2a1d12696..93098daa9b4343cbbbf3b0bb718a528a96dddc80 100644 (file)
@@ -70,6 +70,7 @@
 #include "parse-util.h"
 #include "proc-cmdline.h"
 #include "process-util.h"
+#include "raw-clone.h"
 #include "rlimit-util.h"
 #include "selinux-setup.h"
 #include "selinux-util.h"
@@ -162,7 +163,7 @@ noreturn static void crash(int sig) {
                 /* We want to wait for the core process, hence let's enable SIGCHLD */
                 (void) sigaction(SIGCHLD, &sa, NULL);
 
-                pid = raw_clone(SIGCHLD, NULL);
+                pid = raw_clone(SIGCHLD);
                 if (pid < 0)
                         log_emergency_errno(errno, "Caught <%s>, cannot fork for core dump: %m", signal_to_string(sig));
                 else if (pid == 0) {
@@ -221,7 +222,7 @@ noreturn static void crash(int sig) {
                 log_notice("Executing crash shell in 10s...");
                 (void) sleep(10);
 
-                pid = raw_clone(SIGCHLD, NULL);
+                pid = raw_clone(SIGCHLD);
                 if (pid < 0)
                         log_emergency_errno(errno, "Failed to fork off crash shell: %m");
                 else if (pid == 0) {
index b421c182ce98ed9910d1778d1ad1183d9ea8c455..d1c65e8b0b1afe2a0d5b91af83d0d04e6fd4f50b 100644 (file)
@@ -85,6 +85,7 @@
 #include "process-util.h"
 #include "ptyfwd.h"
 #include "random-util.h"
+#include "raw-clone.h"
 #include "rm-rf.h"
 #include "selinux-util.h"
 #include "signal-util.h"
@@ -2938,8 +2939,7 @@ static int outer_child(
         pid = raw_clone(SIGCHLD|CLONE_NEWNS|
                         (arg_share_system ? 0 : CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS) |
                         (arg_private_network ? CLONE_NEWNET : 0) |
-                        (arg_userns_mode != USER_NAMESPACE_NO ? CLONE_NEWUSER : 0),
-                        NULL);
+                        (arg_userns_mode != USER_NAMESPACE_NO ? CLONE_NEWUSER : 0));
         if (pid < 0)
                 return log_error_errno(errno, "Failed to fork inner child: %m");
         if (pid == 0) {
@@ -3608,7 +3608,7 @@ int main(int argc, char *argv[]) {
                         goto finish;
                 }
 
-                pid = raw_clone(SIGCHLD|CLONE_NEWNS, NULL);
+                pid = raw_clone(SIGCHLD|CLONE_NEWNS);
                 if (pid < 0) {
                         if (errno == EINVAL)
                                 r = log_error_errno(errno, "clone() failed, do you have namespace support enabled in your kernel? (You need UTS, IPC, PID and NET namespacing built in): %m");
index 05cb1eae76f0e140b671a5e51a5988156459d580..9b6d2a7968bfcb6ebb5ea86c6ccfe21786728c57 100644 (file)
@@ -26,6 +26,7 @@
 #include "def.h"
 #include "fileio.h"
 #include "fs-util.h"
+#include "raw-clone.h"
 #include "rm-rf.h"
 #include "string-util.h"
 #include "util.h"
@@ -244,7 +245,7 @@ static void test_raw_clone(void) {
         log_info("before clone: getpid()→"PID_FMT, parent);
         assert_se(raw_getpid() == parent);
 
-        pid = raw_clone(0, NULL);
+        pid = raw_clone(0);
         assert_se(pid >= 0);
 
         pid2 = raw_getpid();