Skip to contentSkip to conversation

Implement clone_with_pids() syscall

PATCH In review Sukadev Bhattiprolu 4 participants 18 messages Nov 1, 2009 – Nov 4, 2009 5 messages not in the archive

Subsystems (5)
Documentation x86 MM core Scheduler +1 more subsystems
lore
Share & tools
lore
#0 lore raw

PATCH v10 1/1 Implement clone_with_pids() syscall

[v10][PATCH 0/9] Implement clone_with_pids() system call

To support application checkpoint/restart, a task must have the same pid it had when it was checkpointed. When containers are nested, the tasks within the containers exist in multiple pid namespaces and hence have multiple pids to specify during restart.

This patchset implements a new system call, clone_with_pids() that lets a process specify the pids of the child process.

Patches 1 through 6 are helper patches needed for choosing a pid for the child process.

PATCH 8 defines a prototype of the new system call. PATCH 9 adds some documentation on the new system call, some/all of which will eventually go into a man page.

Changelog[v10-rc1]:
- [Linus Torvalds] Use PTREGSCALL() implementation for clone rather than the generic system call
- Rename clone3() to clone_with_pids()
- Update Documentation/clone_with_pids() to show example usage with the PTREGSCALL implementation.

Changelog[v9]:
- [Pavel Emelyanov] Drop the patch that made 'pid_max' a property of struct pid_namespace
- [Roland McGrath, H. Peter Anvin and earlier on, Serge Hallyn] To avoid inadvertent truncation clone_flags, preserve the first parameter of clone3() as 'u32 clone_flags' and specify newer flags in clone_args.flags_high (PATCH 8/9 and PATCH 9/9)
- [Eric Biederman] Generalize alloc_pidmap() code to simplify and remove duplication (see PATCH 3/9].

Changelog[v8]:
- [Oren Laadan, Louis Rilling, KOSAKI Motohiro]
The name 'clone2()' is in use - renamed new syscall to clone3().
- [Oren Laadan] ->parent_tidptr and ->child_tidptr need to be 64bit.
- [Oren Laadan] Ensure that unused fields/flags in clone_struct are 0. (Added [PATCH 7/10] to the patchset).

Changelog[v7]:
- [Peter Zijlstra, Arnd Bergmann]
Group the arguments to clone2() into a 'struct clone_arg' to workaround the issue of exceeding 6 arguments to the system call. Also define clone-flags as u64 to allow additional clone-flags.

Changelog[v6]:
- [Nathan Lynch, Arnd Bergmann, H. Peter Anvin, Linus Torvalds] Change 'pid_set.pids' to 'pid_t pids[]' so sizeof(struct pid_set) is constant across architectures (Patches 7, 8).
- (Nathan Lynch) Change pid_set.num_pids to unsigned and remove 'unum_pids < 0' check (Patches 7,8)
- (Pavel Machek) New patch (Patch 9) to add some documentation.

Changelog[v5]:
- Make 'pid_max' a property of pid_ns (Integrated Serge Hallyn's patch into this set)
- (Eric Biederman): Avoid the new function, set_pidmap() - added couple of checks on 'target_pid' in alloc_pidmap() itself.

=== IMPORTANT NOTE:

clone() system call has another limitation - all but one, available bits in clone-flags are in use and if more new clone-flags are needed, we will need a variant of the clone() system call.

It appears to make sense to try and extend this new system call to address this limitation as well. The requirements of a new clone system call could then be summarized as:

- do everything clone() does today, and
- give application an ability to choose pids for the child process in all ancestor pid namespaces, and
- allow more clone_flags

Contstraints:

- system-calls are restricted to 6 parameters and clone() already takes 5 parameters, any extension to clone() interface would require one or more copy_from_user(). (Not sure if copy_from_user() of ~40 bytes would have a significant impact on performance of clone()).

Based on these requirements and constraints, we explored a couple of system call interfaces (in earlier versions of this patchset). Based on input from Arnd Bergmann and others, the new interface of the system call is:

	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 clone_args_size;
		u64 reserved1;
	};

	sys_clone_with_pids(u32 flags_low, struct clone_args *cargs,
			pid_t *pids)

Details of the struct clone_args and the usage are explained in the documentation (PATCH 9/9).

NOTE:
While this patchset enables support for more clone-flags, actual implementation for additional clone-flags is best implemented as a separate patchset (PATCH 8/9 identifies some TODOs)

A message that is not in the archive (sent off-list or not yet received): 20091101204132.GA22116-r/[email protected]

#2 lore raw

PATCH v10 1/9 Factor out code to allocate pidmap page

From: Sukadev Bhattiprolu <sukadev-r/[email protected]>
Date: Tue, 20 Oct 2009 21:27:39 -0700
Subject: [v10][PATCH 1/9] Factor out code to allocate pidmap page

To simplify alloc_pidmap(), move code to allocate a pid map page to a separate function.

Changelog[v3]:
- Earlier version of patchset called alloc_pidmap_page() from two places. But now its called from only one place. Even so, moving this code out into a separate function simplifies alloc_pidmap().
Changelog[v2]:
- (Matt Helsley, Dave Hansen) Have alloc_pidmap_page() return -ENOMEM on error instead of -1.

---
 kernel/pid.c |   45 ++++++++++++++++++++++++++++++---------------
 1 files changed, 30 insertions(+), 15 deletions(-)
kernel/pid.c +30 -15
index d3f722d..7d4bb6e 100644
@@ -122,9 +122,35 @@ static void free_pidmap(struct upid *upid)
 	atomic_inc(&map->nr_free);
 }
 
+static int alloc_pidmap_page(struct pidmap *map)
+{
+	void *page;
+
+	if (likely(map->page))
+		return 0;
+
+	page = kzalloc(PAGE_SIZE, GFP_KERNEL);
+
+	/*
+	 * Free the page if someone raced with us installing it:
+	 */
+	spin_lock_irq(&pidmap_lock);
+	if (map->page)
+		kfree(page);
+	else
+		map->page = page;
+	spin_unlock_irq(&pidmap_lock);
+
+	if (unlikely(!map->page))
+		return -ENOMEM;
+
+	return 0;
+}
+
 static int alloc_pidmap(struct pid_namespace *pid_ns)
 {
 	int i, offset, max_scan, pid, last = pid_ns->last_pid;
+	int rc;
 	struct pidmap *map;
 
 	pid = last + 1;
@@ -134,21 +160,10 @@ static int alloc_pidmap(struct pid_namespace *pid_ns)
 	map = &pid_ns->pidmap[pid/BITS_PER_PAGE];
 	max_scan = (pid_max + BITS_PER_PAGE - 1)/BITS_PER_PAGE - !offset;
 	for (i = 0; i <= max_scan; ++i) {
-		if (unlikely(!map->page)) {
-			void *page = kzalloc(PAGE_SIZE, GFP_KERNEL);
-			/*
-			 * Free the page if someone raced with us
-			 * installing it:
-			 */
-			spin_lock_irq(&pidmap_lock);
-			if (map->page)
-				kfree(page);
-			else
-				map->page = page;
-			spin_unlock_irq(&pidmap_lock);
-			if (unlikely(!map->page))
-				break;
-		}
+		rc = alloc_pidmap_page(map);
+		if (rc)
+			break;
+
 		if (likely(atomic_read(&map->nr_free))) {
 			do {
 				if (!test_and_set_bit(offset, map->page)) {
#3 lore raw

PATCH v10 2/9 Have alloc_pidmap() return actual error code

In reply to a message not in the archive

From: Sukadev Bhattiprolu <sukadev-r/[email protected]>
Date: Tue, 20 Oct 2009 21:29:03 -0700
Subject: [v10][PATCH 2/9] Have alloc_pidmap() return actual error code

alloc_pidmap() can fail either because all pid numbers are in use or because memory allocation failed. With support for setting a specific pid number, alloc_pidmap() would also fail if either the given pid number is invalid or in use.

Rather than have callers assume -ENOMEM, have alloc_pidmap() return the actual error.

---
 kernel/fork.c |    5 +++--
 kernel/pid.c  |   14 +++++++++-----
 2 files changed, 12 insertions(+), 7 deletions(-)
kernel/fork.c +3 -2
index 4c20fff..93626b2 100644
@@ -1156,10 +1156,11 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 		goto bad_fork_cleanup_io;
 
 	if (pid != &init_struct_pid) {
-		retval = -ENOMEM;
 		pid = alloc_pid(p->nsproxy->pid_ns);
-		if (!pid)
+		if (IS_ERR(pid)) {
+			retval = PTR_ERR(pid);
 			goto bad_fork_cleanup_io;
+		}
 
 		if (clone_flags & CLONE_NEWPID) {
 			retval = pid_ns_prepare_proc(p->nsproxy->pid_ns);
kernel/pid.c +9 -5
index 7d4bb6e..c4d9914 100644
@@ -150,7 +150,7 @@ static int alloc_pidmap_page(struct pidmap *map)
 static int alloc_pidmap(struct pid_namespace *pid_ns)
 {
 	int i, offset, max_scan, pid, last = pid_ns->last_pid;
-	int rc;
+	int rc = -EAGAIN;
 	struct pidmap *map;
 
 	pid = last + 1;
@@ -189,12 +189,14 @@ static int alloc_pidmap(struct pid_namespace *pid_ns)
 		} else {
 			map = &pid_ns->pidmap[0];
 			offset = RESERVED_PIDS;
-			if (unlikely(last == offset))
+			if (unlikely(last == offset)) {
+				rc = -EAGAIN;
 				break;
+			}
 		}
 		pid = mk_pid(pid_ns, map, offset);
 	}
-	return -1;
+	return rc;
 }
 
 int next_pidmap(struct pid_namespace *pid_ns, int last)
@@ -263,8 +265,10 @@ struct pid *alloc_pid(struct pid_namespace *ns)
 	struct upid *upid;
 
 	pid = kmem_cache_alloc(ns->pid_cachep, GFP_KERNEL);
-	if (!pid)
+	if (!pid) {
+		pid = ERR_PTR(-ENOMEM);
 		goto out;
+	}
 
 	tmp = ns;
 	for (i = ns->level; i >= 0; i--) {
@@ -299,7 +303,7 @@ out_free:
 		free_pidmap(pid->numbers + i);
 
 	kmem_cache_free(ns->pid_cachep, pid);
-	pid = NULL;
+	pid = ERR_PTR(nr);
 	goto out;
 }
 
Signature · 1 line
1.6.0.4
#4 lore raw

PATCH v10 3/9 Define set_pidmap() function

In reply to a message not in the archive

From: Sukadev Bhattiprolu <suka@suka.(none)>
Date: Thu, 22 Oct 2009 16:57:28 -0700
Subject: [v10][PATCH 3/9] Define set_pidmap() function

Define a set_pidmap() interface which is like alloc_pidmap() only that caller specifies the pid number to be assigned.

Changelog[v9]:
- Completely rewrote this patch based on Eric Biederman's code.
Changelog[v7]:
- [Eric Biederman] Generalize alloc_pidmap() to take a range of pids.
Changelog[v6]:
- Separate target_pid > 0 case to minimize the number of checks needed.
Changelog[v3]:
- (Eric Biederman): Avoid set_pidmap() function. Added couple of checks for target_pid in alloc_pidmap() itself.
Changelog[v2]:
- (Serge Hallyn) Check for 'pid < 0' in set_pidmap().(Code actually checks for 'pid <= 0' for completeness).

---
 kernel/pid.c |   41 +++++++++++++++++++++++++++++++++--------
 1 files changed, 33 insertions(+), 8 deletions(-)
kernel/pid.c +33 -8
index c4d9914..c849327 100644
@@ -147,18 +147,19 @@ static int alloc_pidmap_page(struct pidmap *map)
 	return 0;
 }
 
-static int alloc_pidmap(struct pid_namespace *pid_ns)
+static int do_alloc_pidmap(struct pid_namespace *pid_ns, int last, int min,
+		int max)
 {
-	int i, offset, max_scan, pid, last = pid_ns->last_pid;
+	int i, offset, max_scan, pid;
 	int rc = -EAGAIN;
 	struct pidmap *map;
 
 	pid = last + 1;
 	if (pid >= pid_max)
-		pid = RESERVED_PIDS;
+		pid = min;
 	offset = pid & BITS_PER_PAGE_MASK;
 	map = &pid_ns->pidmap[pid/BITS_PER_PAGE];
-	max_scan = (pid_max + BITS_PER_PAGE - 1)/BITS_PER_PAGE - !offset;
+	max_scan = (max + BITS_PER_PAGE - 1)/BITS_PER_PAGE - !offset;
 	for (i = 0; i <= max_scan; ++i) {
 		rc = alloc_pidmap_page(map);
 		if (rc)
@@ -168,7 +169,6 @@ static int alloc_pidmap(struct pid_namespace *pid_ns)
 			do {
 				if (!test_and_set_bit(offset, map->page)) {
 					atomic_dec(&map->nr_free);
-					pid_ns->last_pid = pid;
 					return pid;
 				}
 				offset = find_next_offset(map, offset);
@@ -179,16 +179,16 @@ static int alloc_pidmap(struct pid_namespace *pid_ns)
 			 * bitmap block and the final block was the same
 			 * as the starting point, pid is before last_pid.
 			 */
-			} while (offset < BITS_PER_PAGE && pid < pid_max &&
+			} while (offset < BITS_PER_PAGE && pid < max &&
 					(i != max_scan || pid < last ||
 					    !((last+1) & BITS_PER_PAGE_MASK)));
 		}
-		if (map < &pid_ns->pidmap[(pid_max-1)/BITS_PER_PAGE]) {
+		if (map < &pid_ns->pidmap[(max-1)/BITS_PER_PAGE]) {
 			++map;
 			offset = 0;
 		} else {
 			map = &pid_ns->pidmap[0];
-			offset = RESERVED_PIDS;
+			offset = min;
 			if (unlikely(last == offset)) {
 				rc = -EAGAIN;
 				break;
@@ -199,6 +199,31 @@ static int alloc_pidmap(struct pid_namespace *pid_ns)
 	return rc;
 }
 
+static int alloc_pidmap(struct pid_namespace *pid_ns)
+{
+	int nr;
+
+	nr = do_alloc_pidmap(pid_ns, pid_ns->last_pid, RESERVED_PIDS, pid_max);
+	if (nr >= 0)
+		pid_ns->last_pid = nr;
+	return nr;
+}
+
+static int set_pidmap(struct pid_namespace *pid_ns, int target)
+{
+	if (!target)
+		return alloc_pidmap(pid_ns);
+
+	if (target >= pid_max)
+		return -EINVAL;
+
+	if ((target < 0) || (target < RESERVED_PIDS && 
+				pid_ns->last_pid >= RESERVED_PIDS))
+		return -EINVAL;
+
+	return do_alloc_pidmap(pid_ns, target - 1, target, target + 1);
+}
+
 int next_pidmap(struct pid_namespace *pid_ns, int last)
 {
 	int offset;
Signature · 1 line
1.6.0.4
#5 lore raw

PATCH v10 4/9 Add target_pids parameter to alloc_pid()

In reply to a message not in the archive

From: Sukadev Bhattiprolu <sukadev-r/[email protected]>
Date: Tue, 20 Oct 2009 21:35:07 -0700
Subject: [v10][PATCH 4/9] Add target_pids parameter to alloc_pid()

This parameter is currently NULL, but will be used in a follow-on patch.

---
 include/linux/pid.h |    2 +-
 kernel/fork.c       |    3 ++-
 kernel/pid.c        |    9 +++++++--
 3 files changed, 10 insertions(+), 4 deletions(-)
include/linux/pid.h +1 -1
index 49f1c2f..914185d 100644
@@ -119,7 +119,7 @@ extern struct pid *find_get_pid(int nr);
 extern struct pid *find_ge_pid(int nr, struct pid_namespace *);
 int next_pidmap(struct pid_namespace *pid_ns, int last);
 
-extern struct pid *alloc_pid(struct pid_namespace *ns);
+extern struct pid *alloc_pid(struct pid_namespace *ns, pid_t *target_pids);
 extern void free_pid(struct pid *pid);
 
 /*
kernel/fork.c +2 -1
index 93626b2..3f1dddf 100644
@@ -980,6 +980,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 	int retval;
 	struct task_struct *p;
 	int cgroup_callbacks_done = 0;
+	pid_t *target_pids = NULL;
 
 	if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
 		return ERR_PTR(-EINVAL);
@@ -1156,7 +1157,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 		goto bad_fork_cleanup_io;
 
 	if (pid != &init_struct_pid) {
-		pid = alloc_pid(p->nsproxy->pid_ns);
+		pid = alloc_pid(p->nsproxy->pid_ns, target_pids);
 		if (IS_ERR(pid)) {
 			retval = PTR_ERR(pid);
 			goto bad_fork_cleanup_io;
kernel/pid.c +7 -2
index c849327..7c73096 100644
@@ -281,13 +281,14 @@ void free_pid(struct pid *pid)
 	call_rcu(&pid->rcu, delayed_put_pid);
 }
 
-struct pid *alloc_pid(struct pid_namespace *ns)
+struct pid *alloc_pid(struct pid_namespace *ns, pid_t *target_pids)
 {
 	struct pid *pid;
 	enum pid_type type;
 	int i, nr;
 	struct pid_namespace *tmp;
 	struct upid *upid;
+	pid_t tpid;
 
 	pid = kmem_cache_alloc(ns->pid_cachep, GFP_KERNEL);
 	if (!pid) {
@@ -297,7 +298,11 @@ struct pid *alloc_pid(struct pid_namespace *ns)
 
 	tmp = ns;
 	for (i = ns->level; i >= 0; i--) {
-		nr = alloc_pidmap(tmp);
+		tpid = 0;
+		if (target_pids)
+			tpid = target_pids[i];
+
+		nr = set_pidmap(tmp, tpid);
 		if (nr < 0)
 			goto out_free;
 
Signature · 1 line
1.6.0.4
#6 lore raw

PATCH v10 5/9 Add target_pids parameter to copy_process()

In reply to a message not in the archive

From: Sukadev Bhattiprolu <sukadev-r/[email protected]>
Date: Tue, 20 Oct 2009 21:51:45 -0700
Subject: [v10][PATCH 5/9] Add target_pids parameter to copy_process()

Add a 'target_pids' parameter to copy_process(). The new parameter will be used in a follow-on patch when clone_with_pids() is implemented.

---
 kernel/fork.c |    7 ++++---
 1 files changed, 4 insertions(+), 3 deletions(-)
kernel/fork.c +4 -3
index 3f1dddf..c8a06de 100644
@@ -975,12 +975,12 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 					unsigned long stack_size,
 					int __user *child_tidptr,
 					struct pid *pid,
+					pid_t *target_pids,
 					int trace)
 {
 	int retval;
 	struct task_struct *p;
 	int cgroup_callbacks_done = 0;
-	pid_t *target_pids = NULL;
 
 	if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
 		return ERR_PTR(-EINVAL);
@@ -1361,7 +1361,7 @@ struct task_struct * __cpuinit fork_idle(int cpu)
 	struct pt_regs regs;
 
 	task = copy_process(CLONE_VM, 0, idle_regs(&regs), 0, NULL,
-			    &init_struct_pid, 0);
+			    &init_struct_pid, NULL, 0);
 	if (!IS_ERR(task))
 		init_idle(task, cpu);
 
@@ -1384,6 +1384,7 @@ long do_fork(unsigned long clone_flags,
 	struct task_struct *p;
 	int trace = 0;
 	long nr;
+	pid_t *target_pids = NULL;
 
 	/*
 	 * Do some preliminary argument and permissions checking before we
@@ -1424,7 +1425,7 @@ long do_fork(unsigned long clone_flags,
 		trace = tracehook_prepare_clone(clone_flags);
 
 	p = copy_process(clone_flags, stack_start, regs, stack_size,
-			 child_tidptr, NULL, trace);
+			 child_tidptr, NULL, target_pids, trace);
 	/*
 	 * Do this prior waking up the new thread - the thread pointer
 	 * might get invalid after that point, if the thread exits quickly.
Signature · 1 line
1.6.0.4
#7 lore raw

PATCH v10 6/9 Check invalid clone flags

In reply to a message not in the archive

From: Sukadev Bhattiprolu <sukadev-r/[email protected]>
Date: Tue, 20 Oct 2009 21:52:40 -0700
Subject: [v10][PATCH 6/9] Check invalid clone flags

As pointed out by Oren Laadan, we want to ensure that unused bits in the clone-flags remain unused and available for future. To ensure this, define a mask of clone-flags and check the flags in the clone() system calls.

Changelog[v9]:
- Include the unused clone-flag (CLONE_UNUSED) to VALID_CLONE_FLAGS to avoid breaking any applications that may have set it. IOW, this patch/check only applies to clone-flags bits 33 and higher.

Changelog[v8]:
- New patch in set

---
 include/linux/sched.h |   12 ++++++++++++
 kernel/fork.c         |    3 +++
 2 files changed, 15 insertions(+), 0 deletions(-)
include/linux/sched.h +12 -0
index 75e6e60..6b319a0 100644
@@ -29,6 +29,18 @@
 #define CLONE_NEWNET		0x40000000	/* New network namespace */
 #define CLONE_IO		0x80000000	/* Clone io context */
 
+#define CLONE_UNUSED        	0x00001000	/* Can be reused ? */
+
+#define VALID_CLONE_FLAGS	(CSIGNAL | CLONE_VM | CLONE_FS | CLONE_FILES |\
+				 CLONE_SIGHAND | CLONE_UNUSED | CLONE_PTRACE |\
+				 CLONE_VFORK  | CLONE_PARENT | CLONE_THREAD  |\
+				 CLONE_NEWNS  | CLONE_SYSVSEM | CLONE_SETTLS |\
+				 CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID  |\
+				 CLONE_DETACHED | CLONE_UNTRACED             |\
+				 CLONE_CHILD_SETTID | CLONE_STOPPED          |\
+				 CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWUSER |\
+				 CLONE_NEWPID | CLONE_NEWNET| CLONE_IO)
+
 /*
  * Scheduling policies
  */
kernel/fork.c +3 -0
index c8a06de..11f77ed 100644
@@ -982,6 +982,9 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 	struct task_struct *p;
 	int cgroup_callbacks_done = 0;
 
+	if (clone_flags & ~VALID_CLONE_FLAGS)
+		return ERR_PTR(-EINVAL);
+
 	if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
 		return ERR_PTR(-EINVAL);
 
Signature · 1 line
1.6.0.4
#8 lore raw

PATCH v10 7/9 Define do_fork_with_pids()

In reply to a message not in the archive

From: Sukadev Bhattiprolu <sukadev-r/[email protected]>
Date: Tue, 20 Oct 2009 21:53:39 -0700
Subject: [v10][PATCH 7/9] Define do_fork_with_pids()

do_fork_with_pids() is same as do_fork(), except that it takes an additional, 'pid_set', parameter. This parameter, currently unused, specifies the set of target pids of the process in each of its pid namespaces.

Changelog[v7]:
- Drop 'struct pid_set' object and pass in 'pid_t *target_pids' instead of 'struct pid_set *'.

Changelog[v6]:
- (Nathan Lynch, Arnd Bergmann, H. Peter Anvin, Linus Torvalds) Change 'pid_set.pids' to a 'pid_t pids[]' so size of 'struct pid_set' is constant across architectures.
- (Nathan Lynch) Change 'pid_set.num_pids' to 'unsigned int'.

Changelog[v4]:
- Rename 'struct target_pid_set' to 'struct pid_set' since it may be useful in other contexts.

Changelog[v3]:
- Fix "long-line" warning from checkpatch.pl

Changelog[v2]:
- To facilitate moving architecture-inpdendent code to kernel/fork.c pass in 'struct target_pid_set __user *' to do_fork_with_pids() rather than 'pid_t *' (next patch moves the arch-independent code to kernel/fork.c)

---
 include/linux/sched.h |    3 +++
 kernel/fork.c         |   17 +++++++++++++++--
 2 files changed, 18 insertions(+), 2 deletions(-)
include/linux/sched.h +3 -0
index 6b319a0..265efe5 100644
@@ -2153,6 +2153,9 @@ extern int disallow_signal(int);
 
 extern int do_execve(char *, char __user * __user *, char __user * __user *, struct pt_regs *);
 extern long do_fork(unsigned long, unsigned long, struct pt_regs *, unsigned long, int __user *, int __user *);
+extern long do_fork_with_pids(unsigned long, unsigned long, struct pt_regs *,
+				unsigned long, int __user *, int __user *,
+				unsigned int, pid_t __user *);
 struct task_struct *fork_idle(int);
 
 extern void set_task_comm(struct task_struct *tsk, char *from);
kernel/fork.c +15 -2
index 11f77ed..210e841 100644
@@ -1377,12 +1377,14 @@ struct task_struct * __cpuinit fork_idle(int cpu)
  * It copies the process, and if successful kick-starts
  * it and waits for it to finish using the VM if required.
  */
-long do_fork(unsigned long clone_flags,
+long do_fork_with_pids(unsigned long clone_flags,
 	      unsigned long stack_start,
 	      struct pt_regs *regs,
 	      unsigned long stack_size,
 	      int __user *parent_tidptr,
-	      int __user *child_tidptr)
+	      int __user *child_tidptr,
+	      unsigned int num_pids,
+	      pid_t __user *upids)
 {
 	struct task_struct *p;
 	int trace = 0;
@@ -1485,6 +1487,17 @@ long do_fork(unsigned long clone_flags,
 	return nr;
 }
 
+long do_fork(unsigned long clone_flags,
+	      unsigned long stack_start,
+	      struct pt_regs *regs,
+	      unsigned long stack_size,
+	      int __user *parent_tidptr,
+	      int __user *child_tidptr)
+{
+	return do_fork_with_pids(clone_flags, stack_start, regs, stack_size,
+			parent_tidptr, child_tidptr, 0, NULL);
+}
+
 #ifndef ARCH_MIN_MMSTRUCT_ALIGN
 #define ARCH_MIN_MMSTRUCT_ALIGN 0
 #endif
Signature · 1 line
1.6.0.4
#9 lore raw

PATCH v10 8/9 Define clone_with_pids() syscall

In reply to a message not in the archive

From: Sukadev Bhattiprolu <suka@suka.(none)>
Date: Tue, 20 Oct 2009 22:04:57 -0700
Subject: [v10][PATCH 8/9] Define clone_with_pids() syscall

Container restart requires that a task have the same pid it had when it was checkpointed. When containers are nested the tasks within the containers exist in multiple pid namespaces and hence have multiple pids to specify during restart.

clone_with_pids(), intended for use during restart, is the same as clone(), except that it takes a 'pids' paramter. This parameter lets caller choose specific pid numbers for the child process, in the process's active and ancestor pid namespaces. (Descendant pid namespaces in general don't matter since processes don't have pids in them anyway, but see comments in copy_target_pids() regarding CLONE_NEWPID).

clone_with_pids() also attempts to address a second limitation of the clone() system call. clone() is restricted to 32 clone flags and all but one of these are in use. If more new clone flags are needed, we will be forced to define a new variant of the clone() system call. To address this, clone_with_pids() allows at least 64 clone flags with some room for more if necessary.

To prevent unprivileged processes from misusing this interface, clone_with_pids() currently needs CAP_SYS_ADMIN, when the 'pids' parameter is non-NULL.

See Documentation/clone_with_pids in next patch for more details and an example of its usage.

NOTE:
- System calls are restricted to 6 parameters and the number and sizes of parameters needed for clone_with_pids() exceed 6 integers. The new prototype works around this restriction while providing some flexibility if clone_with_pids() needs to be further extended in the future.
TODO:
- We should convert clone-flags to 64-bit value in all architectures. Its probably best to do that as a separate patchset since clone_flags touches several functions and that patchset seems independent of this new system call.

Changelog[v10-rc1]:
- Rename clone3() to clone_with_pids()
- [Linus Torvalds] Use PTREGSCALL() rather than the generic syscall implementation

Changelog[v9]:
- [Roland McGrath, H. Peter Anvin] To avoid confusion on 64-bit architectures split the new clone-flags into 'low' and 'high' words and pass in the 'lower' flags as the first argument. This would maintain similarity of the clone3() with clone()/ clone2(). Also has the side-effect of the name matching the number of parameters :-)
- [Roland McGrath] Rename structure to 'clone_args' and add a 'child_stack_size' field

Changelog[v8]
- [Oren Laadan] parent_tid and child_tid fields in 'struct clone_arg' must be 64-bit.
- clone2() is in use in IA64. Rename system call to clone3().

Changelog[v7]:
- [Peter Zijlstra, Arnd Bergmann] Rename system call to clone2() and group parameters into a new 'struct clone_struct' object.

Changelog[v6]:
- (Nathan Lynch, Arnd Bergmann, H. Peter Anvin, Linus Torvalds) Change 'pid_set.pids' to a 'pid_t pids[]' so size of 'struct pid_set' is constant across architectures.
- (Nathan Lynch) Change pid_set.num_pids to unsigned and remove 'unum_pids < 0' check.

Changelog[v4]:
- (Oren Laadan) rename 'struct target_pid_set' to 'struct pid_set'

Changelog[v3]:
- (Oren Laadan) Allow CLONE_NEWPID flag (by allocating an extra pid in the target_pids[] list and setting it 0. See copy_target_pids()).
- (Oren Laadan) Specified target pids should apply only to youngest pid-namespaces (see copy_target_pids())
- (Matt Helsley) Update patch description.

Changelog[v2]:
- Remove unnecessary printk and add a note to callers of copy_target_pids() to free target_pids.
- (Serge Hallyn) Mention CAP_SYS_ADMIN restriction in patch description.
- (Oren Laadan) Add checks for 'num_pids < 0' (return -EINVAL) and 'num_pids == 0' (fall back to normal clone()).
- Move arch-independent code (sanity checks and copy-in of target-pids) into kernel/fork.c and simplify sys_clone_with_pids()

Changelog[v1]:
- Fixed some compile errors (had fixed these errors earlier in my git tree but had not refreshed patches before emailing them)

---
 arch/x86/include/asm/syscalls.h    |    1 +
 arch/x86/include/asm/unistd_32.h   |    1 +
 arch/x86/kernel/entry_32.S         |    1 +
 arch/x86/kernel/process_32.c       |   63 +++++++++++++++++++++++
 arch/x86/kernel/syscall_table_32.S |    1 +
 include/linux/types.h              |   11 ++++
 kernel/fork.c                      |   96 +++++++++++++++++++++++++++++++++++-
 7 files changed, 173 insertions(+), 1 deletions(-)
arch/x86/include/asm/syscalls.h +1 -0
index 372b76e..3205f55 100644
@@ -40,6 +40,7 @@ long sys_iopl(struct pt_regs *);
 
 /* kernel/process_32.c */
 int sys_clone(struct pt_regs *);
+int sys_clone_with_pids(struct pt_regs *);
 int sys_execve(struct pt_regs *);
 
 /* kernel/signal.c */
arch/x86/include/asm/unistd_32.h +1 -0
index 6fb3c20..a427571 100644
@@ -342,6 +342,7 @@
 #define __NR_pwritev		334
 #define __NR_rt_tgsigqueueinfo	335
 #define __NR_perf_event_open	336
+#define __NR_clone_with_pids	337
 
 #ifdef __KERNEL__
 
arch/x86/kernel/entry_32.S +1 -0
index c097e7d..c7bd1f6 100644
@@ -718,6 +718,7 @@ ptregs_##name: \
 PTREGSCALL(iopl)
 PTREGSCALL(fork)
 PTREGSCALL(clone)
+PTREGSCALL(clone_with_pids)
 PTREGSCALL(vfork)
 PTREGSCALL(execve)
 PTREGSCALL(sigaltstack)
arch/x86/kernel/process_32.c +63 -0

69 lines not on this page · raw message

arch/x86/kernel/syscall_table_32.S +1 -0
index 0157cd2..1357e83 100644
@@ -336,3 +336,4 @@ ENTRY(sys_call_table)
 	.long sys_pwritev
 	.long sys_rt_tgsigqueueinfo	/* 335 */
 	.long sys_perf_event_open
+	.long ptregs_clone_with_pids
include/linux/types.h +11 -0
index c42724f..626c60b 100644
@@ -204,6 +204,17 @@ struct ustat {
 	char			f_fpack[6];
 };
 
+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 clone_args_size;
+	u64 reserved1;
+};
+
 #endif	/* __KERNEL__ */
 #endif /*  __ASSEMBLY__ */
 #endif /* _LINUX_TYPES_H */
kernel/fork.c +95 -1

120 lines not on this page · raw message

Signature · 1 line
1.6.0.4

A message that is not in the archive (sent off-list or not yet received): 20091101204548.GG23168-r/[email protected]

#11 lore raw

Re: [v10][PATCH 8/9] Define clone_with_pids() syscall

Quoted 3 lines · Sukadev Bhattiprolu

From: Sukadev Bhattiprolu <suka@suka.(none)>
Date: Tue, 20 Oct 2009 22:04:57 -0700
Subject: [v10][PATCH 8/9] Define clone_with_pids() syscall

[...]

Quoted 28 lines
arch/x86/kernel/process_32.c +20 -0
index 4cf7956..41081eb 100644
@@ -445,6 +445,69 @@ int sys_clone(struct pt_regs *regs)
 	return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr);
 }
 
+int sys_clone_with_pids(struct pt_regs *regs)
+{
+	int rc;
+	struct clone_args kcs;
+	unsigned long flags;
+	int __user *parent_tid_ptr;
+	int __user *child_tid_ptr;
+	unsigned long __user child_stack;
+	unsigned long stack_size;
+	unsigned int flags_low;
+	struct clone_args __user *ucs;
+	pid_t __user *pids;
+
+	flags_low = regs->bx;
+	ucs = (int __user *)regs->cx;
+	pids = (int __user *)regs->dx;
+
+	rc = copy_from_user(&kcs, ucs, sizeof(kcs));
+	if (rc)
+		return -EFAULT;

arch/x86/kernel/process_32.c int sys_clone(struct pt_regs *regs) · line 477

Quoted 10 lines
arch/x86/kernel/process_32.c +10 -0
+
+	/*
+	 * TODO: If size of clone_args is not what the kernel expects, it
+	 * 	 could be that kernel is newer and has an extended structure.
+	 * 	 When that happens, this check needs to be smarter (and we
+	 * 	 need an additional copy_from_user()). For now, assume exact
+	 * 	 match.
+	 */
+	if (kcs.clone_args_size != sizeof(kcs))
+		return -EINVAL;

I wonder if this is a reason to move the clone_args_size outside the structure and pass it as a regular argument ? This will rid the (futuristic) additional copy-from-user (in case it causes a concern for clone performance ?)

Oren.

A message that is not in the archive (sent off-list or not yet received): [email protected]

#13 lore raw

Re: [v10][PATCH 8/9] Define clone_with_pids() syscall

Oren Laadan [[email protected]] wrote:

| 
| > +	/*
| > +	 * TODO: If size of clone_args is not what the kernel expects, it
| > +	 * 	 could be that kernel is newer and has an extended structure.
| > +	 * 	 When that happens, this check needs to be smarter (and we
| > +	 * 	 need an additional copy_from_user()). For now, assume exact
| > +	 * 	 match.
| > +	 */
| > +	if (kcs.clone_args_size != sizeof(kcs))
| > +		return -EINVAL;
| 
| I wonder if this is a reason to move the clone_args_size outside the
| structure and pass it as a regular argument ?  This will rid the
| (futuristic) additional copy-from-user (in case it causes a concern
| for clone performance ?)

Your idea makes sense to me.

Roland, Peter Arnd: do you have any concerns with removing clone_args_size from the structure and making it a parameter ?

	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;
		u64 reserved1;
	};

	sys_clone_with_pids(u32 flags_low, struct clone_args *cargs,
			u32 nr_pids, u32 clone_args_size)

Sukadev

A message that is not in the archive (sent off-list or not yet received): 20091103064454.GA22483-r/[email protected]

#15 lore raw

Re: [v10][PATCH 8/9] Define clone_with_pids() syscall

Quoted 18 lines · Sukadev Bhattiprolu

Oren Laadan [[email protected]] wrote:

| 
| > +   /*
| > +    * TODO: If size of clone_args is not what the kernel expects, it
| > +    *       could be that kernel is newer and has an extended structure.
| > +    *       When that happens, this check needs to be smarter (and we
| > +    *       need an additional copy_from_user()). For now, assume exact
| > +    *       match.
| > +    */
| > +   if (kcs.clone_args_size != sizeof(kcs))
| > +           return -EINVAL;
| 
| I wonder if this is a reason to move the clone_args_size outside the
| structure and pass it as a regular argument ?  This will rid the
| (futuristic) additional copy-from-user (in case it causes a concern
| for clone performance ?)
Quoted 4 lines

Your idea makes sense to me.

Roland, Peter Arnd: do you have any concerns with removing clone_args_size from the structure and making it a parameter ?

As I mentioned before when it came up the first time, I think that the clone_args_size argument is completely bogus and unnecessary in the presence of the flags word, but I agree that if we have it, it should better be a separate argument.

	Arnd <><
#16 lore raw

Re: [v10][PATCH 8/9] Define clone_with_pids() syscall

In reply to a message not in the archive
Quoted 2 lines · Sukadev Bhattiprolu
arch/x86/kernel/process_32.c +2 -0
+int sys_clone_with_pids(struct pt_regs *regs)
+{

...

Quoted 9 lines
arch/x86/kernel/process_32.c +9 -0
+       rc = copy_from_user(&kcs, ucs, sizeof(kcs));
+       if (rc)
+               return -EFAULT;
+
+       /*
+        * TODO: If size of clone_args is not what the kernel expects, it
+        *       could be that kernel is newer and has an extended structure.
+        *       When that happens, this check needs to be smarter (and we
+        *       need an additional copy_from_user()). For now, assume exact

arch/x86/kernel/process_32.c int sys_clone(struct pt_regs *regs) · line 483

Quoted 10 lines
arch/x86/kernel/process_32.c +10 -0
+        *       match.
+        */
+       if (kcs.clone_args_size != sizeof(kcs))
+               return -EINVAL;
+
+       /*
+        * To avoid future compatibility issues, ensure unused fields are 0.
+        */
+       if (kcs.reserved1 || kcs.clone_flags_high)
+               return -EINVAL;

Suka, is there a reason we don't have these bits in some arch-independent code? I would have thought the stuff in the process_32.c code would be just as thin a wrapper as possible to unwrap the pt_regs and call into generic code.

-- Dave

#17 lore raw

Re: [v10][PATCH 8/9] Define clone_with_pids() syscall

Dave Hansen [[email protected]] wrote:

| On Sun, 2009-11-01 at 12:45 -0800, Sukadev Bhattiprolu wrote:
| > +int sys_clone_with_pids(struct pt_regs *regs)
| > +{
| ...
| > +       rc = copy_from_user(&kcs, ucs, sizeof(kcs));
| > +       if (rc)
| > +               return -EFAULT;
| > +
| > +       /*
| > +        * TODO: If size of clone_args is not what the kernel expects, it
| > +        *       could be that kernel is newer and has an extended structure.
| > +        *       When that happens, this check needs to be smarter (and we
| > +        *       need an additional copy_from_user()). For now, assume exact
| > +        *       match.
| > +        */
| > +       if (kcs.clone_args_size != sizeof(kcs))
| > +               return -EINVAL;
| > +
| > +       /*
| > +        * To avoid future compatibility issues, ensure unused fields are 0.
| > +        */
| > +       if (kcs.reserved1 || kcs.clone_flags_high)
| > +               return -EINVAL;
| 
| Suka, is there a reason we don't have these bits in some
| arch-independent code?  I would have thought the stuff in the
| process_32.c code would be just as thin a wrapper as possible to unwrap
| the pt_regs and call into generic code.

Yes, it can be in arch independent code: but couple of minor inconvinences:

- currently the arch independent code does not know about the 'clone_args' structure. This helps keep this patchset smaller. It could know about it in the future (when more flags are added).

- and we need to check these *before* unwrapping the values from pt_regs.

Well, you are right. Will define 'check_clone_with_pids_params()' in kernel/fork.c and call it here before unwrapping the params.

Suka

#18 lore raw

Re: [v10][PATCH 8/9] Define clone_with_pids() syscall

In reply to Dave Hansen

Dave Hansen [[email protected]] wrote:

| Suka, is there a reason we don't have these bits in some
| arch-independent code?  I would have thought the stuff in the
| process_32.c code would be just as thin a wrapper as possible to unwrap
| the pt_regs and call into generic code.

Here is the updated patch addressing your comment and Oren's comment about making 'clone_args_size' a parameter. There are corresponding minor changes to the Documentation patch which I will post later.

---
From 3779f8bc34189fcc32df1c78504bd42ec2dfc566 Mon Sep 17 00:00:00 2001
From: Sukadev Bhattiprolu <suka@suka.(none)>
Date: Tue, 20 Oct 2009 22:04:57 -0700
Subject: [PATCH 8/9] Define clone_with_pids() syscall

Container restart requires that a task have the same pid it had when it was checkpointed. When containers are nested the tasks within the containers exist in multiple pid namespaces and hence have multiple pids to specify during restart.

clone_with_pids(), intended for use during restart, is the same as clone(), except that it takes a 'pids' paramter. This parameter lets caller choose specific pid numbers for the child process, in the process's active and ancestor pid namespaces. (Descendant pid namespaces in general don't matter since processes don't have pids in them anyway, but see comments in copy_target_pids() regarding CLONE_NEWPID).

clone_with_pids() also attempts to address a second limitation of the clone() system call. clone() is restricted to 32 clone flags and all but one of these are in use. If more new clone flags are needed, we will be forced to define a new variant of the clone() system call. To address this, clone_with_pids() allows at least 64 clone flags with some room for more if necessary.

To prevent unprivileged processes from misusing this interface, clone_with_pids() currently needs CAP_SYS_ADMIN, when the 'pids' parameter is non-NULL.

See Documentation/clone_with_pids in next patch for more details and an example of its usage.

NOTE:
- System calls are restricted to 6 parameters and the number and sizes of parameters needed for clone_with_pids() exceed 6 integers. The new prototype works around this restriction while providing some flexibility if clone_with_pids() needs to be further extended in the future.
TODO:
- We should convert clone-flags to 64-bit value in all architectures. Its probably best to do that as a separate patchset since clone_flags touches several functions and that patchset seems independent of this new system call.

Changelog[v11]:
- [Dave Hansen] Move clone_args validation checks to arch-indpeendent code.
- [Oren Laadan] Make args_size a parameter to system call and remove it from 'struct clone_args'

Changelog[v10]:
- Rename clone3() to clone_with_pids()
- [Linus Torvalds] Use PTREGSCALL() rather than the generic syscall implementation

Changelog[v9]:
- [Roland McGrath, H. Peter Anvin] To avoid confusion on 64-bit architectures split the new clone-flags into 'low' and 'high' words and pass in the 'lower' flags as the first argument. This would maintain similarity of the clone3() with clone()/ clone2(). Also has the side-effect of the name matching the number of parameters :-)
- [Roland McGrath] Rename structure to 'clone_args' and add a 'child_stack_size' field

Changelog[v8]
- [Oren Laadan] parent_tid and child_tid fields in 'struct clone_arg' must be 64-bit.
- clone2() is in use in IA64. Rename system call to clone3().

Changelog[v7]:
- [Peter Zijlstra, Arnd Bergmann] Rename system call to clone2() and group parameters into a new 'struct clone_struct' object.

Changelog[v6]:
- (Nathan Lynch, Arnd Bergmann, H. Peter Anvin, Linus Torvalds) Change 'pid_set.pids' to a 'pid_t pids[]' so size of 'struct pid_set' is constant across architectures.
- (Nathan Lynch) Change pid_set.num_pids to unsigned and remove 'unum_pids < 0' check.

Changelog[v4]:
- (Oren Laadan) rename 'struct target_pid_set' to 'struct pid_set'

Changelog[v3]:
- (Oren Laadan) Allow CLONE_NEWPID flag (by allocating an extra pid in the target_pids[] list and setting it 0. See copy_target_pids()).
- (Oren Laadan) Specified target pids should apply only to youngest pid-namespaces (see copy_target_pids())
- (Matt Helsley) Update patch description.

Changelog[v2]:
- Remove unnecessary printk and add a note to callers of copy_target_pids() to free target_pids.
- (Serge Hallyn) Mention CAP_SYS_ADMIN restriction in patch description.
- (Oren Laadan) Add checks for 'num_pids < 0' (return -EINVAL) and 'num_pids == 0' (fall back to normal clone()).
- Move arch-independent code (sanity checks and copy-in of target-pids) into kernel/fork.c and simplify sys_clone_with_pids()

Changelog[v1]:
- Fixed some compile errors (had fixed these errors earlier in my git tree but had not refreshed patches before emailing them)

---
 arch/x86/include/asm/syscalls.h    |    1 +
 arch/x86/include/asm/unistd_32.h   |    1 +
 arch/x86/kernel/entry_32.S         |    1 +
 arch/x86/kernel/process_32.c       |   49 ++++++++++++++
 arch/x86/kernel/syscall_table_32.S |    1 +
 include/linux/sched.h              |    2 +
 include/linux/types.h              |   11 +++
 kernel/fork.c                      |  124 +++++++++++++++++++++++++++++++++++-
 8 files changed, 189 insertions(+), 1 deletions(-)
arch/x86/include/asm/syscalls.h +1 -0
index 372b76e..3205f55 100644
@@ -40,6 +40,7 @@ long sys_iopl(struct pt_regs *);
 
 /* kernel/process_32.c */
 int sys_clone(struct pt_regs *);
+int sys_clone_with_pids(struct pt_regs *);
 int sys_execve(struct pt_regs *);
 
 /* kernel/signal.c */
arch/x86/include/asm/unistd_32.h +1 -0
index 6fb3c20..a427571 100644
@@ -342,6 +342,7 @@
 #define __NR_pwritev		334
 #define __NR_rt_tgsigqueueinfo	335
 #define __NR_perf_event_open	336
+#define __NR_clone_with_pids	337
 
 #ifdef __KERNEL__
 
arch/x86/kernel/entry_32.S +1 -0
index c097e7d..c7bd1f6 100644
@@ -718,6 +718,7 @@ ptregs_##name: \
 PTREGSCALL(iopl)
 PTREGSCALL(fork)
 PTREGSCALL(clone)
+PTREGSCALL(clone_with_pids)
 PTREGSCALL(vfork)
 PTREGSCALL(execve)
 PTREGSCALL(sigaltstack)
arch/x86/kernel/process_32.c +49 -0

55 lines not on this page · raw message

arch/x86/kernel/syscall_table_32.S +1 -0
index 0157cd2..1357e83 100644
@@ -336,3 +336,4 @@ ENTRY(sys_call_table)
 	.long sys_pwritev
 	.long sys_rt_tgsigqueueinfo	/* 335 */
 	.long sys_perf_event_open
+	.long ptregs_clone_with_pids
include/linux/sched.h +2 -0
index 265efe5..0095ba0 100644
@@ -2153,6 +2153,8 @@ extern int disallow_signal(int);
 
 extern int do_execve(char *, char __user * __user *, char __user * __user *, struct pt_regs *);
 extern long do_fork(unsigned long, unsigned long, struct pt_regs *, unsigned long, int __user *, int __user *);
+extern int fetch_clone_args_from_user(struct clone_args __user *, int,
+				struct clone_args *);
 extern long do_fork_with_pids(unsigned long, unsigned long, struct pt_regs *,
 				unsigned long, int __user *, int __user *,
 				unsigned int, pid_t __user *);
include/linux/types.h +11 -0
index c42724f..0d08683 100644
@@ -204,6 +204,17 @@ struct ustat {
 	char			f_fpack[6];
 };
 
+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;
+	u64 reserved1;
+};
+
 #endif	/* __KERNEL__ */
 #endif /*  __ASSEMBLY__ */
 #endif /* _LINUX_TYPES_H */
kernel/fork.c +123 -1

148 lines not on this page · raw message

Signature · 1 line
1.6.0.4
#19 lore raw

PATCH v10 9/9 Document clone_with_pids() syscall

In reply to a message not in the archive

From: Sukadev Bhattiprolu <suka@suka.(none)>
Date: Sun, 25 Oct 2009 20:20:00 -0700
Subject: [v10][PATCH 9/9] Document clone_with_pids() syscall

This gives a brief overview of the clone_with_pids() system call. We should eventually describe more details in existing clone(2) man page or in a new man page.

Changelog[v10-rc1]:
- Rename clone3() to clone_with_pids() and fix some typos.
- Modify example to show usage with the ptregs implementation.
Changelog[v9]:
- [Pavel Machek]: Fix an inconsistency and rename new file to Documentation/clone3.
- [Roland McGrath, H. Peter Anvin] Updates to description and example to reflect new prototype of clone3() and the updated/ renamed 'struct clone_args'.

Changelog[v8]:
- clone2() is already in use in IA64. Rename syscall to clone3()
- Add notes to say that we return -EINVAL if invalid clone flags are specified or if the reserved fields are not 0.
Changelog[v7]:
- Rename clone_with_pids() to clone2()
- Changes to reflect new prototype of clone2() (using clone_struct).

---
 Documentation/clone_with_pids |  320 +++++++++++++++++++++++++++++++++++++++++
 1 files changed, 320 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/clone_with_pids
Documentation/clone_with_pids new file +320 -0
new file mode 100644
index 0000000..917992a
@@ -0,0 +1,320 @@
+
+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 clone_args_size;
+	u64 reserved1;
+};
+
+
+clone_with_pids(u32 flags_low, struct clone_args * __user cargs,
+		pid_t * __user pids)
+
+	In addition to doing everything that clone() system call does,
+	the clone_with_pids() system call:
+
+		- allows additional clone flags (31 of 32 bits in the flags
+		  parameter to clone() are in use)
+
+		- allows user to specify a pid for the child process in its
+		  active and ancestor pid namespaces.
+
+	This system call is meant to be used when restarting an application
+	from a checkpoint.  Such restart requires that the processes in the
+	application have the same pids they had when the application was
+	checkpointed. When containers are nested, the processes within the
+	containers exist in multiple pid namespaces and hence have multiple
+	pids to specify during restart.
+
+	The @flags_low parameter is identical to the 'clone_flags' parameter
+	in existing clone() system call.
+
+	The fields in 'struct clone_args' are meant to be used as follows:
+
+	u64 clone_flags_high:
+
+		When clone_with_pids() supports more than 32 clone flags, the
+		higher bits in the clone_flags should be specified in this
+		field.  This field is currently unused and must be set to 0.
+
+	u64 child_stack_base;
+	u64 child_stack_size;
+
+		These two fields correspond to the 'child_stack' fields
+		in clone() and clone2() system calls (on IA64).
+
+	u64 parent_tid_ptr;
+	u64 child_tid_ptr;
+
+		These two fields correspond to the 'parent_tid_ptr' and
+		'child_tid_ptr' fields in the clone() system call
+
+	u32 nr_pids;
+
+		nr_pids specifies the number of pids in the @pids array
+		parameter to clone_with_pids() (see below). nr_pids should
+		not exceed the current nesting level of the calling process
+		(i.e if the process is in init_pid_ns, nr_pids must be 1,
+		if process is in a pid namespace that is a child of
+		init-pid-ns, nr_pids cannot exceed 2, and so on).
+
+	u32 clone_args_size;
+
+		clone_args_size specifes the sizeof(struct clone_args) and is
+		intended to enable extending this structure in the future,
+		while preserving backward compatibility.  For now, this field
+		must be set to the sizeof(struct clone_args) and this size must
+		match the kernel's view of the structure.
+
+	u64 reserved1;
+
+		reserved1 is intended to enable extending the functionality
+		of the clone_with_pids() system call in the future, while
+		preserving backward compatibility. It must currently be set
+		to 0.
+
+	The @pids parameter defines the set of pids that should be assigned to
+	the child process in its active and ancestor pid namespaces. The
+	descendant pid namespaces do not matter since a process does not have a
+	pid in descendant namespaces, unless the process is in a new pid
+	namespace in which case the process is a container-init (and must have
+	the pid 1 in that namespace).
+
+	See CLONE_NEWPID section of clone(2) man page for details about pid
+	namespaces.
+
+	The order of pids in @pids corresponds to the nesting order of pid-
+	namespaces, with @pids[0] corresponding to the init_pid_ns.
+
+	If a pid in the @pids list is 0, the kernel will assign the next
+	available pid in the pid namespace, for the process.
+
+	If a pid in the @pids list is non-zero, the kernel tries to assign
+	the specified pid in that namespace.  If that pid is already in use
+	by another process, the system call fails (see EBUSY below).
+
+	On success, the system call returns the pid of the child process in
+	the parent's active pid namespace.
+
+	On failure, clone_with_pids() returns -1 and sets 'errno' to one of
+	following values (the child process is not created).
+
+	EPERM	Caller does not have the SYS_ADMIN privilege needed to execute
+		this call.
+
+	EINVAL	The number of pids specified in 'clone_args.nr_pids' exceeds
+		the current nesting level of parent process
+
+	EINVAL	Not all specified clone-flags are valid.
+
+	EINVAL	The reserved fields in the clone_args argument are not 0.
+
+	EBUSY	A requested pid is in use by another process in that name space.
+
+---
+/* Example clone_with_pids() usage - Create a child with pid CHILD_TID */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <wait.h>
+#include <sys/syscall.h>
+
+#define __NR_clone_with_pids	337
+#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 clone_args_size;
+
+        u64 reserved1;
+};
+
+#define exit		_exit
+
+/*
+ * Following clone_with_pids() is based on code posted by Oren Laadan at:
+ * https://lists.linux-foundation.org/pipermail/containers/2009-June/018463.html
+ */
+#if defined(__i386__) && defined(__NR_clone_with_pids)
+
+int clone_with_pids(int flags_low, struct clone_args *clone_args, int *pids)
+{
+	long retval;
+
+	__asm__  __volatile__(
+		 "movl %0, %%ebx\n\t"		/* flags -> 1st (ebx) */
+		 "movl %1, %%ecx\n\t"		/* clone_args -> 2nd (ecx)*/
+		 "movl %2, %%edx\n\t"		/* pids */
+		 "pushl %%ebp\n\t"		/* save value of ebp */
+		:
+		:"b" (flags_low),
+		 "c" (clone_args),
+		 "d" (pids)
+		);
+
+	__asm__ __volatile__(
+		 "int $0x80\n\t"	/* Linux/i386 system call */
+		 "testl %0,%0\n\t"	/* check return value */
+		 "jne 1f\n\t"		/* jump if parent */
+		 "popl %%ebx\n\t"	/* get subthread function */
+		 "call *%%ebx\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_clone_with_pids), "i" (__NR_exit)
+		:"ebx", "ecx", "edx"
+		);
+
+	if (retval < 0) {
+		errno = -retval;
+		retval = -1;
+	}
+	return retval;
+}
+
+/*
+ * Allocate a stack for the clone-child and arrange to have the child
+ * execute @child_fn with @child_arg as the argument.
+ */
+void *setup_stack(int (*child_fn)(void *), void *child_arg)
+{
+	void *child_stack;
+	void **new_stack;
+
+        child_stack = malloc(STACKSIZE);
+        if (!child_stack) {
+		perror("malloc()");
+		exit(1);
+	}
+        child_stack = (char *)child_stack + (STACKSIZE - 4);
+
+	new_stack = (void **)child_stack;
+	*--new_stack = child_arg;
+	*--new_stack = child_fn;
+
+	return new_stack;
+}
+
+#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;
+}
+
+#define CHILD_TID	377
+struct clone_args clone_args;
+void *child_arg = &clone_args;
+int child_tid;
+
+int do_child(void *arg)
+{
+	struct clone_args *cs = (struct clone_args *)arg;
+	int ctid;
+
+	/* Verify we pushed the arguments correctly on the stack... */
+	if (arg != child_arg)  {
+		printf("Child: Incorrect child arg pointer, expected %p,"
+				"actual %p\n", child_arg, arg);
+		exit(1);
+	}
+
+	/* ... and that we got the thread-id we expected */
+	ctid = *((int *)cs->child_tid_ptr);
+	if (ctid != CHILD_TID) {
+		printf("Child: Incorrect child tid, expected %d, actual %d\n",
+				CHILD_TID, ctid);
+		exit(1);
+	}
+	sleep(3);
+
+	printf("[%d, %d]: Child exiting\n", getpid(), ctid);
+	exit(0);
+}
+
+static int do_clone(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 = &clone_args;
+
+	stack = setup_stack(child_fn, child_arg);
+
+	memset(ca, 0, sizeof(*ca));
+	ca->child_stack_base 	= (u64)stack;
+	ca->child_stack_size 	= (u64)0;
+	ca->parent_tid_ptr   	= (u64)0;
+	ca->child_tid_ptr 	= (u64)&child_tid;
+	ca->nr_pids 		= nr_pids;
+	ca->clone_args_size 	= sizeof(*ca);
+
+	rc = clone_with_pids(flags_low, ca, pids_list);
+
+	printf("[%d, %d]: clone_with_pids() returned %d, error %d\n",
+		 getpid(), gettid(), rc, errno);
+
+	return rc;
+}
+
+pid_t pids_list[] = { CHILD_TID, CHILD_TID };
+main()
+{
+	int rc, pid, ret, status;
+	unsigned long flags;
+	int nr_pids = 1;
+
+	flags = SIGCHLD|CLONE_PARENT_SETTID|CLONE_CHILD_SETTID;
+
+	pid = do_clone(do_child, &clone_args, flags, nr_pids, pids_list);
+
+	printf("[%d, %d]: Parent waiting for %d\n", getpid(), gettid(), pid);
+
+	rc = waitpid(pid, &status, __WALL);
+	if (rc < 0) {
+		printf("waitpid(): rc %d, error %d\n", rc, errno);
+	} else {
+		printf("[%d, %d]: child %d:\n\t wait-status 0x%x\n", getpid(),
+			 gettid(), rc, status);
+
+		if (WIFEXITED(status)) {
+			printf("\t EXITED, %d\n", WEXITSTATUS(status));
+		} else if (WIFSIGNALED(status)) {
+			printf("\t SIGNALED, %d\n", WTERMSIG(status));
+		}
+	}
+}
Signature · 1 line
1.6.0.4
Acked-by
#20 lore raw
In reply to a message not in the archive

Hi,

What happened to eclone() or clonex() ?

('e' or 'x' for extended, and we extended it in a way so that future extension need not require a new syscall).

Anything but clone_with_pids() ...

Otherwise, for patches 3 and 6:

Oren.

Quoted 115 lines · Sukadev Bhattiprolu

[v10][PATCH 0/9] Implement clone_with_pids() system call

To support application checkpoint/restart, a task must have the same pid it had when it was checkpointed. When containers are nested, the tasks within the containers exist in multiple pid namespaces and hence have multiple pids to specify during restart.

This patchset implements a new system call, clone_with_pids() that lets a process specify the pids of the child process.

Patches 1 through 6 are helper patches needed for choosing a pid for the child process.

PATCH 8 defines a prototype of the new system call. PATCH 9 adds some documentation on the new system call, some/all of which will eventually go into a man page.

Changelog[v10-rc1]:
- [Linus Torvalds] Use PTREGSCALL() implementation for clone rather than the generic system call
- Rename clone3() to clone_with_pids()
- Update Documentation/clone_with_pids() to show example usage with the PTREGSCALL implementation.

Changelog[v9]:
- [Pavel Emelyanov] Drop the patch that made 'pid_max' a property of struct pid_namespace
- [Roland McGrath, H. Peter Anvin and earlier on, Serge Hallyn] To avoid inadvertent truncation clone_flags, preserve the first parameter of clone3() as 'u32 clone_flags' and specify newer flags in clone_args.flags_high (PATCH 8/9 and PATCH 9/9)
- [Eric Biederman] Generalize alloc_pidmap() code to simplify and remove duplication (see PATCH 3/9].

Changelog[v8]:
- [Oren Laadan, Louis Rilling, KOSAKI Motohiro]
The name 'clone2()' is in use - renamed new syscall to clone3().
- [Oren Laadan] ->parent_tidptr and ->child_tidptr need to be 64bit.
- [Oren Laadan] Ensure that unused fields/flags in clone_struct are 0. (Added [PATCH 7/10] to the patchset).

Changelog[v7]:
- [Peter Zijlstra, Arnd Bergmann]
Group the arguments to clone2() into a 'struct clone_arg' to workaround the issue of exceeding 6 arguments to the system call. Also define clone-flags as u64 to allow additional clone-flags.

Changelog[v6]:
- [Nathan Lynch, Arnd Bergmann, H. Peter Anvin, Linus Torvalds] Change 'pid_set.pids' to 'pid_t pids[]' so sizeof(struct pid_set) is constant across architectures (Patches 7, 8).
- (Nathan Lynch) Change pid_set.num_pids to unsigned and remove 'unum_pids < 0' check (Patches 7,8)
- (Pavel Machek) New patch (Patch 9) to add some documentation.

Changelog[v5]:
- Make 'pid_max' a property of pid_ns (Integrated Serge Hallyn's patch into this set)
- (Eric Biederman): Avoid the new function, set_pidmap() - added couple of checks on 'target_pid' in alloc_pidmap() itself.

=== IMPORTANT NOTE:

clone() system call has another limitation - all but one, available bits in clone-flags are in use and if more new clone-flags are needed, we will need a variant of the clone() system call.

It appears to make sense to try and extend this new system call to address this limitation as well. The requirements of a new clone system call could then be summarized as:

- do everything clone() does today, and
- give application an ability to choose pids for the child process in all ancestor pid namespaces, and
- allow more clone_flags

Contstraints:

- system-calls are restricted to 6 parameters and clone() already takes 5 parameters, any extension to clone() interface would require one or more copy_from_user(). (Not sure if copy_from_user() of ~40 bytes would have a significant impact on performance of clone()).

Based on these requirements and constraints, we explored a couple of system call interfaces (in earlier versions of this patchset). Based on input from Arnd Bergmann and others, the new interface of the system call is:

	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 clone_args_size;
		u64 reserved1;
	};

	sys_clone_with_pids(u32 flags_low, struct clone_args *cargs,
			pid_t *pids)

Details of the struct clone_args and the usage are explained in the documentation (PATCH 9/9).

NOTE:
While this patchset enables support for more clone-flags, actual implementation for additional clone-flags is best implemented as a separate patchset (PATCH 8/9 identifies some TODOs)

_______________________________________________
Containers mailing list Containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org
https://lists.linux-foundation.org/mailman/listinfo/containers

A message that is not in the archive (sent off-list or not yet received): [email protected]

#22 lore raw

Oren Laadan [[email protected]] wrote:

| Hi,
| 
| What happened to eclone() or clonex() ?

Well, TBH, I don't know exactly why clone3() sucks - when there are more than two flavors of a call, numbers maybe the easiest to suffix. But if it sucks bc its cryptic about the nature of the extension, then eclone(), clonex() or even clone_extended() are about as descriptive as clone3() IMHO.

Given the discussion about ckpt v. checkpoint, I would like to go with a descriptive name.

Sukadev