Application checkpoint/restart (c/r) is the ability to save the state
of a running application so that it can later resume its execution
from the time at which it was checkpointed, on the same or a different
machine.
This version introduces 'clone_with_pids()' syscall to preset pid(s)
for a child process. It is used by restart(2) to recreate process
hierarchy with the same pids as at checkpoint time.
It also adds a freezer state CHECKPOINTING to safeguard processes
during a checkpoint. Other important changes include support for
threads and zombies, credentials, signal handling, and improved
restart logic. See below for a more detailed changelog.
Compiled and tested against v2.6.31-rc3.
For more information, check out Documentation/checkpoint/*.txt
Q: How useful is this code as it stands in real-world usage?
A: Right now, the application can be single- or multi-processes and
threads. Supports open files - regular files and directories on
ext[234], pipes, and /dev/{null,zero,random,urandom}. All sort of
shared memory work. sysv IPC also works (except for semaphore
undo). It's already suitable for many types of batch jobs. (Note:
it is assumed that the fs view is available at restart).
Q: What can it checkpoint and restart ?
A: A (single threaded) process can checkpoint itself, aka "self"
checkpoint, if it calls the new system calls. Otherise, for an
"external" checkpoint, the caller must first freeze the target
processes. One can either checkpoint an entire container (and
we make best effort to ensure that the result is self-contained),
or merely a subtree of a process hierarchy.
Q: What about namespaces ?
A: Currrently, UTS and IPC namespaces are restored. They demonstrate
how namespaces are handled. More to come.
Q: What additional work needs to be done to it?
A: Fill in the gory details following the examples so far. WIP
includes (pending) signals, fifos, pseudo terminals, unix-
domain sockets, event-poll, architectures: powerpc and x86_64,
and more file systems types.
Q: How can I try it ?
A: This one can actually be used for simple batch jobs (pipes, too),
a whole container or just a subtree of tasks. Try it:
create the freezer cgroup:
$ mount -t cgroup -ofreezer freezer /cgroup
$ mkdir /cgroup/0
run the test, freeze it:
$ test/multitask &
[1] 2754
$ for i in `pidof multitask`; do echo $i > /cgroup/0/tasks; done
$ echo FROZEN > /cgruop/0/freezer.state
checkpoint:
$ ./ckpt 2754 > ckpt.out
restart:
$ ./mktree < ckpt.out
voila :)
To do all this, you'll need:
The git tree tracking v17, branch 'ckpt-v17' (and past versions):
git://git.ncl.cs.columbia.edu/pub/git/linux-cr.git
Restarting multiple processes requires 'mktree' userspace tool with
the matching branch (v17):
git://git.ncl.cs.columbia.edu/pub/git/user-cr.git
Oren.
Changelog:
[2009-Jul-21] v17
- Introduce syscall clone_with_pids() to restore original pids
- Support threads and zombies
- Save/restore task->files
- Save/restore task->sighand
- Save/restore futex
- Save/restore credentials
- Introduce PF_RESTARTING to skip notifications on task exit
- restart(2) allow caller to ask to freeze tasks after restart
- restart(2) isn't idempotent: return -EINTR if interrupted
- Improve debugging output handling
- Make multi-process restart logic more robust and complete
- Correctly select return value for restarting tasks on success
- Tighten ptrace test for checkpoint to PTRACE_MODE_ATTACH
- Use CHECKPOINTING state for frozen checkpointed tasks
- Fix compilation without CONFIG_CHECKPOINT
- Fix compilation with CONFIG_COMPAT
- Fix headers includes and exports
- Leak detection performed in two steps
- Detect "inverse" leaks of objects (dis)appearing unexpectedly
- Memory: save/restore mm->{flags,def_flags,saved_auxv}
- Memory: only collect sub-objects of mm once (leak detection)
- Files: validate f_mode after restore
- Namespaces: leak detection for nsproxy sub-components
- Namespaces: proper restart from namespace(s) without namespace(s)
- Save global constants in header instead of per-object
- IPC: replace sys_unshare() with create_ipc_ns()
- IPC: restore objects in suitable namespace
- IPC: correct behavior under !CONFIG_IPC_NS
- UTS: save/restore all fields
- UTS: replace sys_unshare() with create_uts_ns()
- X86_32: sanitize cpu, debug, and segment registers on restart
- cgroup_freezer: add CHECKPOINTING state to safeguard checkpoint
- cgroup_freezer: add interface to freeze a cgroup (given a task)
[2009-May-27] v16
- Privilege checks for IPC checkpoint
- Fix error string generation during checkpoint
- Use kzalloc for header allocation
- Restart blocks are arch-independent
- Redo pipe c/r using splice
- Fixes to s390 arch
- Remove powerpc arch (temporary)
- Explicitly restore ->nsproxy
- All objects in image are precedeed by 'struct ckpt_hdr'
- Fix leaks detection (and leaks)
- Reorder of patchset
- Misc bugs and compilation fixes
[2009-Apr-12] v15
- Minor fixes
[2009-Apr-28] v14
- Tested against kernel v2.6.30-rc3 on x86_32.
- Refactor files chekpoint to use f_ops (file operations)
- Refactor mm/vma to use vma_ops
- Explicitly handle VDSO vma (and require compat mode)
- Added code to c/r restat-blocks (restart timeout related syscalls)
- Added code to c/r namespaces: uts, ipc (with Dan Smith)
- Added code to c/r sysvipc (shm, msg, sem)
- Support for VM_CLONE shared memory
- Added resource leak detection for whole-container checkpoint
- Added sysctl gauge to allow unprivileged restart/checkpoint
- Improve and simplify the code and logic of shared objects
- Rework image format: shared objects appear prior to their use
- Merge checkpoint and restart functionality into same files
- Massive renaming of functions: prefix "ckpt_" for generics,
"checkpoint_" for checkpoint, and "restore_" for restart.
- Report checkpoint errors as a valid (string record) in the output
- Merged PPC architecture (by Nathan Lunch),
- Requires updates to userspace tools too.
- Misc nits and bug fixes
[2009-Mar-31] v14-rc2
- Change along Dave's suggestion to use f_ops->checkpoint() for files
- Merge patch simplifying Kconfig, with CONFIG_CHECKPOINT_SUPPORT
- Merge support for PPC arch (Nathan Lynch)
- Misc cleanups and fixes in response to comments
[2009-Mar-20] v14-rc1:
- The 'h.parent' field of 'struct cr_hdr' isn't used - discard
- Check whether calls to cr_hbuf_get() succeed or fail.
- Fixed of pipe c/r code
- Prevent deadlock by refusing c/r when a pipe inode == ctx->file inode
- Refuse non-self checkpoint if a task isn't frozen
- Use unsigned fields in checkpoint headers unless otherwise required
- Rename functions in files c/r to better reflect their role
- Add support for anonymous shared memory
- Merge support for s390 arch (Dan Smith, Serge Hallyn)
[2008-Dec-03] v13:
- Cleanups of 'struct cr_ctx' - remove unused fields
- Misc fixes for comments
[2008-Dec-17] v12:
- Fix re-alloc/reset of pgarr chain to correctly reuse buffers
(empty pgarr are saves in a separate pool chain)
- Add a couple of missed calls to cr_hbuf_put()
- cr_kwrite/cr_kread() again use vfs_read(), vfs_write() (safer)
- Split cr_write/cr_read() to two parts: _cr_write/read() helper
- Befriend with sparse: explicit conversion to 'void __user *'
- Redrefine 'pr_fmt' ind replace cr_debug() with pr_debug()
[2008-Dec-05] v11:
- Use contents of 'init->fs->root' instead of pointing to it
- Ignore symlinks (there is no such thing as an open symlink)
- cr_scan_fds() retries from scratch if it hits size limits
- Add missing test for VM_MAYSHARE when dumping memory
- Improve documentation about: behavior when tasks aren't fronen,
life span of the object hash, references to objects in the hash
[2008-Nov-26] v10:
- Grab vfs root of container init, rather than current process
- Acquire dcache_lock around call to __d_path() in cr_fill_name()
- Force end-of-string in cr_read_string() (fix possible DoS)
- Introduce cr_write_buffer(), cr_read_buffer() and cr_read_buf_type()
[2008-Nov-10] v9:
- Support multiple processes c/r
- Extend checkpoint header with archtiecture dependent header
- Misc bug fixes (see individual changelogs)
- Rebase to v2.6.28-rc3.
[2008-Oct-29] v8:
- Support "external" checkpoint
- Include Dave Hansen's 'deny-checkpoint' patch
- Split docs in Documentation/checkpoint/..., and improve contents
[2008-Oct-17] v7:
- Fix save/restore state of FPU
- Fix argument given to kunmap_atomic() in memory dump/restore
[2008-Oct-07] v6:
- Balance all calls to cr_hbuf_get() with matching cr_hbuf_put()
(even though it's not really needed)
- Add assumptions and what's-missing to documentation
- Misc fixes and cleanups
[2008-Sep-11] v5:
- Config is now 'def_bool n' by default
- Improve memory dump/restore code (following Dave Hansen's comments)
- Change dump format (and code) to allow chunks of <vaddrs, pages>
instead of one long list of each
- Fix use of follow_page() to avoid faulting in non-present pages
- Memory restore now maps user pages explicitly to copy data into them,
instead of reading directly to user space; got rid of mprotect_fixup()
- Remove preempt_disable() when restoring debug registers
- Rename headers files s/ckpt/checkpoint/
- Fix misc bugs in files dump/restore
- Fixes and cleanups on some error paths
- Fix misc coding style
[2008-Sep-09] v4:
- Various fixes and clean-ups
- Fix calculation of hash table size
- Fix header structure alignment
- Use stand list_... for cr_pgarr
[2008-Aug-29] v3:
- Various fixes and clean-ups
- Use standard hlist_... for hash table
- Better use of standard kmalloc/kfree
[2008-Aug-20] v2:
- Added Dump and restore of open files (regular and directories)
- Added basic handling of shared objects, and improve handling of
'parent tag' concept
- Added documentation
- Improved ABI, 64bit padding for image data
- Improved locking when saving/restoring memory
- Added UTS information to header (release, version, machine)
- Cleanup extraction of filename from a file pointer
- Refactor to allow easier reviewing
- Remove requirement for CAPS_SYS_ADMIN until we come up with a
security policy (this means that file restore may fail)
- Other cleanup and response to comments for v1
[2008-Jul-29] v1:
- Initial version: support a single task with address space of only
private anonymous or file-mapped VMAs; syscalls ignore pid/crid
argument and act on current process.
--
At the containers mini-conference before OLS, the consensus among
all the stakeholders was that doing checkpoint/restart in the kernel
as much as possible was the best approach. With this approach, the
kernel will export a relatively opaque 'blob' of data to userspace
which can then be handed to the new kernel at restore time.
This is different than what had been proposed before, which was
that a userspace application would be responsible for collecting
all of this data. We were also planning on adding lots of new,
little kernel interfaces for all of the things that needed
checkpointing. This unites those into a single, grand interface.
The 'blob' will contain copies of select portions of kernel
structures such as vmas and mm_structs. It will also contain
copies of the actual memory that the process uses. Any changes
in this blob's format between kernel revisions can be handled by
an in-userspace conversion program.
This is a similar approach to virtually all of the commercial
checkpoint/restart products out there, as well as the research
project Zap.
These patches basically serialize internel kernel state and write
it out to a file descriptor. The checkpoint and restore are done
with two new system calls: sys_checkpoint and sys_restart.
In this incarnation, they can only work checkpoint and restore a
single task. The task's address space may consist of only private,
simple vma's - anonymous or file-mapped. The open files may consist
of only simple files and directories.
--
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Add public interface to freeze a cgroup freezer given a task that
belongs to that cgroup: cgroup_freezer_make_frozen(task)
Freezing the root cgroup is not permitted. Freezing the cgroup to
which current process belong is also not permitted.
This will be used for restart(2) to be able to leave the restarted
processes in a frozen state, instead of resuming execution.
This is useful for debugging, if the user would like to attach a
debugger to the restarted task(s).
It is also useful if the restart procedure would like to perform
additional setup once the tasks are restored but before they are
allowed to proceed execution.
Signed-off-by: Oren Laadan <redacted>
CC: Matt Helsley <redacted>
Cc: Paul Menage <redacted>
Cc: Li Zefan <redacted>
Cc: Cedric Le Goater <redacted>
---
include/linux/freezer.h | 1 +
kernel/cgroup_freezer.c | 27 +++++++++++++++++++++++++++
2 files changed, 28 insertions(+), 0 deletions(-)
@@ -466,4 +466,31 @@ void cgroup_freezer_end_checkpoint(struct task_struct *task)*/WARN_ON(freezer_checkpointing(task,CGROUP_FROZEN)!=CGROUP_CHECKPOINTING);}++intcgroup_freezer_make_frozen(structtask_struct*task)+{+structfreezer*freezer;+structcgroup_subsys_state*css;+intret=-ENODEV;++task_lock(task);+css=task_subsys_state(task,freezer_subsys_id);+css_get(css);/* make sure freezer doesn't go away */+freezer=container_of(css,structfreezer,css);+task_unlock(task);++/* Never freeze the root cgroup */+if(!test_bit(CSS_ROOT,&css->flags)&&+cgroup_lock_live_group(css->cgroup)){+/* do not freeze outselves, ei ?! */+if(css!=task_subsys_state(current,freezer_subsys_id))+ret=freezer_change_state(css->cgroup,CGROUP_FROZEN);+else+ret=-EPERM;+cgroup_unlock();+}++css_put(css);+returnret;+}#endif /* CONFIG_CHECKPOINT */
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Dave Hansen <redacted>
Let's not steal too much space in the 'General Setup' menu.
Take a cue from the cgroups code and create a submenu.
This can go upstream now.
Signed-off-by: Dave Hansen <redacted>
Acked-by: Oren Laadan <redacted>
---
init/Kconfig | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
@@ -211,6 +212,10 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)vdso_base=VDSO32_MBASE;#endif+/* in case restart(2) mandates a specific location */+if(start)+vdso_base=start;+current->mm->context.vdso_base=0;/* vDSO has a problem and was disabled, just don't "enable" it for the
@@ -234,6 +239,12 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)gotofail_mmapsem;}+/* for restart(2), double check that we got we asked for */+if(start&&vdso_base!=start){+ret=-EBUSY;+gotofail_mmapsem;+}+/**ourvmaflagsdon'thaveVM_WRITEsobydefault,theprocessisn't*allowedtowritethosepages.
@@ -224,6 +225,10 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)vdso_pages=vdso32_pages;#endif+/* in case restart(2) mandates a specific location */+if(start)+vdso_base=start;+/**vDSOhasaproblemandwasdisabled,justdon't"enable"itfor*theprocess
@@ -246,6 +251,12 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)gotoout_up;}+/* for restart(2), double check that we got we asked for */+if(start&&vdso_base!=start){+rc=-EINVAL;+gotoout_up;+}+/**ourvmaflagsdon'thaveVM_WRITEsobydefault,theprocess*isn'tallowedtowritethosepages.
@@ -59,7 +59,7 @@ int __init vsyscall_init(void)}/* Setup a VMA at program startup for the vsyscall page */-intarch_setup_additional_pages(structlinux_binprm*bprm,intuses_interp)+intarch_setup_additional_pages(structlinux_binprm*bprm,unsignedlongstart,intuses_interp){structmm_struct*mm=current->mm;unsignedlongaddr;
@@ -310,7 +310,8 @@ int __init sysenter_setup(void)}/* Setup a VMA at program startup for the vsyscall page */-intarch_setup_additional_pages(structlinux_binprm*bprm,intuses_interp)+intarch_setup_additional_pages(structlinux_binprm*bprm,+unsignedlongstart,intuses_interp){structmm_struct*mm=current->mm;unsignedlongaddr;
@@ -331,13 +332,17 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)if(compat)addr=VDSO_HIGH_BASE;else{-addr=get_unmapped_area(NULL,0,PAGE_SIZE,0,0);+addr=get_unmapped_area(NULL,start,PAGE_SIZE,0,0);if(IS_ERR_VALUE(addr)){ret=addr;gotoup_fail;}}+/* for restart(2), double check that we got we asked for */+if(start&&addr!=start)+gotoup_fail;+current->mm->context.vdso=(void*)addr;if(compat_uses_vma||!compat){
@@ -99,7 +99,8 @@ static unsigned long vdso_addr(unsigned long start, unsigned len)/* Setup a VMA at program startup for the vsyscall page.Notcalledforcompattasks*/-intarch_setup_additional_pages(structlinux_binprm*bprm,intuses_interp)+intarch_setup_additional_pages(structlinux_binprm*bprm,+unsignedlongstart,intuses_interp){structmm_struct*mm=current->mm;unsignedlongaddr;
@@ -109,13 +110,17 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)return0;down_write(&mm->mmap_sem);-addr=vdso_addr(mm->start_stack,vdso_size);+addr=start?:vdso_addr(mm->start_stack,vdso_size);addr=get_unmapped_area(NULL,addr,vdso_size,0,0);if(IS_ERR_VALUE(addr)){ret=addr;gotoup_fail;}+/* for restart(2), double check that we got we asked for */+if(start&&addr!=start)+gotoup_fail;+current->mm->context.vdso=(void*)addr;ret=install_special_mapping(mm,addr,vdso_size,
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
We now handle anonymous and file-mapped shared memory. Support for IPC
shared memory requires support for IPC first. We extend ckpt_write_vma()
to detect shared memory VMAs and handle it separately than private
memory.
There is not much to do for file-mapped shared memory, except to force
msync() on the region to ensure that the file system is consistent
with the checkpoint image. Use our internal type CKPT_VMA_SHM_FILE.
Anonymous shared memory is always backed by inode in shmem filesystem.
We use that inode to look up the VMA in the objhash and register it if
not found (on first encounter). In this case, the type of the VMA is
CKPT_VMA_SHM_ANON, and we dump the contents. On the other hand, if it is
found there, we must have already saved it before, so we change the
type to CKPT_VMA_SHM_ANON_SKIP and skip it.
To dump the contents of a shmem VMA, we loop through the pages of the
inode in the shmem filesystem, and dump the contents of each dirty
(allocated) page - unallocated pages must be clean.
Note that we save the original size of a shmem VMA because it may have
been re-mapped partially. The format itself remains like with private
VMAs, except that instead of addresses we record _indices_ (page nr)
into the backing inode.
Signed-off-by: Oren Laadan <redacted>
---
checkpoint/memory.c | 143 +++++++++++++++++++++++++++++++++++----
checkpoint/objhash.c | 19 +++++
include/linux/checkpoint.h | 15 +++--
include/linux/checkpoint_hdr.h | 8 ++
mm/filemap.c | 39 +++++++++++-
mm/mmap.c | 2 +-
mm/shmem.c | 30 ++++++++
7 files changed, 233 insertions(+), 23 deletions(-)
@@ -289,17 +338,16 @@ static struct page *consider_private_page(struct vm_area_struct *vma,*Returnsthenumberofpagescollected*/staticintvma_fill_pgarr(structckpt_ctx*ctx,-structvm_area_struct*vma,-unsignedlong*start)+structvm_area_struct*vma,structinode*inode,+unsignedlong*start,unsignedlongend){-unsignedlongend=vma->vm_end;unsignedlongaddr=*start;structckpt_pgarr*pgarr;intnr_used;intcnt=0;/* this function is only for private memory (anon or file-mapped) */-BUG_ON(vma->vm_flags&(VM_SHARED|VM_MAYSHARE));+BUG_ON(inode&&vma);do{pgarr=pgarr_current(ctx);
@@ -311,7 +359,11 @@ static int vma_fill_pgarr(struct ckpt_ctx *ctx,while(addr<end){structpage*page;-page=consider_private_page(vma,addr);+if(vma)+page=consider_private_page(vma,addr);+else+page=consider_shared_page(inode,addr);+if(IS_ERR(page))returnPTR_ERR(page);
@@ -323,7 +375,10 @@ static int vma_fill_pgarr(struct ckpt_ctx *ctx,pgarr->nr_used++;}-addr+=PAGE_SIZE;+if(vma)+addr+=PAGE_SIZE;+else+addr++;if(pgarr_is_full(pgarr))break;
@@ -395,23 +450,32 @@ static int vma_dump_pages(struct ckpt_ctx *ctx, int total)}/**-*checkpoint_memory_contents-dumpcontentsofaVMAwithprivatememory+*checkpoint_memory_contents-dumpcontentsofamemoryregion*@ctx-checkpointcontext-*@vma-vmatoscan+*@vma-vmatoscan(--or--)+*@inode-inodetoscan**Collectlistsofpagesthatneedstobedumped,andcorresponding*virtualaddressesintoctx->pgarr_listpage-arraychain.Thendump*theaddresses,followedbythepagecontents.*/staticintcheckpoint_memory_contents(structckpt_ctx*ctx,-structvm_area_struct*vma)+structvm_area_struct*vma,+structinode*inode){structckpt_hdr_pgarr*h;unsignedlongaddr,end;intcnt,ret;-addr=vma->vm_start;-end=vma->vm_end;+BUG_ON(vma&&inode);++if(vma){+addr=vma->vm_start;+end=vma->vm_end;+}else{+addr=0;+end=PAGE_ALIGN(i_size_read(inode))>>PAGE_CACHE_SHIFT;+}/**Workiteratively,collectinganddumpingatmostCKPT_PGARR_BATCH
@@ -437,7 +501,7 @@ static int checkpoint_memory_contents(struct ckpt_ctx *ctx,*/while(addr<end){-cnt=vma_fill_pgarr(ctx,vma,&addr);+cnt=vma_fill_pgarr(ctx,vma,inode,&addr,end);if(cnt==0)break;elseif(cnt<0)
@@ -481,7 +545,7 @@ static int checkpoint_memory_contents(struct ckpt_ctx *ctx,*@vma_objref:vmaobjref*/intgeneric_vma_checkpoint(structckpt_ctx*ctx,structvm_area_struct*vma,-enumvma_typetype,intvma_objref)+enumvma_typetype,intvma_objref,intino_objref){structckpt_hdr_vma*h;intret;
@@ -1666,7 +1668,42 @@ static int filemap_checkpoint(struct ckpt_ctx *ctx, struct vm_area_struct *vma)if(vma_objref<0)returnvma_objref;-returnprivate_vma_checkpoint(ctx,vma,CKPT_VMA_FILE,vma_objref);+if(vma->vm_flags&(VM_SHARED|VM_MAYSHARE)){+/*+*Citingmmap(2):"Updates to the mapping are visible+*tootherprocessesthatmapthisfile,andare+*carriedthroughtotheunderlyingfile.Thefile+*maynotactuallybeupdateduntilmsync(2)or+*munmap(2)iscalled"+*+*Citingmsync(2):"Without use of this call there is+*noguaranteethatchangesarewrittenbackbefore+*munmap(2)iscalled."+*+*Forcemsyncforregionofsharedmappedfiles,to+*ensurethatthatthefilesystemisconsistentwith+*thecheckpointimage.(inspiredbysys_msync).+*/++ino_objref=ckpt_obj_lookup_add(ctx,file->f_dentry->d_inode,+CKPT_OBJ_INODE,&first);+if(ino_objref<0)+returnino_objref;++if(first){+ret=vfs_fsync(file,file->f_path.dentry,0);+if(ret<0)+returnret;+}++ret=generic_vma_checkpoint(ctx,vma,CKPT_VMA_SHM_FILE,+vma_objref,ino_objref);+}else{+ret=private_vma_checkpoint(ctx,vma,CKPT_VMA_FILE,+vma_objref);+}++returnret;}intfilemap_restore(structckpt_ctx*ctx,
@@ -2381,6 +2382,32 @@ static void shmem_destroy_inode(struct inode *inode)kmem_cache_free(shmem_inode_cachep,SHMEM_I(inode));}+#ifdef CONFIG_CHECKPOINT+staticintshmem_checkpoint(structckpt_ctx*ctx,structvm_area_struct*vma)+{+enumvma_typevma_type;+intino_objref;+intfirst;++/* should be private anonymous ... verify that this is the case */+if(vma->vm_flags&CKPT_VMA_NOT_SUPPORTED){+pr_warning("c/r: unsupported VMA %#lx\n",vma->vm_flags);+return-ENOSYS;+}++BUG_ON(!vma->vm_file);++ino_objref=ckpt_obj_lookup_add(ctx,vma->vm_file->f_dentry->d_inode,+CKPT_OBJ_INODE,&first);+if(ino_objref<0)+returnino_objref;++vma_type=(first?CKPT_VMA_SHM_ANON:CKPT_VMA_SHM_ANON_SKIP);++returnshmem_vma_checkpoint(ctx,vma,vma_type,ino_objref);+}+#endif /* CONFIG_CHECKPOINT */+staticvoidinit_once(void*foo){structshmem_inode_info*p=(structshmem_inode_info*)foo;
--
1.6.0.4
--
To unsubscribe from this list: send the line "unsubscribe linux-api" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Checkpoint of sysvipc shared memory is performed in two steps: first,
the entire ipc namespace is dumped as a whole by iterating through all
shm objects and dumping the contents of each one. The shmem inode is
registered in the objhash. Second, for each vma that refers to ipc
shared memory we find the inode in the objhash, and save the objref.
(If we find a new inode, that indicates that the ipc namespace is not
entirely frozen and someone must have manipulated it since step 1).
Handling of shm objects that have been deleted (via IPC_RMID) is left
to a later patch in this series.
Changelog[v17]:
- Restore objects in the right namespace
- Properly initialize ctx->deferqueue
- Fix compilation with CONFIG_CHECKPOINT=n
Signed-off-by: Oren Laadan <redacted>
---
checkpoint/memory.c | 28 ++++-
checkpoint/sys.c | 13 ++
include/linux/checkpoint.h | 3 +
include/linux/checkpoint_hdr.h | 19 +++-
include/linux/checkpoint_types.h | 1 +
include/linux/shm.h | 15 ++
ipc/Makefile | 2 +-
ipc/checkpoint.c | 4 +-
ipc/checkpoint_shm.c | 261 ++++++++++++++++++++++++++++++++++++++
ipc/shm.c | 84 +++++++++++-
ipc/util.h | 8 +
11 files changed, 424 insertions(+), 14 deletions(-)
create mode 100644 ipc/checkpoint_shm.c
@@ -459,9 +460,9 @@ static int vma_dump_pages(struct ckpt_ctx *ctx, int total)*virtualaddressesintoctx->pgarr_listpage-arraychain.Thendump*theaddresses,followedbythepagecontents.*/-staticintcheckpoint_memory_contents(structckpt_ctx*ctx,-structvm_area_struct*vma,-structinode*inode)+intcheckpoint_memory_contents(structckpt_ctx*ctx,+structvm_area_struct*vma,+structinode*inode){structckpt_hdr_pgarr*h;unsignedlongaddr,end;
@@ -1077,6 +1078,13 @@ static int anon_private_restore(struct ckpt_ctx *ctx,returnprivate_vma_restore(ctx,mm,NULL,h);}+staticintbad_vma_restore(structckpt_ctx*ctx,+structmm_struct*mm,+structckpt_hdr_vma*h)+{+return-EINVAL;+}+/* callbacks to restore vma per its type: */structrestore_vma_ops{char*vma_name;
@@ -113,9 +113,9 @@ static int do_checkpoint_ipc_ns(struct ckpt_ctx *ctx,if(ret<0)returnret;-#if 0 /* NEXT FEW PATCHES */ret=checkpoint_ipc_any(ctx,ipc_ns,IPC_SHM_IDS,CKPT_HDR_IPC_SHM,checkpoint_ipc_shm);+#if 0 /* NEXT FEW PATCHES */if(ret<0)returnret;ret=checkpoint_ipc_any(ctx,ipc_ns,IPC_MSG_IDS,
@@ -286,9 +286,9 @@ static struct ipc_namespace *do_restore_ipc_ns(struct ckpt_ctx *ctx)get_ipc_ns(ipc_ns);#endif-#if 0 /* NEXT FEW PATCHES */ret=restore_ipc_any(ctx,ipc_ns,IPC_SHM_IDS,CKPT_HDR_IPC_SHM,restore_ipc_shm);+#if 0 /* NEXT FEW PATCHES */if(ret<0)gotoout;ret=restore_ipc_any(ctx,ipc_ns,IPC_MSG_IDS,
@@ -0,0 +1,261 @@+/*+*Checkpoint/restart-dumpstateofsysvipcshm+*+*Copyright(C)2009OrenLaadan+*+*ThisfileissubjecttothetermsandconditionsoftheGNUGeneralPublic+*License.SeethefileCOPYINGinthemaindirectoryoftheLinux+*distributionformoredetails.+*/++/* default debug level for output */+#define CKPT_DFLAG CKPT_DIPC++#include<linux/mm.h>+#include<linux/shm.h>+#include<linux/shmem_fs.h>+#include<linux/hugetlb.h>+#include<linux/rwsem.h>+#include<linux/sched.h>+#include<linux/file.h>+#include<linux/syscalls.h>+#include<linux/nsproxy.h>+#include<linux/ipc_namespace.h>+#include<linux/deferqueue.h>++#include<linux/msg.h> /* needed for util.h that uses 'struct msg_msg' */+#include"util.h"++#include<linux/checkpoint.h>+#include<linux/checkpoint_hdr.h>++/************************************************************************+*ipccheckpoint+*/++staticintfill_ipc_shm_hdr(structckpt_ctx*ctx,+structckpt_hdr_ipc_shm*h,+structshmid_kernel*shp)+{+intret=0;++ipc_lock_by_ptr(&shp->shm_perm);++ret=checkpoint_fill_ipc_perms(&h->perms,&shp->shm_perm);+if(ret<0)+gotounlock;++h->shm_segsz=shp->shm_segsz;+h->shm_atim=shp->shm_atim;+h->shm_dtim=shp->shm_dtim;+h->shm_ctim=shp->shm_ctim;+h->shm_cprid=shp->shm_cprid;+h->shm_lprid=shp->shm_lprid;++if(shp->mlock_user)+h->mlock_uid=shp->mlock_user->uid;+else+h->mlock_uid=(unsignedint)-1;++h->flags=0;+/* check if shm was setup with SHM_NORESERVE */+if(SHMEM_I(shp->shm_file->f_dentry->d_inode)->flags&VM_NORESERVE)+h->flags|=SHM_NORESERVE;+/* check if shm was setup with SHM_HUGETLB (unsupported yet) */+if(is_file_hugepages(shp->shm_file)){+pr_warning("c/r: unsupported SHM_HUGETLB\n");+ret=-ENOSYS;+}++unlock:+ipc_unlock(&shp->shm_perm);+ckpt_debug("shm: cprid %d lprid %d segsz %lld mlock %d\n",+h->shm_cprid,h->shm_lprid,h->shm_segsz,h->mlock_uid);++returnret;+}++intcheckpoint_ipc_shm(intid,void*p,void*data)+{+structckpt_hdr_ipc_shm*h;+structckpt_ctx*ctx=(structckpt_ctx*)data;+structkern_ipc_perm*perm=(structkern_ipc_perm*)p;+structshmid_kernel*shp;+structinode*inode;+intfirst,objref;+intret;++shp=container_of(perm,structshmid_kernel,shm_perm);+inode=shp->shm_file->f_dentry->d_inode;++objref=ckpt_obj_lookup_add(ctx,inode,CKPT_OBJ_INODE,&first);+if(objref<0)+returnobjref;+/* this must be the first time we see this region */+BUG_ON(!first);++h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_IPC_SHM);+if(!h)+return-ENOMEM;++ret=fill_ipc_shm_hdr(ctx,h,shp);+if(ret<0)+gotoout;++h->objref=objref;+ckpt_debug("shm: objref %d\n",h->objref);++ret=ckpt_write_obj(ctx,&h->h);+if(ret<0)+gotoout;++ret=checkpoint_memory_contents(ctx,NULL,inode);+out:+ckpt_hdr_put(ctx,h);+returnret;+}++/************************************************************************+*ipcrestart+*/++structdq_ipcshm_del{+/*+*XXX:alwayskeep->ipcnsfirstsothatput_ipc_ns()can+*besafelyprovidedasthedtorforthisdeferqueueobject+*/+structipc_namespace*ipcns;+intid;+};++staticintipc_shm_delete(void*data)+{+structdq_ipcshm_del*dq=(structdq_ipcshm_del*)data;+mm_segment_told_fs;+intret;++old_fs=get_fs();+set_fs(get_ds());+ret=shmctl_down(dq->ipcns,dq->id,IPC_RMID,NULL,0);+set_fs(old_fs);++put_ipc_ns(dq->ipcns);+returnret;+}++staticintload_ipc_shm_hdr(structckpt_ctx*ctx,+structckpt_hdr_ipc_shm*h,+structshmid_kernel*shp)+{+intret;++ret=restore_load_ipc_perms(&h->perms,&shp->shm_perm);+if(ret<0)+returnret;++ckpt_debug("shm: cprid %d lprid %d segsz %lld mlock %d\n",+h->shm_cprid,h->shm_lprid,h->shm_segsz,h->mlock_uid);++if(h->shm_cprid<0||h->shm_lprid<0)+return-EINVAL;++shp->shm_segsz=h->shm_segsz;+shp->shm_atim=h->shm_atim;+shp->shm_dtim=h->shm_dtim;+shp->shm_ctim=h->shm_ctim;+shp->shm_cprid=h->shm_cprid;+shp->shm_lprid=h->shm_lprid;++return0;+}++intrestore_ipc_shm(structckpt_ctx*ctx,structipc_namespace*ns)+{+structckpt_hdr_ipc_shm*h;+structkern_ipc_perm*perms;+structshmid_kernel*shp;+structipc_ids*shm_ids=&ns->ids[IPC_SHM_IDS];+structfile*file;+intshmflag;+intret;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_IPC_SHM);+if(IS_ERR(h))+returnPTR_ERR(h);++ret=-EINVAL;+if(h->perms.id<0)+gotoout;++#define CKPT_SHMFL_MASK (SHM_NORESERVE | SHM_HUGETLB)+if(h->flags&~CKPT_SHMFL_MASK)+gotoout;++ret=-ENOSYS;+if(h->mlock_uid!=(unsignedint)-1)/* FIXME: support SHM_LOCK */+gotoout;+if(h->flags&SHM_HUGETLB)/* FIXME: support SHM_HUGETLB */+gotoout;++/*+*SHM_DESTmeansthattheshmistobedeletedaftercreation.+*However,deletingbeforeit'sactuallyattachedisquitesilly.+*Instead,wedeferthistasktountilrestarthassucceeded.+*/+if(h->perms.mode&SHM_DEST){+structdq_ipcshm_deldq;++/* to not confuse the rest of the code */+h->perms.mode&=~SHM_DEST;++dq.id=h->perms.id;+dq.ipcns=ns;+get_ipc_ns(dq.ipcns);++/* XXX can safely use put_ipc_ns() as dtor, see above */+ret=deferqueue_add(ctx->deferqueue,&dq,sizeof(dq),+(deferqueue_func_t)ipc_shm_delete,+(deferqueue_func_t)put_ipc_ns);+if(ret<0)+gotoout;+}++shmflag=h->flags|h->perms.mode|IPC_CREAT|IPC_EXCL;+ckpt_debug("shm: do_shmget size %lld flag %#x id %d\n",+h->shm_segsz,shmflag,h->perms.id);+ret=do_shmget(ns,h->perms.key,h->shm_segsz,shmflag,h->perms.id);+ckpt_debug("shm: do_shmget ret %d\n",ret);+if(ret<0)+gotoout;++down_write(&shm_ids->rw_mutex);++/* we are the sole owners/users of this ipc_ns, it can't go away */+perms=ipc_lock(shm_ids,h->perms.id);+BUG_ON(IS_ERR(perms));/* ipc_ns is private to us */++shp=container_of(perms,structshmid_kernel,shm_perm);+file=shp->shm_file;+get_file(file);++ret=load_ipc_shm_hdr(ctx,h,shp);+if(ret<0)+gotomutex;++/* deposit in objhash and read contents in */+ret=ckpt_obj_insert(ctx,file,h->objref,CKPT_OBJ_FILE);+if(ret<0)+gotomutex;+ret=restore_memory_contents(ctx,file->f_dentry->d_inode);+mutex:+fput(file);+if(ret<0){+ckpt_debug("shm: need to remove (%d)\n",ret);+do_shm_rmid(ns,perms);+}else+ipc_unlock(perms);+up_write(&shm_ids->rw_mutex);+out:+ckpt_hdr_put(ctx,h);+returnret;+}
@@ -305,6 +306,74 @@ int is_file_shm_hugepages(struct file *file)returnret;}+#ifdef CONFIG_CHECKPOINT+staticintipcshm_checkpoint(structckpt_ctx*ctx,structvm_area_struct*vma)+{+intino_objref;+intfirst;++ino_objref=ckpt_obj_lookup_add(ctx,vma->vm_file->f_dentry->d_inode,+CKPT_OBJ_INODE,&first);+if(ino_objref<0)+returnino_objref;++/*+*Thisshouldn'thappen,becauseallIPCregionsshouldhave+*beenalreadydumpedbynowviaipcnamespaces;Itmeans+*theipc_nshasbeenmodifiedrecentlyduringcheckpoint.+*/+if(first)+return-EBUSY;++returngeneric_vma_checkpoint(ctx,vma,CKPT_VMA_SHM_IPC_SKIP,+0,ino_objref);+}++intipcshm_restore(structckpt_ctx*ctx,structmm_struct*mm,+structckpt_hdr_vma*h)+{+structfile*file;+intshmid,shmflg=0;+mm_segment_told_fs;+unsignedlongstart;+unsignedlongaddr;+intret;++if(!h->ino_objref)+return-EINVAL;+/* FIX: verify the vm_flags too */++file=ckpt_obj_fetch(ctx,h->ino_objref,CKPT_OBJ_FILE);+if(IS_ERR(file))+PTR_ERR(file);++shmid=file->f_dentry->d_inode->i_ino;++if(!(h->vm_flags&VM_WRITE))+shmflg|=SHM_RDONLY;++/*+*FIX:do_shmat()haslimitedinterface:all-or-nothing+*mapping.Ifthevma,however,reflectsapartialmapping+*thenweneedtomodifythatfunctiontoaccomplishthe+*desiredoutcome.Partialmappingcanexistduetotheuser+*callshmat()andthenunmappingpartoftheregion.+*Currently,weatleastdetectthisandcallitafoulplay.+*/+if(((h->vm_end-h->vm_start)!=h->ino_size)||h->vm_pgoff)+return-ENOSYS;++old_fs=get_fs();+set_fs(get_ds());+start=h->vm_start;+ret=do_shmat(shmid,(char__user*)start,shmflg,&addr);+set_fs(old_fs);++BUG_ON(ret>=0&&addr!=h->vm_start);+returnret;+}+#endif+staticconststructfile_operationsshm_file_operations={.mmap=shm_mmap,.fsync=shm_fsync,
@@ -445,14 +517,12 @@ static inline int shm_more_checks(struct kern_ipc_perm *ipcp,return0;}-intdo_shmget(key_tkey,size_tsize,intshmflg,intreq_id)+intdo_shmget(structipc_namespace*ns,key_tkey,size_tsize,+intshmflg,intreq_id){-structipc_namespace*ns;structipc_opsshm_ops;structipc_paramsshm_params;-ns=current->nsproxy->ipc_ns;-shm_ops.getnew=newseg;shm_ops.associate=shm_security;shm_ops.more_checks=shm_more_checks;
@@ -466,7 +536,7 @@ int do_shmget(key_t key, size_t size, int shmflg, int req_id)SYSCALL_DEFINE3(shmget,key_t,key,size_t,size,int,shmflg){-returndo_shmget(key,size,shmflg,-1);+returndo_shmget(current->nsproxy->ipc_ns,key,size,shmflg,-1);}staticinlineunsignedlongcopy_shmid_to_user(void__user*buf,structshmid64_ds*in,intversion)
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
@@ -0,0 +1,347 @@++ Checkpoint-Restart support in the Linux kernel+ ==========================================================++Copyright (C) 2008 Oren Laadan++Author: Oren Laadan <orenl@cs.columbia.edu>++License: The GNU Free Documentation License, Version 1.2+ (dual licensed under the GPL v2)++Reviewers: Serge Hallyn <serue@us.ibm.com>+ Dave Hansen <dave@linux.vnet.ibm.com>+++Introduction+============++Application checkpoint/restart [C/R] is the ability to save the state+of a running application so that it can later resume its execution+from the time at which it was checkpointed. An application can be+migrated by checkpointing it on one machine and restarting it on+another. C/R can provide many potential benefits:++* Failure recovery: by rolling back to a previous checkpoint++* Improved response time: by restarting applications from checkpoints+ instead of from scratch.++* Improved system utilization: by suspending long running CPU+ intensive jobs and resuming them when load decreases.++* Fault resilience: by migrating applications off faulty hosts.++* Dynamic load balancing: by migrating applications to less loaded+ hosts.++* Improved service availability and administration: by migrating+ applications before host maintenance so that they continue to run+ with minimal downtime++* Time-travel: by taking periodic checkpoints and restarting from+ any previous checkpoint.++Compared to hypervisor approaches, application C/R is more lightweight+since it need only save the state associated with applications, while+operating system data structures (e.g. buffer cache, drivers state+and the like) are uninteresting.+++Overall design+==============++Checkpoint and restart are done in the kernel as much as possible.+Two new system calls are introduced to provide C/R: sys_checkpoint()+and sys_restart(). They both operate on a process tree (hierarchy),+either a whole container or a subtree of a container.++Checkpointing entire containers ensures that there are no dependencies+on anything outside the container, which guarantees that a matching+restart will succeed (assuming that the file system state remains+consistent). However, it requires that users will always run the tasks+that they wish to checkpoint inside containers. This is ideal for,+e.g., private virtual servers and the like.++In contrast, when checkpointing a subtree of a container it is up to+the user to ensure that dependencies either don't exist or can be+safely ignored. This is useful, for instance, for HPC scenarios or+even a user that would like to periodically checkpoint a long-running+batch job.++An additional system call, a la madvise(), is planned, so that tasks+can advise the kernel how to handle specific resources. For instance,+a task could ask to skip a memory area at checkpoint to save space,+or to use a preset file descriptor at restart instead of restoring it+from the checkpoint image. It will provide the flexibility that is+particularly useful to address the needs of a diverse crowd of users+and use-cases.++Syscall sys_checkpoint() is given a pid that indicates the top of the+hierarchy, a file descriptor to store the image, and flags. The code+serializes internal user- and kernel-state and writes it out to the+file descriptor. The resulting image is stream-able. The processes are+expected to be frozen for the duration of the checkpoint.++In general, a checkpoint consists of 5 steps:+1. Pre-dump+2. Freeze the container/subtree+3. Save tasks' and kernel state <-- sys_checkpoint()+4. Thaw (or kill) the container/subtree+5. Post-dump++Step 3 is done by calling sys_checkpoint(). Steps 1 and 5 are an+optimization to reduce application downtime. In particular, "pre-dump"+works before freezing the container, e.g. the pre-copy for live+migration, and "post-dump" works after the container resumes+execution, e.g. write-back the data to secondary storage.++The kernel exports a relatively opaque 'blob' of data to userspace+which can then be handed to the new kernel at restart time. The+'blob' contains data and state of select portions of kernel structures+such as VMAs and mm_structs, as well as copies of the actual memory+that the tasks use. Any changes in this blob's format between kernel+revisions can be handled by an in-userspace conversion program.++To restart, userspace first create a process hierarchy that matches+that of the checkpoint, and each task calls sys_restart(). The syscall+reads the saved kernel state from a file descriptor, and re-creates+the resources that the tasks need to resume execution. The restart+code is executed by each task that is restored in the new hierarchy to+reconstruct its own state.++In general, a restart consists of 3 steps:+1. Create hierarchy+2. Restore tasks' and kernel state <-- sys_restart()+3. Resume userspace (or freeze tasks)++Because the process hierarchy, during restart in created in userspace,+the restarting tasks have the flexibility to prepare before calling+sys_restart().+++Checkpoint image format+=======================++The checkpoint image format is built of records that consist of a+pre-header identifying its contents, followed by a payload. This+format allow userspace tools to easily parse and skip through the+image without requiring intimate knowledge of the data. It will also+be handy to enable parallel checkpointing in the future where multiple+threads interleave data from multiple processes into a single stream.++The pre-header is defined by 'struct ckpt_hdr' as follows: @type+identifies the type of the payload, @len tells its length in bytes+including the pre-header.++struct ckpt_hdr {+ __s32 type;+ __s32 len;+};++The pre-header must be the first component in all other headers. For+instance, the task data is saved in 'struct ckpt_hdr_task', which+looks something like this:++struct ckpt_hdr_task {+ struct ckpt_hdr h;+ __u32 pid;+ ...+};++THE IMAGE FORMAT IS EXPECTED TO CHANGE over time as more features are+supported, or as existing features change in the kernel and require to+adjust their representation. Any such changes will be be handled by+in-userspace conversion tools.++The general format of the checkpoint image is as follows:+1. Image header+2. Task hierarchy+3. Tasks' state+4. Image trailer++The image always begins with a general header that holds a magic+number, an architecture identifier (little endian format), a format+version number (@rev), followed by information about the kernel+(currently version and UTS data). It also holds the time of the+checkpoint and the flags given to sys_checkpoint(). This header is+followed by an arch-specific header.++The task hierarchy comes next so that userspace tools can read it+early (even from a stream) and re-create the restarting tasks. This is+basically an array of all checkpointed tasks, and their relationships+(parent, siblings, threads, etc).++Then the state of all tasks is saved, in the order that they appear in+the tasks array above. For each state, we save data like task_struct,+namespaces, open files, memory layout, memory contents, cpu state,+signals and signal handlers, etc. For resources that are shared among+multiple processes, we first checkpoint said resource (and only once),+and in the task data we give a reference to it. More about shared+resources below.++Finally, the image always ends with a trailer that holds a (different)+magic number, serving for sanity check.+++Shared objects+==============++Many resources may be shared by multiple tasks (e.g. file descriptors,+memory address space, etc), or even have multiple references from+other resources (e.g. a single inode that represents two ends of a+pipe).++Shared objects are tracked using a hash table (objhash) to ensure that+they are only checkpointed or restored once. To handle a shared+object, it is first looked up in the hash table, to determine if is+the first encounter or a recurring appearance. The hash table itself+is not saved as part of the checkpoint image: it is constructed+dynamically during both checkpoint and restart, and discarded at the+end of the operation.++During checkpoint, when a shared object is encountered for the first+time, it is inserted to the hash table, indexed by its kernel address.+It is assigned an identifier (@objref) in order of appearance, and+then its state if saved. Subsequent lookups of that object in the hash+will yield that entry, in which case only the @objref is saved, as+opposed the entire state of the object.++During restart, shared objects are indexed by their @objref as given+during the checkpoint. On the first appearance of each shared object,+a new resource will be created and its state restored from the image.+Then the object is added to the hash table. Subsequent lookups of the+same unique identifier in the hash table will yield that entry, and+then the existing object instance is reused instead of creating+a new one.++The hash grabs a reference to each object that is inserted, and+maintains this reference for the entire lifetime of the hash. Thus,+it is always safe to reference an object that is stored in the hash.+The hash is "one-way" in the sense that objects that are added are+never deleted from the hash until the hash is discarded. This, in+turn, happens only when the checkpoint (or restart) terminates.++Shared objects are thus saved when they are first seen, and _before_+the parent object that uses them. Therefore by the time the parent+objects needs them, they should already be in the objhash. The one+exception is when more than a single shared resource will be restarted+at once (e.g. like the two ends of a pipe, or all the namespaces in an+nsproxy). In this case the parent object is dumped first followed by+the individual sub-resources).++The checkpoint image is stream-able, meaning that restarting from it+may not require lseek(). This is enforced at checkpoint time, by+carefully selecting the order of shared objects, to respect the rule+that an object is always saved before the objects that refers to it.+++Memory contents format+======================++The memory contents of a given memory address space (->mm) is dumped+as a sequence of vma objects, represented by 'struct ckpt_hdr_vma'.+This header details the vma properties, and a reference to a file+(if file backed) or an inode (or shared memory) object.++The vma header is followed by the actual contents - but only those+pages that need to be saved, i.e. dirty pages. They are written in+chunks of data, where each chunks contains a header that indicates+that number of pages in the chunk, followed by an array of virtual+addresses and then an array of actual page contents. The last chunk+holds zero pages.++To illustrate this, consider a single simple task with two vmas: one+is file mapped with two dumped pages, and the other is anonymous with+three dumped pages. The memory dump will look like this:++ ckpt_hdr + ckpt_hdr_vma+ ckpt_hdr_pgarr (nr_pages = 2)+ addr1, addr2+ page1, page2+ ckpt_hdr_pgarr (nr_pages = 0)+ ckpt_hdr + ckpt_hdr_vma+ ckpt_hdr_pgarr (nr_pages = 3)+ addr3, addr4, addr5+ page3, page4, page5+ ckpt_hdr_pgarr (nr_pages = 0)+++Error handling+==============++Both checkpoint and restart operations may fail due to a variety of+reasons. Using a simple, single return value from the system call is+insufficient to report the reason of a failure.++Checkpoint - to provide informative status report upon failure, the+checkpoint image may contain one (or more) error objects, 'struct+ckpt_hdr_err'. An error objects consists of a mandatory pre-header+followed by a null character ('\0'), and then a string that describes+the error. By default, if an error occurs, this will be the last+object written to the checkpoint image.++Upon failure, the caller can examine the image (e.g. with 'ckptinfo')+and extract the detailed error message. The leading '\0' is useful if+one wants to seek back from the end of the checkpoint image, instead+of parsing the entire image separately.++Restart - to be defined.+++Security+========++The main question is whether sys_checkpoint() and sys_restart()+require privileged or unprivileged operation.++Early versions checked capable(CAP_SYS_ADMIN) assuming that we would+attempt to remove the need for privilege, so that all users could+safely use it. Arnd Bergmann pointed out that it'd make more sense to+let unprivileged users use them now, so that we'll be more careful+about the security as patches roll in.++Checkpoint: the main concern is whether a task that performs the+checkpoint of another task has sufficient privileges to access its+state. We address this by requiring that the checkpointer task will be+able to ptrace the target task, by means of ptrace_may_access() with+read mode.++Restart: the main concern is that we may allow an unprivileged user to+feed the kernel with random data. To this end, the restart works in a+way that does not skip the usual security checks. Task credentials,+i.e. euid, reuid, and LSM security contexts currently come from the+caller, not the checkpoint image. When restoration of credentials+becomes supported, then definitely the ability of the task that calls+sys_restore() to setresuid/setresgid to those values must be checked.++Keeping the restart procedure to operate within the limits of the+caller's credentials means that there various scenarios that cannot+be supported. For instance, a setuid program that opened a protected+log file and then dropped privileges will fail the restart, because+the user won't have enough credentials to reopen the file. In these+cases, we should probably treat restarting like inserting a kernel+module: surely the user can cause havoc by providing incorrect data,+but then again we must trust the root account.++So that's why we don't want CAP_SYS_ADMIN required up-front. That way+we will be forced to more carefully review each of those features.+However, this can be controlled with a sysctl-variable.+++Kernel interfaces+=================++* To checkpoint a vma, the 'struct vm_operations_struct' needs to+provide a method ->checkpoint:+ int checkpoint(struct ckpt_ctx *, struct vma_struct *)+Restart requires a matching (exported) restore:+ int restore(struct ckpt_ctx *, struct mm_struct *, struct ckpt_hdr_vma *)++* To checkpoint a file, the 'struct file_operations' needs to provide+a method ->checkpoint:+ int checkpoint(struct ckpt_ctx *, struct file *)+Restart requires a matching (exported) restore:+ int restore(struct ckpt_ctx *, struct ckpt_hdr_file *)+For most file systems, generic_file_{checkpoint,restore}() can be+used.
@@ -0,0 +1,20 @@+#include<stdio.h>+#include<stdlib.h>+#include<unistd.h>+#include<errno.h>+#include<sys/syscall.h>++intmain(intargc,char*argv[])+{+pid_tpid=getpid();+intret;++ret=syscall(__NR_restart,pid,STDIN_FILENO,0);+if(ret<0)+perror("restart");++printf("should not reach here !\n");++return0;+}+
@@ -0,0 +1,57 @@+#include<stdio.h>+#include<stdlib.h>+#include<unistd.h>+#include<string.h>+#include<errno.h>+#include<math.h>+#include<sys/syscall.h>++#define OUTFILE "/tmp/cr-test.out"++intmain(intargc,char*argv[])+{+pid_tpid=getpid();+FILE*file;+inti,ret;+floata;++close(0);+close(2);++unlink(OUTFILE);+file=fopen(OUTFILE,"w+");+if(!file){+perror("open");+exit(1);+}+if(dup2(0,2)<0){+perror("dup2");+exit(1);+}++a=sqrt(2.53*(getpid()/1.21));++fprintf(file,"hello, world (%.2f)!\n",a);+fflush(file);++for(i=0;i<1000;i++){+sleep(1);+/* make the fpu work -> a = a + i/10 */+a=sqrt(a*a+2*a*(i/10.0)+i*i/100.0);+fprintf(file,"count %d (%.2f)!\n",i,a);+fflush(file);++if(i==2){+ret=syscall(__NR_checkpoint,pid,STDOUT_FILENO,0);+if(ret<0){+fprintf(file,"ckpt: %s\n",strerror(errno));+exit(2);+}+fprintf(file,"checkpoint ret: %d\n",ret);+fflush(file);+}+}++return0;+}+
@@ -0,0 +1,48 @@+#include<stdio.h>+#include<stdlib.h>+#include<unistd.h>+#include<errno.h>+#include<math.h>++#define OUTFILE "/tmp/cr-test.out"++intmain(intargc,char*argv[])+{+FILE*file;+floata;+inti;++close(0);+close(1);+close(2);++unlink(OUTFILE);+file=fopen(OUTFILE,"w+");+if(!file){+perror("open");+exit(1);+}+if(dup2(0,2)<0){+perror("dup2");+exit(1);+}++a=sqrt(2.53*(getpid()/1.21));++fprintf(file,"hello, world (%.2f)!\n",a);+fflush(file);++for(i=0;i<1000;i++){+sleep(1);+/* make the fpu work -> a = a + i/10 */+a=sqrt(a*a+2*a*(i/10.0)+i*i/100.0);+fprintf(file,"count %d (%.2f)!\n",i,a);+fflush(file);+}++fprintf(file,"world, hello (%.2f) !\n",a);+fflush(file);++return0;+}+
@@ -0,0 +1,193 @@++ How to use Checkpoint-Restart+ =========================================+++API+===++The API consists of two new system calls:++* int checkpoint(pid_t pid, int fd, unsigned long flag);++ Checkpoint a (sub-)container whose root task is identified by @pid,+ to the open file indicated by @fd. @flags may be on or more of:+ - CHECKPOINT_SUBTREE : allow checkpoint of sub-container+ (other value are not allowed).++ Returns: a positive checkpoint identifier (ckptid) upon success, 0 if+ it returns from a restart, and -1 if an error occurs. The ckptid will+ uniquely identify a checkpoint image, for as long as the checkpoint+ is kept in the kernel (e.g. if one wishes to keep a checkpoint, or a+ partial checkpoint, residing in kernel memory).++* int sys_restart(pid_t pid, int fd, unsigned long flags);++ Restart a process hierarchy from a checkpoint image that is read from+ the blob stored in the file indicated by @fd. The @flags' will have+ future meaning (must be 0 for now). @pid indicates the root of the+ hierarchy as seen in the coordinator's pid-namespace, and is expected+ to be a child of the coordinator. (Note that this argument may mean+ 'ckptid' to identify an in-kernel checkpoint image, with some @flags+ in the future).++ Returns: -1 if an error occurs, 0 on success when restarting from a+ "self" checkpoint, and return value of system call at the time of the+ checkpoint when restarting from an "external" checkpoint.++ TODO: upon successful "external" restart, the container will end up+ in a frozen state.+++Sysctl/proc+===========++/proc/sys/kernel/ckpt_unpriv_allowed [default = 1]+ controls whether c/r operation is allowed for unprivileged users+++Operation+=========++The granularity of a checkpoint usually is a process hierarchy. The+'pid' argument is interpreted in the caller's pid namespace. So to+checkpoint a container whose init task (pid 1 in that pidns) appears+as pid 3497 the caller's pidns, the caller must use pid 3497. Passing+pid 1 will attempt to checkpoint the caller's container, and if the+caller isn't privileged and init is owned by root, it will fail.++Unless the CHECKPOINT_SUBTREE flag is set, if the caller passes a pid+which does not refer to a container's init task, then sys_checkpoint()+would return -EINVAL.++We assume that during checkpoint and restart the container state is+quiescent. During checkpoint, this means that all affected tasks are+frozen (or otherwise stopped). During restart, this means that all+affected tasks are executing the sys_restart() call. In both cases, if+there are other tasks possible sharing state with the container, they+must not modify it during the operation. It is the responsibility of+the caller to follow this requirement.++If the assumption that all tasks are frozen and that there is no other+sharing doesn't hold - then the results of the operation are undefined+(just as, e.g. not calling execve() immediately after vfork() produces+undefined results). In particular, either checkpoint will fail, or it+may produce a checkpoint image that can't be restarted, or (unlikely)+the restart may produce a container whose state does not match that of+the original container.+++User tools+==========++* ckpt: a tool to perform a checkpoint of a container/subtree+* mktree: a tool to restart a container/subtree+* ckptinfo: a tool to examine a checkpoint image++It is best to use the dedicated user tools for checkpoint and restart.++If you insist, then here is a code snippet that illustrates how a+checkpoint is initiated by a process inside a container - the logic is+similar to fork():+ ...+ ckptid = checkpoint(1, ...);+ switch (crid) {+ case -1:+ perror("checkpoint failed");+ break;+ default:+ fprintf(stderr, "checkpoint succeeded, CRID=%d\n", ret);+ /* proceed with execution after checkpoint */+ ...+ break;+ case 0:+ fprintf(stderr, "returned after restart\n");+ /* proceed with action required following a restart */+ ...+ break;+ }+ ...++And to initiate a restart, the process in an empty container can use+logic similar to execve():+ ...+ if (restart(pid, ...) < 0)+ perror("restart failed");+ /* only get here if restart failed */+ ...++Note, that the code also supports "self" checkpoint, where a process+can checkpoint itself. This mode does not capture the relationships of+the task with other tasks, or any shared resources. It is useful for+application that wish to be able to save and restore their state.+They will either not use (or care about) shared resources, or they+will be aware of the operations and adapt suitably after a restart.+The code above can also be used for "self" checkpoint.+++You may find the following sample programs useful:++* ckpt.c: accepts a 'pid' argument and checkpoint that task to stdout+* rstr.c: restarts a checkpoint image from stdin+* self.c: a simple test program doing self-checkpoint+* test.c: a simple test program to checkpoint+++"External" checkpoint+=====================++To do "external" checkpoint, you need to first freeze that other task+either using the freezer cgroup.++Restart does not preserve the original PID yet, (because we haven't+solved yet the fork-with-specific-pid issue). In a real scenario, you+probably want to first create a new names space, and have the init+task there call 'sys_restart()'.++I tested it this way:+ $ ./test &+ [1] 3493++ $ kill -STOP 3493+ $ ./ckpt 3493 > ckpt.image++ $ mv /tmp/cr-test.out /tmp/cr-test.out.orig+ $ cp /tmp/cr-test.out.orig /tmp/cr-test.out++ $ kill -CONT 3493++ $ ./rstr < ckpt.image+Now compare the output of the two output files.+++"Self checkpoint+================++To do "self" checkpoint, you can incorporate the code from ckpt.c into+your application.++Here is how to test the "self" checkpoint:+ $ ./self > self.image &+ [1] 3512++ $ sleep 3+ $ mv /tmp/cr-test.out /tmp/cr-test.out.orig+ $ cp /tmp/cr-test.out.orig /tmp/cr-test.out++ $ cat /tmp/cr-rest.out+ hello, world (85.46)!+ count 0 (85.46)!+ count 1 (85.56)!+ count 2 (85.76)!+ count 3 (86.46)!++ $ sed -i 's/count/xxxx/g' /tmp/cr-rest.out++ $ ./rstr < self.image &+Now compare the output of the two output files.++Note how in test.c we close stdin, stdout, stderr - that's because+currently we only support regular files (not ttys/ptys).++If you check the output of ps, you'll see that "rstr" changed its name+to "test" or "self", as expected.
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Now we can do "external" checkpoint, i.e. act on another task.
sys_checkpoint() now looks up the target pid (in our namespace) and
checkpoints that corresponding task. That task should be the root of
a container, unless CHECKPOINT_SUBTREE flag is given.
Set state of freezer cgroup of checkpointed task hierarchy to
"CHECKPOINTING" during a checkpoint, to ensure that task(s) cannot be
thawed while at it.
Ensure that all tasks belong to root task's freezer cgroup (the root
task is also tested, to detect it if changes its freezer cgroups
before it moves to "CHECKPOINTING").
sys_restart() remains nearly the same, as the restart is always done
in the context of the restarting task. However, the original task may
have been frozen from user space, or interrupted from a syscall for
the checkpoint. This is accounted for by restoring a suitable retval
for the restarting task, according to how it was checkpointed.
Changelog[v17]:
- Move restore_retval() to this patch
- Tighten ptrace ceckpoint for checkpoint to PTRACE_MODE_ATTACH
- Use CHECKPOINTING state for hierarchy's freezer for checkpoint
Changelog[v16]:
- Use CHECKPOINT_SUBTREE to allow subtree (partial container)
Changelog[v14]:
- Refuse non-self checkpoint if target task isn't frozen
Changelog[v12]:
- Replace obsolete ckpt_debug() with pr_debug()
Changelog[v11]:
- Copy contents of 'init->fs->root' instead of pointing to them
Changelog[v10]:
- Grab vfs root of container init, rather than current process
Signed-off-by: Oren Laadan <redacted>
---
checkpoint/Kconfig | 1 +
checkpoint/checkpoint.c | 99 +++++++++++++++++++++++++++++++++++++-
checkpoint/restart.c | 61 +++++++++++++++++++++++-
checkpoint/sys.c | 10 ++++
include/linux/checkpoint_types.h | 7 ++-
5 files changed, 175 insertions(+), 3 deletions(-)
@@ -255,14 +258,106 @@ static int checkpoint_write_tail(struct ckpt_ctx *ctx)returnret;}+staticintmay_checkpoint_task(structckpt_ctx*ctx,structtask_struct*t)+{+if(t->state==TASK_DEAD){+pr_warning("c/r: task %d is TASK_DEAD\n",task_pid_vnr(t));+return-EAGAIN;+}++if(!ptrace_may_access(t,PTRACE_MODE_ATTACH)){+__ckpt_write_err(ctx,"access to task %d (%s) denied",+task_pid_vnr(t),t->comm);+return-EPERM;+}++/* verify that all tasks belongs to same freezer cgroup */+if(t!=current&&!in_same_cgroup_freezer(t,ctx->root_freezer)){+__ckpt_write_err(ctx,"task %d (%s) not frozen (wrong cgroup)",+task_pid_vnr(t),t->comm);+return-EBUSY;+}++/* FIX: add support for ptraced tasks */+if(task_ptrace(t)){+__ckpt_write_err(ctx,"task %d (%s) is ptraced",+task_pid_vnr(t),t->comm);+return-EBUSY;+}++return0;+}++/* setup checkpoint-specific parts of ctx */+staticintinit_checkpoint_ctx(structckpt_ctx*ctx,pid_tpid)+{+structtask_struct*task;+structnsproxy*nsproxy;+intret;++/*+*Noneedforexplicitcleanuphere,becauseifanerror+*occursthenckpt_ctx_free()iseventuallycalled.+*/++ctx->root_pid=pid;++/* root task */+read_lock(&tasklist_lock);+task=find_task_by_vpid(pid);+if(task)+get_task_struct(task);+read_unlock(&tasklist_lock);+if(!task)+return-ESRCH;+else+ctx->root_task=task;++/* root nsproxy */+rcu_read_lock();+nsproxy=task_nsproxy(task);+if(nsproxy)+get_nsproxy(nsproxy);+rcu_read_unlock();+if(!nsproxy)+return-ESRCH;+else+ctx->root_nsproxy=nsproxy;++/* root freezer */+ctx->root_freezer=task;+geT_task_struct(task);++ret=may_checkpoint_task(ctx,task);+if(ret){+ckpt_write_err(ctx,NULL);+put_task_struct(task);+put_task_struct(task);+put_nsproxy(nsproxy);+returnret;+}++return0;+}+longdo_checkpoint(structckpt_ctx*ctx,pid_tpid){longret;+ret=init_checkpoint_ctx(ctx,pid);+if(ret<0)+returnret;++if(ctx->root_freezer){+ret=cgroup_freezer_begin_checkpoint(ctx->root_freezer);+if(ret<0)+returnret;+}+ret=checkpoint_write_header(ctx);if(ret<0)gotoout;-ret=checkpoint_task(ctx,current);+ret=checkpoint_task(ctx,ctx->root_task);if(ret<0)gotoout;ret=checkpoint_write_tail(ctx);
@@ -273,5 +368,7 @@ long do_checkpoint(struct ckpt_ctx *ctx, pid_t pid)ctx->crid=atomic_inc_return(&ctx_count);ret=ctx->crid;out:+if(ctx->root_freezer)+cgroup_freezer_end_checkpoint(ctx->root_freezer);returnret;}
@@ -322,10 +322,67 @@ static int restore_read_tail(struct ckpt_ctx *ctx)returnret;}+staticlongrestore_retval(void)+{+structpt_regs*regs=task_pt_regs(current);+longret;++/*+*Fortherestart,weenteredthekernelviasys_restart(),+*soourreturnpathisviathesyscallexit.Inparticular,+*thecodeinentry.Swillputthevaluethatwewillreturn+*intoaregister(e.g.regs->eaxinx86),thuspassingitto+*thecallertask.+*+*Whatwedonowdependsonwhathappenedtothecheckpointed+*taskrightbeforethecheckpoint-therearethreecases:+*+*1)Itwascarryingoutasyscallwhenbecamefrozen,or+*2)Itwasrunninginuserspace,or+*3)Itwasdoingaself-checkpoint+*+*Incase#1,ifthesyscallsucceeded,perhapspartially,+*thentheretvalisnon-negative.Ifitfailed,theerror+*maybeoneof-ERESTART...,whichisinterpretedinthe+*signalhandlingcode.Ifthatisthecase,weforcethe+*signalhandlertokickinbyfakingasignaltoourselves+*(alafreeze/thaw)whenret<0.+*+*Incase#2,ourreturnvaluewilloverwritetheoriginal+*valueintheaffectedregister.Workaroundbysimplyusing+*thatsavedvalueofthatregisterasourretval.+*+*Incase#3,thenthestatewasrecordedwhilethetaskwas+*incheckpoint(2)syscall.Thesyscallisexecptedtoreturn+*0whenreturningfromarestart.Fortunately,thisalready+*hasbeenarrangedforatcheckpointtime(theregisterthat+*holdstheretval,e.g.regs->eaxinx86,wassetto+*zero).+*/++/* needed for all 3 cases: get old value/error/retval */+ret=syscall_get_return_value(current,regs);++/* if from a syscall and returning error, kick in signal handlig */+if(syscall_get_nr(current,regs)>=0&&ret<0)+set_tsk_thread_flag(current,TIF_SIGPENDING);++returnret;+}++/* setup restart-specific parts of ctx */+staticintinit_restart_ctx(structckpt_ctx*ctx,pid_tpid)+{+return0;+}+longdo_restart(structckpt_ctx*ctx,pid_tpid){longret;+ret=init_restart_ctx(ctx,pid);+if(ret<0)+returnret;ret=restore_read_header(ctx);if(ret<0)returnret;
@@ -333,7 +390,9 @@ long do_restart(struct ckpt_ctx *ctx, pid_t pid)if(ret<0)returnret;ret=restore_read_tail(ctx);+if(ret<0)+returnret;/* on success, adjust the return value if needed [TODO] */-returnret;+returnrestore_retval(ctx);}
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Sukadev Bhattiprolu <redacted>
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[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)
Signed-off-by: Sukadev Bhattiprolu <redacted>
Acked-by: Serge Hallyn <redacted>
Reviewed-by: Oren Laadan <redacted>
---
include/linux/sched.h | 3 +++
include/linux/types.h | 5 +++++
kernel/fork.c | 16 ++++++++++++++--
3 files changed, 22 insertions(+), 2 deletions(-)
@@ -1455,6 +1456,17 @@ long do_fork(unsigned long clone_flags,returnnr;}+longdo_fork(unsignedlongclone_flags,+unsignedlongstack_start,+structpt_regs*regs,+unsignedlongstack_size,+int__user*parent_tidptr,+int__user*child_tidptr)+{+returndo_fork_with_pids(clone_flags,stack_start,regs,stack_size,+parent_tidptr,child_tidptr,NULL);+}+#ifndef ARCH_MIN_MMSTRUCT_ALIGN#define ARCH_MIN_MMSTRUCT_ALIGN 0#endif
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
(Paraphrasing what's said this message:
http://lists.openwall.net/linux-kernel/2007/12/05/64)
Restart blocks are callbacks used cause a system call to be restarted
with the arguments specified in the system call restart block. It is
useful for system call that are not idempotent, i.e. the argument(s)
might be a relative timeout, where some adjustments are required when
restarting the system call. It relies on the system call itself to set
up its restart point and the argument save area. They are rare: an
actual signal would turn that it an EINTR. The only case that should
ever trigger this is some kernel action that interrupts the system
call, but does not actually result in any user-visible state changes -
like freeze and thaw.
So restart blocks are about time remaining for the system call to
sleep/wait. Generally in c/r, there are two possible time models that
we can follow: absolute, relative. Here, I chose to save the relative
timeout, measured from the beginning of the checkpoint. The time when
the checkpoint (and restart) begin is also saved. This information is
sufficient to restart in either model (absolute or negative).
Which model to use should eventually be a per application choice (and
possible configurable via cradvise() or some sort). For now, we adopt
the relative model, namely, at restart the timeout is set relative to
the beginning of the restart.
To checkpoint, we check if a task has a valid restart block, and if so
we save the *remaining* time that is has to wait/sleep, and the type
of the restart block.
To restart, we fill in the data required at the proper place in the
thread information. If the system call return an error (which is
possibly an -ERESTARTSYS eg), we not only use that error as our own
return value, but also arrange for the task to execute the signal
handler (by faking a signal). The handler, in turn, already has the
code to handle these restart request gracefully.
Signed-off-by: Oren Laadan <redacted>
---
arch/x86/include/asm/checkpoint_hdr.h | 1 -
checkpoint/checkpoint.c | 1 +
checkpoint/process.c | 226 +++++++++++++++++++++++++++++++++
checkpoint/restart.c | 5 +-
checkpoint/sys.c | 1 +
include/linux/checkpoint.h | 4 +
include/linux/checkpoint_hdr.h | 22 +++
include/linux/checkpoint_types.h | 3 +
8 files changed, 260 insertions(+), 3 deletions(-)
@@ -47,6 +50,116 @@ static int checkpoint_task_struct(struct ckpt_ctx *ctx, struct task_struct *t)returnckpt_write_string(ctx,t->comm,TASK_COMM_LEN);}+/* dump the task_struct of a given task */+intcheckpoint_restart_block(structckpt_ctx*ctx,structtask_struct*t)+{+structckpt_hdr_restart_block*h;+structrestart_block*restart_block;+long(*fn)(structrestart_block*);+s64base,expire=0;+intret;++h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_RESTART_BLOCK);+if(!h)+return-ENOMEM;++base=ktime_to_ns(ctx->ktime_begin);+restart_block=&task_thread_info(t)->restart_block;+fn=restart_block->fn;++/* FIX: enumerate clockid_t so we're immune to changes */++if(fn==do_no_restart_syscall){++h->function_type=CKPT_RESTART_BLOCK_NONE;+ckpt_debug("restart_block: non\n");++}elseif(fn==hrtimer_nanosleep_restart){++h->function_type=CKPT_RESTART_BLOCK_HRTIMER_NANOSLEEP;+h->arg_0=restart_block->nanosleep.index;+h->arg_1=(unsignedlong)restart_block->nanosleep.rmtp;+expire=restart_block->nanosleep.expires;+ckpt_debug("restart_block: hrtimer expire %lld now %lld\n",+expire,base);++}elseif(fn==posix_cpu_nsleep_restart){+structtimespects;++h->function_type=CKPT_RESTART_BLOCK_POSIX_CPU_NANOSLEEP;+h->arg_0=restart_block->arg0;+h->arg_1=restart_block->arg1;+ts.tv_sec=restart_block->arg2;+ts.tv_nsec=restart_block->arg3;+expire=timespec_to_ns(&ts);+ckpt_debug("restart_block: posix_cpu expire %lld now %lld\n",+expire,base);++#ifdef CONFIG_COMPAT+}elseif(fn==compat_nanosleep_restart){++h->function_type=CKPT_RESTART_BLOCK_NANOSLEEP;+h->arg_0=restart_block->nanosleep.index;+h->arg_1=(unsignedlong)restart_block->nanosleep.rmtp;+h->arg_2=(unsignedlong)restart_block->nanosleep.compat_rmtp;+expire=restart_block->nanosleep.expires;+ckpt_debug("restart_block: compat expire %lld now %lld\n",+expire,base);++}elseif(fn==compat_clock_nanosleep_restart){++h->function_type=CKPT_RESTART_BLOCK_COMPAT_CLOCK_NANOSLEEP;+h->arg_0=restart_block->nanosleep.index;+h->arg_1=(unsignedlong)restart_block->nanosleep.rmtp;+h->arg_2=(unsignedlong)restart_block->nanosleep.compat_rmtp;+expire=restart_block->nanosleep.expires;+ckpt_debug("restart_block: compat_clock expire %lld now %lld\n",+expire,base);++#endif+}elseif(fn==futex_wait_restart){++h->function_type=CKPT_RESTART_BLOCK_FUTEX;+h->arg_0=(unsignedlong)restart_block->futex.uaddr;+h->arg_1=restart_block->futex.val;+h->arg_2=restart_block->futex.flags;+h->arg_3=restart_block->futex.bitset;+expire=restart_block->futex.time;+ckpt_debug("restart_block: futex expire %lld now %lld\n",+expire,base);++}elseif(fn==do_restart_poll){+structtimespects;++h->function_type=CKPT_RESTART_BLOCK_POLL;+h->arg_0=(unsignedlong)restart_block->poll.ufds;+h->arg_1=restart_block->poll.nfds;+h->arg_2=restart_block->poll.has_timeout;+ts.tv_sec=restart_block->poll.tv_sec;+ts.tv_nsec=restart_block->poll.tv_nsec;+expire=timespec_to_ns(&ts);+ckpt_debug("restart_block: poll expire %lld now %lld\n",+expire,base);++}else{++BUG();++}++/* common to all restart blocks: */+h->arg_4=(base<expire?expire-base:0);++ckpt_debug("restart_block: args %#llx %#llx %#llx %#llx %#llx\n",+h->arg_0,h->arg_1,h->arg_2,h->arg_3,h->arg_4);++ret=ckpt_write_obj(ctx,&h->h);+ckpt_hdr_put(ctx,h);++ckpt_debug("restart_block ret %d\n",ret);+returnret;+}+/* dump the entire state of a given task */intcheckpoint_task(structckpt_ctx*ctx,structtask_struct*t){
@@ -95,6 +212,111 @@ static int restore_task_struct(struct ckpt_ctx *ctx)returnret;}+intrestore_restart_block(structckpt_ctx*ctx)+{+structckpt_hdr_restart_block*h;+structrestart_blockrestart_block;+structtimespects;+clockid_tclockid;+s64expire;+intret=0;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_RESTART_BLOCK);+if(IS_ERR(h))+returnPTR_ERR(h);++expire=ktime_to_ns(ctx->ktime_begin)+h->arg_4;+restart_block.fn=NULL;++ckpt_debug("restart_block: expire %lld begin %lld\n",+expire,ktime_to_ns(ctx->ktime_begin));+ckpt_debug("restart_block: args %#llx %#llx %#llx %#llx %#llx\n",+h->arg_0,h->arg_1,h->arg_2,h->arg_3,h->arg_4);++switch(h->function_type){+caseCKPT_RESTART_BLOCK_NONE:+restart_block.fn=do_no_restart_syscall;+break;+caseCKPT_RESTART_BLOCK_HRTIMER_NANOSLEEP:+clockid=h->arg_0;+if(clockid<0||invalid_clockid(clockid))+break;+restart_block.fn=hrtimer_nanosleep_restart;+restart_block.nanosleep.index=clockid;+restart_block.nanosleep.rmtp=+(structtimespec__user*)(unsignedlong)h->arg_1;+restart_block.nanosleep.expires=expire;+break;+caseCKPT_RESTART_BLOCK_POSIX_CPU_NANOSLEEP:+clockid=h->arg_0;+if(clockid<0||invalid_clockid(clockid))+break;+restart_block.fn=posix_cpu_nsleep_restart;+restart_block.arg0=clockid;+restart_block.arg1=h->arg_1;+ts=ns_to_timespec(expire);+restart_block.arg2=ts.tv_sec;+restart_block.arg3=ts.tv_nsec;+break;+#ifdef CONFIG_COMPAT+caseCKPT_RESTART_BLOCK_COMPAT_NANOSLEEP:+clockid=h->arg_0;+if(clockid<0||invalid_clockid(clockid))+break;+restart_block.fn=compat_nanosleep_restart;+restart_block.nanosleep.index=clockid;+restart_block.nanosleep.rmtp=+(structtimespec__user*)(unsignedlong)h->arg_1;+restart_block.nanosleep.compat_rmtp=+(structcompat_timespec__user*)+(unsignedlong)h->arg_2;+resatrt_block.nanosleep.expires=expire;+break;+caseCKPT_RESTART_BLOCK_COMPAT_CLOCK_NANOSLEEP:+clockid=h->arg_0;+if(clockid<0||invalid_clockid(clockid))+break;+restart_block.fn=compat_clock_nanosleep_restart;+restart_block.nanosleep.index=clockid;+restart_block.nanosleep.rmtp=+(structtimespec__user*)(unsignedlong)h->arg_1;+restart_block.nanosleep.compat_rmtp=+(structcompat_timespec__user*)+(unsignedlong)h->arg_2;+resatrt_block.nanosleep.expires=expire;+break;+#endif+caseCKPT_RESTART_BLOCK_FUTEX:+restart_block.fn=futex_wait_restart;+restart_block.futex.uaddr=(u32*)(unsignedlong)h->arg_0;+restart_block.futex.val=h->arg_1;+restart_block.futex.flags=h->arg_2;+restart_block.futex.bitset=h->arg_3;+restart_block.futex.time=expire;+break;+caseCKPT_RESTART_BLOCK_POLL:+restart_block.fn=do_restart_poll;+restart_block.poll.ufds=+(structpollfd__user*)(unsignedlong)h->arg_0;+restart_block.poll.nfds=h->arg_1;+restart_block.poll.has_timeout=h->arg_2;+ts=ns_to_timespec(expire);+restart_block.poll.tv_sec=ts.tv_sec;+restart_block.poll.tv_nsec=ts.tv_nsec;+break;+default:+break;+}++if(restart_block.fn)+task_thread_info(current)->restart_block=restart_block;+else+ret=-EINVAL;++ckpt_hdr_put(ctx,h);+returnret;+}+/* read the entire state of the current task */intrestore_task(structckpt_ctx*ctx){
@@ -393,6 +395,5 @@ long do_restart(struct ckpt_ctx *ctx, pid_t pid)if(ret<0)returnret;-/* on success, adjust the return value if needed [TODO] */-returnrestore_retval(ctx);+returnrestore_retval();}
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
During checkpoint, a zombie processes need only save p->comm,
p->state, p->exit_state, and p->exit_code.
During restart, zombie processes are created like all other
processes. They validate the saved exit_code restore p->comm
and p->exit_code. Then they call do_exit() instead of waking
up the next task in line.
But before, they place the @ctx in p->checkpoint_ctx, so that
only at exit time they will wake up the next task in line,
and drop the reference to the @ctx.
This provides the guarantee that when the coordinator's wait
completes, all normal tasks completed their restart, and all
zombie tasks are already zombified (as opposed to perhap only
becoming a zombie).
Changelog[v17]:
- Validate t->exit_signal for both threads and leader
- Skip zombies in most of may_checkpoint_task()
- Save/restore t->pdeath_signal
- Validate ->exit_signal and ->pdeath_signal
Signed-off-by: Oren Laadan <redacted>
---
checkpoint/checkpoint.c | 12 +++++--
checkpoint/process.c | 67 +++++++++++++++++++++++++++++++++++-----
checkpoint/restart.c | 40 +++++++++++++++++++++---
include/linux/checkpoint.h | 1 +
include/linux/checkpoint_hdr.h | 1 +
5 files changed, 104 insertions(+), 17 deletions(-)
@@ -280,8 +280,8 @@ static int may_checkpoint_task(struct ckpt_ctx *ctx, struct task_struct *t)ckpt_debug("check %d\n",task_pid_nr_ns(t,ctx->root_nsproxy->pid_ns));-if(t->state==TASK_DEAD){-pr_warning("c/r: task %d is TASK_DEAD\n",task_pid_vnr(t));+if(t->exit_state==EXIT_DEAD){+pr_warning("c/r: task %d is EXIT_DEAD\n",task_pid_vnr(t));return-EAGAIN;}
@@ -291,6 +291,10 @@ static int may_checkpoint_task(struct ckpt_ctx *ctx, struct task_struct *t)return-EPERM;}+/* zombies are cool (and also don't have nsproxy, below...) */+if(t->exit_state)+return0;+/* verify that all tasks belongs to same freezer cgroup */if(t!=current&&!in_same_cgroup_freezer(t,ctx->root_freezer)){__ckpt_write_err(ctx,"task %d (%s) not frozen (wrong cgroup)",
@@ -309,8 +313,8 @@ static int may_checkpoint_task(struct ckpt_ctx *ctx, struct task_struct *t)*FIX:fornow,disallowsiblingsofcontainerinitcreated*viaCLONE_PARENT(uncleariftheywillremainpossible)*/-if(ctx->root_init&&t!=root&&t->tgid!=root->tgid&&-t->real_parent==root->real_parent){+if(ctx->root_init&&t!=root&&+t->real_parent==root->real_parent&&t->tgid!=root->tgid){__ckpt_write_err(ctx,"task %d (%s) is sibling of root",task_pid_vnr(t),t->comm);return-EINVAL;
@@ -35,12 +35,18 @@ static int checkpoint_task_struct(struct ckpt_ctx *ctx, struct task_struct *t)h->state=t->state;h->exit_state=t->exit_state;h->exit_code=t->exit_code;-h->exit_signal=t->exit_signal;-h->set_child_tid=t->set_child_tid;-h->clear_child_tid=t->clear_child_tid;+if(t->exit_state){+/* zombie - skip remaining state */+BUG_ON(t->exit_state!=EXIT_ZOMBIE);+}else{+/* FIXME: save remaining relevant task_struct fields */+h->exit_signal=t->exit_signal;+h->pdeath_signal=t->pdeath_signal;-/* FIXME: save remaining relevant task_struct fields */+h->set_child_tid=t->set_child_tid;+h->clear_child_tid=t->clear_child_tid;+}ret=ckpt_write_obj(ctx,&h->h);ckpt_hdr_put(ctx,h);
@@ -169,6 +175,11 @@ int checkpoint_task(struct ckpt_ctx *ctx, struct task_struct *t)ckpt_debug("task %d\n",ret);if(ret<0)gotoout;++/* zombie - we're done here */+if(t->exit_state)+return0;+ret=checkpoint_thread(ctx,t);ckpt_debug("thread %d\n",ret);if(ret<0)
@@ -187,6 +198,19 @@ int checkpoint_task(struct ckpt_ctx *ctx, struct task_struct *t)*Restart*/+staticinlineintvalid_exit_code(intexit_code)+{+if(exit_code>=0x10000)+return0;+if(exit_code&0xff){+if(exit_code&~0xff)+return0;+if(!valid_signal(exit_code&0xff))+return0;+}+return1;+}+/* read the task_struct into the current task */staticintrestore_task_struct(structckpt_ctx*ctx){
@@ -468,6 +467,8 @@ static int do_restore_task(void)return-EAGAIN;}+current->flags|=PF_RESTARTING;+/* wait for our turn, do the restore, and tell next task in line */ret=wait_task_active(ctx);if(ret<0)
@@ -477,6 +478,13 @@ static int do_restore_task(void)if(ret<0)gotoout;+/*+*zombie:we'redonehere;Save@ctxontask_struct,tobe+*usedtockpt_activate_next(),andreleased,fromdo_exit().+*/+if(ret)+do_exit(current->exit_code);+ret=ckpt_activate_next(ctx);if(ret<0)gotoout;
@@ -493,6 +501,7 @@ static int do_restore_task(void)wake_up_all(&ctx->waitq);}+current->flags&=~PF_RESTARTING;ckpt_ctx_put(ctx);returnret;}
--
1.6.0.4
--
To unsubscribe from this list: send the line "unsubscribe linux-api" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
For each vma, there is a 'struct ckpt_vma'; Then comes the actual
contents, in one or more chunk: each chunk begins with a header that
specifies how many pages it holds, then the virtual addresses of all
the dumped pages in that chunk, followed by the actual contents of all
dumped pages. A header with zero number of pages marks the end of the
contents. Then comes the next vma and so on.
To checkpoint a vma, call the ops->checkpoint() method of that vma.
Normally the per-vma function will invoke generic_vma_checkpoint()
which first writes the vma description, followed by the specific
logic to dump the contents of the pages.
Currently for private mapped memory we save the pathname of the file
that is mapped (restart will use it to re-open it and then map it).
Later we change that to reference a file object.
Changelog[v17]:
- Only collect sub-objects of mm_struct once
- Save mm->{flags,def_flags,saved_auxv}
Changelog[v16]:
- Precede vaddrs/pages with a buffer header
- Checkpoint mm->exe_file
- Handle shared task->mm
Changelog[v14]:
- Modify the ops->checkpoint method to be much more powerful
- Improve support for VDSO (with special_mapping checkpoint callback)
- Save new field 'vdso' in mm_context
- Revert change to pr_debug(), back to ckpt_debug()
- Check whether calls to ckpt_hbuf_get() fail
- Discard field 'h->parent'
Changelog[v13]:
- pgprot_t is an abstract type; use the proper accessor (fix for
64-bit powerpc (Nathan Lynch [off-list ref])
Changelog[v12]:
- Hide pgarr management inside ckpt_private_vma_fill_pgarr()
- Fix management of pgarr chain reset and alloc/expand: keep empty
pgarr in a pool chain
- Replace obsolete ckpt_debug() with pr_debug()
Changelog[v11]:
- Copy contents of 'init->fs->root' instead of pointing to them.
- Add missing test for VM_MAYSHARE when dumping memory
Changelog[v10]:
- Acquire dcache_lock around call to __d_path() in ckpt_fill_name()
Changelog[v9]:
- Introduce ckpt_ctx_checkpoint() for checkpoint-specific ctx setup
- Test if __d_path() changes mnt/dentry (when crossing filesystem
namespace boundary). for now ckpt_fill_fname() fails the checkpoint.
Changelog[v7]:
- Fix argument given to kunmap_atomic() in memory dump/restore
Changelog[v6]:
- Balance all calls to ckpt_hbuf_get() with matching ckpt_hbuf_put()
(even though it's not really needed)
Changelog[v5]:
- Improve memory dump code (following Dave Hansen's comments)
- Change dump format (and code) to allow chunks of <vaddrs, pages>
instead of one long list of each
- Fix use of follow_page() to avoid faulting in non-present pages
Changelog[v4]:
- Use standard list_... for ckpt_pgarr
Signed-off-by: Oren Laadan <redacted>
---
arch/x86/include/asm/checkpoint_hdr.h | 8 +
arch/x86/mm/checkpoint.c | 31 ++
checkpoint/Makefile | 3 +-
checkpoint/checkpoint.c | 3 +
checkpoint/memory.c | 688 +++++++++++++++++++++++++++++++++
checkpoint/objhash.c | 25 ++
checkpoint/process.c | 13 +
checkpoint/sys.c | 3 +
include/linux/checkpoint.h | 26 ++
include/linux/checkpoint_hdr.h | 52 +++
include/linux/checkpoint_types.h | 7 +-
mm/filemap.c | 25 ++
mm/mmap.c | 28 ++
13 files changed, 908 insertions(+), 4 deletions(-)
create mode 100644 checkpoint/memory.c
@@ -0,0 +1,688 @@+/*+*Checkpoint/restartmemorycontents+*+*Copyright(C)2008-2009OrenLaadan+*+*ThisfileissubjecttothetermsandconditionsoftheGNUGeneralPublic+*License.SeethefileCOPYINGinthemaindirectoryoftheLinux+*distributionformoredetails.+*/++/* default debug level for output */+#define CKPT_DFLAG CKPT_DMEM++#include<linux/kernel.h>+#include<linux/sched.h>+#include<linux/slab.h>+#include<linux/file.h>+#include<linux/pagemap.h>+#include<linux/mm_types.h>+#include<linux/proc_fs.h>+#include<linux/checkpoint.h>+#include<linux/checkpoint_hdr.h>++/*+*page-arraychains:eachckpt_pgarrdescribesasetof<structpage*,vaddr>+*tuples(wherevaddristhevirtualaddressofapageinaparticularmm).+*Specifically,weuseseparatearrayssothatallvaddrscanbewritten+*andreadatonce.+*/++structckpt_pgarr{+unsignedlong*vaddrs;+structpage**pages;+unsignedintnr_used;+structlist_headlist;+};++#define CKPT_PGARR_TOTAL (PAGE_SIZE / sizeof(void *))+#define CKPT_PGARR_BATCH (16 * CKPT_PGARR_TOTAL)++staticinlineintpgarr_is_full(structckpt_pgarr*pgarr)+{+return(pgarr->nr_used==CKPT_PGARR_TOTAL);+}++staticinlineintpgarr_nr_free(structckpt_pgarr*pgarr)+{+returnCKPT_PGARR_TOTAL-pgarr->nr_used;+}++/*+*utilitiestoalloc,free,andhandle'structckpt_pgarr'(page-arrays)+*(commontockpt_mem.candrstr_mem.c).+*+*Thecheckpointcontextstructurehastwomembersforpage-arrays:+*ctx->pgarr_list:listheadofpopulatedpage-arraychain+*ctx->pgarr_pool:listheadofemptypage-arraypoolchain+*+*Duringcheckpoint(andrestart)thechaintracksthedirtypages(page+*pointerandvirtualaddress)ofeachMM.ForaparticularMM,theseare+*alwaysaddedtotheheadofthepage-arraychain(ctx->pgarr_list).+*Beforethenextchunkofpages,thechainisreset(bydereferencing+*allpages)butnotfreed;instead,emptydescsriptorsarekeptinpool.+*+*Theheadofthechainpage-array("current")advancesasnecessary.When+*itgetsfull,anewpage-arraydescriptorispushedinfrontofit.The+*newdescriptoristakenfromfirstemptydescriptor(ifoneexists,for+*instance,afterachainreset),orallocatedon-demand.+*+*Whendumpingthedata,thechainistraversedinreverseorder.+*/++/* return first page-array in the chain */+staticinlinestructckpt_pgarr*pgarr_first(structckpt_ctx*ctx)+{+if(list_empty(&ctx->pgarr_list))+returnNULL;+returnlist_first_entry(&ctx->pgarr_list,structckpt_pgarr,list);+}++/* return (and detach) first empty page-array in the pool, if exists */+staticinlinestructckpt_pgarr*pgarr_from_pool(structckpt_ctx*ctx)+{+structckpt_pgarr*pgarr;++if(list_empty(&ctx->pgarr_pool))+returnNULL;+pgarr=list_first_entry(&ctx->pgarr_pool,structckpt_pgarr,list);+list_del(&pgarr->list);+returnpgarr;+}++/* release pages referenced by a page-array */+staticvoidpgarr_release_pages(structckpt_pgarr*pgarr)+{+ckpt_debug("total pages %d\n",pgarr->nr_used);+/*+*bothcheckpointandrestartuse'nr_used',howeverweonly+*collectpagesduringcheckpoint;inrestartwesimplyreturn+*becausepgarr->pagesremainsNULL.+*/+if(pgarr->pages){+structpage**pages=pgarr->pages;+intnr=pgarr->nr_used;++while(nr--)+page_cache_release(pages[nr]);+}++pgarr->nr_used=0;+}++/* free a single page-array object */+staticvoidpgarr_free_one(structckpt_pgarr*pgarr)+{+pgarr_release_pages(pgarr);+kfree(pgarr->pages);+kfree(pgarr->vaddrs);+kfree(pgarr);+}++/* free the chains of page-arrays (populated and empty pool) */+voidckpt_pgarr_free(structckpt_ctx*ctx)+{+structckpt_pgarr*pgarr,*tmp;++list_for_each_entry_safe(pgarr,tmp,&ctx->pgarr_list,list){+list_del(&pgarr->list);+pgarr_free_one(pgarr);+}++list_for_each_entry_safe(pgarr,tmp,&ctx->pgarr_pool,list){+list_del(&pgarr->list);+pgarr_free_one(pgarr);+}+}++/* allocate a single page-array object */+staticstructckpt_pgarr*pgarr_alloc_one(unsignedlongflags)+{+structckpt_pgarr*pgarr;++pgarr=kzalloc(sizeof(*pgarr),GFP_KERNEL);+if(!pgarr)+returnNULL;+pgarr->vaddrs=kmalloc(CKPT_PGARR_TOTAL*sizeof(unsignedlong),+GFP_KERNEL);+if(!pgarr->vaddrs)+gotonomem;++/* pgarr->pages is needed only for checkpoint */+if(flags&CKPT_CTX_CHECKPOINT){+pgarr->pages=kmalloc(CKPT_PGARR_TOTAL*+sizeof(structpage*),GFP_KERNEL);+if(!pgarr->pages)+gotonomem;+}++returnpgarr;+nomem:+pgarr_free_one(pgarr);+returnNULL;+}++/* pgarr_current - return the next available page-array in the chain+*@ctx:checkpointcontext+*+*Returnsthefirstpage-arrayinthelistthathasspace.Otherwise,+*trythenextpage-arrayafterthelastnon-emptyone,andmoveitto+*thefrontofthechain.Extendsthelistifnonehasspace.+*/+staticstructckpt_pgarr*pgarr_current(structckpt_ctx*ctx)+{+structckpt_pgarr*pgarr;++pgarr=pgarr_first(ctx);+if(pgarr&&!pgarr_is_full(pgarr))+returnpgarr;++pgarr=pgarr_from_pool(ctx);+if(!pgarr)+pgarr=pgarr_alloc_one(ctx->kflags);+if(!pgarr)+returnNULL;++list_add(&pgarr->list,&ctx->pgarr_list);+returnpgarr;+}++/* reset the page-array chain (dropping page references if necessary) */+staticvoidpgarr_reset_all(structckpt_ctx*ctx)+{+structckpt_pgarr*pgarr;++list_for_each_entry(pgarr,&ctx->pgarr_list,list)+pgarr_release_pages(pgarr);+list_splice_init(&ctx->pgarr_list,&ctx->pgarr_pool);+}++/**************************************************************************+*Checkpoint+*+*Checkpointisoutsidethecontextofthecheckpointee,soonecannot+*simplyreadpagesfromuser-space.Instead,wescantheaddressspace+*ofthetargettocherry-pickpagesofinterest.Selectedpagesare+*enlistedinapage-arraychain(attachedtothecheckpointcontext).+*Tosavetheircontents,eachpageismappedtokernelmemoryandthen+*dumpedtothefiledescriptor.+*/+++/**+*private_follow_page-returnpagepointerfordirtypages+*@vma-targetvma+*@addr-pageaddress+*+*Looksupthepagethatcorrespondtotheaddressinthevma,and+*returnsthepageifitwasmodified(andgrabsareferencetoit),+*orotherwisereturnsNULL(orerror).+*/+staticstructpage*consider_private_page(structvm_area_struct*vma,+unsignedlongaddr)+{+structpage*page;++/*+*simplifiedversionofget_user_pages():alreadyhavevma,+*onlyneedFOLL_ANON,and(fornow)ignorefaultstats.+*+*follow_page()willreturnNULLifthepageisnotpresent+*(swapped),ZERO_PAGE(0)iftheptewasn'tallocated,and+*theactualpagepointerotherwise.+*+*FIXME:consolidatewithget_user_pages()+*/++cond_resched();+while(!(page=follow_page(vma,addr,FOLL_ANON|FOLL_GET))){+intret;++/* the page is swapped out - bring it in (optimize ?) */+ret=handle_mm_fault(vma->vm_mm,vma,addr,0);+if(ret&VM_FAULT_ERROR){+if(ret&VM_FAULT_OOM)+returnERR_PTR(-ENOMEM);+elseif(ret&VM_FAULT_SIGBUS)+returnERR_PTR(-EFAULT);+else+BUG();+break;+}+cond_resched();+}++if(IS_ERR(page))+returnpage;++/*+*Onlycareaboutdirtypages:eitheranonymousnon-zeropages,+*orfile-backedCOW(copy-on-write)pagesthatweremodified.+*AcleanCOWpageisnotinterestingbecauseitscontentsare+*identicaltothebackingfile;ignoresuchpages.+*Afile-backedbrokenCOWisidentifiedbyitspage_mapping()+*beingunset(NULL)becausethepagewillnolongerbemapped+*totheoriginalfileafterhavingbeenmodified.+*/+if(page==ZERO_PAGE(0)){+/* this is the zero page: ignore */+page_cache_release(page);+page=NULL;+}elseif(vma->vm_file&&(page_mapping(page)!=NULL)){+/* file backed clean cow: ignore */+page_cache_release(page);+page=NULL;+}++returnpage;+}++/**+*vma_fill_pgarr-fillapage-arraywithaddr/pagetuples+*@ctx-checkpointcontext+*@vma-vmatoscan+*@start-startaddress(updated)+*+*Returnsthenumberofpagescollected+*/+staticintvma_fill_pgarr(structckpt_ctx*ctx,+structvm_area_struct*vma,+unsignedlong*start)+{+unsignedlongend=vma->vm_end;+unsignedlongaddr=*start;+structckpt_pgarr*pgarr;+intnr_used;+intcnt=0;++/* this function is only for private memory (anon or file-mapped) */+BUG_ON(vma->vm_flags&(VM_SHARED|VM_MAYSHARE));++do{+pgarr=pgarr_current(ctx);+if(!pgarr)+return-ENOMEM;++nr_used=pgarr->nr_used;++while(addr<end){+structpage*page;++page=consider_private_page(vma,addr);+if(IS_ERR(page))+returnPTR_ERR(page);++if(page){+_ckpt_debug(CKPT_DPAGE,+"got page %#lx\n",addr);+pgarr->pages[pgarr->nr_used]=page;+pgarr->vaddrs[pgarr->nr_used]=addr;+pgarr->nr_used++;+}++addr+=PAGE_SIZE;++if(pgarr_is_full(pgarr))+break;+}++cnt+=pgarr->nr_used-nr_used;++}while((cnt<CKPT_PGARR_BATCH)&&(addr<end));++*start=addr;+returncnt;+}++/* dump contents of a pages: use kmap_atomic() to avoid TLB flush */+staticintcheckpoint_dump_page(structckpt_ctx*ctx,+structpage*page,char*buf)+{+void*ptr;++ptr=kmap_atomic(page,KM_USER1);+memcpy(buf,ptr,PAGE_SIZE);+kunmap_atomic(ptr,KM_USER1);++returnckpt_kwrite(ctx,buf,PAGE_SIZE);+}++/**+*vma_dump_pages-dumppageslistedinthectxpage-arraychain+*@ctx-checkpointcontext+*@total-totalnumberofpages+*+*Firstdumpallvirtualaddresses,followedbythecontentsofallpages+*/+staticintvma_dump_pages(structckpt_ctx*ctx,inttotal)+{+structckpt_pgarr*pgarr;+void*buf;+inti,ret=0;++if(!total)+return0;++i=total*(sizeof(unsignedlong)+PAGE_SIZE);+ret=ckpt_write_obj_type(ctx,NULL,i,CKPT_HDR_BUFFER);+if(ret<0)+returnret;++list_for_each_entry_reverse(pgarr,&ctx->pgarr_list,list){+ret=ckpt_kwrite(ctx,pgarr->vaddrs,+pgarr->nr_used*sizeof(unsignedlong));+if(ret<0)+returnret;+}++buf=(void*)__get_free_page(GFP_KERNEL);+if(!buf)+return-ENOMEM;++list_for_each_entry_reverse(pgarr,&ctx->pgarr_list,list){+for(i=0;i<pgarr->nr_used;i++){+ret=checkpoint_dump_page(ctx,pgarr->pages[i],buf);+if(ret<0)+gotoout;+}+}+out:+free_page((unsignedlong)buf);+returnret;+}++/**+*checkpoint_memory_contents-dumpcontentsofaVMAwithprivatememory+*@ctx-checkpointcontext+*@vma-vmatoscan+*+*Collectlistsofpagesthatneedstobedumped,andcorresponding+*virtualaddressesintoctx->pgarr_listpage-arraychain.Thendump+*theaddresses,followedbythepagecontents.+*/+staticintcheckpoint_memory_contents(structckpt_ctx*ctx,+structvm_area_struct*vma)+{+structckpt_hdr_pgarr*h;+unsignedlongaddr,end;+intcnt,ret;++addr=vma->vm_start;+end=vma->vm_end;++/*+*Workiteratively,collectinganddumpingatmostCKPT_PGARR_BATCH+*ineachround.Eachiterationsisdividedintotwosteps:+*+*(1)scan:scanthroughthePTEsofthevmatocollectthepages+*todump(laterwe'llalsomakethemCOW),whilekeepingalist+*ofpagesandtheircorrespondingaddressesonctx->pgarr_list.+*+*(2)dump:writeoutaheaderspecifyinghowmanypages,followed+*bytheaddressesofallpagesinctx->pgarr_list,followedby+*theactualcontentsofallpages.(Then,releasethereferences+*tothepagesandresetthepage-arraychain).+*+*(Thissplitmakesthelogicsimplerbyfirstcountingthepages+*thatneedsaving.Moreimportantly,itallowsforafuture+*optimizationthatwillreduceapplicationdowntimebydeferring+*theactualwrite-outofthedatatoaftertheapplicationis+*allowedtoresumeexecution).+*+*Afterdumpingtheentirecontents,concludewithaheaderthat+*specifies0pagestomarktheendofthecontents.+*/++while(addr<end){+cnt=vma_fill_pgarr(ctx,vma,&addr);+if(cnt==0)+break;+elseif(cnt<0)+returncnt;++ckpt_debug("collected %d pages\n",cnt);++h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_PGARR);+if(!h)+return-ENOMEM;++h->nr_pages=cnt;+ret=ckpt_write_obj(ctx,&h->h);+ckpt_hdr_put(ctx,h);+if(ret<0)+returnret;++ret=vma_dump_pages(ctx,cnt);+if(ret<0)+returnret;++pgarr_reset_all(ctx);+}++/* mark end of contents with header saying "0" pages */+h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_PGARR);+if(!h)+return-ENOMEM;+h->nr_pages=0;+ret=ckpt_write_obj(ctx,&h->h);+ckpt_hdr_put(ctx,h);++returnret;+}++/**+*generic_vma_checkpoint-dumpmetadataofvma+*@ctx:checkpointcontext+*@vma:vmaobject+*@type:vmatype+*@vma_objref:vmaobjref+*/+intgeneric_vma_checkpoint(structckpt_ctx*ctx,structvm_area_struct*vma,+enumvma_typetype,intvma_objref)+{+structckpt_hdr_vma*h;+intret;++ckpt_debug("vma %#lx-%#lx flags %#lx type %d\n",+vma->vm_start,vma->vm_end,vma->vm_flags,type);++if(vma->vm_flags&CKPT_VMA_NOT_SUPPORTED){+pr_warning("c/r: unsupported VMA %#lx\n",vma->vm_flags);+return-ENOSYS;+}++h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_VMA);+if(!h)+return-ENOMEM;++h->vma_type=type;+h->vma_objref=vma_objref;+h->vm_start=vma->vm_start;+h->vm_end=vma->vm_end;+h->vm_page_prot=pgprot_val(vma->vm_page_prot);+h->vm_flags=vma->vm_flags;+h->vm_pgoff=vma->vm_pgoff;++ret=ckpt_write_obj(ctx,&h->h);+ckpt_hdr_put(ctx,h);++returnret;+}++/**+*private_vma_checkpoint-dumpcontentsofprivate(anon,file)vma+*@ctx:checkpointcontext+*@vma:vmaobject+*@type:vmatype+*@vma_objref:vmaobjref+*/+intprivate_vma_checkpoint(structckpt_ctx*ctx,+structvm_area_struct*vma,+enumvma_typetype,intvma_objref)+{+intret;++BUG_ON(vma->vm_flags&(VM_SHARED|VM_MAYSHARE));++ret=generic_vma_checkpoint(ctx,vma,type,vma_objref);+if(ret<0)+gotoout;+ret=checkpoint_memory_contents(ctx,vma);+out:+returnret;+}++/**+*anonymous_checkpoint-dumpcontentsofprivate-anonymousvma+*@ctx:checkpointcontext+*@vma:vmaobject+*/+staticintanonymous_checkpoint(structckpt_ctx*ctx,+structvm_area_struct*vma)+{+/* should be private anonymous ... verify that this is the case */+if(vma->vm_flags&CKPT_VMA_NOT_SUPPORTED){+pr_warning("c/r: unsupported VMA %#lx\n",vma->vm_flags);+return-ENOSYS;+}++BUG_ON(vma->vm_file);++returnprivate_vma_checkpoint(ctx,vma,CKPT_VMA_ANON,0);+}++staticintdo_checkpoint_mm(structckpt_ctx*ctx,structmm_struct*mm)+{+structckpt_hdr_mm*h;+structvm_area_struct*vma;+intexe_objref=0;+intret;++h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_MM);+if(!h)+return-ENOMEM;++down_read(&mm->mmap_sem);++h->flags=mm->flags;+h->def_flags=mm->def_flags;++h->start_code=mm->start_code;+h->end_code=mm->end_code;+h->start_data=mm->start_data;+h->end_data=mm->end_data;+h->start_brk=mm->start_brk;+h->brk=mm->brk;+h->start_stack=mm->start_stack;+h->arg_start=mm->arg_start;+h->arg_end=mm->arg_end;+h->env_start=mm->env_start;+h->env_end=mm->env_end;++h->map_count=mm->map_count;++/* checkpoint the ->exe_file */+if(mm->exe_file){+exe_objref=checkpoint_obj(ctx,mm->exe_file,CKPT_OBJ_FILE);+if(exe_objref<0){+ret=exe_objref;+gotoout;+}+h->exe_objref=exe_objref;+}++ret=ckpt_write_obj(ctx,&h->h);+if(ret<0)+gotoout;++ret=ckpt_write_buffer(ctx,mm->saved_auxv,sizeof(mm->saved_auxv));+if(ret<0)+returnret;++/* write the vma's */+for(vma=mm->mmap;vma;vma=vma->vm_next){+ckpt_debug("vma %#lx-%#lx flags %#lx\n",+vma->vm_start,vma->vm_end,vma->vm_flags);+if(!vma->vm_ops)+ret=anonymous_checkpoint(ctx,vma);+elseif(vma->vm_ops->checkpoint)+ret=(*vma->vm_ops->checkpoint)(ctx,vma);+else+ret=-ENOSYS;+if(ret<0)+gotoout;+}++ret=checkpoint_mm_context(ctx,mm);+out:+ckpt_hdr_put(ctx,h);+up_read(&mm->mmap_sem);+returnret;+}++intcheckpoint_mm(structckpt_ctx*ctx,void*ptr)+{+returndo_checkpoint_mm(ctx,(structmm_struct*)ptr);+}++intcheckpoint_obj_mm(structckpt_ctx*ctx,structtask_struct*t)+{+structmm_struct*mm;+intobjref;++mm=get_task_mm(t);+objref=checkpoint_obj(ctx,mm,CKPT_OBJ_MM);+mmput(mm);++returnobjref;+}++/***********************************************************************+*Collect+*/++staticintcollect_mm(structckpt_ctx*ctx,structmm_struct*mm)+{+structvm_area_struct*vma;+structfile*file;+intexists;+intret;++/* if already exists, don't proceed inside the struct */+exists=ckpt_obj_lookup(ctx,mm,CKPT_OBJ_MM);++ret=ckpt_obj_collect(ctx,mm,CKPT_OBJ_MM);+if(ret<0||exists)+returnret;++down_read(&mm->mmap_sem);+if(mm->exe_file){+ret=ckpt_obj_collect(ctx,mm->exe_file,CKPT_OBJ_FILE);+if(ret<0)+gotoout;+}+for(vma=mm->mmap;vma;vma=vma->vm_next){+file=vma->vm_file;+if(file){+ret=ckpt_obj_collect(ctx,file,CKPT_OBJ_FILE);+if(ret<0)+break;+}+}+out:+up_read(&mm->mmap_sem);+returnret;++}++intckpt_collect_mm(structckpt_ctx*ctx,structtask_struct*t)+{+structmm_struct*mm;+intret;++mm=get_task_mm(t);+ret=collect_mm(ctx,mm);+mmput(mm);++returnret;+}
@@ -34,6 +34,7 @@#include<linux/hardirq.h> /* for BUG_ON(!in_atomic()) only */#include<linux/memcontrol.h>#include<linux/mm_inline.h> /* for page_is_file_cache() */+#include<linux/checkpoint.h>#include"internal.h"/*
@@ -1648,8 +1649,32 @@ page_not_uptodate:}EXPORT_SYMBOL(filemap_fault);+#ifdef CONFIG_CHECKPOINT+staticintfilemap_checkpoint(structckpt_ctx*ctx,structvm_area_struct*vma)+{+structfile*file=vma->vm_file;+intvma_objref;++if(vma->vm_flags&CKPT_VMA_NOT_SUPPORTED){+pr_warning("c/r: unsupported VMA %#lx\n",vma->vm_flags);+return-ENOSYS;+}++BUG_ON(!file);++vma_objref=checkpoint_obj(ctx,file,CKPT_OBJ_FILE);+if(vma_objref<0)+returnvma_objref;++returnprivate_vma_checkpoint(ctx,vma,CKPT_VMA_FILE,vma_objref);+}+#endif /* CONFIG_CHECKPOINT */+structvm_operations_structgeneric_file_vm_ops={.fault=filemap_fault,+#ifdef CONFIG_CHECKPOINT+.checkpoint=filemap_checkpoint,+#endif};/* This is used for a general mmap of a disk file */
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Dump the file table with 'struct ckpt_hdr_file_table, followed by all
open file descriptors. Because the 'struct file' corresponding to an
fd can be shared, they are assigned an objref and registered in the
object hash. A reference to the 'file *' is kept for as long as it
lives in the hash (the hash is only cleaned up at the end of the
checkpoint).
Also provide generic_checkpoint_file() and generic_restore_file()
which is good for normal files and directories. It does not support
yet unlinked files or directories.
Changelog[v17]:
- Only collect sub-objects of files_struct once
- Better file error debugging
- Use (new) d_unlinked()
Changelog[v16]:
- Fix compile warning in checkpoint_bad()
Changelog[v16]:
- Reorder patch (move earlier in series)
- Handle shared files_struct objects
Changelog[v14]:
- File objects are dumped/restored prior to the first reference
- Introduce a per file-type restore() callback
- Use struct file_operations->checkpoint()
- Put code for generic file descriptors in a separate function
- Use one CKPT_FILE_GENERIC for both regular files and dirs
- Revert change to pr_debug(), back to ckpt_debug()
- Use only unsigned fields in checkpoint headers
- Rename: ckpt_write_files() => checkpoint_fd_table()
- Rename: ckpt_write_fd_data() => checkpoint_file()
- Discard field 'h->parent'
Changelog[v12]:
- Replace obsolete ckpt_debug() with pr_debug()
Changelog[v11]:
- Discard handling of opened symlinks (there is no such thing)
- ckpt_scan_fds() retries from scratch if hits size limits
Changelog[v9]:
- Fix a couple of leaks in ckpt_write_files()
- Drop useless kfree from ckpt_scan_fds()
Changelog[v8]:
- initialize 'coe' to workaround gcc false warning
Changelog[v6]:
- Balance all calls to ckpt_hbuf_get() with matching ckpt_hbuf_put()
(even though it's not really needed)
Signed-off-by: Oren Laadan <redacted>
---
checkpoint/Makefile | 3 +-
checkpoint/checkpoint.c | 11 +
checkpoint/files.c | 382 ++++++++++++++++++++++++++++++++++++++
checkpoint/objhash.c | 53 ++++++
checkpoint/process.c | 34 ++++-
checkpoint/sys.c | 1 +
include/linux/checkpoint.h | 15 ++
include/linux/checkpoint_hdr.h | 49 +++++
include/linux/checkpoint_types.h | 6 +
include/linux/fs.h | 4 +
10 files changed, 556 insertions(+), 2 deletions(-)
create mode 100644 checkpoint/files.c
Export functionality to retrieve specific pages from shared memory
given an inode in shmem-fs; this will be used in the next two patches
to provide support for c/r of shared memory.
mm/shmem.c:
- shmem_getpage() and 'enum sgp_type' moved to linux/mm.h
Signed-off-by: Oren Laadan <redacted>
---
include/linux/mm.h | 11 +++++++++++
mm/shmem.c | 15 ++-------------
2 files changed, 13 insertions(+), 13 deletions(-)
@@ -329,6 +329,17 @@ void put_pages_list(struct list_head *pages);voidsplit_page(structpage*page,unsignedintorder);+/* Flag allocation requirements to shmem_getpage and shmem_swp_alloc */+enumsgp_type{+SGP_READ,/* don't exceed i_size, don't allocate page */+SGP_CACHE,/* don't exceed i_size, may allocate page */+SGP_DIRTY,/* like SGP_CACHE, but set new page dirty */+SGP_WRITE,/* may exceed i_size, may allocate page */+};++externintshmem_getpage(structinode*inode,unsignedlongidx,+structpage**pagep,enumsgp_typesgp,int*type);+/**Compoundpageshaveadestructorfunction.Providea*prototypeforthatfunctionandaccessorfunctions.
@@ -99,14 +99,6 @@ static struct vfsmount *shm_mnt;/* Pretend that each entry is of this size in directory's i_size */#define BOGO_DIRENT_SIZE 20-/* Flag allocation requirements to shmem_getpage and shmem_swp_alloc */-enumsgp_type{-SGP_READ,/* don't exceed i_size, don't allocate page */-SGP_CACHE,/* don't exceed i_size, may allocate page */-SGP_DIRTY,/* like SGP_CACHE, but set new page dirty */-SGP_WRITE,/* may exceed i_size, may allocate page */-};-#ifdef CONFIG_TMPFSstaticunsignedlongshmem_default_max_blocks(void){
@@ -119,9 +111,6 @@ static unsigned long shmem_default_max_inodes(void)}#endif-staticintshmem_getpage(structinode*inode,unsignedlongidx,-structpage**pagep,enumsgp_typesgp,int*type);-staticinlinestructpage*shmem_dir_alloc(gfp_tgfp_mask){/*
We need to use this value in the checkpoint/restart code and would like to
have a constant instead of a magic '3'.
Changelog:
Mar 30:
. Add CHECKPOINT_SUPPORT in Kconfig (Nathan Lynch)
Mar 03:
. Picked up additional use of magic '3' in ptrace.h
Signed-off-by: Dan Smith <redacted>
---
arch/s390/Kconfig | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
These two are used in the next patch when calling vfs_read/write()
Signed-off-by: Oren Laadan <redacted>
---
fs/read_write.c | 10 ----------
include/linux/fs.h | 10 ++++++++++
2 files changed, 10 insertions(+), 10 deletions(-)
Checkpoint of sysvipc semaphores is performed by iterating through all
sem objects and dumping the contents of each one. The semaphore array
of each sem is dumped with that object.
The semaphore array (sem->sem_base) holds an array of 'struct sem',
which is a {int, int}. Because this translates into the same format
on 32- and 64-bit architectures, the checkpoint format is simply the
dump of this array as is.
TODO: this patch does not handle semaphore-undo -- this data should be
saved per-task while iterating through the tasks.
Changelog[v17]:
- Restore objects in the right namespace
- Forward declare struct msg_msg (instead of include linux/msg.h)
- Fix typo in comment
- Don't unlock ipc before calling freeary in error path
Signed-off-by: Oren Laadan <redacted>
---
include/linux/checkpoint_hdr.h | 8 ++
ipc/Makefile | 2 +-
ipc/checkpoint.c | 4 -
ipc/checkpoint_sem.c | 219 ++++++++++++++++++++++++++++++++++++++++
ipc/sem.c | 11 +--
ipc/util.h | 8 ++
6 files changed, 240 insertions(+), 12 deletions(-)
create mode 100644 ipc/checkpoint_sem.c
@@ -119,12 +119,10 @@ static int do_checkpoint_ipc_ns(struct ckpt_ctx *ctx,returnret;ret=checkpoint_ipc_any(ctx,ipc_ns,IPC_MSG_IDS,CKPT_HDR_IPC_MSG,checkpoint_ipc_msg);-#if 0 /* NEXT FEW PATCHES */if(ret<0)returnret;ret=checkpoint_ipc_any(ctx,ipc_ns,IPC_SEM_IDS,CKPT_HDR_IPC_SEM,checkpoint_ipc_sem);-#endifreturnret;}
@@ -288,7 +286,6 @@ static struct ipc_namespace *do_restore_ipc_ns(struct ckpt_ctx *ctx)ret=restore_ipc_any(ctx,ipc_ns,IPC_SHM_IDS,CKPT_HDR_IPC_SHM,restore_ipc_shm);-#if 0 /* NEXT FEW PATCHES */if(ret<0)gotoout;ret=restore_ipc_any(ctx,ipc_ns,IPC_MSG_IDS,
@@ -0,0 +1,219 @@+/*+*Checkpoint/restart-dumpstateofsysvipcsem+*+*Copyright(C)2009OrenLaadan+*+*ThisfileissubjecttothetermsandconditionsoftheGNUGeneralPublic+*License.SeethefileCOPYINGinthemaindirectoryoftheLinux+*distributionformoredetails.+*/++/* default debug level for output */+#define CKPT_DFLAG CKPT_DIPC++#include<linux/mm.h>+#include<linux/sem.h>+#include<linux/rwsem.h>+#include<linux/sched.h>+#include<linux/syscalls.h>+#include<linux/nsproxy.h>+#include<linux/ipc_namespace.h>++structmsg_msg;+#include"util.h"++#include<linux/checkpoint.h>+#include<linux/checkpoint_hdr.h>++/************************************************************************+*ipccheckpoint+*/++staticintfill_ipc_sem_hdr(structckpt_ctx*ctx,+structckpt_hdr_ipc_sem*h,+structsem_array*sem)+{+intret=0;++ipc_lock_by_ptr(&sem->sem_perm);++ret=checkpoint_fill_ipc_perms(&h->perms,&sem->sem_perm);+if(ret<0)+gotounlock;++h->sem_otime=sem->sem_otime;+h->sem_ctime=sem->sem_ctime;+h->sem_nsems=sem->sem_nsems;++unlock:+ipc_unlock(&sem->sem_perm);+ckpt_debug("sem: nsems %u\n",h->sem_nsems);++returnret;+}++/**+*ckpt_write_sem_array-dumpthestateofasemaphorearray+*@ctx:checkpointcontext+*@sem:semphorearray+*+*Thestateofasempahoreisanarrayof'structsem'.Thisstructure+*is{int,int},whichtranslatestothesameformat{32bits,32bits}+*onboth32-and64-bitarchitectures.Sowesimplydumpthearray.+*+*Thesem-undoinformationisnotsavedperipc_ns,butratherpertask.+*/+staticintcheckpoint_sem_array(structckpt_ctx*ctx,structsem_array*sem)+{+/* this is a "best-effort" test, so lock not needed */+if(!list_empty(&sem->sem_pending))+return-EBUSY;++/* our caller holds the mutex, so this is safe */+returnckpt_write_buffer(ctx,sem->sem_base,+sem->sem_nsems*sizeof(*sem->sem_base));+}++intcheckpoint_ipc_sem(intid,void*p,void*data)+{+structckpt_hdr_ipc_sem*h;+structckpt_ctx*ctx=(structckpt_ctx*)data;+structkern_ipc_perm*perm=(structkern_ipc_perm*)p;+structsem_array*sem;+intret;++sem=container_of(perm,structsem_array,sem_perm);++h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_IPC_SEM);+if(!h)+return-ENOMEM;++ret=fill_ipc_sem_hdr(ctx,h,sem);+if(ret<0)+gotoout;++ret=ckpt_write_obj(ctx,&h->h);+if(ret<0)+gotoout;++if(h->sem_nsems)+ret=checkpoint_sem_array(ctx,sem);+out:+ckpt_hdr_put(ctx,h);+returnret;+}++/************************************************************************+*ipcrestart+*/++staticintload_ipc_sem_hdr(structckpt_ctx*ctx,+structckpt_hdr_ipc_sem*h,+structsem_array*sem)+{+intret=0;++ret=restore_load_ipc_perms(&h->perms,&sem->sem_perm);+if(ret<0)+returnret;++ckpt_debug("sem: nsems %u\n",h->sem_nsems);++sem->sem_otime=h->sem_otime;+sem->sem_ctime=h->sem_ctime;+sem->sem_nsems=h->sem_nsems;++return0;+}++/**+*ckpt_read_sem_array-readthestateofasemaphorearray+*@ctx:checkpointcontext+*@sem:semphorearray+*+*Expectthedatainanarrayof'structsem':{32bit,32bit}.+*Seecommentinckpt_write_sem_array().+*+*Thesem-undoinformationisnotrestoredperipc_ns,butratherpertask.+*/+staticstructsem*restore_sem_array(structckpt_ctx*ctx,intnsems)+{+structsem*sma;+inti,ret;++sma=kmalloc(nsems*sizeof(*sma),GFP_KERNEL);+ret=_ckpt_read_buffer(ctx,sma,nsems*sizeof(*sma));+if(ret<0)+gotoout;++/* validate sem array contents */+for(i=0;i<nsems;i++){+if(sma[i].semval<0||sma[i].sempid<0){+ret=-EINVAL;+break;+}+}+out:+if(ret<0){+kfree(sma);+sma=ERR_PTR(ret);+}+returnsma;+}++intrestore_ipc_sem(structckpt_ctx*ctx,structipc_namespace*ns)+{+structckpt_hdr_ipc_sem*h;+structkern_ipc_perm*perms;+structsem_array*sem;+structsem*sma=NULL;+structipc_ids*sem_ids=&ns->ids[IPC_SEM_IDS];+intsemflag,ret;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_IPC_SEM);+if(IS_ERR(h))+returnPTR_ERR(h);++ret=-EINVAL;+if(h->perms.id<0)+gotoout;+if(h->sem_nsems<0)+gotoout;++/* read sempahore array state */+sma=restore_sem_array(ctx,h->sem_nsems);+if(IS_ERR(sma)){+ret=PTR_ERR(sma);+gotoout;+}++/* restore the message queue now */+semflag=h->perms.mode|IPC_CREAT|IPC_EXCL;+ckpt_debug("sem: do_semget key %d flag %#x id %d\n",+h->perms.key,semflag,h->perms.id);+ret=do_semget(ns,h->perms.key,h->sem_nsems,semflag,h->perms.id);+ckpt_debug("sem: do_semget ret %d\n",ret);+if(ret<0)+gotoout;++down_write(&sem_ids->rw_mutex);++/* we are the sole owners/users of this ipc_ns, it can't go away */+perms=ipc_lock(sem_ids,h->perms.id);+BUG_ON(IS_ERR(perms));/* ipc_ns is private to us */++sem=container_of(perms,structsem_array,sem_perm);+memcpy(sem->sem_base,sma,sem->sem_nsems*sizeof(*sma));++ret=load_ipc_sem_hdr(ctx,h,sem);+if(ret<0){+ckpt_debug("sem: need to remove (%d)\n",ret);+freeary(ns,perms);+}else+ipc_unlock(perms);+up_write(&sem_ids->rw_mutex);+out:+kfree(sma);+ckpt_hdr_put(ctx,h);+returnret;+}
@@ -310,14 +309,12 @@ static inline int sem_more_checks(struct kern_ipc_perm *ipcp,return0;}-intdo_semget(key_tkey,intnsems,intsemflg,intreq_id)+intdo_semget(structipc_namespace*ns,key_tkey,intnsems,+intsemflg,intreq_id){-structipc_namespace*ns;structipc_opssem_ops;structipc_paramssem_params;-ns=current->nsproxy->ipc_ns;-if(nsems<0||nsems>ns->sc_semmsl)return-EINVAL;
@@ -334,7 +331,7 @@ int do_semget(key_t key, int nsems, int semflg, int req_id)SYSCALL_DEFINE3(semget,key_t,key,int,nsems,int,semflg){-returndo_semget(key,nsems,semflg,-1);+returndo_semget(current->nsproxy->ipc_ns,key,nsems,semflg,-1);}/*
From: Sukadev Bhattiprolu <redacted>
The new parameter will be used in a follow-on patch when clone_with_pids()
is implemented.
Signed-off-by: Sukadev Bhattiprolu <redacted>
Acked-by: Serge Hallyn <serue-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
Reviewed-by: Oren Laadan <redacted>
---
kernel/fork.c | 7 ++++---
1 files changed, 4 insertions(+), 3 deletions(-)
@@ -1351,6 +1351,7 @@ long do_fork(unsigned long clone_flags,structtask_struct*p;inttrace=0;longnr;+pid_t*target_pids=NULL;/**Dosomepreliminaryargumentandpermissionscheckingbeforewe
@@ -1391,7 +1392,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);/**Dothispriorwakingupthenewthread-thethreadpointer*mightgetinvalidafterthatpoint,ifthethreadexitsquickly.
--
1.6.0.4
--
To unsubscribe from this list: send the line "unsubscribe linux-api" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
From: Dan Smith <danms-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
As suggested by Dave[1], this provides us a way to make the copy-in and
copy-out processes symmetric. CKPT_COPY_ARRAY() provides us a way to do
the same thing but for arrays. It's not critical, but it helps us unify
the checkpoint and restart paths for some things.
Changelog:
Mar 04:
. Removed semicolons
. Added build-time check for __must_be_array in CKPT_COPY_ARRAY
Feb 27:
. Changed CKPT_COPY() to use assignment, eliminating the need
for the CKPT_COPY_BIT() macro
. Add CKPT_COPY_ARRAY() macro to help copying register arrays,
etc
. Move the macro definitions inside the CR #ifdef
Feb 25:
. Changed WARN_ON() to BUILD_BUG_ON()
Signed-off-by: Dan Smith <danms-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Oren Laadan <redacted>
1: https://lists.linux-foundation.org/pipermail/containers/2009-February/015821.html (all the way at the bottom)
---
include/linux/checkpoint.h | 29 +++++++++++++++++++++++++++++
1 files changed, 29 insertions(+), 0 deletions(-)
--
1.6.0.4
--
To unsubscribe from this list: send the line "unsubscribe linux-api" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
From: Dave Hansen <redacted>
This marks ext[234] as being checkpointable. There will be many
more to do this to, but this is a start.
Signed-off-by: Dave Hansen <redacted>
---
fs/ext2/dir.c | 1 +
fs/ext2/file.c | 2 ++
fs/ext3/dir.c | 1 +
fs/ext3/file.c | 1 +
fs/ext4/dir.c | 1 +
fs/ext4/file.c | 1 +
6 files changed, 7 insertions(+), 0 deletions(-)
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
For a given namespace type, say XXX, if a checkpoint was taken on a
CONFIG_XXX_NS system, is restarted on a !CONFIG_XXX_NS, then ensure
that:
1) The global settings of the global (init) namespace do not get
overwritten. Creating new objects in that namespace is ok, as long as
the request identifier is available.
2) All restarting tasks use a single namespace - because it is
impossible to create additional namespaces to accommodate for what had
been checkpointed.
Original patch introducing nsproxy c/r by Dan Smith [off-list ref]
Chagnelog[v17]:
- Only collect sub-objects of struct_nsproxy once.
- Restore namespace pieces directly instead of using sys_unshare()
- Proper handling of restart from namespace(s) without namespace(s)
Signed-off-by: Oren Laadan <redacted>
---
checkpoint/checkpoint.c | 20 ++++++++--
checkpoint/objhash.c | 28 ++++++++++++++
checkpoint/process.c | 81 ++++++++++++++++++++++++++++++++++++++++
include/linux/checkpoint.h | 5 ++
include/linux/checkpoint_hdr.h | 13 ++++++
kernel/nsproxy.c | 76 +++++++++++++++++++++++++++++++++++++
6 files changed, 219 insertions(+), 4 deletions(-)
@@ -520,6 +546,8 @@ int ckpt_obj_contained(struct ckpt_ctx *ctx)/* account for ctx->file reference (if in the table already) */ckpt_obj_users_inc(ctx,ctx->file,1);+/* account for ctx->root_nsproxy reference (if in the table already) */+ckpt_obj_users_inc(ctx,ctx->root_nsproxy,1);hlist_for_each_entry(obj,node,&ctx->obj_hash->list,next){if(!obj->ops->ref_users)
@@ -221,6 +222,81 @@ void exit_task_namespaces(struct task_struct *p)switch_task_namespaces(p,NULL);}+#ifdef CONFIG_CHECKPOINT+intckpt_collect_ns(structckpt_ctx*ctx,structtask_struct*t)+{+structnsproxy*nsproxy;+intexists;+intret;++rcu_read_lock();+nsproxy=task_nsproxy(t);+if(nsproxy)+get_nsproxy(nsproxy);+rcu_read_unlock();++if(!nsproxy)+return0;++/* if already exists, don't proceed inside the struct */+exists=ckpt_obj_lookup(ctx,nsproxy,CKPT_OBJ_NS);++ret=ckpt_obj_collect(ctx,nsproxy,CKPT_OBJ_NS);+if(ret<0||exists)+gotoout;++/* TODO: collect other namespaces here */+out:+put_nsproxy(nsproxy);+returnret;+}++staticintdo_checkpoint_ns(structckpt_ctx*ctx,structnsproxy*nsproxy)+{+structckpt_hdr_ns*h;+intret;++h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_NS);+if(!h)+return-ENOMEM;++/* TODO: Write other namespaces here */++ret=ckpt_write_obj(ctx,&h->h);+ckpt_hdr_put(ctx,h);+returnret;+}+++intcheckpoint_ns(structckpt_ctx*ctx,void*ptr)+{+returndo_checkpoint_ns(ctx,(structnsproxy*)ptr);+}++staticstructnsproxy*do_restore_ns(structckpt_ctx*ctx)+{+structckpt_hdr_ns*h;+structnsproxy*nsproxy=NULL;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_NS);+if(IS_ERR(h))+return(structnsproxy*)h;++nsproxy=current->nsproxy;+get_nsproxy(nsproxy);++/* TODO: add more namespaces here */++ckpt_hdr_put(ctx,h);+returnnsproxy;+}++void*restore_ns(structckpt_ctx*ctx)+{+return(void*)do_restore_ns(ctx);+}+#endif /* CONFIG_CHECKPOINT */+staticint__initnsproxy_cache_init(void){nsproxy_cachep=KMEM_CACHE(nsproxy,SLAB_PANIC);
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Serge E. Hallyn <redacted>
Restore a file's f_cred. This is set to the cred of the task doing
the open, so often it will be the same as that of the restarted task.
Signed-off-by: Serge E. Hallyn <redacted>
---
checkpoint/files.c | 16 ++++++++++++++--
include/linux/checkpoint_hdr.h | 2 +-
2 files changed, 15 insertions(+), 3 deletions(-)
@@ -150,7 +150,11 @@ int checkpoint_file_common(struct ckpt_ctx *ctx, struct file *file,h->f_pos=file->f_pos;h->f_version=file->f_version;-/* FIX: need also file->uid, file->gid, file->f_owner, etc */+h->f_credref=checkpoint_obj(ctx,file->f_cred,CKPT_OBJ_CRED);+if(h->f_credref<0)+returnh->f_credref;++/* FIX: need also file->f_owner, etc */return0;}
@@ -454,8 +458,16 @@ int restore_file_common(struct ckpt_ctx *ctx, struct file *file,fmode_tnew_mode=(__forcefmode_t)file->f_mode;fmode_tsaved_mode=(__forcefmode_t)h->f_mode;intret;+structcred*cred;++/* FIX: need to restore owner etc */-/* FIX: need to restore uid, gid, owner etc */+/* restore the cred */+cred=ckpt_obj_fetch(ctx,h->f_credref,CKPT_OBJ_CRED);+if(IS_ERR(cred))+returnPTR_ERR(cred);+put_cred(file->f_cred);+file->f_cred=get_cred(cred);/* safe to set 1st arg (fd) to 0, as command is F_SETFL */ret=vfs_fcntl(0,F_SETFL,h->f_flags&CKPT_SETFL_MASK,file);
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
A pipe is a double-headed inode with a buffer attached to it. We
checkpoint the pipe buffer only once, as soon as we hit one side of
the pipe, regardless whether it is read- or write- end.
To checkpoint a file descriptor that refers to a pipe (either end), we
first lookup the inode in the hash table: If not found, it is the
first encounter of this pipe. Besides the file descriptor, we also (a)
save the pipe data, and (b) register the pipe inode in the hash. If
found, it is the second encounter of this pipe, namely, as we hit the
other end of the same pipe. In both cases we write the pipe-objref of
the inode.
To restore, create a new pipe and thus have two file pointers (read-
and write- ends). We only use one of them, depending on which side was
checkpointed first. We register the file pointer of the other end in
the hash table, with the pipe_objref given for this pipe from the
checkpoint, to be used later when the other arrives. At this point we
also restore the contents of the pipe buffers.
To save the pipe buffer, given a source pipe, use do_tee() to clone
its contents into a temporary 'struct pipe_inode_info', and then use
do_splice_from() to transfer it directly to the checkpoint image file.
To restore the pipe buffer, with a fresh newly allocated target pipe,
use do_splice_to() to splice the data directly between the checkpoint
image file and the pipe.
Changelog[v17]:
- Forward-declare 'ckpt_ctx' et-al, don't use checkpoint_types.h
Signed-off-by: Oren Laadan <redacted>
---
checkpoint/files.c | 7 ++
fs/pipe.c | 170 ++++++++++++++++++++++++++++++++++++++++
include/linux/checkpoint_hdr.h | 12 +++
include/linux/pipe_fs_i.h | 8 ++
4 files changed, 197 insertions(+), 0 deletions(-)
@@ -809,6 +811,171 @@ pipe_rdwr_open(struct inode *inode, struct file *filp)return0;}+#ifdef CONFIG_CHECKPOINT+staticintcheckpoint_pipe(structckpt_ctx*ctx,structinode*inode)+{+structckpt_hdr_file_pipe_state*h;+structpipe_inode_info*pipe;+intlen,ret=-ENOMEM;++pipe=alloc_pipe_info(NULL);+if(!pipe)+returnret;++pipe->readers=1;/* bluff link_pipe() below */+len=link_pipe(inode->i_pipe,pipe,INT_MAX,SPLICE_F_NONBLOCK);+if(len==-EAGAIN)+len=0;+if(len<0){+ret=len;+gotoout;+}++h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_FILE_PIPE);+if(!h)+gotoout;+h->pipe_len=len;+ret=ckpt_write_obj(ctx,&h->h);+ckpt_hdr_put(ctx,h);+if(ret<0)+gotoout;++ret=do_splice_from(pipe,ctx->file,&ctx->file->f_pos,len,0);+if(ret<0)+gotoout;+if(ret!=len)+ret=-EPIPE;/* can occur due to an error in target file */+out:+__free_pipe_info(pipe);+returnret;+}++staticintpipe_file_checkpoint(structckpt_ctx*ctx,structfile*file)+{+structckpt_hdr_file_pipe*h;+structinode*inode=file->f_dentry->d_inode;+intobjref,first,ret;++objref=ckpt_obj_lookup_add(ctx,inode,CKPT_OBJ_INODE,&first);+if(objref<0)+returnobjref;++h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_FILE);+if(!h)+return-ENOMEM;++h->common.f_type=CKPT_FILE_PIPE;+h->pipe_objref=objref;++ret=checkpoint_file_common(ctx,file,&h->common);+if(ret<0)+gotoout;+ret=ckpt_write_obj(ctx,&h->common.h);+if(ret<0)+gotoout;++if(first)+ret=checkpoint_pipe(ctx,inode);+out:+ckpt_hdr_put(ctx,h);+returnret;+}++staticintrestore_pipe(structckpt_ctx*ctx,structfile*file)+{+structckpt_hdr_file_pipe_state*h;+structpipe_inode_info*pipe;+intlen,ret;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_FILE_PIPE);+if(IS_ERR(h))+returnPTR_ERR(h);++len=h->pipe_len;+ckpt_hdr_put(ctx,h);++if(len<0)+return-EINVAL;++pipe=file->f_dentry->d_inode->i_pipe;+ret=do_splice_to(ctx->file,&ctx->file->f_pos,pipe,len,0);++if(ret>=0&&ret!=len)+ret=-EPIPE;/* can occur due to an error in source file */++returnret;+}++structfile*pipe_file_restore(structckpt_ctx*ctx,structckpt_hdr_file*ptr)+{+structckpt_hdr_file_pipe*h=(structckpt_hdr_file_pipe*)ptr;+structfile*file;+intfds[2],which,ret;++if(ptr->h.type!=CKPT_HDR_FILE||+ptr->h.len!=sizeof(*h)||ptr->f_type!=CKPT_FILE_PIPE)+returnERR_PTR(-EINVAL);++if(h->pipe_objref<=0)+returnERR_PTR(-EINVAL);++file=ckpt_obj_fetch(ctx,h->pipe_objref,CKPT_OBJ_FILE);+/*+*Ifckpt_obj_fetch()returnedERR_PTR(-EINVAL),thenthisis+*thefirsttimeweseethispipesoneedtorestorethe+*contents.Otherwise,usethefilepointerskipforward.+*/+if(!IS_ERR(file)){+get_file(file);+}elseif(PTR_ERR(file)==-EINVAL){+/* first encounter of this pipe: create it */+ret=do_pipe_flags(fds,0);+if(ret<0)+returnfile;++which=(ptr->f_flags&O_WRONLY?1:0);+/*+*Belowwereturnthefilecorerspondingtooneside+*ofthepipeforourcallertouse.Nowinsertthe+*othersideofthepipetothehash,tobepickedup+*whenthatsideisrestored.+*/+file=fget(fds[1-which]);/* the 'other' side */+if(!file)/* this should _never_ happen ! */+returnERR_PTR(-EBADF);+ret=ckpt_obj_insert(ctx,file,h->pipe_objref,CKPT_OBJ_FILE);+if(ret<0)+gotoout;++ret=restore_pipe(ctx,file);+fput(file);+if(ret<0)+returnERR_PTR(ret);++file=fget(fds[which]);/* 'this' side */+if(!file)/* this should _never_ happen ! */+returnERR_PTR(-EBADF);++/* get rid of the file descriptors (caller sets that) */+sys_close(fds[which]);+sys_close(fds[1-which]);+}else{+returnfile;+}++ret=restore_file_common(ctx,file,ptr);+out:+if(ret<0){+fput(file);+file=ERR_PTR(ret);+}++returnfile;+}+#else+#define pipe_file_checkpoint NULL+#endif /* CONFIG_CHECKPOINT */+/**Thefile_operationsstructsarenotstaticbecausethey*arealsousedinlinux/fs/fifo.ctodooperationsonFIFOs.
--
1.6.0.4
--
To unsubscribe from this list: send the line "unsubscribe linux-api" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
From: Sukadev Bhattiprolu <redacted>
To implement support for clone_with_pids() system call we would
need to allocate pidmap page in more than one place. Move this
code to a new function alloc_pidmap_page().
Changelog[v2]:
- (Matt Helsley, Dave Hansen) Have alloc_pidmap_page() return
-ENOMEM on error instead of -1.
Signed-off-by: Sukadev Bhattiprolu <redacted>
Acked-by: Serge Hallyn <redacted>
Reviewed-by: Oren Laadan <redacted>
---
kernel/pid.c | 46 ++++++++++++++++++++++++++++++----------------
1 files changed, 30 insertions(+), 16 deletions(-)
@@ -134,21 +159,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);-/*-*Freethepageifsomeoneracedwithus-*installingit:-*/-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)){
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Sukadev Bhattiprolu <redacted>
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.
Signed-off-by: Sukadev Bhattiprolu <redacted>
Acked-by: Serge Hallyn <redacted>
Reviewed-by: Oren Laadan <redacted>
---
kernel/fork.c | 5 +++--
kernel/pid.c | 9 ++++++---
2 files changed, 9 insertions(+), 5 deletions(-)
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Checkpoint of sysvipc message-queues is performed by iterating through
all 'msq' objects and dumping the contents of each one. The message
queued on each 'msq' are dumped with that object.
Message of a specific queue get written one by one. The queue lock
cannot be held while dumping them, but the loop must be protected from
someone (who ?) writing or reading. To do that we grab the lock, then
hijack the entire chain of messages from the queue, drop the lock,
and then safely dump them in a loop. Finally, with the lock held, we
re-attach the chain while verifying that there isn't other (new) data
on that queue.
Writing the message contents themselves is straight forward. The code
is similar to that in ipc/msgutil.c, the main difference being that
we deal with kernel memory and not user memory.
Changelog[v17]:
- Allocate security context for msg_msg
- Restore objects in the right namespace
- Don't unlock ipc before freeing
Signed-off-by: Oren Laadan <redacted>
---
include/linux/checkpoint_hdr.h | 20 +++
ipc/Makefile | 3 +-
ipc/checkpoint.c | 2 +-
ipc/checkpoint_msg.c | 364 ++++++++++++++++++++++++++++++++++++++++
ipc/msg.c | 10 +-
ipc/msgutil.c | 8 -
ipc/util.h | 13 ++
7 files changed, 403 insertions(+), 17 deletions(-)
create mode 100644 ipc/checkpoint_msg.c
@@ -115,11 +115,11 @@ static int do_checkpoint_ipc_ns(struct ckpt_ctx *ctx,ret=checkpoint_ipc_any(ctx,ipc_ns,IPC_SHM_IDS,CKPT_HDR_IPC_SHM,checkpoint_ipc_shm);-#if 0 /* NEXT FEW PATCHES */if(ret<0)returnret;ret=checkpoint_ipc_any(ctx,ipc_ns,IPC_MSG_IDS,CKPT_HDR_IPC_MSG,checkpoint_ipc_msg);+#if 0 /* NEXT FEW PATCHES */if(ret<0)returnret;ret=checkpoint_ipc_any(ctx,ipc_ns,IPC_SEM_IDS,
@@ -0,0 +1,364 @@+/*+*Checkpoint/restart-dumpstateofsysvipcmsg+*+*Copyright(C)2009OrenLaadan+*+*ThisfileissubjecttothetermsandconditionsoftheGNUGeneralPublic+*License.SeethefileCOPYINGinthemaindirectoryoftheLinux+*distributionformoredetails.+*/++/* default debug level for output */+#define CKPT_DFLAG CKPT_DIPC++#include<linux/mm.h>+#include<linux/msg.h>+#include<linux/rwsem.h>+#include<linux/sched.h>+#include<linux/syscalls.h>+#include<linux/nsproxy.h>+#include<linux/security.h>+#include<linux/ipc_namespace.h>++#include"util.h"++#include<linux/checkpoint.h>+#include<linux/checkpoint_hdr.h>++/************************************************************************+*ipccheckpoint+*/++staticintfill_ipc_msg_hdr(structckpt_ctx*ctx,+structckpt_hdr_ipc_msg*h,+structmsg_queue*msq)+{+intret=0;++ipc_lock_by_ptr(&msq->q_perm);++ret=checkpoint_fill_ipc_perms(&h->perms,&msq->q_perm);+if(ret<0)+gotounlock;++h->q_stime=msq->q_stime;+h->q_rtime=msq->q_rtime;+h->q_ctime=msq->q_ctime;+h->q_cbytes=msq->q_cbytes;+h->q_qnum=msq->q_qnum;+h->q_qbytes=msq->q_qbytes;+h->q_lspid=msq->q_lspid;+h->q_lrpid=msq->q_lrpid;++unlock:+ipc_unlock(&msq->q_perm);+ckpt_debug("msg: lspid %d rspid %d qnum %lld qbytes %lld\n",+h->q_lspid,h->q_lrpid,h->q_qnum,h->q_qbytes);++returnret;+}++staticintcheckpoint_msg_contents(structckpt_ctx*ctx,structmsg_msg*msg)+{+structckpt_hdr_ipc_msg_msg*h;+structmsg_msgseg*seg;+inttotal,len;+intret;++h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_IPC_MSG_MSG);+if(!h)+return-ENOMEM;++h->m_type=msg->m_type;+h->m_ts=msg->m_ts;++ret=ckpt_write_obj(ctx,&h->h);+ckpt_hdr_put(ctx,h);+if(ret<0)+returnret;++total=msg->m_ts;+len=min(total,(int)DATALEN_MSG);+ret=ckpt_write_buffer(ctx,(msg+1),len);+if(ret<0)+returnret;++seg=msg->next;+total-=len;++while(total){+len=min(total,(int)DATALEN_SEG);+ret=ckpt_write_buffer(ctx,(seg+1),len);+if(ret<0)+break;+seg=seg->next;+total-=len;+}++returnret;+}++staticintcheckpoint_msg_queue(structckpt_ctx*ctx,structmsg_queue*msq)+{+structlist_headmessages;+structmsg_msg*msg;+intret=-EBUSY;++/*+*Scanningthemsqrequiresthelock,butthenwecan'twrite+*dataoutfrominside.Instead,wegrabthelock,removeall+*messagestoourownlist,dropthelock,writethemessages,+*andfinallyre-attachthethemtothemsqwiththelocktaken.+*/+ipc_lock_by_ptr(&msq->q_perm);+if(!list_empty(&msq->q_receivers))+gotounlock;+if(!list_empty(&msq->q_senders))+gotounlock;+if(list_empty(&msq->q_messages))+gotounlock;+/* temporarily take out all messages */+INIT_LIST_HEAD(&messages);+list_splice_init(&msq->q_messages,&messages);+unlock:+ipc_unlock(&msq->q_perm);++list_for_each_entry(msg,&messages,m_list){+ret=checkpoint_msg_contents(ctx,msg);+if(ret<0)+break;+}++/* put all the messages back in */+ipc_lock_by_ptr(&msq->q_perm);+list_splice(&messages,&msq->q_messages);+ipc_unlock(&msq->q_perm);++returnret;+}++intcheckpoint_ipc_msg(intid,void*p,void*data)+{+structckpt_hdr_ipc_msg*h;+structckpt_ctx*ctx=(structckpt_ctx*)data;+structkern_ipc_perm*perm=(structkern_ipc_perm*)p;+structmsg_queue*msq;+intret;++msq=container_of(perm,structmsg_queue,q_perm);++h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_IPC_MSG);+if(!h)+return-ENOMEM;++ret=fill_ipc_msg_hdr(ctx,h,msq);+if(ret<0)+gotoout;++ret=ckpt_write_obj(ctx,&h->h);+if(ret<0)+gotoout;++if(h->q_qnum)+ret=checkpoint_msg_queue(ctx,msq);+out:+ckpt_hdr_put(ctx,h);+returnret;+}+++/************************************************************************+*ipcrestart+*/++staticintload_ipc_msg_hdr(structckpt_ctx*ctx,+structckpt_hdr_ipc_msg*h,+structmsg_queue*msq)+{+intret=0;++ret=restore_load_ipc_perms(&h->perms,&msq->q_perm);+if(ret<0)+returnret;++ckpt_debug("msq: lspid %d lrpid %d qnum %lld qbytes %lld\n",+h->q_lspid,h->q_lrpid,h->q_qnum,h->q_qbytes);++if(h->q_lspid<0||h->q_lrpid<0)+return-EINVAL;++msq->q_stime=h->q_stime;+msq->q_rtime=h->q_rtime;+msq->q_ctime=h->q_ctime;+msq->q_lspid=h->q_lspid;+msq->q_lrpid=h->q_lrpid;++return0;+}++staticstructmsg_msg*restore_msg_contents_one(structckpt_ctx*ctx,int*clen)+{+structckpt_hdr_ipc_msg_msg*h;+structmsg_msg*msg=NULL;+structmsg_msgseg*seg,**pseg;+inttotal,len;+intret;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_IPC_MSG_MSG);+if(IS_ERR(h))+return(structmsg_msg*)h;++ret=-EINVAL;+if(h->m_type<1)+gotoout;+if(h->m_ts>current->nsproxy->ipc_ns->msg_ctlmax)+gotoout;++total=h->m_ts;+len=min(total,(int)DATALEN_MSG);+msg=kmalloc(sizeof(*msg)+len,GFP_KERNEL);+if(!msg){+ret=-ENOMEM;+gotoout;+}+msg->next=NULL;+pseg=&msg->next;++ret=_ckpt_read_buffer(ctx,(msg+1),len);+if(ret<0)+gotoout;++total-=len;+while(total){+len=min(total,(int)DATALEN_SEG);+seg=kmalloc(sizeof(*seg)+len,GFP_KERNEL);+if(!seg){+ret=-ENOMEM;+gotoout;+}+seg->next=NULL;+*pseg=seg;+pseg=&seg->next;++ret=_ckpt_read_buffer(ctx,(seg+1),len);+if(ret<0)+gotoout;+total-=len;+}++msg->m_type=h->m_type;+msg->m_ts=h->m_ts;+*clen=h->m_ts;+ret=security_msg_msg_alloc(msg);+out:+if(ret<0&&msg){+free_msg(msg);+msg=ERR_PTR(ret);+}+ckpt_hdr_put(ctx,h);+returnmsg;+}++staticinlinevoidfree_msg_list(structlist_head*queue)+{+structmsg_msg*msg,*tmp;++list_for_each_entry_safe(msg,tmp,queue,m_list)+free_msg(msg);+}++staticintrestore_msg_contents(structckpt_ctx*ctx,structlist_head*queue,+unsignedlongqnum,unsignedlong*cbytes)+{+structmsg_msg*msg;+intclen=0;+intret=0;++INIT_LIST_HEAD(queue);++*cbytes=0;+while(qnum--){+msg=restore_msg_contents_one(ctx,&clen);+if(IS_ERR(msg))+gotofail;+list_add_tail(&msg->m_list,queue);+*cbytes+=clen;+}+return0;+fail:+ret=PTR_ERR(msg);+free_msg_list(queue);+returnret;+}++intrestore_ipc_msg(structckpt_ctx*ctx,structipc_namespace*ns)+{+structckpt_hdr_ipc_msg*h;+structkern_ipc_perm*perms;+structmsg_queue*msq;+structipc_ids*msg_ids=&ns->ids[IPC_MSG_IDS];+structlist_headmessages;+unsignedlongcbytes;+intmsgflag;+intret;++INIT_LIST_HEAD(&messages);++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_IPC_MSG);+if(IS_ERR(h))+returnPTR_ERR(h);++ret=-EINVAL;+if(h->perms.id<0)+gotoout;++/* read queued messages into temporary queue */+ret=restore_msg_contents(ctx,&messages,h->q_qnum,&cbytes);+if(ret<0)+gotoout;++ret=-EINVAL;+if(h->q_cbytes!=cbytes)+gotoout;++/* restore the message queue */+msgflag=h->perms.mode|IPC_CREAT|IPC_EXCL;+ckpt_debug("msg: do_msgget key %d flag %#x id %d\n",+h->perms.key,msgflag,h->perms.id);+ret=do_msgget(ns,h->perms.key,msgflag,h->perms.id);+ckpt_debug("msg: do_msgget ret %d\n",ret);+if(ret<0)+gotoout;++down_write(&msg_ids->rw_mutex);++/* we are the sole owners/users of this ipc_ns, it can't go away */+perms=ipc_lock(msg_ids,h->perms.id);+BUG_ON(IS_ERR(perms));/* ipc_ns is private to us */++msq=container_of(perms,structmsg_queue,q_perm);+BUG_ON(!list_empty(&msq->q_messages));/* ipc_ns is private to us */++/* attach queued messages we read before */+list_splice_init(&messages,&msq->q_messages);++/* adjust msq and namespace statistics */+atomic_add(h->q_cbytes,&ns->msg_bytes);+atomic_add(h->q_qnum,&ns->msg_hdrs);+msq->q_cbytes=h->q_cbytes;+msq->q_qbytes=h->q_qbytes;+msq->q_qnum=h->q_qnum;++ret=load_ipc_msg_hdr(ctx,h,msq);++if(ret<0){+ckpt_debug("msq: need to remove (%d)\n",ret);+freeque(ns,perms);+}else+ipc_unlock(perms);+up_write(&msg_ids->rw_mutex);+out:+free_msg_list(&messages);/* no-op if all ok, else cleanup msgs */+ckpt_hdr_put(ctx,h);+returnret;+}
@@ -311,14 +310,11 @@ static inline int msg_security(struct kern_ipc_perm *ipcp, int msgflg)returnsecurity_msg_queue_associate(msq,msgflg);}-intdo_msgget(key_tkey,intmsgflg,intreq_id)+intdo_msgget(structipc_namespace*ns,key_tkey,intmsgflg,intreq_id){-structipc_namespace*ns;structipc_opsmsg_ops;structipc_paramsmsg_params;-ns=current->nsproxy->ipc_ns;-msg_ops.getnew=newque;msg_ops.associate=msg_security;msg_ops.more_checks=NULL;
@@ -331,7 +327,7 @@ int do_msgget(key_t key, int msgflg, int req_id)SYSCALL_DEFINE2(msgget,key_t,key,int,msgflg){-returndo_msgget(key,msgflg,-1);+returndo_msgget(current->nsproxy->ipc_ns,key,msgflg,-1);}staticinlineunsignedlong
@@ -36,14 +36,6 @@ struct ipc_namespace init_ipc_ns = {atomic_tnr_ipc_ns=ATOMIC_INIT(1);-structmsg_msgseg{-structmsg_msgseg*next;-/* the next part of the message follows immediately */-};--#define DATALEN_MSG (PAGE_SIZE-sizeof(struct msg_msg))-#define DATALEN_SEG (PAGE_SIZE-sizeof(struct msg_msgseg))-structmsg_msg*load_msg(constvoid__user*src,intlen){structmsg_msg*msg;
@@ -141,6 +141,14 @@ extern void free_msg(struct msg_msg *msg);externstructmsg_msg*load_msg(constvoid__user*src,intlen);externintstore_msg(void__user*dest,structmsg_msg*msg,intlen);+structmsg_msgseg{+structmsg_msgseg*next;+/* the next part of the message follows immediately */+};++#define DATALEN_MSG (PAGE_SIZE-sizeof(struct msg_msg))+#define DATALEN_SEG (PAGE_SIZE-sizeof(struct msg_msgseg))+externvoidrecompute_msgmni(structipc_namespace*);staticinlineintipc_buildid(intid,intseq)
@@ -182,6 +190,8 @@ int do_shmget(struct ipc_namespace *ns, key_t key, size_t size, int shmflg,intreq_id);voiddo_shm_rmid(structipc_namespace*ns,structkern_ipc_perm*ipcp);+intdo_msgget(structipc_namespace*ns,key_tkey,intmsgflg,intreq_id);+voidfreeque(structipc_namespace*ns,structkern_ipc_perm*ipcp);#ifdef CONFIG_CHECKPOINTexternintcheckpoint_fill_ipc_perms(structckpt_hdr_ipc_perms*h,
@@ -191,6 +201,9 @@ extern int restore_load_ipc_perms(struct ckpt_hdr_ipc_perms *h,externintcheckpoint_ipc_shm(intid,void*p,void*data);externintrestore_ipc_shm(structckpt_ctx*ctx,structipc_namespace*ns);++externintcheckpoint_ipc_msg(intid,void*p,void*data);+externintrestore_ipc_msg(structckpt_ctx*ctx,structipc_namespace*ns);#endif#endif
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
For each fd read 'struct ckpt_hdr_file_desc' and lookup objref in the
hash table; If not found in the hash table, (first occurence), read in
'struct ckpt_hdr_file', create a new file and register in the hash.
Otherwise attach the file pointer from the hash as an FD.
Changelog[v17]:
- Validate f_mode after restore against saved f_mode
- Fail if f_flags have O_CREAT|O_EXCL|O_NOCTTY|O_TRUN
- Reorder patch (move earlier in series)
- Handle shared files_struct objects
Changelog[v14]:
- Introduce a per file-type restore() callback
- Revert change to pr_debug(), back to ckpt_debug()
- Rename: restore_files() => restore_fd_table()
- Rename: ckpt_read_fd_data() => restore_file()
- Check whether calls to ckpt_hbuf_get() fail
- Discard field 'hh->parent'
Changelog[v12]:
- Replace obsolete ckpt_debug() with pr_debug()
Changelog[v6]:
- Balance all calls to ckpt_hbuf_get() with matching ckpt_hbuf_put()
(even though it's not really needed)
Signed-off-by: Oren Laadan <redacted>
---
checkpoint/files.c | 310 ++++++++++++++++++++++++++++++++++++++++++++
checkpoint/objhash.c | 2 +
checkpoint/process.c | 20 +++
include/linux/checkpoint.h | 7 +
4 files changed, 339 insertions(+), 0 deletions(-)
@@ -380,3 +382,311 @@ int ckpt_collect_file_table(struct ckpt_ctx *ctx, struct task_struct *t)returnret;}++/**************************************************************************+*Restart+*/++/**+*restore_open_fname-readafilenameandopenafile+*@ctx:checkpointcontext+*@flags:fileflags+*/+structfile*restore_open_fname(structckpt_ctx*ctx,intflags)+{+structckpt_hdr*h;+structfile*file;+char*fname;++/* prevent bad input from doing bad things */+if(flags&(O_CREAT|O_EXCL|O_NOCTTY|O_TRUNC))+returnERR_PTR(-EINVAL);++h=ckpt_read_buf_type(ctx,PATH_MAX,CKPT_HDR_FILE_NAME);+if(IS_ERR(h))+return(structfile*)h;+fname=(char*)(h+1);+ckpt_debug("fname '%s' flags %#x\n",fname,flags);++file=filp_open(fname,flags,0);+ckpt_hdr_put(ctx,h);++returnfile;+}++staticintclose_all_fds(structfiles_struct*files)+{+int*fdtable;+intnfds;++nfds=scan_fds(files,&fdtable);+if(nfds<0)+returnnfds;+while(nfds--)+sys_close(fdtable[nfds]);+kfree(fdtable);+return0;+}++/**+*attach_file-attachalonelyfileptrtoafiledescriptor+*@file:lonelyfilepointer+*/+staticintattach_file(structfile*file)+{+intfd=get_unused_fd_flags(0);++if(fd>=0){+get_file(file);+fsnotify_open(file->f_path.dentry);+fd_install(fd,file);+}+returnfd;+}++#define CKPT_SETFL_MASK \+(O_APPEND|O_NONBLOCK|O_NDELAY|FASYNC|O_DIRECT|O_NOATIME)++intrestore_file_common(structckpt_ctx*ctx,structfile*file,+structckpt_hdr_file*h)+{+fmode_tnew_mode=(__forcefmode_t)file->f_mode;+fmode_tsaved_mode=(__forcefmode_t)h->f_mode;+intret;++/* FIX: need to restore uid, gid, owner etc */++/* safe to set 1st arg (fd) to 0, as command is F_SETFL */+ret=vfs_fcntl(0,F_SETFL,h->f_flags&CKPT_SETFL_MASK,file);+if(ret<0)+returnret;++/*+*Normallyf_modeissetbyopen,andmodifiedonlyvia+*fcntl(),soitsvaluenowshouldmatchthatatcheckpoint.+*However,afilemaybedowngradedfrom(read-)writeto+*read-only,e.g:+*-mark_files_ro()unsetsFMODE_WRITE+*-nfs4_file_downgrade()too,andalsosertFMODE_READ+*Validatethenewf_modeagainstsavedf_mode,allowing:+*-newwithFMODE_WRITE,savedwithoutFMODE_WRITE+*-newwithoutFMODE_READ,savedwithFMODE_READ+*/+if((new_mode&FMODE_WRITE)&&!(saved_mode&FMODE_WRITE)){+new_mode&=~FMODE_WRITE;+if(!(new_mode&FMODE_READ)&&(saved_mode&FMODE_READ))+new_mode|=FMODE_READ;+}+/* finally, at this point new mode should match saved mode */+if(new_mode^saved_mode)+return-EINVAL;++if(file->f_mode&FMODE_LSEEK)+ret=vfs_llseek(file,h->f_pos,SEEK_SET);++returnret;+}++staticstructfile*generic_file_restore(structckpt_ctx*ctx,+structckpt_hdr_file*ptr)+{+structfile*file;+intret;++if(ptr->h.type!=CKPT_HDR_FILE||+ptr->h.len!=sizeof(*ptr)||ptr->f_type!=CKPT_FILE_GENERIC)+returnERR_PTR(-EINVAL);++file=restore_open_fname(ctx,ptr->f_flags);+if(IS_ERR(file))+returnfile;++ret=restore_file_common(ctx,file,ptr);+if(ret<0){+fput(file);+file=ERR_PTR(ret);+}+returnfile;+}++structrestore_file_ops{+char*file_name;+enumfile_typefile_type;+structfile*(*restore)(structckpt_ctx*ctx,+structckpt_hdr_file*ptr);+};++staticstructrestore_file_opsrestore_file_ops[]={+/* ignored file */+{+.file_name="IGNORE",+.file_type=CKPT_FILE_IGNORE,+.restore=NULL,+},+/* regular file/directory */+{+.file_name="GENERIC",+.file_type=CKPT_FILE_GENERIC,+.restore=generic_file_restore,+},+};++staticstructfile*do_restore_file(structckpt_ctx*ctx)+{+structrestore_file_ops*ops;+structckpt_hdr_file*h;+structfile*file=ERR_PTR(-EINVAL);++/*+*All'structckpt_hdr_file_...'beginwithckpt_hdr_file,+*buttheactualobjectdependsonthefiletype.Thelength+*shouldneverbemorethanpage.+*/+h=ckpt_read_buf_type(ctx,PAGE_SIZE,CKPT_HDR_FILE);+if(IS_ERR(h))+return(structfile*)h;+ckpt_debug("flags %#x mode %#x type %d\n",+h->f_flags,h->f_mode,h->f_type);++if(h->f_type>=CKPT_FILE_MAX)+gotoout;++ops=&restore_file_ops[h->f_type];+BUG_ON(ops->file_type!=h->f_type);++if(ops->restore)+file=ops->restore(ctx,h);+out:+ckpt_hdr_put(ctx,h);+returnfile;+}++/* restore callback for file pointer */+void*restore_file(structckpt_ctx*ctx)+{+return(void*)do_restore_file(ctx);+}++/**+*ckpt_read_file_desc-restorethestateofagivenfiledescriptor+*@ctx:checkpointcontext+*+*Restoresthestateofafiledescriptor;looksuptheobjref(inthe+*header)inthehashtable,andiffoundpicksthematchingfileand+*useit;otherwisecallsrestore_filetorestorethefiletoo.+*/+staticintrestore_file_desc(structckpt_ctx*ctx)+{+structckpt_hdr_file_desc*h;+structfile*file;+intnewfd,ret;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_FILE_DESC);+if(IS_ERR(h))+returnPTR_ERR(h);+ckpt_debug("ref %d fd %d c.o.e %d\n",+h->fd_objref,h->fd_descriptor,h->fd_close_on_exec);++ret=-EINVAL;+if(h->fd_objref<=0||h->fd_descriptor<0)+gotoout;++file=ckpt_obj_fetch(ctx,h->fd_objref,CKPT_OBJ_FILE);+if(IS_ERR(file)){+ret=PTR_ERR(file);+gotoout;+}++newfd=attach_file(file);+if(newfd<0){+ret=newfd;+gotoout;+}++ckpt_debug("newfd got %d wanted %d\n",newfd,h->fd_descriptor);++/* reposition if newfd isn't desired fd */+if(newfd!=h->fd_descriptor){+ret=sys_dup2(newfd,h->fd_descriptor);+if(ret<0)+gotoout;+sys_close(newfd);+}++if(h->fd_close_on_exec)+set_close_on_exec(h->fd_descriptor,1);++ret=0;+out:+ckpt_hdr_put(ctx,h);+returnret;+}++/* restore callback for file table */+staticstructfiles_struct*do_restore_file_table(structckpt_ctx*ctx)+{+structckpt_hdr_file_table*h;+structfiles_struct*files;+inti,ret;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_FILE_TABLE);+if(IS_ERR(h))+return(structfiles_struct*)h;++ckpt_debug("nfds %d\n",h->fdt_nfds);++ret=-EMFILE;+if(h->fdt_nfds<0||h->fdt_nfds>sysctl_nr_open)+gotoout;++/*+*Weassumethatrestartingtasks,ascreatedinuser-space,+*havedistinctfiles_structobjectseach.Ifnot,weneedto+*calldup_fd()tomakesurewedon'toverwriteanalready+*restoredone.+*/++/* point of no return -- close all file descriptors */+ret=close_all_fds(current->files);+if(ret<0)+gotoout;++for(i=0;i<h->fdt_nfds;i++){+ret=restore_file_desc(ctx);+if(ret<0)+break;+}+out:+ckpt_hdr_put(ctx,h);+if(!ret){+files=current->files;+atomic_inc(&files->count);+}else{+files=ERR_PTR(ret);+}+returnfiles;+}++void*restore_file_table(structckpt_ctx*ctx)+{+return(void*)do_restore_file_table(ctx);+}++intrestore_obj_file_table(structckpt_ctx*ctx,intfiles_objref)+{+structfiles_struct*files;++files=ckpt_obj_fetch(ctx,files_objref,CKPT_OBJ_FILE_TABLE);+if(IS_ERR(files))+returnPTR_ERR(files);++if(files!=current->files){+task_lock(current);+put_files_struct(current->files);+current->files=files;+task_unlock(current);+atomic_inc(&files->count);+}++return0;+}
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
@@ -19,6 +19,7 @@ struct file_ra_state;structuser_struct;structwriteback_control;structrlimit;+structckpt_ctx;#ifndef CONFIG_DISCONTIGMEM /* Don't use mapnrs, do it properly */externunsignedlongmax_mapnr;
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
The bulk of the work is in ckpt_read_vma(), which has been refactored:
the part that create the suitable 'struct file *' for the mapping is
now larger and moved to a separate function. What's left is to read
the VMA description, get the file pointer, create the mapping, and
proceed to read the contents in.
Both anonymous shared VMAs that have been read earlier (as indicated
by a look up to objhash) and file-mapped shared VMAs are skipped.
Anonymous shared VMAs seen for the first time have their contents
read in directly to the backing inode, as indexed by the page numbers
(as opposed to virtual addresses).
Changelog[v14]:
- Introduce patch
Signed-off-by: Oren Laadan <redacted>
---
checkpoint/memory.c | 66 ++++++++++++++++++++++++++++++++-----------
include/linux/checkpoint.h | 6 ++++
include/linux/mm.h | 2 +
mm/filemap.c | 13 ++++++++-
mm/shmem.c | 49 ++++++++++++++++++++++++++++++++
5 files changed, 118 insertions(+), 18 deletions(-)
@@ -877,14 +905,15 @@ static int read_pages_contents(struct ckpt_ctx *ctx)}out:-up_read(&mm->mmap_sem);+up_read(¤t->mm->mmap_sem);kfree(buf);return0;}/**-*restore_memory_contents-restorecontentsofaVMAwithprivatememory+*restore_memory_contents-restorecontentsofamemoryregion*@ctx-restartcontext+*@inode-backinginode**Readsaheaderthatspecifieshowmanypageswillfollow,thenreads*alistofvirtualaddressesintoctx->pgarr_listpage-arraychain,
@@ -892,7 +921,7 @@ static int read_pages_contents(struct ckpt_ctx *ctx)*thesestepsuntilreachingaheaderspecifying"0"pages,whichmarks*theendofthecontents.*/-staticintrestore_memory_contents(structckpt_ctx*ctx)+intrestore_memory_contents(structckpt_ctx*ctx,structinode*inode){structckpt_hdr_pgarr*h;unsignedlongnr_pages;
@@ -919,7 +948,7 @@ static int restore_memory_contents(struct ckpt_ctx *ctx)ret=read_pages_vaddrs(ctx,nr_pages);if(ret<0)break;-ret=read_pages_contents(ctx);+ret=read_pages_contents(ctx,inode);if(ret<0)break;pgarr_reset_all(ctx);
@@ -977,9 +1006,9 @@ static unsigned long calc_map_flags_bits(unsigned long orig_vm_flags)*@file-filetomap(NULLforanonymous)*@h-vmaheaderdata*/-staticunsignedlonggeneric_vma_restore(structmm_struct*mm,-structfile*file,-structckpt_hdr_vma*h)+unsignedlonggeneric_vma_restore(structmm_struct*mm,+structfile*file,+structckpt_hdr_vma*h){unsignedlongvm_size,vm_start,vm_flags,vm_prot,vm_pgoff;unsignedlongaddr;
@@ -2406,6 +2406,55 @@ static int shmem_checkpoint(struct ckpt_ctx *ctx, struct vm_area_struct *vma)returnshmem_vma_checkpoint(ctx,vma,vma_type,ino_objref);}++intshmem_restore(structckpt_ctx*ctx,+structmm_struct*mm,structckpt_hdr_vma*h)+{+unsignedlongaddr;+structfile*file;+intret=0;++file=ckpt_obj_fetch(ctx,h->ino_objref,CKPT_OBJ_FILE);+if(PTR_ERR(file)==-EINVAL)+file=NULL;+if(IS_ERR(file))+returnPTR_ERR(file);++/* if file is NULL, this is the premiere - create and insert */+if(!file){+if(h->vma_type!=CKPT_VMA_SHM_ANON)+return-EINVAL;+/*+*intheorycouldpassNULLtommapandletitcreate+*thefile.But,if'shm_size!=vm_end-vm_start',+*orif'vm_pgoff!=0',thenthevmareflectsonlya+*portionoftheshmobjectandweneedto"manually"+*createthefullshmobject.+*/+file=shmem_file_setup("/dev/zero",h->ino_size,h->vm_flags);+if(IS_ERR(file))+returnPTR_ERR(file);+ret=ckpt_obj_insert(ctx,file,h->ino_objref,CKPT_OBJ_FILE);+if(ret<0)+gotoout;+}else{+if(h->vma_type!=CKPT_VMA_SHM_ANON_SKIP)+return-EINVAL;+/* Already need fput() for the file above; keep path simple */+get_file(file);+}++addr=generic_vma_restore(mm,file,h);+if(IS_ERR((void*)addr))+returnPTR_ERR((void*)addr);++if(h->vma_type==CKPT_VMA_SHM_ANON)+ret=restore_memory_contents(ctx,file->f_dentry->d_inode);+out:+fput(file);+returnret;+}+#endif /* CONFIG_CHECKPOINT */staticvoidinit_once(void*foo)
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Checkpointing of multiple processes works by recording the tasks tree
structure below a given "root" task. The root task is expected to be a
container init, and then an entire container is checkpointed. However,
passing CHECKPOINT_SUBTREE to checkpoint(2) relaxes this requirement
and allows to checkpoint a subtree of processes from the root task.
For a given root task, do a DFS scan of the tasks tree and collect them
into an array (keeping a reference to each task). Using DFS simplifies
the recreation of tasks either in user space or kernel space. For each
task collected, test if it can be checkpointed, and save its pid, tgid,
and ppid.
The actual work is divided into two passes: a first scan counts the
tasks, then memory is allocated and a second scan fills the array.
Whether checkpoints and restarts require CAP_SYS_ADMIN is determined
by sysctl 'ckpt_unpriv_allowed': if 1, then regular permission checks
are intended to prevent privilege escalation, however if 0 it prevents
unprivileged users from exploiting any privilege escalation bugs.
The logic is suitable for creation of processes during restart either
in userspace or by the kernel.
Currently we ignore threads and zombies.
Changelog[v16]:
- CHECKPOINT_SUBTREE flags allows subtree (not whole container)
- sysctl variable 'ckpt_unpriv_allowed' controls needed privileges
Changelog[v14]:
- Refuse non-self checkpoint if target task isn't frozen
- Refuse checkpoint (for now) if task is ptraced
- Revert change to pr_debug(), back to ckpt_debug()
- Use only unsigned fields in checkpoint headers
- Check retval of ckpt_tree_count_tasks() in ckpt_build_tree()
- Discard 'h.parent' field
- Check whether calls to ckpt_hbuf_get() fail
- Disallow threads or siblings to container init
Changelog[v13]:
- Release tasklist_lock in error path in ckpt_tree_count_tasks()
- Use separate index for 'tasks_arr' and 'hh' in ckpt_write_pids()
Changelog[v12]:
- Replace obsolete ckpt_debug() with pr_debug()
Signed-off-by: Oren Laadan <redacted>
---
checkpoint/checkpoint.c | 287 ++++++++++++++++++++++++++++++++++++--
checkpoint/restart.c | 2 +-
checkpoint/sys.c | 33 ++++-
include/linux/checkpoint.h | 14 ++
include/linux/checkpoint_hdr.h | 16 ++-
include/linux/checkpoint_types.h | 4 +
kernel/sysctl.c | 17 +++
7 files changed, 357 insertions(+), 16 deletions(-)
@@ -259,8 +259,27 @@ static int checkpoint_write_tail(struct ckpt_ctx *ctx)returnret;}+/* dump all tasks in ctx->tasks_arr[] */+staticintcheckpoint_all_tasks(structckpt_ctx*ctx)+{+intn,ret=0;++for(n=0;n<ctx->nr_tasks;n++){+ckpt_debug("dumping task #%d\n",n);+ret=checkpoint_task(ctx,ctx->tasks_arr[n]);+if(ret<0)+break;+}++returnret;+}+staticintmay_checkpoint_task(structckpt_ctx*ctx,structtask_struct*t){+structtask_struct*root=ctx->root_task;++ckpt_debug("check %d\n",task_pid_nr_ns(t,ctx->root_nsproxy->pid_ns));+if(t->state==TASK_DEAD){pr_warning("c/r: task %d is TASK_DEAD\n",task_pid_vnr(t));return-EAGAIN;
@@ -286,15 +305,256 @@ static int may_checkpoint_task(struct ckpt_ctx *ctx, struct task_struct *t)return-EBUSY;}+/*+*FIX:fornow,disallowsiblingsofcontainerinitcreated+*viaCLONE_PARENT(uncleariftheywillremainpossible)+*/+if(ctx->root_init&&t!=root&&t->tgid!=root->tgid&&+t->real_parent==root->real_parent){+__ckpt_write_err(ctx,"task %d (%s) is sibling of root",+task_pid_vnr(t),t->comm);+return-EINVAL;+}++/* FIX: change this when namespaces are added */+if(task_nsproxy(t)!=ctx->root_nsproxy)+return-EPERM;+return0;}+#define CKPT_HDR_PIDS_CHUNK 256++staticintcheckpoint_pids(structckpt_ctx*ctx)+{+structckpt_hdr_pids*h;+structpid_namespace*ns;+structtask_struct*task;+structtask_struct**tasks_arr;+intnr_tasks,n,pos=0,ret=0;++ns=ctx->root_nsproxy->pid_ns;+tasks_arr=ctx->tasks_arr;+nr_tasks=ctx->nr_tasks;+BUG_ON(nr_tasks<=0);++ret=ckpt_write_obj_type(ctx,NULL,+sizeof(*h)*nr_tasks,+CKPT_HDR_BUFFER);+if(ret<0)+returnret;++h=ckpt_hdr_get(ctx,sizeof(*h)*CKPT_HDR_PIDS_CHUNK);+if(!h)+return-ENOMEM;++do{+rcu_read_lock();+for(n=0;n<min(nr_tasks,CKPT_HDR_PIDS_CHUNK);n++){+task=tasks_arr[pos];++h[n].vpid=task_pid_nr_ns(task,ns);+h[n].vtgid=task_tgid_nr_ns(task,ns);+h[n].vpgid=task_pgrp_nr_ns(task,ns);+h[n].vsid=task_session_nr_ns(task,ns);+h[n].vppid=task_tgid_nr_ns(task->real_parent,ns);+ckpt_debug("task[%d]: vpid %d vtgid %d parent %d\n",+pos,h[n].vpid,h[n].vtgid,h[n].vppid);+pos++;+}+rcu_read_unlock();++n=min(nr_tasks,CKPT_HDR_PIDS_CHUNK);+ret=ckpt_kwrite(ctx,h,n*sizeof(*h));+if(ret<0)+break;++nr_tasks-=n;+}while(nr_tasks>0);++_ckpt_hdr_put(ctx,h,sizeof(*h)*CKPT_HDR_PIDS_CHUNK);+returnret;+}++/* count number of tasks in tree (and optionally fill pid's in array) */+staticinttree_count_tasks(structckpt_ctx*ctx)+{+structtask_struct*root;+structtask_struct*task;+structtask_struct*parent;+structtask_struct**tasks_arr=ctx->tasks_arr;+intnr_tasks=ctx->nr_tasks;+intnr=0;+intret;++read_lock(&tasklist_lock);++/* we hold the lock, so root_task->real_parent can't change */+task=ctx->root_task;+if(ctx->root_init){+/* container-init: start from container parent */+parent=task->real_parent;+root=parent;+}else{+/* non-container-init: start from root task and down */+parent=NULL;+root=task;+}++/* count tasks via DFS scan of the tree */+while(1){+/* is this task cool ? */+ret=may_checkpoint_task(ctx,task);+if(ret<0){+nr=ret;+break;+}+if(tasks_arr){+/* unlikely... but if so then try again later */+if(nr==nr_tasks){+nr=-EAGAIN;/* cleanup in ckpt_ctx_free() */+break;+}+tasks_arr[nr]=task;+get_task_struct(task);+}+nr++;+/* if has children - proceed with child */+if(!list_empty(&task->children)){+parent=task;+task=list_entry(task->children.next,+structtask_struct,sibling);+continue;+}+while(task!=root){+/* if has sibling - proceed with sibling */+if(!list_is_last(&task->sibling,&parent->children)){+task=list_entry(task->sibling.next,+structtask_struct,sibling);+break;+}++/* else, trace back to parent and proceed */+task=parent;+parent=parent->real_parent;+}+if(task==root)+break;+}++read_unlock(&tasklist_lock);++if(nr<0)+ckpt_write_err(ctx,NULL);+returnnr;+}++/*+*build_tree-scanthetaskstreeinDFSorderandfillinarray+*@ctx:checkpointcontext+*+*UsingDFSordersimplifiestherestartlogictore-createthetasks.+*+*Onsuccess,ctx->tasks_arrwillbeallocatedandpopulatedwithall+*tasks(referencetaken),andctx->nr_taskswillholdthetotalcount.+*Thearrayiscleanedupbyckpt_ctx_free().+*/+staticintbuild_tree(structckpt_ctx*ctx)+{+intn,m;++/* count tasks (no side effects) */+n=tree_count_tasks(ctx);+if(n<0)+returnn;++ctx->nr_tasks=n;+ctx->tasks_arr=kzalloc(n*sizeof(*ctx->tasks_arr),GFP_KERNEL);+if(!ctx->tasks_arr)+return-ENOMEM;++/* count again (now will fill array) */+m=tree_count_tasks(ctx);++/* unlikely, but ... (cleanup in ckpt_ctx_free) */+if(m<0)+returnm;+elseif(m!=n)+return-EBUSY;++return0;+}++/* dump the array that describes the tasks tree */+staticintcheckpoint_tree(structckpt_ctx*ctx)+{+structckpt_hdr_tree*h;+intret;++h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_TREE);+if(!h)+return-ENOMEM;++h->nr_tasks=ctx->nr_tasks;++ret=ckpt_write_obj(ctx,&h->h);+ckpt_hdr_put(ctx,h);+if(ret<0)+returnret;++ret=checkpoint_pids(ctx);+returnret;+}++staticstructtask_struct*get_freezer_task(structtask_struct*root_task)+{+structtask_struct*p;++/*+*Forthedurationofcheckpointwedeep-freezealltasks.+*Normallydoitthroughtheroottask'sfreezercgroup.+*However,iftheroottaskisalsothecurrenttask(doing+*self-checkpoint)wecan'tfreezeourselves.Inthiscase,+*choosethenextavailable(non-dead)taskinstead.We'll+*useitsfreezercgrouptoverifythatalltasksbelongto+*thesamecgroup.+*/++if(root_task!=current){+get_task_struct(root_task);+returnroot_task;+}++/* search among threads, then children */+read_lock(&tasklist_lock);++for(p=next_thread(root_task);p!=root_task;p=next_thread(p)){+if(p->state==TASK_DEAD)+continue;+if(!in_same_cgroup_freezer(p,root_task))+gotoout;+}++list_for_each_entry(p,&root_task->children,sibling){+if(p->state==TASK_DEAD)+continue;+if(!in_same_cgroup_freezer(p,root_task))+gotoout;+}++p=NULL;+out:+read_unlock(&tasklist_lock);+if(p)+get_task_struct(p);+returnp;+}+/* setup checkpoint-specific parts of ctx */staticintinit_checkpoint_ctx(structckpt_ctx*ctx,pid_tpid){structtask_struct*task;structnsproxy*nsproxy;-intret;/**Noneedforexplicitcleanuphere,becauseifanerror
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Matt Helsley <redacted>
When the cgroup freezer is used to freeze tasks we do not want to thaw
those tasks during resume. Currently we test the cgroup freezer
state of the resuming tasks to see if the cgroup is FROZEN. If so
then we don't thaw the task. However, the FREEZING state also indicates
that the task should remain frozen.
This also avoids a problem pointed out by Oren Ladaan: the freezer state
transition from FREEZING to FROZEN is updated lazily when userspace reads
or writes the freezer.state file in the cgroup filesystem. This means that
resume will thaw tasks in cgroups which should be in the FROZEN state if
there is no read/write of the freezer.state file to trigger this
transition before suspend.
NOTE: Another "simple" solution would be to always update the cgroup
freezer state during resume. However it's a bad choice for several reasons:
Updating the cgroup freezer state is somewhat expensive because it requires
walking all the tasks in the cgroup and checking if they are each frozen.
Worse, this could easily make resume run in N^2 time where N is the number
of tasks in the cgroup. Finally, updating the freezer state from this code
path requires trickier locking because of the way locks must be ordered.
Instead of updating the freezer state we rely on the fact that lazy
updates only manage the transition from FREEZING to FROZEN. We know that
a cgroup with the FREEZING state may actually be FROZEN so test for that
state too. This makes sense in the resume path even for partially-frozen
cgroups -- those that really are FREEZING but not FROZEN.
Reported-by: Oren Ladaan <redacted>
Signed-off-by: Matt Helsley <redacted>
Cc: Cedric Le Goater <redacted>
Cc: Paul Menage <redacted>
Cc: Li Zefan <redacted>
Cc: Rafael J. Wysocki <redacted>
Cc: Pavel Machek <redacted>
Cc: linux-pm@lists.linux-foundation.org
Seems like a candidate for -stable.
---
include/linux/freezer.h | 7 +++++--
kernel/cgroup_freezer.c | 9 ++++++---
kernel/power/process.c | 2 +-
3 files changed, 12 insertions(+), 6 deletions(-)
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Serge E. Hallyn <redacted>
When restarting tasks, we want to be able to change xuid and
xgid in a struct cred, and do so with security checks. Break
the core functionality of set{fs,res}{u,g}id into cred_setX
which performs the access checks based on current_cred(),
but performs the requested change on a passed-in cred.
This will allow us to securely construct struct creds based
on a checkpoint image, constrained by the caller's permissions,
and apply them to the caller at the end of sys_restart().
Signed-off-by: Serge E. Hallyn <redacted>
---
include/linux/cred.h | 8 +++
kernel/cred.c | 114 ++++++++++++++++++++++++++++++++++++++++++
kernel/sys.c | 134 ++++++++------------------------------------------
3 files changed, 143 insertions(+), 113 deletions(-)
@@ -21,6 +21,9 @@ struct user_struct;structcred;structinode;+/* defined in sys.c, used in cred_setresuid */+externintset_user(structcred*new);+/**COWSupplementarygroupslist*/
@@ -344,4 +347,9 @@ do { \*(_fsgid)=__cred->fsgid;\}while(0)+intcred_setresuid(structcred*new,uid_truid,uid_teuid,uid_tsuid);+intcred_setresgid(structcred*new,gid_trgid,gid_tegid,gid_tsgid);+intcred_setfsuid(structcred*new,uid_tuid,uid_t*old_fsuid);+intcred_setfsgid(structcred*new,gid_tgid,gid_t*old_fsgid);+#endif /* _LINUX_CRED_H */
@@ -559,11 +559,12 @@ error:/**changetheuserstructinacredentialssettomatchthenewUID*/-staticintset_user(structcred*new)+intset_user(structcred*new){structuser_struct*new_user;-new_user=alloc_uid(current_user_ns(),new->uid);+/* is this ok? */+new_user=alloc_uid(new->user->user_ns,new->uid);if(!new_user)return-EAGAIN;
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Serge E. Hallyn <redacted>
Break out the core function which checks privilege and (if
allowed) creates a new user namespace, with the passed-in
creating user_struct. Note that a user_namespace, unlike
other namespace pointers, is not stored in the nsproxy.
Rather it is purely a property of user_structs.
This will let us keep the task restore code simpler.
Signed-off-by: Serge E. Hallyn <redacted>
---
include/linux/user_namespace.h | 8 ++++++
kernel/user_namespace.c | 53 ++++++++++++++++++++++++++++------------
2 files changed, 45 insertions(+), 16 deletions(-)
@@ -27,7 +20,7 @@ int create_user_ns(struct cred *new)ns=kmalloc(sizeof(structuser_namespace),GFP_KERNEL);if(!ns)-return-ENOMEM;+returnERR_PTR(-ENOMEM);kref_init(&ns->kref);
@@ -38,12 +31,43 @@ int create_user_ns(struct cred *new)root_user=alloc_uid(ns,0);if(!root_user){kfree(ns);-return-ENOMEM;+returnERR_PTR(-ENOMEM);}/* set the new root user in the credentials under preparation */-ns->creator=new->user;-new->user=root_user;+ns->creator=creator;++/* alloc_uid() incremented the userns refcount. Just set it to 1 */+kref_set(&ns->kref,1);++*newroot=root_user;+returnns;+}++structuser_namespace*new_user_ns(structuser_struct*creator,+structuser_struct**newroot)+{+if(!capable(CAP_SYS_ADMIN))+returnERR_PTR(-EPERM);+return_new_user_ns(creator,newroot);+}++/*+*Createanewusernamespace,derivingthecreatorfromtheuserinthe+*passedcredentials,andreplacingthatuserwiththenewrootuserforthe+*newnamespace.+*+*Thisiscalledbycopy_creds(),whichwillfinishsettingthetargettask's+*credentials.+*/+intcreate_user_ns(structcred*new)+{+structuser_namespace*ns;++ns=new_user_ns(new->user,&new->user);+if(IS_ERR(ns))+returnPTR_ERR(ns);+new->uid=new->euid=new->suid=new->fsuid=0;new->gid=new->egid=new->sgid=new->fsgid=0;put_group_info(new->group_info);
@@ -54,9 +78,6 @@ int create_user_ns(struct cred *new)#endif/* tgcred will be cleared in our caller bc CLONE_THREAD won't be set */-/* alloc_uid() incremented the userns refcount. Just set it to 1 */-kref_set(&ns->kref,1);-return0;}
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Sukadev Bhattiprolu <redacted>
With support for setting a specific pid number for a process,
alloc_pidmap() will need a paramter a 'target_pid' parameter.
Changelog[v2]:
- (Serge Hallyn) Check for 'pid < 0' in set_pidmap().(Code
actually checks for 'pid <= 0' for completeness).
Signed-off-by: Sukadev Bhattiprolu <redacted>
Acked-by: Serge Hallyn <redacted>
Reviewed-by: Oren Laadan <redacted>
---
kernel/pid.c | 28 ++++++++++++++++++++++++++--
1 files changed, 26 insertions(+), 2 deletions(-)
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
To restore zombie's we will create the a task, that, on its turn to
run, calls do_exit(). Unlike normal tasks that exit, we need to
prevent notification side effects that send signals to other
processes, e.g. parent (SIGCHLD) or child tasks (per child's request).
There are three main cases for such notifications:
1) do_notify_parent(): parent of a process is notified about a change
in status (e.g. become zombie, reparent, etc). If parent ignores,
then mark child for immediate release (skip zombie).
2) kill_orphan_pgrp(): a process group that becomes orphaned will
signal stopped jobs (HUP then CONT).
3) reparent_thread(): children of a process are signaled (per request)
with p->pdeath_signal
Remember that restoring signal state (for any restarting task) must
complete _before_ it is allowed to resume execution, and not during
the resume. Otherwise, a running task may send a signal to another
task that hasn't restored yet, so the new signal will be lost
soon-after.
I considered two possible way to address this:
1. Add another sync point to restart: all tasks will first restore
their state without signals (all signals blocked), and zombies call
do_exit(). A sync point then will ensure that all zombies are gone and
their effects done. Then all tasks restore their signal state (and
mask), and sync (new point) again. Only then they may resume
execution.
The main disadvantage is the added complexity and inefficiency,
for no good reason.
2. Introduce PF_RESTARTING: mark all restarting tasks with a new flag,
and teach the above three notifications to skip sending the signal if
theis flag is set.
The main advantage is simplicity and completeness. Also, such a flag
may to be useful later on. This the method implemented.
Signed-off-by: Oren Laadan <redacted>
---
kernel/exit.c | 7 ++++++-
kernel/signal.c | 4 ++++
2 files changed, 10 insertions(+), 1 deletions(-)
@@ -299,6 +299,10 @@ kill_orphaned_pgrp(struct task_struct *tsk, struct task_struct *parent)structpid*pgrp=task_pgrp(tsk);structtask_struct*ignored_task=tsk;+/* restarting zombie doesn't trigger signals */+if(tsk->flags&PF_RESTARTING)+return;+if(!parent)/* exit: our father is in a different pgrp than*weareandweweretheonlyconnectionoutside.
@@ -1413,6 +1413,10 @@ int do_notify_parent(struct task_struct *tsk, int sig)BUG_ON(!task_ptrace(tsk)&&(tsk->group_leader!=tsk||!thread_group_empty(tsk)));+/* restarting zombie doesn't notify parent */+if(tsk->flags&PF_RESTARTING)+returnret;+info.si_signo=sig;info.si_errno=0;/*
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Matt Helsley <redacted>
These lists record which futexes the task holds. To keep the overhead of
robust futexes low the list is kept in userspace. When the task exits the
kernel carefully walks these lists to recover held futexes that
other tasks may be attempting to acquire with FUTEX_WAIT.
Because they point to userspace memory that is saved/restored by
checkpoint/restart saving the list pointers themselves is safe.
While saving the pointers is safe during checkpoint, restart is tricky
because the robust futex ABI contains provisions for changes based on
checking the size of the list head. So we need to save the length of
the list head too in order to make sure that the kernel used during
restart is capable of handling that ABI. Since there is only one ABI
supported at the moment taking the list head's size is simple. Should
the ABI change we will need to use the same size as specified during
sys_set_robust_list() and hence some new means of determining the length
of this userspace structure in sys_checkpoint would be required.
Rather than rewrite the logic that checks and handles the ABI we reuse
sys_set_robust_list() by factoring out the body of the function and
calling it during restart.
Signed-off-by: Matt Helsley <redacted>
[orenl@cs.columbia.edu: move save/restore code to checkpoint/process.c]
---
checkpoint/process.c | 48 ++++++++++++++++++++++++++++++++++++++++
include/linux/checkpoint_hdr.h | 5 ++++
include/linux/compat.h | 3 +-
include/linux/futex.h | 1 +
kernel/futex.c | 19 ++++++++++-----
kernel/futex_compat.c | 13 ++++++++--
6 files changed, 78 insertions(+), 11 deletions(-)
@@ -18,6 +18,52 @@#include<linux/checkpoint.h>#include<linux/checkpoint_hdr.h>++#ifdef CONFIG_FUTEX+staticvoidsave_task_robust_futex_list(structckpt_hdr_task*h,+structtask_struct*t)+{+/*+*Theseare__userpointersandthuscanbesavedwithout+*theobjhash.+*/+h->robust_futex_list=(unsignedlong)t->robust_list;+h->robust_futex_head_len=sizeof(*t->robust_list);+#ifdef CONFIG_COMPAT+h->compat_robust_futex_list=ptr_to_compat(t->compat_robust_list);+h->compat_robust_futex_head_len=sizeof(*t->compat_robust_list);+#endif+}++staticvoidrestore_task_robust_futex_list(structckpt_hdr_task*h)+{+/* Since we restore the memory map the address remains the same and+*thisissafe.Thisisthesameas[compat_]sys_set_robust_list()*/+if(h->robust_futex_list){+structrobust_list_head__user*rfl;+rfl=(void__user*)(unsignedlong)h->robust_futex_list;+do_set_robust_list(rfl,h->robust_futex_head_len);+}+#ifdef CONFIG_COMPAT+if(h->compat_robust_futex_list){+structcompat_robust_list_head__user*crfl;+crfl=compat_ptr(h->compat_robust_futex_list);+do_compat_set_robust_list(crfl,h->compat_robust_futex_head_len);+}+#endif+}+#else /* !CONFIG_FUTEX */+staticinlinevoidsave_task_robust_futex_list(structckpt_hdr_task*h,+structtask_struct*t)+{+}++staticinlinevoidrestore_task_robust_futex_list(structckpt_hdr_task*h)+{+}+#endif /* CONFIG_FUTEX */++/************************************************************************Checkpoint*/
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Restoring the memory address space begins with nuking the existing one
of the current process, and then reading the vma state and contents.
Call do_mmap_pgoffset() for each vma and then read in the data.
Changelog[v17]:
- Restore mm->{flags,def_flags,saved_auxv}
- Fix bogus warning in do_restore_mm()
Changelog[v16]:
- Restore mm->exe_file
Changelog[v14]:
- Introduce per vma-type restore() function
- Merge restart code into same file as checkpoint (memory.c)
- Compare saved 'vdso' field of mm_context with current value
- Check whether calls to ckpt_hbuf_get() fail
- Discard field 'h->parent'
- Revert change to pr_debug(), back to ckpt_debug()
Changelog[v13]:
- Avoid access to hh->vma_type after the header is freed
- Test for no vma's in exit_mmap() before calling unmap_vma() (or it
may crash if restart fails after having removed all vma's)
Changelog[v12]:
- Replace obsolete ckpt_debug() with pr_debug()
Changelog[v9]:
- Introduce ckpt_ctx_checkpoint() for checkpoint-specific ctx setup
Changelog[v7]:
- Fix argument given to kunmap_atomic() in memory dump/restore
Changelog[v6]:
- Balance all calls to ckpt_hbuf_get() with matching ckpt_hbuf_put()
(even though it's not really needed)
Changelog[v5]:
- Improve memory restore code (following Dave Hansen's comments)
- Change dump format (and code) to allow chunks of <vaddrs, pages>
instead of one long list of each
- Memory restore now maps user pages explicitly to copy data into them,
instead of reading directly to user space; got rid of mprotect_fixup()
Changelog[v4]:
- Use standard list_... for ckpt_pgarr
Signed-off-by: Oren Laadan <redacted>
---
arch/x86/include/asm/ldt.h | 7 +
arch/x86/mm/checkpoint.c | 64 ++++++
checkpoint/memory.c | 472 ++++++++++++++++++++++++++++++++++++++++
checkpoint/objhash.c | 1 +
checkpoint/process.c | 3 +
checkpoint/restart.c | 4 +
fs/exec.c | 2 +-
include/linux/checkpoint.h | 7 +
include/linux/checkpoint_hdr.h | 2 +-
include/linux/mm.h | 13 +
mm/filemap.c | 19 ++
mm/mmap.c | 23 ++-
12 files changed, 614 insertions(+), 3 deletions(-)
@@ -686,3 +689,472 @@ int ckpt_collect_mm(struct ckpt_ctx *ctx, struct task_struct *t)returnret;}++/***********************************************************************+*Restart+*+*Unlikecheckpoint,restartisexecutedinthecontextofeachrestarting+*process:vmaregionsarerestoredviaacalltommap(),andthedatais+*readintotheaddressspaceofthecurrentprocess.+*/++/**+*read_pages_vaddrs-readaddressesofpagestopage-arraychain+*@ctx-restartcontext+*@nr_pages-numberofaddresstoread+*/+staticintread_pages_vaddrs(structckpt_ctx*ctx,unsignedlongnr_pages)+{+structckpt_pgarr*pgarr;+unsignedlong*vaddrp;+intnr,ret;++while(nr_pages){+pgarr=pgarr_current(ctx);+if(!pgarr)+return-ENOMEM;+nr=pgarr_nr_free(pgarr);+if(nr>nr_pages)+nr=nr_pages;+vaddrp=&pgarr->vaddrs[pgarr->nr_used];+ret=ckpt_kread(ctx,vaddrp,nr*sizeof(unsignedlong));+if(ret<0)+returnret;+pgarr->nr_used+=nr;+nr_pages-=nr;+}+return0;+}++staticintrestore_read_page(structckpt_ctx*ctx,structpage*page,void*p)+{+void*ptr;+intret;++ret=ckpt_kread(ctx,p,PAGE_SIZE);+if(ret<0)+returnret;++ptr=kmap_atomic(page,KM_USER1);+memcpy(ptr,p,PAGE_SIZE);+kunmap_atomic(ptr,KM_USER1);++return0;+}++/**+*read_pages_contents-readindataofpagesinpage-arraychain+*@ctx-restartcontext+*/+staticintread_pages_contents(structckpt_ctx*ctx)+{+structmm_struct*mm=current->mm;+structckpt_pgarr*pgarr;+unsignedlong*vaddrs;+char*buf;+inti,ret=0;++buf=kmalloc(PAGE_SIZE,GFP_KERNEL);+if(!buf)+return-ENOMEM;++down_read(&mm->mmap_sem);+list_for_each_entry_reverse(pgarr,&ctx->pgarr_list,list){+vaddrs=pgarr->vaddrs;+for(i=0;i<pgarr->nr_used;i++){+structpage*page;++_ckpt_debug(CKPT_DPAGE,"got page %#lx\n",vaddrs[i]);+ret=get_user_pages(current,mm,vaddrs[i],+1,1,1,&page,NULL);+if(ret<0)+gotoout;++ret=restore_read_page(ctx,page,buf);+page_cache_release(page);++if(ret<0)+gotoout;+}+}++out:+up_read(&mm->mmap_sem);+kfree(buf);+return0;+}++/**+*restore_memory_contents-restorecontentsofaVMAwithprivatememory+*@ctx-restartcontext+*+*Readsaheaderthatspecifieshowmanypageswillfollow,thenreads+*alistofvirtualaddressesintoctx->pgarr_listpage-arraychain,+*followedbytheactualcontentsofthecorrespondingpages.Iterates+*thesestepsuntilreachingaheaderspecifying"0"pages,whichmarks+*theendofthecontents.+*/+staticintrestore_memory_contents(structckpt_ctx*ctx)+{+structckpt_hdr_pgarr*h;+unsignedlongnr_pages;+intlen,ret=0;++while(1){+h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_PGARR);+if(IS_ERR(h))+break;++ckpt_debug("total pages %ld\n",(unsignedlong)h->nr_pages);++nr_pages=h->nr_pages;+ckpt_hdr_put(ctx,h);++if(!nr_pages)+break;++len=nr_pages*(sizeof(unsignedlong)+PAGE_SIZE);+ret=_ckpt_read_buffer(ctx,NULL,len);+if(ret<0)+break;++ret=read_pages_vaddrs(ctx,nr_pages);+if(ret<0)+break;+ret=read_pages_contents(ctx);+if(ret<0)+break;+pgarr_reset_all(ctx);+}++returnret;+}++/**+*calc_map_prot_bits-convertvm_flagstommapprotection+*orig_vm_flags:sourcevm_flags+*/+staticunsignedlongcalc_map_prot_bits(unsignedlongorig_vm_flags)+{+unsignedlongvm_prot=0;++if(orig_vm_flags&VM_READ)+vm_prot|=PROT_READ;+if(orig_vm_flags&VM_WRITE)+vm_prot|=PROT_WRITE;+if(orig_vm_flags&VM_EXEC)+vm_prot|=PROT_EXEC;+if(orig_vm_flags&PROT_SEM)/* only (?) with IPC-SHM */+vm_prot|=PROT_SEM;++returnvm_prot;+}++/**+*calc_map_flags_bits-convertvm_flagstommapflags+*orig_vm_flags:sourcevm_flags+*/+staticunsignedlongcalc_map_flags_bits(unsignedlongorig_vm_flags)+{+unsignedlongvm_flags=0;++vm_flags=MAP_FIXED;+if(orig_vm_flags&VM_GROWSDOWN)+vm_flags|=MAP_GROWSDOWN;+if(orig_vm_flags&VM_DENYWRITE)+vm_flags|=MAP_DENYWRITE;+if(orig_vm_flags&VM_EXECUTABLE)+vm_flags|=MAP_EXECUTABLE;+if(orig_vm_flags&VM_MAYSHARE)+vm_flags|=MAP_SHARED;+else+vm_flags|=MAP_PRIVATE;++returnvm_flags;+}++/**+*generic_vma_restore-restoreavma+*@mm-addressspace+*@file-filetomap(NULLforanonymous)+*@h-vmaheaderdata+*/+staticunsignedlonggeneric_vma_restore(structmm_struct*mm,+structfile*file,+structckpt_hdr_vma*h)+{+unsignedlongvm_size,vm_start,vm_flags,vm_prot,vm_pgoff;+unsignedlongaddr;++if(h->vm_end<h->vm_start)+return-EINVAL;+if(h->vma_objref<0)+return-EINVAL;+if(h->vm_flags&CKPT_VMA_NOT_SUPPORTED)+return-ENOSYS;++vm_start=h->vm_start;+vm_pgoff=h->vm_pgoff;+vm_size=h->vm_end-h->vm_start;+vm_prot=calc_map_prot_bits(h->vm_flags);+vm_flags=calc_map_flags_bits(h->vm_flags);++down_write(&mm->mmap_sem);+addr=do_mmap_pgoff(file,vm_start,vm_size,+vm_prot,vm_flags,vm_pgoff);+up_write(&mm->mmap_sem);+ckpt_debug("size %#lx prot %#lx flag %#lx pgoff %#lx => %#lx\n",+vm_size,vm_prot,vm_flags,vm_pgoff,addr);++returnaddr;+}++/**+*private_vma_restore-readvmadata,recreateitandreadcontents+*@ctx:checkpointcontext+*@mm:memoryaddressspace+*@file:filetouseformapping+*@h-vmaheaderdata+*/+intprivate_vma_restore(structckpt_ctx*ctx,structmm_struct*mm,+structfile*file,structckpt_hdr_vma*h)+{+unsignedlongaddr;++if(h->vm_flags&VM_SHARED)+return-EINVAL;++addr=generic_vma_restore(mm,file,h);+if(IS_ERR((void*)addr))+returnPTR_ERR((void*)addr);++returnrestore_memory_contents(ctx);+}++/**+*anon_private_restore-readvmadata,recreateitandreadcontents+*@ctx:checkpointcontext+*@mm:memoryaddressspace+*@h-vmaheaderdata+*/+staticintanon_private_restore(structckpt_ctx*ctx,+structmm_struct*mm,+structckpt_hdr_vma*h)+{+/*+*vm_pgoffforanonymousmappingisthe"global"page+*offset(namelyfromaddr0x0),soweforceazero+*/+h->vm_pgoff=0;++returnprivate_vma_restore(ctx,mm,NULL,h);+}++/* callbacks to restore vma per its type: */+structrestore_vma_ops{+char*vma_name;+enumvma_typevma_type;+int(*restore)(structckpt_ctx*ctx,+structmm_struct*mm,+structckpt_hdr_vma*ptr);+};++staticstructrestore_vma_opsrestore_vma_ops[]={+/* ignored vma */+{+.vma_name="IGNORE",+.vma_type=CKPT_VMA_IGNORE,+.restore=NULL,+},+/* special mapping (vdso) */+{+.vma_name="VDSO",+.vma_type=CKPT_VMA_VDSO,+.restore=special_mapping_restore,+},+/* anonymous private */+{+.vma_name="ANON PRIVATE",+.vma_type=CKPT_VMA_ANON,+.restore=anon_private_restore,+},+/* file-mapped private */+{+.vma_name="FILE PRIVATE",+.vma_type=CKPT_VMA_FILE,+.restore=filemap_restore,+},+};++/**+*restore_vma-readvmadata,recreateitandreadcontents+*@ctx:checkpointcontext+*@mm:memoryaddressspace+*/+staticintrestore_vma(structckpt_ctx*ctx,structmm_struct*mm)+{+structckpt_hdr_vma*h;+structrestore_vma_ops*ops;+intret;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_VMA);+if(IS_ERR(h))+returnPTR_ERR(h);++ckpt_debug("vma %#lx-%#lx flags %#lx type %d vmaref %d\n",+(unsignedlong)h->vm_start,(unsignedlong)h->vm_end,+(unsignedlong)h->vm_flags,(int)h->vma_type,+(int)h->vma_objref);++ret=-EINVAL;+if(h->vm_end<h->vm_start)+gotoout;+if(h->vma_objref<0)+gotoout;+if(h->vma_type>=CKPT_VMA_MAX)+gotoout;++ops=&restore_vma_ops[h->vma_type];++/* make sure we don't change this accidentally */+BUG_ON(ops->vma_type!=h->vma_type);++if(ops->restore){+ckpt_debug("vma type %s\n",ops->vma_name);+ret=ops->restore(ctx,mm,h);+}else{+ckpt_debug("vma ignored\n");+ret=0;+}+out:+ckpt_hdr_put(ctx,h);+returnret;+}++staticintdestroy_mm(structmm_struct*mm)+{+structvm_area_struct*vmnext=mm->mmap;+structvm_area_struct*vma;+intret;++while(vmnext){+vma=vmnext;+vmnext=vmnext->vm_next;+ret=do_munmap(mm,vma->vm_start,vma->vm_end-vma->vm_start);+if(ret<0){+pr_warning("c/r: failed do_munmap (%d)\n",ret);+returnret;+}+}+return0;+}++staticstructmm_struct*do_restore_mm(structckpt_ctx*ctx)+{+structckpt_hdr_mm*h;+structmm_struct*mm=NULL;+structfile*file;+unsignedintnr;+intret;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_MM);+if(IS_ERR(h))+return(structmm_struct*)h;++ckpt_debug("map_count %d\n",h->map_count);++/* XXX need more sanity checks */++ret=-EINVAL;+if((h->start_code>h->end_code)||+(h->start_data>h->end_data))+gotoout;+if(h->exe_objref<0)+gotoout;+if(h->def_flags&~VM_LOCKED)+gotoout;+if(h->flags&~(MMF_DUMP_FILTER_MASK|+((1<<MMF_DUMP_FILTER_BITS)-1)))+gotoout;++mm=current->mm;++/* point of no return -- destruct current mm */+down_write(&mm->mmap_sem);+ret=destroy_mm(mm);+if(ret<0){+up_write(&mm->mmap_sem);+gotoout;+}++mm->flags=h->flags;+mm->def_flags=h->def_flags;++mm->start_code=h->start_code;+mm->end_code=h->end_code;+mm->start_data=h->start_data;+mm->end_data=h->end_data;+mm->start_brk=h->start_brk;+mm->brk=h->brk;+mm->start_stack=h->start_stack;+mm->arg_start=h->arg_start;+mm->arg_end=h->arg_end;+mm->env_start=h->env_start;+mm->env_end=h->env_end;++/* restore the ->exe_file */+if(h->exe_objref){+file=ckpt_obj_fetch(ctx,h->exe_objref,CKPT_OBJ_FILE);+if(IS_ERR(file)){+up_write(&mm->mmap_sem);+ret=PTR_ERR(file);+gotoout;+}+set_mm_exe_file(mm,file);+}++ret=_ckpt_read_buffer(ctx,mm->saved_auxv,sizeof(mm->saved_auxv));+up_write(&mm->mmap_sem);+if(ret<0)+gotoout;++for(nr=h->map_count;nr;nr--){+ret=restore_vma(ctx,mm);+if(ret<0)+gotoout;+}++ret=restore_mm_context(ctx,mm);+out:+ckpt_hdr_put(ctx,h);+if(ret<0)+returnERR_PTR(ret);+/* restore_obj() expect an extra reference */+atomic_inc(&mm->mm_users);+returnmm;+}++void*restore_mm(structckpt_ctx*ctx)+{+return(void*)do_restore_mm(ctx);+}++intrestore_obj_mm(structckpt_ctx*ctx,intmm_objref)+{+structmm_struct*mm;+intret;++mm=ckpt_obj_fetch(ctx,mm_objref,CKPT_OBJ_MM);+if(IS_ERR(mm))+returnPTR_ERR(mm);++if(mm==current->mm)+return0;++ret=exec_mmap(mm);+if(ret<0)+returnret;++atomic_inc(&mm->mm_users);+return0;+}
@@ -368,6 +368,9 @@ static int restore_task_objs(struct ckpt_ctx *ctx)ret=restore_obj_file_table(ctx,h->files_objref);ckpt_debug("file_table: ret %d (%p)\n",ret,current->files);+ret=restore_obj_mm(ctx,h->mm_objref);+ckpt_debug("mm: ret %d (%p)\n",ret,current->mm);+ckpt_hdr_put(ctx,h);returnret;}
@@ -2113,7 +2113,7 @@ void exit_mmap(struct mm_struct *mm)tlb=tlb_gather_mmu(mm,1);/* update_hiwater_rss(mm) here? but nobody should be looking *//* Use -1 here to ensure all VMAs in the mm are unmapped */-end=unmap_vmas(&tlb,vma,0,-1,&nr_accounted,NULL);+end=vma?unmap_vmas(&tlb,vma,0,-1,&nr_accounted,NULL):0;vm_unacct_memory(nr_accounted);free_pgtables(tlb,vma,FIRST_USER_ADDRESS,0);tlb_finish_mmu(tlb,0,end);
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Add the helpers to checkpoint and restore the contents of 'struct
kern_ipc_perm'. Add header structures for ipc state. Put place-holders
to save and restore ipc state.
Save and restores the common state (parameters) of ipc namespace.
Generic code to iterate through the objects of sysvipc shared memory,
message queues and semaphores. The logic to save and restore the state
of these objects will be added in the next few patches.
Right now, we return -EPERM if the user calling sys_restart() isn't
allowed to create an object with the checkpointed uid. We may prefer
to simply use the caller's uid in that case - but that could lead to
subtle userspace bugs? Unsure, so going for the stricter behavior.
TODO: restore kern_ipc_perms->security.
Changelog[v17]:
- Fix include: use checkpoint.h not checkpoint_hdr.h
- Collect nsproxy->ipc_ns
- Restore objects in the right namespace
- If !CONFIG_IPC_NS only restore objects, not global settings
- Don't overwrite global ipc-ns if !CONFIG_IPC_NS
- Reset the checkpointed uid and gid info on ipc objects
- Fix compilation with CONFIG_SYSVIPC=n
Changelog [Dan Smith [off-list ref]]
- Fix compilation with CONFIG_SYSVIPC=n
- Update to match UTS changes
Signed-off-by: Oren Laadan <redacted>
---
checkpoint/checkpoint.c | 2 -
checkpoint/objhash.c | 28 ++++
include/linux/checkpoint.h | 13 ++
include/linux/checkpoint_hdr.h | 54 +++++++
include/linux/checkpoint_types.h | 1 +
init/Kconfig | 6 +
ipc/Makefile | 2 +-
ipc/checkpoint.c | 317 ++++++++++++++++++++++++++++++++++++++
ipc/namespace.c | 2 +-
ipc/util.h | 10 ++
kernel/nsproxy.c | 22 ++-
11 files changed, 449 insertions(+), 8 deletions(-)
create mode 100644 ipc/checkpoint.c
@@ -0,0 +1,317 @@+/*+*Checkpointlogicandhelpers+*+*Copyright(C)2009OrenLaadan+*+*ThisfileissubjecttothetermsandconditionsoftheGNUGeneralPublic+*License.SeethefileCOPYINGinthemaindirectoryoftheLinux+*distributionformoredetails.+*/++/* default debug level for output */+#define CKPT_DFLAG CKPT_DIPC++#include<linux/ipc.h>+#include<linux/msg.h>+#include<linux/sched.h>+#include<linux/ipc_namespace.h>+#include<linux/checkpoint.h>+#include<linux/checkpoint_hdr.h>++#include"util.h"++/* for ckpt_debug */+staticchar*ipc_ind_to_str[]={"sem","msg","shm"};++#define shm_ids(ns) ((ns)->ids[IPC_SHM_IDS])+#define msg_ids(ns) ((ns)->ids[IPC_MSG_IDS])+#define sem_ids(ns) ((ns)->ids[IPC_SEM_IDS])++/**************************************************************************+*Checkpoint+*/++intcheckpoint_fill_ipc_perms(structckpt_hdr_ipc_perms*h,+structkern_ipc_perm*perm)+{+if(ipcperms(perm,S_IROTH))+return-EACCES;++h->id=perm->id;+h->key=perm->key;+h->uid=perm->uid;+h->gid=perm->gid;+h->cuid=perm->cuid;+h->cgid=perm->cgid;+h->mode=perm->mode&S_IRWXUGO;+h->seq=perm->seq;++return0;+}++staticintcheckpoint_ipc_any(structckpt_ctx*ctx,+structipc_namespace*ipc_ns,+intipc_ind,intipc_type,+int(*func)(intid,void*p,void*data))+{+structckpt_hdr_ipc*h;+structipc_ids*ipc_ids=&ipc_ns->ids[ipc_ind];+intret=-ENOMEM;++down_read(&ipc_ids->rw_mutex);+h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_IPC);+if(!h)+gotoout;++h->ipc_type=ipc_type;+h->ipc_count=ipc_ids->in_use;+ckpt_debug("ipc-%s count %d\n",ipc_ind_to_str[ipc_ind],h->ipc_count);++ret=ckpt_write_obj(ctx,&h->h);+ckpt_hdr_put(ctx,h);+if(ret<0)+gotoout;++ret=idr_for_each(&ipc_ids->ipcs_idr,func,ctx);+ckpt_debug("ipc-%s ret %d\n",ipc_ind_to_str[ipc_ind],ret);+out:+up_read(&ipc_ids->rw_mutex);+returnret;+}++staticintdo_checkpoint_ipc_ns(structckpt_ctx*ctx,+structipc_namespace*ipc_ns)+{+structckpt_hdr_ipcns*h;+intret;++h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_IPC_NS);+if(!h)+return-ENOMEM;++down_read(&shm_ids(ipc_ns).rw_mutex);+h->shm_ctlmax=ipc_ns->shm_ctlmax;+h->shm_ctlall=ipc_ns->shm_ctlall;+h->shm_ctlmni=ipc_ns->shm_ctlmni;+up_read(&shm_ids(ipc_ns).rw_mutex);++down_read(&msg_ids(ipc_ns).rw_mutex);+h->msg_ctlmax=ipc_ns->msg_ctlmax;+h->msg_ctlmnb=ipc_ns->msg_ctlmnb;+h->msg_ctlmni=ipc_ns->msg_ctlmni;+up_read(&msg_ids(ipc_ns).rw_mutex);++down_read(&sem_ids(ipc_ns).rw_mutex);+h->sem_ctl_msl=ipc_ns->sem_ctls[0];+h->sem_ctl_mns=ipc_ns->sem_ctls[1];+h->sem_ctl_opm=ipc_ns->sem_ctls[2];+h->sem_ctl_mni=ipc_ns->sem_ctls[3];+up_read(&sem_ids(ipc_ns).rw_mutex);++ret=ckpt_write_obj(ctx,&h->h);+ckpt_hdr_put(ctx,h);+if(ret<0)+returnret;++#if 0 /* NEXT FEW PATCHES */+ret=checkpoint_ipc_any(ctx,ipc_ns,IPC_SHM_IDS,+CKPT_HDR_IPC_SHM,checkpoint_ipc_shm);+if(ret<0)+returnret;+ret=checkpoint_ipc_any(ctx,ipc_ns,IPC_MSG_IDS,+CKPT_HDR_IPC_MSG,checkpoint_ipc_msg);+if(ret<0)+returnret;+ret=checkpoint_ipc_any(ctx,ipc_ns,IPC_SEM_IDS,+CKPT_HDR_IPC_SEM,checkpoint_ipc_sem);+#endif+returnret;+}++intcheckpoint_ipc_ns(structckpt_ctx*ctx,void*ptr)+{+returndo_checkpoint_ipc_ns(ctx,(structipc_namespace*)ptr);+}++/**************************************************************************+*Restart+*/++/*+*checkwhethercurrenttaskmaycreateipcobjectwith+*checkpointeduidsandgids.+*Return1ifok,0ifnot.+*/+staticintvalidate_created_perms(structckpt_hdr_ipc_perms*h)+{+conststructcred*cred=current_cred();+uid_tuid=cred->uid,euid=cred->euid;++/* actually I don't know - is CAP_IPC_OWNER the right one? */+if(((h->uid!=uid&&h->uid==euid)||+(h->cuid!=uid&&h->cuid!=euid)||+!in_group_p(h->cgid)||+!in_group_p(h->gid))&&+!capable(CAP_IPC_OWNER))+return0;+return1;+}++intrestore_load_ipc_perms(structckpt_hdr_ipc_perms*h,+structkern_ipc_perm*perm)+{+if(h->id<0)+return-EINVAL;+if(CKPT_TST_OVERFLOW_16(h->uid,perm->uid)||+CKPT_TST_OVERFLOW_16(h->gid,perm->gid)||+CKPT_TST_OVERFLOW_16(h->cuid,perm->cuid)||+CKPT_TST_OVERFLOW_16(h->cgid,perm->cgid)||+CKPT_TST_OVERFLOW_16(h->mode,perm->mode))+return-EINVAL;+if(h->seq>=USHORT_MAX)+return-EINVAL;+if(h->mode&~S_IRWXUGO)+return-EINVAL;++/* FIX: verify the ->mode field makes sense */++perm->id=h->id;+perm->key=h->key;++if(!validate_created_perms(h))+return-EPERM;+perm->uid=h->uid;+perm->gid=h->gid;+perm->cuid=h->cuid;+perm->cgid=h->cgid;+perm->mode=h->mode;+perm->seq=h->seq;+/*+*Todo:restoreperm->security.+*Atthemomentitgetssetbysecurity_x_alloc()calledthrough+*ipcget()->ipcget_public()->ops-.getnew(->nequeueforinstance)+*WewillwanttoasktheLSMtoconsiderresettingthe+*checkpointed->security,basedoncurrent_security(),+*thecheckpointed->security,andthecheckpointfilecontext.+*/++return0;+}++staticintrestore_ipc_any(structckpt_ctx*ctx,structipc_namespace*ipc_ns,+intipc_ind,intipc_type,+int(*func)(structckpt_ctx*ctx,+structipc_namespace*ns))+{+structckpt_hdr_ipc*h;+intn,ret;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_IPC);+if(IS_ERR(h))+returnPTR_ERR(h);++ckpt_debug("ipc-%s: count %d\n",ipc_ind_to_str[ipc_ind],h->ipc_count);++ret=-EINVAL;+if(h->ipc_type!=ipc_type)+gotoout;++ret=0;+for(n=0;n<h->ipc_count;n++){+ret=(*func)(ctx,ipc_ns);+if(ret<0)+gotoout;+}+out:+ckpt_debug("ipc-%s: ret %d\n",ipc_ind_to_str[ipc_ind],ret);+ckpt_hdr_put(ctx,h);+returnret;+}++staticstructipc_namespace*do_restore_ipc_ns(structckpt_ctx*ctx)+{+structipc_namespace*ipc_ns=NULL;+structckpt_hdr_ipcns*h;+intret;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_IPC_NS);+if(IS_ERR(h))+returnERR_PTR(PTR_ERR(h));++ret=-EINVAL;+if(h->shm_ctlmax<0||h->shm_ctlall<0||h->shm_ctlmni<0)+gotoout;+if(h->msg_ctlmax<0||h->msg_ctlmnb<0||h->msg_ctlmni<0)+gotoout;+if(h->sem_ctl_msl<0||h->sem_ctl_mns<0||+h->sem_ctl_opm<0||h->sem_ctl_mni<0)+gotoout;++/*+*If!CONFIG_IPC_NS,donotrestoretheglobalIPCstate,as+*itisusedbyotherprocesses.Itisoktotrytorestore+*the{shm,msg,sem}objects:intheworstcasetherequested+*identifierswillbeinuse.+*/+#ifdef CONFIG_IPC_NS+ret=-ENOMEM;+ipc_ns=create_ipc_ns();+if(!ipc_ns)+gotoout;++down_read(&shm_ids(ipc_ns).rw_mutex);+ipc_ns->shm_ctlmax=h->shm_ctlmax;+ipc_ns->shm_ctlall=h->shm_ctlall;+ipc_ns->shm_ctlmni=h->shm_ctlmni;+up_read(&shm_ids(ipc_ns).rw_mutex);++down_read(&msg_ids(ipc_ns).rw_mutex);+ipc_ns->msg_ctlmax=h->msg_ctlmax;+ipc_ns->msg_ctlmnb=h->msg_ctlmnb;+ipc_ns->msg_ctlmni=h->msg_ctlmni;+up_read(&msg_ids(ipc_ns).rw_mutex);++down_read(&sem_ids(ipc_ns).rw_mutex);+ipc_ns->sem_ctls[0]=h->sem_ctl_msl;+ipc_ns->sem_ctls[1]=h->sem_ctl_mns;+ipc_ns->sem_ctls[2]=h->sem_ctl_opm;+ipc_ns->sem_ctls[3]=h->sem_ctl_mni;+up_read(&sem_ids(ipc_ns).rw_mutex);+#else+ret=-EEXIST;+/* complain if image contains multiple namespaces */+if(ctx->stats.ipc_ns)+gotoout;+ipc_ns=current->nsproxy->ipc_ns;+get_ipc_ns(ipc_ns);+#endif++#if 0 /* NEXT FEW PATCHES */+ret=restore_ipc_any(ctx,ipc_ns,IPC_SHM_IDS,+CKPT_HDR_IPC_SHM,restore_ipc_shm);+if(ret<0)+gotoout;+ret=restore_ipc_any(ctx,ipc_ns,IPC_MSG_IDS,+CKPT_HDR_IPC_MSG,restore_ipc_msg);+if(ret<0)+gotoout;+ret=restore_ipc_any(ctx,ipc_ns,IPC_SEM_IDS,+CKPT_HDR_IPC_SEM,restore_ipc_sem);+#endif+if(ret<0)+gotoout;++ctx->stats.ipc_ns++;+out:+ckpt_hdr_put(ctx,h);+if(ret<0){+put_ipc_ns(ipc_ns);+ipc_ns=ERR_PTR(ret);+}+returnipc_ns;+}++void*restore_ipc_ns(structckpt_ctx*ctx)+{+return(void*)do_restore_ipc_ns(ctx);+}
@@ -325,6 +338,7 @@ static struct nsproxy *do_restore_ns(struct ckpt_ctx *ctx)get_nsproxy(nsproxy);BUG_ON(nsproxy->uts_ns!=uts_ns);+BUG_ON(nsproxy->ipc_ns!=ipc_ns);#endif/* TODO: add more namespaces here */
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Sukadev Bhattiprolu <redacted>
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 'target_pid_set' 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).
Unlike clone(), clone_with_pids() needs CAP_SYS_ADMIN, at least for now, to
prevent unprivileged processes from misusing this interface.
Call clone_with_pids as follows:
pid_t pids[] = { 0, 77, 99 };
struct target_pid_set pid_set;
pid_set.num_pids = sizeof(pids) / sizeof(int);
pid_set.target_pids = &pids;
syscall(__NR_clone_with_pids, flags, stack, NULL, NULL, NULL, &pid_set);
If a target-pid is 0, the kernel continues to assign a pid for the process in
that namespace. In the above example, pids[0] is 0, meaning the kernel will
assign next available pid to the process in init_pid_ns. But kernel will assign
pid 77 in the child pid namespace 1 and pid 99 in pid namespace 2. If either
77 or 99 are taken, the system call fails with -EBUSY.
If 'pid_set.num_pids' exceeds the current nesting level of pid namespaces,
the system call fails with -EINVAL.
Its mostly an exploratory patch seeking feedback on the interface.
NOTE:
Compared to clone(), clone_with_pids() needs to pass in two more
pieces of information:
- number of pids in the set
- user buffer containing the list of pids.
But since clone() already takes 5 parameters, use a 'struct
target_pid_set'.
TODO:
- Gently tested.
- May need additional sanity checks in do_fork_with_pids().
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)
Signed-off-by: Sukadev Bhattiprolu <redacted>
---
arch/x86/include/asm/syscalls.h | 2 +
arch/x86/include/asm/unistd_32.h | 1 +
arch/x86/kernel/entry_32.S | 1 +
arch/x86/kernel/process_32.c | 21 +++++++
arch/x86/kernel/syscall_table_32.S | 1 +
kernel/fork.c | 108 +++++++++++++++++++++++++++++++++++-
6 files changed, 133 insertions(+), 1 deletions(-)
@@ -1336,6 +1336,97 @@ struct task_struct * __cpuinit fork_idle(int cpu)}/*+*Ifuserspecifiedany'target-pids'in@upid_setp,copythemfrom+*userandreturnapointertoalocalcopyofthelistofpids.The+*callermustfreethelist,whentheyaredoneusingit.+*+*Ifuserdidnotspecifyanytargetpids,returnNULL(callershould+*treatthislikenormalclone).+*+*Onanyerrors,returntheerrorcode+*/+staticpid_t*copy_target_pids(void__user*upid_setp)+{+intj;+intrc;+intsize;+intunum_pids;/* # of pids specified by user */+intknum_pids;/* # of pids needed in kernel */+pid_t*target_pids;+structtarget_pid_setpid_set;++if(!upid_setp)+returnNULL;++rc=copy_from_user(&pid_set,upid_setp,sizeof(pid_set));+if(rc)+returnERR_PTR(-EFAULT);++unum_pids=pid_set.num_pids;+knum_pids=task_pid(current)->level+1;++if(!unum_pids)+returnNULL;++if(unum_pids<0||unum_pids>knum_pids)+returnERR_PTR(-EINVAL);++/*+*Tokeepalloc_pid()simple,allocateanextrapid_tintarget_pids[]+*andsetitto0.Thislastentryintarget_pids[]correspondstothe+*(yet-to-be-created)descendantpid-namespaceifCLONE_NEWPIDwas+*specified.IfCLONE_NEWPIDwasnotspecified,thislastentrywill+*simplybeignored.+*/+target_pids=kzalloc((knum_pids+1)*sizeof(pid_t),GFP_KERNEL);+if(!target_pids)+returnERR_PTR(-ENOMEM);++/*+*Aprocessrunninginalevel2pidnamespacehasthreepidnamespaces+*andhencethreepidnumbers.Ifthisprocessischeckpointed,+*informationaboutthesethreenamespacesaresaved.Werefertothese+*namespacesas'knownnamespaces'.+*+*Ifthischeckpointedprocessishoweverrestartedinalevel3pid+*namespace,therestartedprocesshasanextraancestorpidnamespace+*(i.e'unknownnamespace')and'knum_pids'exceeds'unum_pids'.+*+*Duringrestart,theprocessrequestsspecificpidsforits'known+*namespaces'andletskernelassignpidstoits'unknownnamespaces'.+*+*Sincetherequested-pidscorrespondto'knownnamespaces'andsince+*'known-namespaces'areyoungerthan(i.edescendantsof)'unknown-+*namespaces',copyrequestedpidstotheback-endoftarget_pids[]+*(i.ebeforethelastentryforCLONE_NEWPIDmentionedabove).+*Anyentriesintarget_pids[]notcorrespondingtoarequestedpid+*willbesettozeroandkernelassignsapidinthosenamespaces.+*+*NOTE:Theorderofpidsintarget_pids[]isoldestpidnamespaceto+*youngest(target_pids[0]correspondstoinit_pid_ns).i.e.+*theorderis:+*+*-pidsfor'unknown-namespaces'(ifany)+*-pidsfor'known-namespaces'(requestedpids)+*-0inthelastentry(forCLONE_NEWPID).+*/+j=knum_pids-unum_pids;+size=unum_pids*sizeof(pid_t);++rc=copy_from_user(&target_pids[j],pid_set.target_pids,size);+if(rc){+rc=-EFAULT;+gotoout_free;+}++returntarget_pids;++out_free:+kfree(target_pids);+returnERR_PTR(rc);+}++/**Ok,thisisthemainfork-routine.**Itcopiestheprocess,andifsuccessfulkick-starts
@@ -1352,7 +1443,7 @@ long do_fork_with_pids(unsigned long clone_flags,structtask_struct*p;inttrace=0;longnr;-pid_t*target_pids=NULL;+pid_t*target_pids;/**Dosomepreliminaryargumentandpermissionscheckingbeforewe
@@ -1386,6 +1477,17 @@ long do_fork_with_pids(unsigned long clone_flags,}}+target_pids=copy_target_pids(pid_setp);++if(target_pids){+if(IS_ERR(target_pids))+returnPTR_ERR(target_pids);++nr=-EPERM;+if(!capable(CAP_SYS_ADMIN))+gotoout_free;+}+/**Whencalledfromkernel_thread,don'tdousertracingstuff.*/
@@ -1453,6 +1555,10 @@ long do_fork_with_pids(unsigned long clone_flags,}else{nr=PTR_ERR(p);}++out_free:+kfree(target_pids);+returnnr;}
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Create trivial sys_checkpoint and sys_restore system calls. They will
enable to checkpoint and restart an entire container, to and from a
checkpoint image file descriptor.
The syscalls take a pid, a file descriptor (for the image file) and
flags as arguments. The pid identifies the top-most (root) task in the
process tree, e.g. the container init: for sys_checkpoint the first
argument identifies the pid of the target container/subtree; for
sys_restart it will identify the pid of restarting root task.
A checkpoint, much like a process coredump, dumps the state of multiple
processes at once, including the state of the container. The checkpoint
image is written to (and read from) the file descriptor directly from
the kernel. This way the data is generated and then pushed out naturally
as resources and tasks are scanned to save their state. This is the
approach taken by, e.g., Zap and OpenVZ.
By using a return value and not a file descriptor, we can distinguish
between a return from checkpoint, a return from restart (in case of a
checkpoint that includes self, i.e. a task checkpointing its own
container, or itself), and an error condition, in a manner analogous
to a fork() call.
We don't use copy_from_user()/copy_to_user() because it requires
holding the entire image in user space, and does not make sense for
restart. Also, we don't use a pipe, pseudo-fs file and the like,
because they work by generating data on demand as the user pulls it
(unless the entire image is buffered in the kernel) and would require
more complex logic. They also would significantly complicate
checkpoint that includes self.
Changelog[v17]:
- Move checkpoint closer to namespaces (kconfig)
- Kill "Enable" in c/r config option
Changelog[v16]:
- Change sys_restart() first argument to be 'pid_t pid'
Changelog[v14]:
- Change CONFIG_CHEKCPOINT_RESTART to CONFIG_CHECKPOINT (Ingo)
- Remove line 'def_bool n' (default is already 'n')
- Add CHECKPOINT_SUPPORT in Kconfig (Nathan Lynch)
Changelog[v5]:
- Config is 'def_bool n' by default
Signed-off-by: Oren Laadan <redacted>
Acked-by: Serge Hallyn <redacted>
Signed-off-by: Dave Hansen <redacted>
---
arch/x86/Kconfig | 4 +++
arch/x86/include/asm/unistd_32.h | 2 +
arch/x86/kernel/syscall_table_32.S | 2 +
checkpoint/Kconfig | 14 ++++++++++++
checkpoint/Makefile | 5 ++++
checkpoint/sys.c | 41 ++++++++++++++++++++++++++++++++++++
include/linux/syscalls.h | 2 +
init/Kconfig | 2 +
kernel/sys_ni.c | 4 +++
9 files changed, 76 insertions(+), 0 deletions(-)
create mode 100644 checkpoint/Kconfig
create mode 100644 checkpoint/Makefile
create mode 100644 checkpoint/sys.c
@@ -0,0 +1,14 @@+# Architectures should define CHECKPOINT_SUPPORT when they have+# implemented the hooks for processor state etc. needed by the+# core checkpoint/restart code.++configCHECKPOINT+bool"Checkpoint/restart (EXPERIMENTAL)"+depends onCHECKPOINT_SUPPORT&&EXPERIMENTAL+help+Applicationcheckpoint/restartistheabilitytosavethe+stateofarunningapplicationsothatitcanlaterresume+itsexecutionfromthetimeatwhichitwascheckpointed.++Turningthisoptiononwillenablecheckpointandrestart+functionalityinthekernel.
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
While we assume all normal files and directories can be checkpointed,
there are, as usual in the VFS, specialized places that will always
need an ability to override these defaults. Although we could do this
completely in the checkpoint code, that would bitrot quickly.
This adds a new 'file_operations' function for checkpointing a file.
It is assumed that there should be a dirt-simple way to make something
(un)checkpointable that fits in with current code.
As you can see in the ext[234] patches down the road, all that we have
to do to make something simple be supported is add a single "generic"
f_op entry.
Also introduce vfs_fcntl() so that it can be called from restart (see
patch adding restart of files).
Changelog[v17]
- Forward-declare 'ckpt_ctx' et-al, don't use checkpoint_types.h
Signed-off-by: Oren Laadan <redacted>
---
fs/fcntl.c | 21 +++++++++++++--------
include/linux/fs.h | 6 ++++++
2 files changed, 19 insertions(+), 8 deletions(-)
@@ -339,6 +339,18 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,returnerr;}+intvfs_fcntl(intfd,unsignedintcmd,unsignedlongarg,structfile*filp)+{+interr;++err=security_file_fcntl(filp,cmd,arg);+if(err)+gotoout;+err=do_fcntl(fd,cmd,arg,filp);+out:+returnerr;+}+SYSCALL_DEFINE3(fcntl,unsignedint,fd,unsignedint,cmd,unsignedlong,arg){structfile*filp;
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Dan Smith <redacted>
This patch adds a "phase" of checkpoint that saves out information about any
namespaces the task(s) may have. Do this by tracking the namespace objects
of the tasks and making sure that tasks with the same namespace that follow
get properly referenced in the checkpoint stream.
Changes[v17]:
- Collect nsproxy->uts_ns
- Save uts string lengths once in ckpt_hdr_const
- Save and restore all fields of uts-ns
- Don't overwrite global uts-ns if !CONFIG_UTS_NS
- Replace sys_unshare() with create_uts_ns()
- Take uts_sem around access to uts data
Changes:
- Remove the kernel restore path
- Punt on nested namespaces
- Use __NEW_UTS_LEN in nodename and domainname buffers
- Add a note to Documentation/checkpoint/internals.txt to indicate where
in the save/restore process the UTS information is kept
- Store (and track) the objref of the namespace itself instead of the
nsproxy (based on comments from Dave on IRC)
- Remove explicit check for non-root nsproxy
- Store the nodename and domainname lengths and use ckpt_write_string()
to store the actual name strings
- Catch failure of ckpt_obj_add_ptr() in ckpt_write_namespaces()
- Remove "types" bitfield and use the "is this new" flag to determine
whether or not we should write out a new ns descriptor
- Replace kernel restore path
- Move the namespace information to be directly after the task
information record
- Update Documentation to reflect new location of namespace info
- Support checkpoint and restart of nested UTS namespaces
Signed-off-by: Dan Smith <redacted>
Signed-off-by: Oren Laadan <redacted>
---
checkpoint/Makefile | 1 +
checkpoint/checkpoint.c | 5 +-
checkpoint/namespace.c | 100 ++++++++++++++++++++++++++++++++++++++
checkpoint/objhash.c | 26 ++++++++++
checkpoint/process.c | 2 +
checkpoint/restart.c | 32 ++++++++++++
include/linux/checkpoint.h | 5 ++
include/linux/checkpoint_hdr.h | 16 ++++++
include/linux/checkpoint_types.h | 6 ++
include/linux/utsname.h | 1 +
kernel/nsproxy.c | 47 +++++++++++++++++-
kernel/utsname.c | 3 +-
12 files changed, 240 insertions(+), 4 deletions(-)
create mode 100644 checkpoint/namespace.c
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Restarting of multiple processes expects all restarting tasks to call
sys_restart(). Once inside the system call, each task will restart
itself at the same order that they were saved. The internals of the
syscall will take care of in-kernel synchronization bewteen tasks.
This patch does _not_ create the task tree in the kernel. Instead it
assumes that all tasks are created in some way and then invoke the
restart syscall. You can use the userspace mktree.c program to do
that.
There is one special task - the coordinator - that is not part of the
restarted hierarchy. The coordinator task allocates the restart
context (ctx) and orchestrates the restart. Thus even if a restart
fails after, or during the restore of the root task, the user
perceives a clean exit and an error message.
The coordinator task will:
1) read header and tree, create @ctx (wake up restarting tasks)
2) set the ->checkpoint_ctx field of itself and all descendants
3) wait for all restarting tasks to reach sync point #1
4) activate first restarting task (root task)
5) wait for all other tasks to complete and reach sync point #3
6) wake up everybody
(Note that in step #2 the coordinator assumes that the entire task
hierarchy exists by the time it enters sys_restart; this is arranged
in user space by 'mktree')
Task that are restarting has three sync points:
1) wait for its ->checkpoint_ctx to be set (by the coordinator)
2) wait for the task's turn to restore (be active)
[...now the task restores its state...]
3) wait for all other tasks to complete
The third sync point ensures that a task may only resume execution
after all tasks have successfully restored their state (or fail if an
error has occured). This prevents tasks from returning to user space
prematurely, before the entire restart completes.
If a single task wishes to restart, it can set the "RESTART_TASKSELF"
flag to restart(2) to skip the logic of the coordinator.
The root-task is a child of the coordinator, identified by the @pid
given to sys_restart() in the pid-ns of the coordinator. Restarting
tasks that aren't the coordinator, should set the @pid argument of
restart(2) syscall to zero.
All tasks explicitly test for an error flag on the checkpoint context
when they wakeup from sync points. If an error occurs during the
restart of some task, it will mark the @ctx with an error flag, and
wakeup the other tasks.
An array of pids (the one saved during the checkpoint) is used to
synchronize the operation. The first task in the array is the init
task (*). The restart context (@ctx) maintains a "current position" in
the array, which indicates which task is currently active. Once the
currently active task completes its own restart, it increments that
position and wakes up the next task.
Restart assumes that userspace provides meaningful data, otherwise
it's garbage-in-garbage-out. In this case, the syscall may block
indefinitely, but in TASK_INTERRUPTIBLE, so the user can ctrl-c or
otherwise kill the stray restarting tasks.
In terms of security, restart runs as the user the invokes it, so it
will not allow a user to do more than is otherwise permitted by the
usual system semantics and policy.
Currently we ignore threads and zombies, as well as session ids.
Add support for multiple processes
(*) For containers, restart should be called inside a fresh container
by the init task of that container. However, it is also possible to
restart applications not necessarily inside a container, and without
restoring the original pids of the processes (that is, provided that
the application can tolerate such behavior). This is useful to allow
multi-process restart of tasks not isolated inside a container, and
also for debugging.
Changelog[v17]:
- Add uflag RESTART_FROZEN to freeze tasks after restart
- Fix restore_retval() and use only for restarting tasks
- Coordinator converts -ERSTART... to -EINTR
- Coordinator marks and sets descendants' ->checkpoint_ctx
- Coordinator properly detects errors when woken up from wait
- Fix race where root_task could kick start too early
- Add a sync point for restarting tasks
- Multiple fixes to restart logic
Changelog[v14]:
- Revert change to pr_debug(), back to ckpt_debug()
- Discard field 'h.parent'
- Check whether calls to ckpt_hbuf_get() fail
Changelog[v13]:
- Clear root_task->checkpoint_ctx regardless of error condition
- Remove unused argument 'ctx' from do_restore_task() prototype
- Remove unused member 'pids_err' from 'struct ckpt_ctx'
Changelog[v12]:
- Replace obsolete ckpt_debug() with pr_debug()
Signed-off-by: Oren Laadan <redacted>
---
checkpoint/restart.c | 461 ++++++++++++++++++++++++++++++++++++--
checkpoint/sys.c | 33 ++-
include/linux/checkpoint.h | 39 +++-
include/linux/checkpoint_types.h | 15 +-
include/linux/sched.h | 4 +
kernel/exit.c | 5 +
kernel/fork.c | 3 +
7 files changed, 519 insertions(+), 41 deletions(-)
@@ -324,6 +327,414 @@ static int restore_read_tail(struct ckpt_ctx *ctx)returnret;}+/* restore_read_tree - read the tasks tree into the checkpoint context */+staticintrestore_read_tree(structckpt_ctx*ctx)+{+structckpt_hdr_tree*h;+intsize,ret;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_TREE);+if(IS_ERR(h))+returnPTR_ERR(h);++ret=-EINVAL;+if(h->nr_tasks<0)+gotoout;++ctx->nr_pids=h->nr_tasks;+size=sizeof(*ctx->pids_arr)*ctx->nr_pids;+if(size<0)/* overflow ? */+gotoout;++ctx->pids_arr=kmalloc(size,GFP_KERNEL);+if(!ctx->pids_arr){+ret=-ENOMEM;+gotoout;+}+ret=_ckpt_read_buffer(ctx,ctx->pids_arr,size);+out:+ckpt_hdr_put(ctx,h);+returnret;+}++staticinlinepid_tget_active_pid(structckpt_ctx*ctx)+{+intactive=ctx->active_pid;+returnactive>=0?ctx->pids_arr[active].vpid:0;+}++staticinlineintis_task_active(structckpt_ctx*ctx,pid_tpid)+{+returnget_active_pid(ctx)==pid;+}++staticinlinevoidckpt_notify_error(structckpt_ctx*ctx)+{+ckpt_debug("ctx with root pid %d (%p)",ctx->root_pid,ctx);+ckpt_set_ctx_error(ctx);+complete(&ctx->complete);+}++staticintckpt_activate_next(structckpt_ctx*ctx)+{+structtask_struct*task;+intactive;+pid_tpid;++active=++ctx->active_pid;+if(active>=ctx->nr_pids){+complete(&ctx->complete);+return0;+}++pid=get_active_pid(ctx);+ckpt_debug("active pid %d (%d < %d)\n",pid,active,ctx->nr_pids);++rcu_read_lock();+task=find_task_by_pid_ns(pid,ctx->root_nsproxy->pid_ns);+if(task)+wake_up_process(task);+rcu_read_unlock();++if(!task){+ckpt_notify_error(ctx);+return-ESRCH;+}++return0;+}++staticintwait_task_active(structckpt_ctx*ctx)+{+pid_tpid=task_pid_vnr(current);+intret;++ckpt_debug("pid %d waiting\n",pid);+ret=wait_event_interruptible(ctx->waitq,+is_task_active(ctx,pid)||+ckpt_test_ctx_error(ctx));+if(!ret&&ckpt_test_ctx_error(ctx)){+force_sig(SIGKILL,current);+ret=-EBUSY;+}+returnret;+}++staticintwait_task_sync(structckpt_ctx*ctx)+{+ckpt_debug("pid %d syncing\n",task_pid_vnr(current));+wait_event_interruptible(ctx->waitq,ckpt_test_ctx_complete(ctx));+if(ckpt_test_ctx_error(ctx)){+force_sig(SIGKILL,current);+return-EBUSY;+}+return0;+}++staticintdo_restore_task(void)+{+DECLARE_WAIT_QUEUE_HEAD(waitq);+structckpt_ctx*ctx,*old_ctx;+intret;++/*+*Waitforcoordinatortomakebecomevisible,thengraba+*referencetoitsrestartcontext.Ifwe'rethelasttaskto+*doit,notifythecoordinator.+*/+ret=wait_event_interruptible(waitq,current->checkpoint_ctx);+if(ret<0)+returnret;++ctx=xchg(¤t->checkpoint_ctx,NULL);+if(!ctx)+return-EAGAIN;+ckpt_ctx_get(ctx);++/*+*Putthe@ctxbackonourtask_struct.Ifanancestortried+*toprepare_descendants()onus(althoughextremlyunlikely)+*wewillencounterthectxthathexchg()edthereandbail.+*/+old_ctx=xchg(¤t->checkpoint_ctx,ctx);+if(old_ctx){+ckpt_debug("self-set of checkpoint_ctx failed\n");+/* alert coordinator of unexpected ctx */+ckpt_notify_error(old_ctx);+ckpt_ctx_put(old_ctx);+/* alert our coordinator that we bail */+ckpt_notify_error(ctx);+ckpt_ctx_put(ctx);+return-EAGAIN;+}++/* wait for our turn, do the restore, and tell next task in line */+ret=wait_task_active(ctx);+if(ret<0)+gotoout;++ret=restore_task(ctx);+if(ret<0)+gotoout;++ret=ckpt_activate_next(ctx);+if(ret<0)+gotoout;++ret=wait_task_sync(ctx);+out:+old_ctx=xchg(¤t->checkpoint_ctx,NULL);+if(old_ctx)+ckpt_ctx_put(old_ctx);++/* if we're first to fail - notify others */+if(ret<0&&!ckpt_test_ctx_error(ctx)){+ckpt_notify_error(ctx);+wake_up_all(&ctx->waitq);+}++ckpt_ctx_put(ctx);+returnret;+}++/**+*prepare_descendants-set->restart_tskofalldescendants+*@ctx:checkpointcontext+*@root:rootprocessforrestart+*+*Calledbythecoodinatortosetthe->restart_tskpointerofthe+*roottaskandallitsdescendants.+*/+staticintprepare_descendants(structckpt_ctx*ctx,structtask_struct*root)+{+structtask_struct*leader=root;+structtask_struct*parent=NULL;+structtask_struct*task=root;+structckpt_ctx*old_ctx;+intnr_pids=ctx->nr_pids;+intret=0;++read_lock(&tasklist_lock);+while(nr_pids){+ckpt_debug("consider task %d\n",task_pid_vnr(task));+if(task_ptrace(task)&PT_PTRACED){+ret=-EBUSY;+break;+}+/*+*Settask->restart_tskofallnon-zombiedescendants.+*Ifadescendantalreadyhasa->checkpoint_ctx,it+*mustbeacoordinator(foradifferentrestart?)so+*wefail.+*+*Notethatownancestorscannotinterferesincethey+*won'tdescendpastus,asown->checkpoint_ctxmust+*alreadybeset.+*/+if(!task->exit_state){+ckpt_ctx_get(ctx);+old_ctx=xchg(&task->checkpoint_ctx,ctx);+if(old_ctx){+ckpt_debug("bad task %d\n",task_pid_vnr(task));+ckpt_ctx_put(old_ctx);+ret=-EAGAIN;+break;+}+ckpt_debug("prepare task %d\n",task_pid_vnr(task));+wake_up_process(task);+nr_pids--;+}++/* if has children - proceed with child */+if(!list_empty(&task->children)){+parent=task;+task=list_entry(task->children.next,+structtask_struct,sibling);+continue;+}+while(task!=root){+/* if has sibling - proceed with sibling */+if(!list_is_last(&task->sibling,&parent->children)){+task=list_entry(task->sibling.next,+structtask_struct,sibling);+break;+}++/* else, trace back to parent and proceed */+task=parent;+parent=parent->real_parent;+}+if(task==root){+/* in case root task in multi-threaded */+root=task=next_thread(task);+if(root==leader)+break;+}+}+read_unlock(&tasklist_lock);+ckpt_debug("left %d ret %d root/task %d\n",nr_pids,ret,task==root);++/* fail unless number of processes matches */+if(!ret&&(nr_pids||task!=root))+ret=-ESRCH;++returnret;+}++staticintwait_all_tasks_finish(structckpt_ctx*ctx)+{+intret;++init_completion(&ctx->complete);++ret=ckpt_activate_next(ctx);+if(ret<0)+returnret;++ret=wait_for_completion_interruptible(&ctx->complete);++if(ckpt_test_ctx_error(ctx))+ret=-EBUSY;+returnret;+}++staticstructtask_struct*choose_root_task(structckpt_ctx*ctx,pid_tpid)+{+structtask_struct*task;++if(ctx->uflags&RESTART_TASKSELF){+ctx->root_pid=pid;+ctx->root_task=current;+get_task_struct(current);+returncurrent;+}++read_lock(&tasklist_lock);+list_for_each_entry(task,¤t->children,sibling){+if(task_pid_vnr(task)==pid){+get_task_struct(task);+ctx->root_task=task;+ctx->root_pid=pid;+break;+}+}+read_unlock(&tasklist_lock);++returntask;+}++/* setup restart-specific parts of ctx */+staticintinit_restart_ctx(structckpt_ctx*ctx,pid_tpid)+{+structnsproxy*nsproxy;++/*+*Noneedforexplicitcleanuphere,becauseifanerror+*occursthenckpt_ctx_free()iseventuallycalled.+*/++ctx->root_task=choose_root_task(ctx,pid);+if(!ctx->root_task)+return-ESRCH;++rcu_read_lock();+nsproxy=task_nsproxy(ctx->root_task);+if(nsproxy){+get_nsproxy(nsproxy);+ctx->root_nsproxy=nsproxy;+}+rcu_read_unlock();+if(!nsproxy)+return-ESRCH;++ctx->active_pid=-1;/* see ckpt_activate_next, get_active_pid */++return0;+}++staticintdo_restore_coord(structckpt_ctx*ctx,pid_tpid)+{+structckpt_ctx*old_ctx;+intret;++ret=restore_read_header(ctx);+if(ret<0)+returnret;+ret=restore_read_tree(ctx);+if(ret<0)+returnret;++if((ctx->uflags&RESTART_TASKSELF)&&ctx->nr_pids!=1)+return-EINVAL;++ret=init_restart_ctx(ctx,pid);+if(ret<0)+returnret;++/*+*Populateown->checkpoint_ctx:ifanancestorattemptsto+*prepare_descendants()onus,itwillfail.Furthermore,+*thatancestorwon'tproceeddeepertointerferewithour+*descendantsthatarerestarting(e.g.byxchg()ingtheir+*->checkpoint_ctxpointertemporarily).+*/+ckpt_ctx_get(ctx);+old_ctx=xchg(¤t->checkpoint_ctx,ctx);+if(old_ctx){+/*+*Weareabad-behavingdescendant:anancestormust+*havedoneprepare_descendants()onusaspartofa+*restart.Oh,well...alertancestor(coordinator)+*withanerroron@old_ctx.+*/+ckpt_debug("bad bavhing checkpoint_ctx\n");+ckpt_notify_error(old_ctx);+ckpt_ctx_put(old_ctx);+return-EBUSY;+}++if(ctx->uflags&RESTART_TASKSELF){+ret=restore_task(ctx);+if(ret<0)+gotoout;+}else{+/* prepare descendants' t->restart_tsk point to coord */+ret=prepare_descendants(ctx,ctx->root_task);+if(ret<0)+gotoout;+/* wait for all other tasks to complete do_restore_task() */+ret=wait_all_tasks_finish(ctx);+if(ret<0)+gotoout;+}++ret=restore_read_tail(ctx);+if(ret<0)+gotoout;++if(ctx->uflags&RESTART_FROZEN){+ret=cgroup_freezer_make_frozen(ctx->root_task);+ckpt_debug("freezing restart tasks ... %d\n",ret);+}+out:+if(ret<0)+ckpt_set_ctx_error(ctx);+else+ckpt_set_ctx_success(ctx);++if(!(ctx->uflags&RESTART_TASKSELF))+wake_up_all(&ctx->waitq);+/*+*Ifanancestorattemptstoprepare_descendants()onus,it+*xchg()sour->checkpoint_ctx,andfreeit.Our@ctxwill,+*instead,pointtothectxthatsaidancestorplaced.+*/+ctx=xchg(¤t->checkpoint_ctx,NULL);+ckpt_ctx_put(ctx);++returnret;+}+staticlongrestore_retval(void){structpt_regs*regs=task_pt_regs(current);
@@ -372,28 +783,40 @@ static long restore_retval(void)returnret;}-/* setup restart-specific parts of ctx */-staticintinit_restart_ctx(structckpt_ctx*ctx,pid_tpid)-{-return0;-}-longdo_restart(structckpt_ctx*ctx,pid_tpid){longret;-ret=init_restart_ctx(ctx,pid);-if(ret<0)-returnret;-ret=restore_read_header(ctx);-if(ret<0)-returnret;-ret=restore_task(ctx);-if(ret<0)-returnret;-ret=restore_read_tail(ctx);-if(ret<0)-returnret;+if(ctx)+ret=do_restore_coord(ctx,pid);+else+ret=do_restore_task();-returnrestore_retval();+/* restart(2) isn't idempotent: should not be auto-restarted */+if(ret==-ERESTARTSYS||ret==-ERESTARTNOINTR||+ret==-ERESTARTNOHAND||ret==-ERESTART_RESTARTBLOCK)+ret=-EINTR;++/*+*Theretvalfromwhatwereturntothecallerwhenallgoes+*well:thisiseithertheretvalfromtheoriginalsyscall+*thatwasinterruptedduringcheckpoint,orthecontentsof+*(saved)eaxifthetaskwasinuserspace.+*+*Thecoordinator(ctx!=NULL)isexempt:don'tadjustitsretval.+*Butinself-restart(whereRESTART_TASKSELF),thecoordinator+*_itself_isarestartingtask.+*/++if(!ctx||(ctx->uflags&RESTART_TASKSELF)){+if(ret<0){+/* partial restore is undefined: terminate */+ckpt_debug("restart err %d, exiting\n",ret);+force_sig(SIGKILL,current);+}else{+ret=restore_retval();+}+}++returnret;}
@@ -16,6 +16,7 @@#include<linux/nsproxy.h>#include<linux/fs.h>#include<linux/ktime.h>+#include<linux/wait.h>structckpt_ctx{intcrid;/* unique checkpoint id */
@@ -35,10 +36,20 @@ struct ckpt_ctx {structfile*file;/* input/output file */inttotal;/* total read/written */-structtask_struct**tasks_arr;/* array of all tasks in container */-intnr_tasks;/* size of tasks array */+atomic_trefcount;charerr_string[256];/* checkpoint: error string */++/* [multi-process checkpoint] */+structtask_struct**tasks_arr;/* array of all tasks [checkpoint] */+intnr_tasks;/* size of tasks array */++/* [multi-process restart] */+structckpt_hdr_pids*pids_arr;/* array of all pids [restart] */+intnr_pids;/* size of pids array */+intactive_pid;/* (next) position in pids array */+structcompletioncomplete;/* container root and other tasks on */+wait_queue_head_twaitq;/* start, end, and restart ordering */};#endif /* __KERNEL__ */
@@ -1692,6 +1695,7 @@ extern cputime_t task_gtime(struct task_struct *p);#define PF_SPREAD_PAGE 0x01000000 /* Spread page cache over cpuset */#define PF_SPREAD_SLAB 0x02000000 /* Spread some slab caches over cpuset */#define PF_THREAD_BOUND 0x04000000 /* Thread bound to specific cpu */+#define PF_RESTARTING 0x08000000 /* Process is restarting (c/r) */#define PF_MEMPOLICY 0x10000000 /* Non-default NUMA mempolicy */#define PF_MUTEX_TESTER 0x20000000 /* Thread belongs to the rt mutex tester */#define PF_FREEZER_SKIP 0x40000000 /* Freezer should not count it as freezeable */
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
The state of shared objects is saved once. On the first encounter, the
state is dumped and the object is assigned a unique identifier (objref)
and also stored in a hash table (indexed by its physical kernel address).
From then on the object will be found in the hash and only its identifier
is saved.
On restart the identifier is looked up in the hash table; if not found
then the state is read, the object is created, and added to the hash
table (this time indexed by its identifier). Otherwise, the object in
the hash table is used.
The hash is "one-way": objects added to it are never deleted until the
hash it discarded. The hash is discarded at the end of checkpoint or
restart, whether successful or not.
The hash keeps a reference to every object that is added to it, matching
the object's type, and maintains this reference during its lifetime.
Therefore, it is always safe to use an object that is stored in the hash.
Changelog[v17]:
- Add ckpt_obj->flags with CKPT_OBJ_CHECKPOINTED flag
- Add prototype of ckpt_obj_lookup
- Complain on attempt to add NULL ptr to objhash
- Prepare for 'leaks detection'
Changelog[v16]:
- Introduce ckpt_obj_lookup() to find an object by its ptr
Changelog[v14]:
- Introduce 'struct ckpt_obj_ops' to better modularize shared objs.
- Replace long 'switch' statements with table lookups and callbacks.
- Introduce checkpoint_obj() and restart_obj() helpers
- Shared objects now dumped/saved right before they are referenced
- Cleanup interface of shared objects
Changelog[v13]:
- Use hash_long() with 'unsigned long' cast to support 64bit archs
(Nathan Lynch [off-list ref])
Changelog[v11]:
- Doc: be explicit about grabbing a reference and object lifetime
Changelog[v4]:
- Fix calculation of hash table size
Changelog[v3]:
- Use standard hlist_... for hash table
Signed-off-by: Oren Laadan <redacted>
---
checkpoint/Makefile | 1 +
checkpoint/objhash.c | 419 ++++++++++++++++++++++++++++++++++++++
checkpoint/restart.c | 50 +++++-
checkpoint/sys.c | 6 +
include/linux/checkpoint.h | 18 ++
include/linux/checkpoint_hdr.h | 14 ++
include/linux/checkpoint_types.h | 2 +
7 files changed, 508 insertions(+), 2 deletions(-)
create mode 100644 checkpoint/objhash.c
@@ -25,6 +25,34 @@#include<linux/checkpoint_hdr.h>/**+*_ckpt_read_objref-dispatchhandlingofasharedobject+*@ctx:checkpointcontext+*@hh:objrectdescriptor+*/+staticint_ckpt_read_objref(structckpt_ctx*ctx,structckpt_hdr*hh)+{+structckpt_hdr*h;+intret;++h=ckpt_hdr_get(ctx,hh->len);+if(!h)+return-ENOMEM;++*h=*hh;/* yay ! */++_ckpt_debug(CKPT_DOBJ,"shared len %d type %d\n",h->len,h->type);+ret=ckpt_kread(ctx,(h+1),hh->len-sizeof(structckpt_hdr));+if(ret<0)+gotoout;++ret=restore_obj(ctx,(structckpt_hdr_objref*)h);+out:+ckpt_hdr_put(ctx,h);+returnret;+}+++/***_ckpt_read_obj-readanobject(ckpt_hdrfollowedbypayload)*@ctx:checkpointcontext*@h:desiredckpt_hdr
@@ -46,7 +75,15 @@ static int _ckpt_read_obj(struct ckpt_ctx *ctx, struct ckpt_hdr *h,h->type,h->len,len,max);if(h->len<sizeof(*h))return-EINVAL;+/* if len specified, enforce, else if maximum specified, enforce */+if(h->type==CKPT_HDR_OBJREF){+ret=_ckpt_read_objref(ctx,h);+if(ret<0)+returnret;+gotoagain;+}+if((len&&h->len!=len)||(!len&&max&&h->len>max))return-EINVAL;
@@ -155,6 +192,7 @@ static void *ckpt_read_obj(struct ckpt_ctx *ctx, int len, int max)structckpt_hdr*h;intret;+again:ret=ckpt_kread(ctx,&hh,sizeof(hh));if(ret<0)returnERR_PTR(ret);
@@ -162,6 +200,14 @@ static void *ckpt_read_obj(struct ckpt_ctx *ctx, int len, int max)hh.type,hh.len,len,max);if(hh.len<sizeof(*h))returnERR_PTR(-EINVAL);++if(hh.type==CKPT_HDR_OBJREF){+ret=_ckpt_read_objref(ctx,&hh);+if(ret<0)+returnERR_PTR(ret);+gotoagain;+}+/* if len specified, enforce, else if maximum specified, enforce */if((len&&hh.len!=len)||(!len&&max&&hh.len>max))returnERR_PTR(-EINVAL);
@@ -214,8 +260,8 @@ void *ckpt_read_obj_type(struct ckpt_ctx *ctx, int len, int type)*@type:desiredobjecttype**Thisdiffersfromckpt_read_obj_type()inthatthelengthofthe-*incomingobjectisflexible(uptothemaximumspecifiedby@len),-*asdeterminedbytheckpt_hdrdata.+*incomingobjectisflexible(uptothemaximumspecifiedby@len;+*unlimitedif@lenis0),asdeterminedbytheckpt_hdrdata.**Return:newbufferallocatedonsuccess,errorpointerotherwise*/
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Serge E. Hallyn <redacted>
[ Andrew: I am punting on dealing with the subsystem cooperation
issues in this version, in favor of trying to get LSM issues
straightened out ]
An application checkpoint image will store capability sets
(and the bounding set) as __u64s. Define checkpoint and
restart functions to translate between those and kernel_cap_t's.
Define a common function do_capset_tocred() which applies capability
set changes to a passed-in struct cred.
The restore function uses do_capset_tocred() to apply the restored
capabilities to the struct cred being crafted, subject to the
current task's (task executing sys_restart()) permissions.
Changelog:
Jun 09: Can't choose securebits or drop bounding set if
file capabilities aren't compiled into the kernel.
Also just store caps in __u32s (looks cleaner).
Jun 01: Made the checkpoint and restore functions and the
ckpt_hdr_capabilities struct more opaque to the
rest of the c/r code, as suggested by Andrew Morgan,
and using naming suggested by Oren.
Jun 01: Add commented BUILD_BUG_ON() to point out that the
current implementation depends on 64-bit capabilities.
(Andrew Morgan and Alexey Dobriyan).
May 28: add helpers to c/r securebits
Signed-off-by: Serge E. Hallyn <redacted>
---
include/linux/capability.h | 6 ++
include/linux/checkpoint_hdr.h | 11 +++
kernel/capability.c | 164 +++++++++++++++++++++++++++++++++++++---
security/commoncap.c | 19 +----
4 files changed, 172 insertions(+), 28 deletions(-)
@@ -893,24 +893,9 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,*capability-based-privilegeenvironment.*/casePR_SET_SECUREBITS:-error=-EPERM;-if((((new->securebits&SECURE_ALL_LOCKS)>>1)-&(new->securebits^arg2))/*[1]*/-||((new->securebits&SECURE_ALL_LOCKS&~arg2))/*[2]*/-||(arg2&~(SECURE_ALL_LOCKS|SECURE_ALL_BITS))/*[3]*/-||(cap_capable(current,current_cred(),CAP_SETPCAP,-SECURITY_CAP_AUDIT)!=0)/*[4]*/-/*-*[1]nochangingofbitsthatarelocked-*[2]nounlockingoflocks-*[3]nosettingofunsupportedbits-*[4]doinganythingrequiresprivilege(goreadabout-*the"sendmail capabilities bug")-*/-)-/* cannot change a locked bit */+error=apply_securebits(arg2,new);+if(error)gotoerror;-new->securebits=arg2;gotochanged;casePR_GET_SECUREBITS:
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Serge E. Hallyn <redacted>
This patch adds the checkpointing and restart of credentials
(uids, gids, and capabilities) to Oren's c/r patchset (on top
of v14). It goes to great pains to re-use (and define when
needed) common helpers, in order to make sure that as security
code is modified, the cr code will be updated. Some of the
helpers should still be moved (i.e. _creds() functions should
be in kernel/cred.c).
When building the credentials for the restarted process, I
1. create a new struct cred as a copy of the running task's
cred (using prepare_cred())
2. always authorize any changes to the new struct cred
based on the permissions of current_cred() (not the current
transient state of the new cred).
While this may mean that certain transient_cred1->transient_cred2
states are allowed which otherwise wouldn't be allowed, the
fact remains that current_cred() is allowed to transition to
transient_cred2.
The reconstructed creds are applied to the task at the very
end of the sys_restart call. This ensures that any objects which
need to be re-created (file, socket, etc) are re-created using
the creds of the task calling sys_restart - preventing an unpriv
user from creating a privileged object, and ensuring that a
root task can restart a process which had started out privileged,
created some privileged objects, then dropped its privilege.
With these patches, the root user can restart checkpoint images
(created by either hallyn or root) of user hallyn's tasks,
resulting in a program owned by hallyn.
Changelog:
Jun 15: Fix user_ns handling when !CONFIG_USER_N
Set creator_ref=0 for root_ns (discard @flags)
Don't overwrite global user-ns if CONFIG_USER_NS
Jun 10: Merge with ckpt-v16-dev (Oren Laadan)
Jun 01: Don't check ordering of groups in group_info, bc
set_groups() will sort it for us.
May 28: 1. Restore securebits
2. Address Alexey's comments: move prototypes out of
sched.h, validate ngroups < NGROUPS_MAX, validate
groups are sorted, and get rid of ckpt_hdr_cred->version.
3. remove bogus unused flag RESTORE_CREATE_USERNS
May 26: Move group, user, userns, creds c/r functions out
of checkpoint/process.c and into the appropriate files.
May 26: Define struct ckpt_hdr_task_creds and move task cred
objref c/r into {checkpoint_restore}_task_shared().
May 26: Take cred refs around checkpoint_write_creds()
May 20: Remove the limit on number of groups in groupinfo
at checkpoint time
May 20: Remove the depth limit on empty user namespaces
May 20: Better document checkpoint_user
May 18: fix more refcounting: if (userns 5, uid 0) had
no active tasks or child user_namespaces, then
it shouldn't exist at restart or it, its namespace,
and its whole chain of creators will be leaked.
May 14: fix some refcounting:
1. a new user_ns needs a ref to remain pinned
by its root user
2. current_user_ns needs an extra ref bc objhash
drops two on restart
3. cred needs a ref for the real credentials bc
commit_creds eats one ref.
May 13: folded in fix to userns refcounting.
Signed-off-by: Serge E. Hallyn <redacted>
[orenl@cs.columbia.edu: merge with ckpt-v16-dev]
---
checkpoint/namespace.c | 41 ++++++++++
checkpoint/objhash.c | 82 ++++++++++++++++++++
checkpoint/process.c | 111 ++++++++++++++++++++++++++-
include/linux/capability.h | 6 +-
include/linux/checkpoint.h | 12 +++
include/linux/checkpoint_hdr.h | 59 ++++++++++++++
include/linux/checkpoint_types.h | 2 +
kernel/cred.c | 123 +++++++++++++++++++++++++++++
kernel/groups.c | 69 +++++++++++++++++
kernel/user.c | 158 ++++++++++++++++++++++++++++++++++++++
kernel/user_namespace.c | 89 +++++++++++++++++++++
11 files changed, 746 insertions(+), 6 deletions(-)
@@ -430,6 +474,34 @@ static int restore_task_ns(struct ckpt_ctx *ctx)returnret;}+staticintrestore_task_creds(structckpt_ctx*ctx)+{+structckpt_hdr_task_creds*h;+structcred*realcred,*ecred;+intret=0;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_TASK_CREDS);+if(IS_ERR(h))+returnPTR_ERR(h);++realcred=ckpt_obj_fetch(ctx,h->cred_ref,CKPT_OBJ_CRED);+if(IS_ERR(realcred)){+ret=PTR_ERR(realcred);+gotoout;+}+ecred=ckpt_obj_fetch(ctx,h->ecred_ref,CKPT_OBJ_CRED);+if(IS_ERR(ecred)){+ret=PTR_ERR(ecred);+gotoout;+}+ctx->realcred=realcred;+ctx->ecred=ecred;++out:+ckpt_hdr_put(ctx,h);+returnret;+}+staticintrestore_task_objs(structckpt_ctx*ctx){structckpt_hdr_task_objs*h;
@@ -440,7 +512,9 @@ static int restore_task_objs(struct ckpt_ctx *ctx)*andbecausesharedobjectsarerestoredbeforetheyare*referenced.Seecommentincheckpoint_task_objs.*/-ret=restore_task_ns(ctx);+ret=restore_task_creds(ctx);+if(!ret)+ret=restore_task_ns(ctx);if(ret<0)returnret;
@@ -458,6 +532,33 @@ static int restore_task_objs(struct ckpt_ctx *ctx)returnret;}+staticintrestore_creds(structckpt_ctx*ctx)+{+intret;+conststructcred*old;+structcred*rcred,*ecred;++rcred=ctx->realcred;+ecred=ctx->ecred;++/* commit_creds will take one ref for the eff creds, but+*expectsustoholdareffortheobjcreds,sotakea+*refhere*/+get_cred(rcred);+ret=commit_creds(rcred);+if(ret)+returnret;++if(ecred==rcred)+return0;++old=override_creds(ecred);/* override_creds otoh takes new ref */+put_cred(old);++ctx->realcred=ctx->ecred=NULL;+return0;+}+intrestore_restart_block(structckpt_ctx*ctx){structckpt_hdr_restart_block*h;
@@ -591,6 +692,10 @@ int restore_task(struct ckpt_ctx *ctx)gotoout;ret=restore_cpu(ctx);ckpt_debug("cpu %d\n",ret);+if(ret<0)+gotoout;+ret=restore_creds(ctx);+ckpt_debug("creds: ret %d\n",ret);out:returnret;}
@@ -65,6 +66,7 @@ struct ckpt_ctx {intactive_pid;/* (next) position in pids array */structcompletioncomplete;/* container root and other tasks on */wait_queue_head_twaitq;/* start, end, and restart ordering */+structcred*realcred,*ecred;/* tmp storage for cred at restart */structckpt_statsstats;/* statistics */};
@@ -703,3 +704,125 @@ int cred_setfsgid(struct cred *new, gid_t gid, gid_t *old_fsgid)}return-EPERM;}++#ifdef CONFIG_CHECKPOINT+staticintdo_checkpoint_cred(structckpt_ctx*ctx,conststructcred*cred)+{+intret;+intgroupinfo_ref,user_ref;+structckpt_hdr_cred*h;++groupinfo_ref=checkpoint_obj(ctx,cred->group_info,+CKPT_OBJ_GROUPINFO);+if(groupinfo_ref<0)+returngroupinfo_ref;+user_ref=checkpoint_obj(ctx,cred->user,CKPT_OBJ_USER);+if(user_ref<0)+returnuser_ref;++h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_CRED);+if(!h)+return-ENOMEM;++h->uid=cred->uid;+h->suid=cred->suid;+h->euid=cred->euid;+h->fsuid=cred->fsuid;++h->gid=cred->gid;+h->sgid=cred->sgid;+h->egid=cred->egid;+h->fsgid=cred->fsgid;++checkpoint_capabilities(&h->cap_s,cred);++h->user_ref=user_ref;+h->groupinfo_ref=groupinfo_ref;++ret=ckpt_write_obj(ctx,(structckpt_hdr*)h);+ckpt_hdr_put(ctx,h);++returnret;+}++intcheckpoint_cred(structckpt_ctx*ctx,void*ptr)+{+returndo_checkpoint_cred(ctx,(structcred*)ptr);+}++staticstructcred*do_restore_cred(structckpt_ctx*ctx)+{+structcred*cred;+structckpt_hdr_cred*h;+structuser_struct*user;+structgroup_info*groupinfo;+intret=-EINVAL;+uid_tolduid;+gid_toldgid;+inti;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_CRED);+if(IS_ERR(h))+returnERR_PTR(PTR_ERR(h));++cred=prepare_creds();+if(!cred)+gotoerror;+++/* Do we care if the target user and target group were compatible?+*Probably.Butthen,wecan'tdoanysetuidwithoutCAP_SETUID,+*sowemusthavebeenprivilegedtoabuseit...*/+groupinfo=ckpt_obj_fetch(ctx,h->groupinfo_ref,CKPT_OBJ_GROUPINFO);+if(IS_ERR(groupinfo))+gotoerr_putcred;+user=ckpt_obj_fetch(ctx,h->user_ref,CKPT_OBJ_USER);+if(IS_ERR(user))+gotoerr_putcred;++/*+*TODO:thischeckshouldgointothecommonhelperin+*kernel/sys.c,andshouldaccountforusernamespaces+*/+if(!capable(CAP_SETGID))+for(i=0;i<groupinfo->ngroups;i++){+if(!in_egroup_p(GROUP_AT(groupinfo,i)))+gotoerr_putcred;+}+ret=set_groups(cred,groupinfo);+if(ret<0)+gotoerr_putcred;+free_uid(cred->user);+cred->user=get_uid(user);+ret=cred_setresuid(cred,h->uid,h->euid,h->suid);+if(ret<0)+gotoerr_putcred;+ret=cred_setfsuid(cred,h->fsuid,&olduid);+if(olduid!=h->fsuid&&ret<0)+gotoerr_putcred;+ret=cred_setresgid(cred,h->gid,h->egid,h->sgid);+if(ret<0)+gotoerr_putcred;+ret=cred_setfsgid(cred,h->fsgid,&oldgid);+if(oldgid!=h->fsgid&&ret<0)+gotoerr_putcred;+ret=restore_capabilities(&h->cap_s,cred);+if(ret)+gotoerr_putcred;++ckpt_hdr_put(ctx,h);+returncred;++err_putcred:+abort_creds(cred);+error:+ckpt_hdr_put(ctx,h);+returnERR_PTR(ret);+}++void*restore_cred(structckpt_ctx*ctx)+{+return(void*)do_restore_cred(ctx);+}++#endif
@@ -6,6 +6,7 @@#include<linux/slab.h>#include<linux/security.h>#include<linux/syscalls.h>+#include<linux/checkpoint.h>#include<asm/uaccess.h>/* init to 2 - one for init_task, one to ensure it is never freed */
@@ -286,3 +287,71 @@ int in_egroup_p(gid_t grp)}EXPORT_SYMBOL(in_egroup_p);++#ifdef CONFIG_CHECKPOINT+staticintdo_checkpoint_groupinfo(structckpt_ctx*ctx,structgroup_info*g)+{+intret,i,size;+structckpt_hdr_groupinfo*h;++size=sizeof(*h)+g->ngroups*sizeof(__u32);+h=ckpt_hdr_get_type(ctx,size,CKPT_HDR_GROUPINFO);+if(!h)+return-ENOMEM;++h->ngroups=g->ngroups;+for(i=0;i<g->ngroups;i++)+h->groups[i]=GROUP_AT(g,i);++ret=ckpt_write_obj(ctx,(structckpt_hdr*)h);+ckpt_hdr_put(ctx,h);++returnret;+}++intcheckpoint_groupinfo(structckpt_ctx*ctx,void*ptr)+{+returndo_checkpoint_groupinfo(ctx,(structgroup_info*)ptr);+}++/*+*TODO-switchtoreadinginsmallerblocks?+*/+#define MAX_GROUPINFO_SIZE (sizeof(*h)+NGROUPS_MAX*sizeof(gid_t))+staticstructgroup_info*do_restore_groupinfo(structckpt_ctx*ctx)+{+structgroup_info*g;+structckpt_hdr_groupinfo*h;+inti;++h=ckpt_read_buf_type(ctx,MAX_GROUPINFO_SIZE,CKPT_HDR_GROUPINFO);+if(IS_ERR(h))+returnERR_PTR(PTR_ERR(h));++g=ERR_PTR(-EINVAL);+if(h->ngroups>NGROUPS_MAX)+gotoout;++for(i=1;i<h->ngroups;i++)+if(h->groups[i-1]>=h->groups[i])+gotoout;++g=groups_alloc(h->ngroups);+if(!g){+g=ERR_PTR(-ENOMEM);+gotoout;+}+for(i=0;i<h->ngroups;i++)+GROUP_AT(g,i)=h->groups[i];++out:+ckpt_hdr_put(ctx,h);+returng;+}++void*restore_groupinfo(structckpt_ctx*ctx)+{+return(void*)do_restore_groupinfo(ctx);+}++#endif
@@ -508,3 +509,160 @@ static int __init uid_cache_init(void)}module_init(uid_cache_init);++#ifdef CONFIG_CHECKPOINT+/*+*writetheuserstruct+*TODOkeyringwillneedtobedumped+*+*Hereiswhatwe'redoing.Rememberataskcandoclone(CLONE_NEWUSER)+*resultinginaclonedtaskinanewusernamespace,withuid0inthat+*newuser_ns.Inthatcase,theparent'suser(uid+user_ns)isthe+*'creator'ofthenewuser_ns.+*Here,wecalltheuser_nsofthectx->root_taskthe'root_ns'.Whenwe+*checkpointauser-struct,wemuststorethechainofcreators.We+*mustnotdosorecursively,thisbeingthekernel.In+*checkpoint_write_user()wewalkandrecordinmemorythelistofcreatorsup+*toeitherthelatestuser_structwhichhasalreadybeensaved,orthe+*root_ns.Thenwewalkthatchainbackward,writingouttheuser_nsand+*user_structtothecheckpointimage.+*/+#define UNSAVED_STRIDE 50+staticintdo_checkpoint_user(structckpt_ctx*ctx,structuser_struct*u)+{+structuser_namespace*ns,*root_ns;+structckpt_hdr_user_struct*h;+intns_objref;+intret,i,unsaved_ns_nr=0;+structuser_struct*save_u;+structuser_struct**unsaved_creators;+intstep=1,size;++/* if we've already saved the userns, then life is good */+ns_objref=ckpt_obj_lookup(ctx,u->user_ns,CKPT_OBJ_USER_NS);+if(ns_objref)+gotowrite_user;++root_ns=task_cred_xxx(ctx->root_task,user)->user_ns;++if(u->user_ns==root_ns)+gotosave_last_ns;++size=UNSAVED_STRIDE*sizeof(structuser_struct*);+unsaved_creators=kmalloc(size,GFP_KERNEL);+if(!unsaved_creators)+return-ENOMEM;+save_u=u;+do{+ns=save_u->user_ns;+save_u=ns->creator;+if(ckpt_obj_lookup(ctx,save_u,CKPT_OBJ_USER))+gotofound;+unsaved_creators[unsaved_ns_nr++]=save_u;+if(unsaved_ns_nr==step*UNSAVED_STRIDE){+step++;+size=step*UNSAVED_STRIDE*sizeof(structuser_struct*);+unsaved_creators=krealloc(unsaved_creators,size,+GFP_KERNEL);+if(!unsaved_creators)+return-ENOMEM;+}+}while(ns!=root_ns);++found:+for(i=unsaved_ns_nr-1;i>=0;i--){+ret=checkpoint_obj(ctx,unsaved_creators[i],CKPT_OBJ_USER);+if(ret<0){+kfree(unsaved_creators);+returnret;+}+}+kfree(unsaved_creators);++save_last_ns:+ns_objref=checkpoint_obj(ctx,u->user_ns,CKPT_OBJ_USER_NS);+if(ns_objref<0)+returnns_objref;++write_user:+h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_USER);+if(!h)+return-ENOMEM;++h->uid=u->uid;+h->userns_ref=ns_objref;++/* write out the user_struct */+ret=ckpt_write_obj(ctx,(structckpt_hdr*)h);+ckpt_hdr_put(ctx,h);++returnret;+}++intcheckpoint_user(structckpt_ctx*ctx,void*ptr)+{+returndo_checkpoint_user(ctx,(structuser_struct*)ptr);+}++staticintmay_setuid(structuser_namespace*ns,uid_tuid)+{+/*+*thisnextcheckwillonedaybecome+*ifcapable(CAP_SETUID,ns)return1;+*followedbyuid_equiv(current_userns,current_uid,ns,uid)+*insteadofjustuids.+*/+if(capable(CAP_SETUID))+return1;++/*+*thismaybeoverlystrict,butsincewemightendup+*restartingaprivilegedprogramhere,wedonotwant+*someonewithonlyCAP_SYS_ADMINbutnoCAP_SETUIDto+*beabletocreaterandomuseridseveninausernshe+*created.+*/+if(current_user()->user_ns!=ns)+return0;+if(current_uid()==uid||+current_euid()==uid||+current_suid()==uid)+return1;+return0;+}++staticstructuser_struct*do_restore_user(structckpt_ctx*ctx)+{+structuser_struct*u;+structuser_namespace*ns;+structckpt_hdr_user_struct*h;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_USER);+if(IS_ERR(h))+returnERR_PTR(PTR_ERR(h));++ns=ckpt_obj_fetch(ctx,h->userns_ref,CKPT_OBJ_USER_NS);+if(IS_ERR(ns)){+u=ERR_PTR(PTR_ERR(ns));+gotoout;+}++if(!may_setuid(ns,h->uid)){+u=ERR_PTR(-EPERM);+gotoout;+}+u=alloc_uid(ns,h->uid);+if(!u)+u=ERR_PTR(-EINVAL);++out:+ckpt_hdr_put(ctx,h);+returnu;+}++void*restore_user(structckpt_ctx*ctx)+{+return(void*)do_restore_user(ctx);+}++#endif
@@ -103,3 +104,91 @@ void free_user_ns(struct kref *kref)schedule_work(&ns->destroyer);}EXPORT_SYMBOL(free_user_ns);++#ifdef CONFIG_CHECKPOINT+/*+*do_checkpoint_userns()isonlycalledfromdo_checkpoint_user().+*Whencalled,wealwaysknowthateither:+*1.Thisistheroot_ns(user_nsofthectx->root_task),+*inwhichcaseweseth->creator_ref=0.+*or+*2.Thecreatorhasalreadybeenwrittenouttothe+*checkpointimage(andsavedintheobjhash)+*/+staticintdo_checkpoint_userns(structckpt_ctx*ctx,structuser_namespace*ns)+{+structckpt_hdr_user_ns*h;+structuser_namespace*root_ns;+intcreator_ref=0;+intret;++root_ns=task_cred_xxx(ctx->root_task,user)->user_ns;+if(ns!=root_ns){+creator_ref=ckpt_obj_lookup(ctx,ns->creator,CKPT_OBJ_USER);+if(!creator_ref)+return-EINVAL;+}++h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_USER_NS);+if(!h)+return-ENOMEM;+h->creator_ref=creator_ref;+ret=ckpt_write_obj(ctx,(structckpt_hdr*)h);+ckpt_hdr_put(ctx,h);++returnret;+}++intcheckpoint_userns(structckpt_ctx*ctx,void*ptr)+{+returndo_checkpoint_userns(ctx,(structuser_namespace*)ptr);+}++staticstructuser_namespace*do_restore_userns(structckpt_ctx*ctx)+{+structckpt_hdr_user_ns*h;+structuser_namespace*ns;+structuser_struct*new_root,*creator;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_USER_NS);+if(IS_ERR(h))+returnERR_PTR(PTR_ERR(h));++if(!h->creator_ref){+ns=get_user_ns(current_user_ns());+gotoout;+}++creator=ckpt_obj_fetch(ctx,h->creator_ref,CKPT_OBJ_USER);+if(IS_ERR(creator)){+ns=ERR_PTR(-EINVAL);+gotoout;+}++ns=new_user_ns(creator,&new_root);+if(IS_ERR(ns))+gotoout;++/* ns only referenced from new_root, which we discard below */+get_user_ns(ns);++/* new_user_ns() doesn't bump creator's refcount */+get_uid(creator);++/*+*Freethenewrootuser.Ifweactuallyneededit,+*thenitwillshowuplaterinthecheckpointimage+*Theobjhashwillkeeptheusernspinneduntilthen.+*/+free_uid(new_root);+out:+ctx->stats.user_ns++;+ckpt_hdr_put(ctx,h);+returnns;+}++void*restore_userns(structckpt_ctx*ctx)+{+return(void*)do_restore_userns(ctx);+}+#endif
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Matt Helsley <redacted>
The CHECKPOINTING state prevents userspace from unfreezing tasks until
sys_checkpoint() is finished. When doing container checkpoint userspace
will do:
echo FROZEN > /cgroups/my_container/freezer.state
...
rc = sys_checkpoint( <pid of container root> );
To ensure a consistent checkpoint image userspace should not be allowed
to thaw the cgroup (echo THAWED > /cgroups/my_container/freezer.state)
during checkpoint.
"CHECKPOINTING" can only be set on a "FROZEN" cgroup using the checkpoint
system call. Once in the "CHECKPOINTING" state, the cgroup may not leave until
the checkpoint system call is finished and ready to return. Then the
freezer state returns to "FROZEN". Writing any new state to freezer.state while
checkpointing will return EBUSY. These semantics ensure that userspace cannot
unfreeze the cgroup midway through the checkpoint system call.
The cgroup_freezer_begin_checkpoint() and cgroup_freezer_end_checkpoint()
make relatively few assumptions about the task that is passed in. However the
way they are called in do_checkpoint() assumes that the root of the container
is in the same freezer cgroup as all the other tasks that will be
checkpointed.
Notes:
As a side-effect this prevents the multiple tasks from entering the
CHECKPOINTING state simultaneously. All but one will get -EBUSY.
Signed-off-by: Oren Laadan <redacted>
Signed-off-by: Matt Helsley <redacted>
Cc: Paul Menage <redacted>
Cc: Li Zefan <redacted>
Cc: Cedric Le Goater <redacted>
---
Documentation/cgroups/freezer-subsystem.txt | 10 ++
include/linux/freezer.h | 8 ++
kernel/cgroup_freezer.c | 166 ++++++++++++++++++++-------
3 files changed, 142 insertions(+), 42 deletions(-)
@@ -100,3 +100,13 @@ things happens: and returns EINVAL) 3) The tasks that blocked the cgroup from entering the "FROZEN" state disappear from the cgroup's set of tasks.++When the cgroup freezer is used to guard container checkpoint operations the+freezer.state may be "CHECKPOINTING". "CHECKPOINTING" can only be set on a+"FROZEN" cgroup using the checkpoint system call. Once in the "CHECKPOINTING"+state, the cgroup may not leave until the checkpoint system call returns the+freezer state to "FROZEN". Writing any new state to freezer.state while+checkpointing will return EBUSY. These semantics ensure that userspace cannot+unfreeze the cgroup midway through the checkpoint system call. Note that,+unlike "FROZEN" and "FREEZING", there is no corresponding "CHECKPOINTED"+state.
@@ -63,6 +64,44 @@ int cgroup_freezing_or_frozen(struct task_struct *task)return(state==CGROUP_FREEZING)||(state==CGROUP_FROZEN);}+/* Task is frozen or will freeze immediately when next it gets woken */+staticboolis_task_frozen_enough(structtask_struct*task)+{+returnfrozen(task)||+(task_is_stopped_or_traced(task)&&freezing(task));+}++/*+*callermustholdfreezer->lock+*/+staticvoidupdate_freezer_state(structcgroup*cgroup,+structfreezer*freezer)+{+structcgroup_iterit;+structtask_struct*task;+unsignedintnfrozen=0,ntotal=0;++cgroup_iter_start(cgroup,&it);+while((task=cgroup_iter_next(cgroup,&it))){+ntotal++;+if(is_task_frozen_enough(task))+nfrozen++;+}++/*+*TransitiontoFROZENwhennonewtaskscanbeaddedensures+*thatweneverexistintheFROZENstatewhilethereareunfrozen+*tasks.+*/+if(nfrozen==ntotal)+freezer->state=CGROUP_FROZEN;+elseif(nfrozen>0)+freezer->state=CGROUP_FREEZING;+else+freezer->state=CGROUP_THAWED;+cgroup_iter_end(cgroup,&it);+}+/**cgroups_write_string()limitsthesizeoffreezerstatestringsto*CGROUP_LOCAL_BUFFER_SIZE
@@ -153,13 +193,6 @@ static void freezer_destroy(struct cgroup_subsys *ss,kfree(cgroup_freezer(cgroup));}-/* Task is frozen or will freeze immediately when next it gets woken */-staticboolis_task_frozen_enough(structtask_struct*task)-{-returnfrozen(task)||-(task_is_stopped_or_traced(task)&&freezing(task));-}-/**Thecalltocgroup_lock()inthefreezer.statewritemethodprevents*awritetothatfileracingagainstanattach,andhencethe
@@ -317,7 +319,10 @@ static int freezer_change_state(struct cgroup *cgroup,freezer=cgroup_freezer(cgroup);spin_lock_irq(&freezer->lock);-+if(freezer->state==CGROUP_CHECKPOINTING){+retval=-EBUSY;+gotoout;+}update_freezer_state(cgroup,freezer);if(goal_state==freezer->state)gotoout;
@@ -385,3 +390,80 @@ struct cgroup_subsys freezer_subsys = {.fork=freezer_fork,.exit=NULL,};++#ifdef CONFIG_CHECKPOINT+/*+*Callerisexpectedtoensurethatneither@pnor@qmaychangeits+*freezercgroupduringthistestinawaythatmayaffecttheresult.+*E.g.,whencalledformc/r,@pmustbeinCHECKPOINTINGcgroup,so+*maynotchangecgroup,andeither@qisalsothere,orisnotthere+*andmaynotjoin.+*/+intin_same_cgroup_freezer(structtask_struct*p,structtask_struct*q)+{+structcgroup_subsys_state*p_css,*q_css;++task_lock(p);+p_css=task_subsys_state(p,freezer_subsys_id);+task_unlock(p);++task_lock(q);+q_css=task_subsys_state(q,freezer_subsys_id);+task_unlock(q);++return(p_css==q_css);+}++/*+*cgroupfreezerstatechangesmadewithouttheaidofthecgroupfilesystem+*mustgothroughthisfunctiontoensureproperlockingisobserved.+*/+staticintfreezer_checkpointing(structtask_struct*task,+enumfreezer_statenext_state)+{+structfreezer*freezer;+structcgroup_subsys_state*css;+enumfreezer_statestate;++task_lock(task);+css=task_subsys_state(task,freezer_subsys_id);+css_get(css);/* make sure freezer doesn't go away */+freezer=container_of(css,structfreezer,css);+task_unlock(task);++if(freezer->state==CGROUP_FREEZING){+/* May be in middle of a lazy FREEZING -> FROZEN transition */+if(cgroup_lock_live_group(css->cgroup)){+spin_lock_irq(&freezer->lock);+update_freezer_state(css->cgroup,freezer);+spin_unlock_irq(&freezer->lock);+cgroup_unlock();+}+}++spin_lock_irq(&freezer->lock);+state=freezer->state;+if((state==CGROUP_FROZEN&&next_state==CGROUP_CHECKPOINTING)||+(state==CGROUP_CHECKPOINTING&&next_state==CGROUP_FROZEN))+freezer->state=next_state;+spin_unlock_irq(&freezer->lock);+css_put(css);+returnstate;+}++intcgroup_freezer_begin_checkpoint(structtask_struct*task)+{+if(freezer_checkpointing(task,CGROUP_CHECKPOINTING)!=CGROUP_FROZEN)+return-EBUSY;+return0;+}++voidcgroup_freezer_end_checkpoint(structtask_struct*task)+{+/*+*Ifweweren'tinCHECKPOINTINGstatethenuserspacecouldhave+*unfrozenataskandgivenusaninconsistentcheckpointimage+*/+WARN_ON(freezer_checkpointing(task,CGROUP_FROZEN)!=CGROUP_CHECKPOINTING);+}+#endif /* CONFIG_CHECKPOINT */
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Matt Helsley <redacted>
Update stale comments regarding locking order and add a little more detail
so it's easier to follow the locking between the cgroup freezer and the
power management freezer code.
Signed-off-by: Matt Helsley <redacted>
Cc: Oren Laadan <redacted>
Cc: Cedric Le Goater <redacted>
Cc: Paul Menage <redacted>
Cc: Li Zefan <redacted>
---
kernel/cgroup_freezer.c | 21 +++++++++++++--------
1 files changed, 13 insertions(+), 8 deletions(-)
@@ -88,10 +88,10 @@ struct cgroup_subsys freezer_subsys;/* Locks taken and their ordering*-------------------------------*css_set_lock*cgroup_mutex(AKAcgroup_lock)-*task->alloc_lock(AKAtask_lock)*freezer->lock+*css_set_lock+*task->alloc_lock(AKAtask_lock)*task->sighand->siglock**cgroupcodeforcescss_set_locktobetakenbeforetask->alloc_lock
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
To support c/r of restart-blocks (system call that need to be
restarted because they were interrupted but there was no userspace
visible side-effect), export restart-block callbacks for poll()
and futex() syscalls.
More details on c/r of restart-blocks and how it works in the
following patch.
Signed-off-by: Oren Laadan <redacted>
Acked-by: Serge Hallyn <redacted>
---
fs/select.c | 2 +-
include/linux/futex.h | 11 +++++++++++
include/linux/poll.h | 3 +++
include/linux/posix-timers.h | 6 ++++++
kernel/compat.c | 4 ++--
kernel/futex.c | 12 +-----------
kernel/posix-timers.c | 2 +-
7 files changed, 25 insertions(+), 15 deletions(-)
@@ -205,7 +205,7 @@ static int no_timer_create(struct k_itimer *new_timer)/**Returnnonzeroifweknowapriorithisclockid_tvalueisbogus.*/-staticinlineintinvalid_clockid(constclockid_twhich_clock)+intinvalid_clockid(constclockid_twhich_clock){if(which_clock<0)/* CPU clock, posix_cpu_* will check it */return0;
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Add a interface to postpone an action until the end of the entire
checkpoint or restart operation. This is useful when during the
scan of tasks an operation cannot be performed in place, to avoid
the need for a second scan.
One use case is when restoring an ipc shared memory region that has
been deleted (but is still attached), during restart it needs to be
create, attached and then deleted. However, creation and attachment
are performed in distinct locations, so deletion can not be performed
on the spot. Instead, this work (delete) is deferred until later.
(This example is in one of the following patches).
This interface allows chronic procrastination in the kernel:
deferqueue_create(void):
Allocates and returns a new deferqueue.
deferqueue_run(deferqueue):
Executes all the pending works in the queue. Returns the number
of works executed, or an error upon the first error reported by
a deferred work.
deferqueue_add(deferqueue, data, size, func, dtor):
Enqueue a deferred work. @function is the callback function to
do the work, which will be called with @data as an argument.
@size tells the size of data. @dtor is a destructor callback
that is invoked for deferred works remaining in the queue when
the queue is destroyed. NOTE: for a given deferred work, @dtor
is _not_ called if @func was already called (regardless of the
return value of the latter).
deferqueue_destroy(deferqueue):
Free the deferqueue and any queued items while invoking the
@dtor callback for each queued item.
Why aren't we using the existing kernel workqueue mechanism? We need
to defer to work until the end of the operation: not earlier, since we
need other things to be in place; not later, to not block waiting for
it. However, the workqueue schedules the work for 'some time later'.
Also, the kernel workqueue may run in any task context, but we require
many times that an operation be run in the context of some specific
restarting task (e.g., restoring IPC state of a certain ipc_ns).
Instead, this mechanism is a simple way for the c/r operation as a
whole, and later a task in particular, to defer some action until
later (but not arbitrarily later) _in the restore_ operation.
Changelog[v17]
- Fix deferqueue_add() function
Signed-off-by: Oren Laadan <redacted>
---
checkpoint/Kconfig | 5 ++
include/linux/deferqueue.h | 58 +++++++++++++++++++++++
kernel/Makefile | 1 +
kernel/deferqueue.c | 109 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 173 insertions(+), 0 deletions(-)
create mode 100644 include/linux/deferqueue.h
create mode 100644 kernel/deferqueue.c
@@ -2,10 +2,15 @@# implemented the hooks for processor state etc. needed by the# core checkpoint/restart code.+configDEFERQUEUE+bool+defaultn+configCHECKPOINTbool"Checkpoint/restart (EXPERIMENTAL)"depends onCHECKPOINT_SUPPORT&&EXPERIMENTALdepends onCGROUP_FREEZER+selectDEFERQUEUEhelpApplicationcheckpoint/restartistheabilitytosavethestateofarunningapplicationsothatitcanlaterresume
@@ -0,0 +1,109 @@+/*+*Infrastructuretomanagedeferredwork+*+*Thisdiffersfromaworkqueueinthattheworkmustbedeferred+*untilspecificallyrunbythecaller.+*+*Astheonlyusercurrentlyischeckpoint/restart,whichhas+*verysimpleusage,thelockingiskeptsimple.Addingrules+*isprotectedbythehead->lock.Butdeferqueue_run()isonly+*calledonce,afterallentrieshavebeenadded.Soitisnot+*protected.Similarly,_destroyisonlycalledoncewhenthe+*ckpt_ctxisreleeased,soitisnotlockedorrefcounted.These+*canofcoursebeaddedifneededbyotherusers.+*+*Whynotuseworkqueue?Weneedtodeferworkuntiltheendofan+*operation:notearlier,sinceweneedotherthingstobeinplace;+*notlater,tonotblockwaitingforit.However,theworkqueue+*schedulestheworkfor'sometimelater'.Also,workqueuemayrun+*inanytaskcontext,butwerequiremanytimesthatanoperation+*beruninthecontextofsomespecificrestartingtask(e.g.,+*restoringIPCstateofacertainipc_ns).+*+*Instead,thismechanismisasimplewayforthec/roperationasa+*whole,andlaterataskinparticular,todefersomeactionuntil+*later(butnotarbitrarilylater)_intherestore_operation.+*+*Copyright(C)2009OrenLaadan+*+*ThisfileissubjecttothetermsandconditionsoftheGNUGeneralPublic+*License.SeethefileCOPYINGinthemaindirectoryoftheLinux+*distributionformoredetails.+*+*/++#include<linux/module.h>+#include<linux/kernel.h>+#include<linux/deferqueue.h>++structdeferqueue_head*deferqueue_create(void)+{+structdeferqueue_head*h=kmalloc(sizeof(*h),GFP_KERNEL);+if(h){+spin_lock_init(&h->lock);+INIT_LIST_HEAD(&h->list);+}+returnh;+}++voiddeferqueue_destroy(structdeferqueue_head*h)+{+if(!list_empty(&h->list)){+structdeferqueue_entry*dq,*n;++pr_debug("%s: freeing non-empty queue\n",__func__);+list_for_each_entry_safe(dq,n,&h->list,list){+dq->destructor(dq->data);+list_del(&dq->list);+kfree(dq);+}+}+kfree(h);+}++intdeferqueue_add(structdeferqueue_head*head,void*data,intsize,+deferqueue_func_tfunc,deferqueue_func_tdtor)+{+structdeferqueue_entry*dq;++dq=kmalloc(sizeof(*dq)+size,GFP_KERNEL);+if(!dq)+return-ENOMEM;++dq->function=func;+dq->destructor=dtor;+memcpy(dq->data,data,size);++pr_debug("%s: adding work %p func %p dtor %p\n",+__func__,dq,func,dtor);+spin_lock(&head->lock);+list_add_tail(&dq->list,&head->list);+spin_unlock(&head->lock);+return0;+}++/*+*deferqueue_run-performallworkintheworkqueue+*@head:deferqueue_headfromwhichtorun+*+*returns:numberofworksperformed,or<0onerror+*/+intdeferqueue_run(structdeferqueue_head*head)+{+structdeferqueue_entry*dq,*n;+intnr=0;+intret;++list_for_each_entry_safe(dq,n,&head->list,list){+pr_debug("doing work %p function %p\n",dq,dq->function);+/* don't call destructor - function callback should do it */+ret=dq->function(dq->data);+if(ret<0)+pr_debug("wq function failed %d\n",ret);+list_del(&dq->list);+kfree(dq);+nr++;+}++returnnr;+}
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
During restart, we need to allocate ipc objects that with the same
identifiers as recorded during checkpoint. Modify the allocation
code allow an in-kernel caller to request a specific ipc identifier.
The system call interface remains unchanged.
Signed-off-by: Oren Laadan <redacted>
---
ipc/msg.c | 17 ++++++++++++-----
ipc/sem.c | 17 ++++++++++++-----
ipc/shm.c | 19 +++++++++++++------
ipc/util.c | 42 +++++++++++++++++++++++++++++-------------
ipc/util.h | 9 +++++----
5 files changed, 71 insertions(+), 33 deletions(-)
@@ -381,7 +397,7 @@ retry:elseif(!err)err=-ENOMEM;else-err=ops->getnew(ns,params);+err=ops->getnew(ns,params,req_id);}else{/* ipc object has been locked by ipc_findkey() */
@@ -94,7 +94,7 @@ void __init ipc_init_proc_interface(const char *path, const char *header,#define ipcid_to_idx(id) ((id) % SEQ_MULTIPLIER)/* must be called with ids->rw_mutex acquired for writing */-intipc_addid(structipc_ids*,structkern_ipc_perm*,int);+intipc_addid(structipc_ids*,structkern_ipc_perm*,int,int);/* must be called with ids->rw_mutex acquired for reading */intipc_get_maxid(structipc_ids*);
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Dan Smith <redacted>
Implement the s390 arch-specific checkpoint/restart helpers. This
is on top of Oren Laadan's c/r code.
With these, I am able to checkpoint and restart simple programs as per
Oren's patch intro. While on x86 I never had to freeze a single task
to checkpoint it, on s390 I do need to. That is a prereq for consistent
snapshots (esp with multiple processes) anyway so I don't see that as
a problem.
Changelog:
Jun 15:
. Fix checkpoint and restart compat wrappers
May 28:
. Export asm/checkpoint_hdr.h to userspace
. Define CKPT_ARCH_ID for S390
Apr 11:
. Introduce ckpt_arch_vdso()
Feb 27:
. Add checkpoint_s390.h
. Fixed up save and restore of PSW, with the non-address bits
properly masked out
Feb 25:
. Make checkpoint_hdr.h safe for inclusion in userspace
. Replace comment about vsdo code
. Add comment about restoring access registers
. Write and read an empty ckpt_hdr_head_arch record to appease
code (mktree) that expects it to be there
. Utilize NUM_CKPT_WORDS in checkpoint_hdr.h
Feb 24:
. Use CKPT_COPY() to unify the un/loading of cpu and mm state
. Fix fprs definition in ckpt_hdr_cpu
. Remove debug WARN_ON() from checkpoint.c
Feb 23:
. Macro-ize the un/packing of trace flags
. Fix the crash when externally-linked
. Break out the restart functions into restart.c
. Remove unneeded s390_enable_sie() call
Jan 30:
. Switched types in ckpt_hdr_cpu to __u64 etc.
(Per Oren suggestion)
. Replaced direct inclusion of structs in
ckpt_hdr_cpu with the struct members.
(Per Oren suggestion)
. Also ended up adding a bunch of new things
into restart (mm_segment, ksp, etc) in vain
attempt to get code using fpu to not segfault
after restart.
Signed-off-by: Serge E. Hallyn <redacted>
Signed-off-by: Dan Smith <redacted>
---
arch/s390/include/asm/Kbuild | 1 +
arch/s390/include/asm/checkpoint_hdr.h | 89 +++++++++++++++
arch/s390/include/asm/unistd.h | 4 +-
arch/s390/kernel/compat_wrapper.S | 14 +++
arch/s390/kernel/syscalls.S | 2 +
arch/s390/mm/Makefile | 1 +
arch/s390/mm/checkpoint.c | 183 ++++++++++++++++++++++++++++++++
arch/s390/mm/checkpoint_s390.h | 23 ++++
include/linux/checkpoint_hdr.h | 2 +
9 files changed, 318 insertions(+), 1 deletions(-)
create mode 100644 arch/s390/include/asm/checkpoint_hdr.h
create mode 100644 arch/s390/mm/checkpoint.c
create mode 100644 arch/s390/mm/checkpoint_s390.h
@@ -0,0 +1,183 @@+/*+*Checkpoint/restart-architecturespecificsupportfors390+*+*CopyrightIBMCorp.2009+*+*ThisfileissubjecttothetermsandconditionsoftheGNUGeneralPublic+*License.SeethefileCOPYINGinthemaindirectoryoftheLinux+*distributionformoredetails.+*/++#include<linux/kernel.h>+#include<asm/system.h>+#include<asm/pgtable.h>+#include<asm/elf.h>++#include<linux/checkpoint.h>+#include<linux/checkpoint_hdr.h>++/**************************************************************************+*Checkpoint+*/++staticvoids390_copy_regs(intop,structckpt_hdr_cpu*h,+structtask_struct*t)+{+structpt_regs*regs=task_pt_regs(t);+structthread_struct*thr=&t->thread;++/* Save the whole PSW to facilitate forensic debugging, but only+*restoretheaddressportiontoavoidlettinguserspacedo+*badthingsbymanipulatingitsvalue.+*/+if(op==CKPT_CPT){+CKPT_COPY(op,h->psw_t_addr,regs->psw.addr);+}else{+regs->psw.addr&=~PSW_ADDR_INSN;+regs->psw.addr|=h->psw_t_addr;+}++CKPT_COPY(op,h->args[0],regs->args[0]);+CKPT_COPY(op,h->orig_gpr2,regs->orig_gpr2);+CKPT_COPY(op,h->svcnr,regs->svcnr);+CKPT_COPY(op,h->ilc,regs->ilc);+CKPT_COPY(op,h->ieee_instruction_pointer,+thr->ieee_instruction_pointer);+CKPT_COPY(op,h->psw_t_mask,regs->psw.mask);+CKPT_COPY(op,h->fpc,thr->fp_regs.fpc);+CKPT_COPY(op,h->starting_addr,thr->per_info.starting_addr);+CKPT_COPY(op,h->ending_addr,thr->per_info.ending_addr);+CKPT_COPY(op,h->address,thr->per_info.lowcore.words.address);+CKPT_COPY(op,h->perc_atmid,thr->per_info.lowcore.words.perc_atmid);+CKPT_COPY(op,h->access_id,thr->per_info.lowcore.words.access_id);+CKPT_COPY(op,h->single_step,thr->per_info.single_step);+CKPT_COPY(op,h->instruction_fetch,thr->per_info.instruction_fetch);++CKPT_COPY_ARRAY(op,h->gprs,regs->gprs,NUM_GPRS);+CKPT_COPY_ARRAY(op,h->fprs,thr->fp_regs.fprs,NUM_FPRS);+CKPT_COPY_ARRAY(op,h->acrs,thr->acrs,NUM_ACRS);+CKPT_COPY_ARRAY(op,h->per_control_regs,+thr->per_info.control_regs.words.cr,NUM_CR_WORDS);+}++staticvoids390_mm(intop,structckpt_hdr_mm_context*h,+structmm_struct*mm)+{+CKPT_COPY(op,h->noexec,mm->context.noexec);+CKPT_COPY(op,h->has_pgste,mm->context.has_pgste);+CKPT_COPY(op,h->alloc_pgste,mm->context.alloc_pgste);+CKPT_COPY(op,h->asce_bits,mm->context.asce_bits);+CKPT_COPY(op,h->asce_limit,mm->context.asce_limit);+}++intcheckpoint_thread(structckpt_ctx*ctx,structtask_struct*t)+{+return0;+}++/* dump the cpu state and registers of a given task */+intcheckpoint_cpu(structckpt_ctx*ctx,structtask_struct*t)+{+structckpt_hdr_cpu*h;+intret;++h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_CPU);+if(!h)+return-ENOMEM;++s390_copy_regs(CKPT_CPT,h,t);++ret=ckpt_write_obj(ctx,(structckpt_hdr*)h);+ckpt_hdr_put(ctx,h);++returnret;+}++/* Write an empty header since it is assumed to be there */+intcheckpoint_write_header_arch(structckpt_ctx*ctx)+{+structckpt_hdr_header_arch*h;+intret;++h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_HEADER_ARCH);+if(!h)+return-ENOMEM;++ret=ckpt_write_obj(ctx,(structckpt_hdr*)h);+ckpt_hdr_put(ctx,h);++returnret;+}++intcheckpoint_mm_context(structckpt_ctx*ctx,structmm_struct*mm)+{+structckpt_hdr_mm_context*h;+intret;++h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_MM_CONTEXT);+if(!h)+return-ENOMEM;++s390_mm(CKPT_CPT,h,mm);++ret=ckpt_write_obj(ctx,(structckpt_hdr*)h);+ckpt_hdr_put(ctx,h);++returnret;+}++/**************************************************************************+*Restart+*/++intrestore_thread(structckpt_ctx*ctx)+{+return0;+}++intrestore_cpu(structckpt_ctx*ctx)+{+structckpt_hdr_cpu*h;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_CPU);+if(IS_ERR(h))+returnPTR_ERR(h);++s390_copy_regs(CKPT_RST,h,current);++/* s390 does not restore the access registers after a syscall,+*butdoesonataskswitch.Sincewe'reswitchingtasks(in+*away),weneedtoreplicatethatbehaviorhere.+*/+restore_access_regs(h->acrs);++ckpt_hdr_put(ctx,h);+return0;+}++intrestore_read_header_arch(structckpt_ctx*ctx)+{+structckpt_hdr_header_arch*h;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_HEADER_ARCH);+if(IS_ERR(h))+returnPTR_ERR(h);++ckpt_hdr_put(ctx,h);+return0;+}+++intrestore_mm_context(structckpt_ctx*ctx,structmm_struct*mm)+{+structckpt_hdr_mm_context*h;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_MM_CONTEXT);+if(IS_ERR(h))+returnPTR_ERR(h);++s390_mm(CKPT_RST,h,mm);++ckpt_hdr_put(ctx,h);+return0;+}
@@ -87,7 +87,9 @@ enum {/* architecture */enum{+/* do not change order (will break ABI) */CKPT_ARCH_X86_32=1,+CKPT_ARCH_S390X,};/* shared objrects (objref) */
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
During pipes c/r pipes we need to save and restore pipe buffers. But
do_splice() requires two file descriptors, therefore we can't use it,
as we always have one file descriptor (checkpoint image) and one
pipe_inode_info.
This patch exports interfaces that work at the pipe_inode_info level,
namely link_pipe(), do_splice_to() and do_splice_from(). They are used
in the following patch to to save and restore pipe buffers without
unnecessary data copy.
It slightly modifies both do_splice_to() and do_splice_from() to
detect the case of pipe-to-pipe transfer, in which case they invoke
splice_pipe_to_pipe() directly.
Signed-off-by: Oren Laadan <redacted>
---
fs/splice.c | 61 ++++++++++++++++++++++++++++++++---------------
include/linux/splice.h | 9 +++++++
2 files changed, 50 insertions(+), 20 deletions(-)
@@ -1055,18 +1055,43 @@ ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe, struct file *out,EXPORT_SYMBOL(generic_splice_sendpage);/*+*Aftertheinodeslimmingpatch,i_pipe/i_bdev/i_cdevsharethesame+*location,sochecking->i_pipeisnotenoughtoverifythatthisisa+*pipe.+*/+staticinlinestructpipe_inode_info*pipe_info(structinode*inode)+{+if(S_ISFIFO(inode->i_mode))+returninode->i_pipe;++returnNULL;+}++staticintsplice_pipe_to_pipe(structpipe_inode_info*ipipe,+structpipe_inode_info*opipe,+size_tlen,unsignedintflags);++/**Attempttoinitiateasplicefrompipetofile.*/-staticlongdo_splice_from(structpipe_inode_info*pipe,structfile*out,-loff_t*ppos,size_tlen,unsignedintflags)+longdo_splice_from(structpipe_inode_info*pipe,structfile*out,+loff_t*ppos,size_tlen,unsignedintflags){ssize_t(*splice_write)(structpipe_inode_info*,structfile*,loff_t*,size_t,unsignedint);+structpipe_inode_info*opipe;intret;if(unlikely(!(out->f_mode&FMODE_WRITE)))return-EBADF;+/* When called directly (e.g. from c/r) output may be a pipe */+opipe=pipe_info(out->f_path.dentry->d_inode);+if(opipe){+BUG_ON(opipe==pipe);+returnsplice_pipe_to_pipe(pipe,opipe,len,flags);+}+if(unlikely(out->f_flags&O_APPEND))return-EINVAL;
@@ -1084,17 +1109,25 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,/**Attempttoinitiateasplicefromafiletoapipe.*/-staticlongdo_splice_to(structfile*in,loff_t*ppos,-structpipe_inode_info*pipe,size_tlen,-unsignedintflags)+longdo_splice_to(structfile*in,loff_t*ppos,+structpipe_inode_info*pipe,size_tlen,+unsignedintflags){ssize_t(*splice_read)(structfile*,loff_t*,structpipe_inode_info*,size_t,unsignedint);+structpipe_inode_info*ipipe;intret;if(unlikely(!(in->f_mode&FMODE_READ)))return-EBADF;+/* When called firectly (e.g. from c/r) input may be a pipe */+ipipe=pipe_info(in->f_path.dentry->d_inode);+if(ipipe){+BUG_ON(ipipe==pipe);+returnsplice_pipe_to_pipe(ipipe,pipe,len,flags);+}+ret=rw_verify_area(READ,in,ppos,len);if(unlikely(ret<0))returnret;
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Add a 'users' count to objhash items, and, for a !CHECKPOINT_SUBTREE
checkpoint, return an error code if the actual objects' counts are
higher, indicating leaks (references to the objects from a task not
being checkpointed). Of course, by this time most of the checkpoint
image has been written out to disk, so this is purely advisory. But
then, it's probably naive to argue that anything more than an advisory
'this went wrong' error code is useful.
The comparison of the objhash user counts to object refcounts as a
basis for checking for leaks comes from Alexey's OpenVZ-based c/r
patchset.
"Leak detection" occurs _before_ any real state is saved, as a
pre-step. This prevents races due to sharing with outside world where
the sharing ceases before the leak test takes place, thus protecting
the checkpoint image from inconsistencies.
Once leak testing concludes, checkpoint will proceed. Because objects
are already in the objhash, checkpoint_obj() cannot distinguish
between the first and subsequent encounters. This is solved with a
flag (CKPT_OBJ_CHECKPOINTED) per object.
Two additional checks take place during checkpoint: for objects that
were created during, and objects destroyed, while the leak-detection
pre-step took place.
Changelog[v17]:
- Leak detection is performed in two-steps
- Detect reverse-leaks (objects disappearing unexpectedly)
- Skip reverse-leak detection if ops->ref_users isn't defined
Signed-off-by: Oren Laadan <redacted>
---
checkpoint/checkpoint.c | 36 ++++++++++
checkpoint/objhash.c | 153 +++++++++++++++++++++++++++++++++++++++++++-
checkpoint/process.c | 5 ++
include/linux/checkpoint.h | 5 ++
4 files changed, 196 insertions(+), 3 deletions(-)
@@ -380,6 +380,20 @@ static int checkpoint_pids(struct ckpt_ctx *ctx)returnret;}+staticintcollect_objects(structckpt_ctx*ctx)+{+intn,ret=0;++for(n=0;n<ctx->nr_tasks;n++){+ckpt_debug("dumping task #%d\n",n);+ret=ckpt_collect_task(ctx,ctx->tasks_arr[n]);+if(ret<0)+break;+}++returnret;+}+/* count number of tasks in tree (and optionally fill pid's in array) */staticinttree_count_tasks(structckpt_ctx*ctx){
@@ -619,6 +633,21 @@ long do_checkpoint(struct ckpt_ctx *ctx, pid_t pid)if(ret<0)gotoout;+if(!(ctx->uflags&CHECKPOINT_SUBTREE)){+/*+*Verifythatallobjectsarecontained(noleaks):+*Firstcollectthemallintothewhilecountingusers+*andthencomparetotheobjects'realusercounts.+*/+ret=collect_objects(ctx);+if(ret<0)+gotoout;+if(!ckpt_obj_contained(ctx)){+ret=-EAGAIN;+gotoout;+}+}+ret=checkpoint_write_header(ctx);if(ret<0)gotoout;
@@ -628,6 +657,13 @@ long do_checkpoint(struct ckpt_ctx *ctx, pid_t pid)ret=checkpoint_all_tasks(ctx);if(ret<0)gotoout;++/* verify that all objects were indeed checkpointed */+if(!ckpt_obj_checkpointed(ctx)){+ret=-EAGAIN;+gotoout;+}+ret=checkpoint_write_tail(ctx);if(ret<0)gotoout;
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
This patch adds the checkpointing and restart of signal handling
state - 'struct sighand_struct'. Since the contents of this state
only affect userspace, no input validation is required.
Add _NSIG to kernel constants saved/tested with image header.
Number of signals (_NSIG) is arch-dependent, but is within __KERNEL__
and not visibile to userspace compile. Therefore, define per arch
CKPT_ARCH_NSIG in <asm/checkpoint_hdr.h>.
Signed-off-by: Oren Laadan <redacted>
---
arch/s390/include/asm/checkpoint_hdr.h | 8 ++
arch/x86/include/asm/checkpoint_hdr.h | 8 ++
checkpoint/Makefile | 3 +-
checkpoint/checkpoint.c | 2 +
checkpoint/objhash.c | 26 +++++
checkpoint/process.c | 20 ++++
checkpoint/restart.c | 3 +
checkpoint/signal.c | 170 ++++++++++++++++++++++++++++++++
include/linux/checkpoint.h | 7 ++
include/linux/checkpoint_hdr.h | 22 ++++
10 files changed, 268 insertions(+), 1 deletions(-)
create mode 100644 checkpoint/signal.c
@@ -524,10 +537,17 @@ static int restore_task_objs(struct ckpt_ctx *ctx)ret=restore_obj_file_table(ctx,h->files_objref);ckpt_debug("file_table: ret %d (%p)\n",ret,current->files);+if(ret<0)+gotoout;ret=restore_obj_mm(ctx,h->mm_objref);ckpt_debug("mm: ret %d (%p)\n",ret,current->mm);+if(ret<0)+gotoout;+ret=restore_obj_sighand(ctx,h->sighand_objref);+ckpt_debug("sighand: ret %d (%p)\n",ret,current->sighand);+out:ckpt_hdr_put(ctx,h);returnret;}
@@ -323,6 +323,9 @@ static int check_kernel_const(struct ckpt_hdr_const *h)/* mm */if(h->mm_saved_auxv_len!=sizeof(mm->saved_auxv))return-EINVAL;+/* signal */+if(h->signal_nsig!=_NSIG)+return-EINVAL;/* uts */if(h->uts_sysname_len!=sizeof(uts->sysname))return-EINVAL;
@@ -408,6 +414,22 @@ struct ckpt_hdr_pgarr {__u64nr_pages;/* number of pages to saved */}__attribute__((aligned(8)));+/* signals */+structckpt_hdr_sigset{+__u8sigset[CKPT_ARCH_NSIG/8];+}__attribute__((aligned(8)));++structckpt_hdr_sigaction{+__u64_sa_handler;+__u64sa_flags;+__u64sa_restorer;+structckpt_hdr_sigsetsa_mask;+}__attribute__((aligned(8)));++structckpt_hdr_sighand{+structckpt_hdrh;+structckpt_hdr_sigactionaction[0];+}__attribute__((aligned(8)));/* ipc commons */structckpt_hdr_ipcns{
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Alexey Dobriyan <redacted>
This is a mess.
Pre unified-x86 code did check for breakpoint addr
to be "< TASK_SIZE - 3 (or 7)". This was fine from security POV,
but banned valid breakpoint usage when address is close to TASK_SIZE.
E. g. 1-byte breakpoint at TASK_SIZE - 1 should be allowed, but it wasn't.
Then came commit 84929801e14d968caeb84795bfbb88f04283fbd9
("[PATCH] x86_64: TASK_SIZE fixes for compatibility mode processes")
which for some reason touched ptrace as well and made effective
TASK_SIZE of 32-bit process depending on IA32_PAGE_OFFSET
which is not a constant!:
#define IA32_PAGE_OFFSET ((current->personality & ADDR_LIMIT_3GB) ? 0xc0000000 : 0xFFFFe000)
^^^^^^^
Maximum addr for breakpoint became dependent on personality of ptracer.
Commit also relaxed danger zone for 32-bit processes from 8 bytes to 4
not taking into account that 8-byte wide breakpoints are possible even
for 32-bit processes. This was fine, however, because 64-bit kernel
addresses are too far from 32-bit ones.
Then came utrace with commit 2047b08be67b70875d8765fc81d34ce28041bec3
("x86: x86 ptrace getreg/putreg merge") which copy-pasted and ifdeffed 32-bit
part of TASK_SIZE_OF() leaving 8-byte issue as-is.
So, what patch fixes?
1) Too strict logic near TASK_SIZE boundary -- as long as we don't cross
TASK_SIZE_MAX, we're fine.
2) Too smart logic of using breakpoints over non-existent kernel
boundary -- we should only protect against setting up after
TASK_SIZE_MAX, the rest is none of kernel business. This fixes
IA32_PAGE_OFFSET beartrap as well.
As a bonus, remove uberhack and big comment determining DR7 validness,
rewrite with clear algorithm when it's obvious what's going on.
Make DR validness checker suitable for C/R. On restart DR registers
must be checked the same way they are checked on PTRACE_POKEUSR.
Question 1: TIF_DEBUG can set even if none of breakpoints is turned on,
should this be optimized?
Question 2: Breakpoints are allowed to be globally enabled, is this a
security risk?
Signed-off-by: Alexey Dobriyan <redacted>
---
arch/x86/kernel/ptrace.c | 175 +++++++++++++++++++++++++++-------------------
1 files changed, 103 insertions(+), 72 deletions(-)
@@ -481,77 +466,123 @@ static unsigned long ptrace_get_debugreg(struct task_struct *child, int n)return0;}+staticintptrace_check_debugreg(int_32bit,+unsignedlongdr0,unsignedlongdr1,+unsignedlongdr2,unsignedlongdr3,+unsignedlongdr6,unsignedlongdr7)+{+/* Breakpoint type: 00: --x, 01: -w-, 10: undefined, 11: rw- */+unsignedintrw[4];+/* Breakpoint length: 00: 1 byte, 01: 2 bytes, 10: 8 bytes, 11: 4 bytes */+unsignedintlen[4];+intn;++if(dr0>=TASK_SIZE_MAX)+return-EINVAL;+if(dr1>=TASK_SIZE_MAX)+return-EINVAL;+if(dr2>=TASK_SIZE_MAX)+return-EINVAL;+if(dr3>=TASK_SIZE_MAX)+return-EINVAL;++for(n=0;n<4;n++){+rw[n]=(dr7>>(16+n*4))&0x3;+len[n]=(dr7>>(16+n*4+2))&0x3;++if(rw[n]==0x2)+return-EINVAL;+if(rw[n]==0x0&&len[n]!=0x0)+return-EINVAL;+if(_32bit&&len[n]==0x2)+return-EINVAL;++if(len[n]==0x0)+len[n]=1;+elseif(len[n]==0x1)+len[n]=2;+elseif(len[n]==0x2)+len[n]=8;+elseif(len[n]==0x3)+len[n]=4;+/* From now breakpoint length is in bytes. */+}++if(dr6&~0xFFFFFFFFUL)+return-EINVAL;+if(dr7&~0xFFFFFFFFUL)+return-EINVAL;++if(dr7==0)+return0;++if(dr0+len[0]>TASK_SIZE_MAX)+return-EINVAL;+if(dr1+len[1]>TASK_SIZE_MAX)+return-EINVAL;+if(dr2+len[2]>TASK_SIZE_MAX)+return-EINVAL;+if(dr3+len[3]>TASK_SIZE_MAX)+return-EINVAL;++return0;+}+staticintptrace_set_debugreg(structtask_struct*child,intn,unsignedlongdata){-inti;+unsignedlongdr0,dr1,dr2,dr3,dr6,dr7;+int_32bit;if(unlikely(n==4||n==5))return-EIO;-if(n<4&&unlikely(data>=debugreg_addr_limit(child)))-return-EIO;-+dr0=child->thread.debugreg0;+dr1=child->thread.debugreg1;+dr2=child->thread.debugreg2;+dr3=child->thread.debugreg3;+dr6=child->thread.debugreg6;+dr7=child->thread.debugreg7;switch(n){-case0:child->thread.debugreg0=data;break;-case1:child->thread.debugreg1=data;break;-case2:child->thread.debugreg2=data;break;-case3:child->thread.debugreg3=data;break;-+case0:+dr0=data;+break;+case1:+dr1=data;+break;+case2:+dr2=data;+break;+case3:+dr3=data;+break;case6:-if((data&~0xffffffffUL)!=0)-return-EIO;-child->thread.debugreg6=data;+dr6=data;break;-case7:-/*-*Sanity-checkdata.Takeonehalf-byteatoncewith-*check=(val>>(16+4*i))&0xf.Itcontainsthe-*R/WiandLENibits;bits0and1areR/Wi,andbits-*2and3areLENi.Givenalistofinvalidvalues,-*wedomask|=1<<invalid_value,sothat-*(mask>>check)&1isacorrecttestforinvalid-*values.-*-*R/Wicontainsthetypeofthebreakpoint/-*watchpoint,LENicontainsthelengthofthewatched-*datainthewatchpointcase.-*-*Theinvalidvaluesare:-*-LENi==0x10(undefined),somask|=0x0f00.[32-bit]-*-R/Wi==0x10(breakonI/Oreadsorwrites),so-*mask|=0x4444.-*-R/Wi==0x00&&LENi!=0x00,sowehavemask|=-*0x1110.-*-*Finally,mask=0x0f00|0x4444|0x1110==0x5f54.-*-*SeetheIntelManual"System Programming Guide",-*15.2.4-*-*NotethatLENi==0x10isdefinedonx86_64inlong-*mode(i.e.evenfor32-bituserspacesoftware,but-*64-bitkernel),sothex86_64maskvalueis0x5454.-*SeetheAMDmanualno.24593(AMD64SystemProgramming)-*/-#ifdef CONFIG_X86_32-#define DR7_MASK 0x5f54-#else-#define DR7_MASK 0x5554-#endif-data&=~DR_CONTROL_RESERVED;-for(i=0;i<4;i++)-if((DR7_MASK>>((data>>(16+4*i))&0xf))&1)-return-EIO;-child->thread.debugreg7=data;-if(data)-set_tsk_thread_flag(child,TIF_DEBUG);-else-clear_tsk_thread_flag(child,TIF_DEBUG);+dr7=data&~DR_CONTROL_RESERVED;break;}+_32bit=(sizeof(unsignedlong)==4);+#ifdef CONFIG_COMPAT+if(test_tsk_thread_flag(child,TIF_IA32))+_32bit=1;+#endif+if(ptrace_check_debugreg(_32bit,dr0,dr1,dr2,dr3,dr6,dr7))+return-EIO;++child->thread.debugreg0=dr0;+child->thread.debugreg1=dr1;+child->thread.debugreg2=dr2;+child->thread.debugreg3=dr3;+child->thread.debugreg6=dr6;+child->thread.debugreg7=dr7;+if(dr7)+set_tsk_thread_flag(child,TIF_DEBUG);+else+clear_tsk_thread_flag(child,TIF_DEBUG);+return0;}
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Add logic to save and restore architecture specific state, including
thread-specific state, CPU registers and FPU state.
In addition, architecture capabilities are saved in an architecure
specific extension of the header (ckpt_hdr_head_arch); Currently this
includes only FPU capabilities.
Currently only x86-32 is supported.
Changelog[v17]:
- Fix compilation for architectures that don't support checkpoint
- Validate cpu registers and TLS descriptors on restart
- Validate debug registers on restart
- Export asm/checkpoint_hdr.h to userspace
Changelog[v16]:
- All objects are preceded by ckpt_hdr (TLS and xstate_buf)
- Add architecture identifier to main header
Changelog[v14]:
- Use new interface ckpt_hdr_get/put()
- Embed struct ckpt_hdr in struct ckpt_hdr...
- Remove preempt_disable/enable() around init_fpu() and fix leak
- Revert change to pr_debug(), back to ckpt_debug()
- Move code related to task_struct to checkpoint/process.c
Changelog[v12]:
- A couple of missed calls to ckpt_hbuf_put()
- Replace obsolete ckpt_debug() with pr_debug()
Changelog[v9]:
- Add arch-specific header that details architecture capabilities;
split FPU restore to send capabilities only once.
- Test for zero TLS entries in ckpt_write_thread()
- Fix asm/checkpoint_hdr.h so it can be included from user-space
Changelog[v7]:
- Fix save/restore state of FPU
Changelog[v5]:
- Remove preempt_disable() when restoring debug registers
Changelog[v4]:
- Fix header structure alignment
Changelog[v2]:
- Pad header structures to 64 bits to ensure compatibility
- Follow Dave Hansen's refactoring of the original post
Signed-off-by: Oren Laadan <redacted>
---
arch/x86/include/asm/Kbuild | 1 +
arch/x86/include/asm/checkpoint_hdr.h | 122 ++++++++
arch/x86/include/asm/ptrace.h | 5 +
arch/x86/kernel/ptrace.c | 8 +-
arch/x86/mm/Makefile | 2 +
arch/x86/mm/checkpoint.c | 534 +++++++++++++++++++++++++++++++++
checkpoint/checkpoint.c | 7 +-
checkpoint/process.c | 20 ++-
checkpoint/restart.c | 6 +
include/linux/checkpoint.h | 9 +
include/linux/checkpoint_hdr.h | 16 +-
11 files changed, 722 insertions(+), 8 deletions(-)
create mode 100644 arch/x86/include/asm/checkpoint_hdr.h
create mode 100644 arch/x86/mm/checkpoint.c
@@ -0,0 +1,534 @@+/*+*Checkpoint/restart-architecturespecificsupportforx86+*+*Copyright(C)2008-2009OrenLaadan+*+*ThisfileissubjecttothetermsandconditionsoftheGNUGeneralPublic+*License.SeethefileCOPYINGinthemaindirectoryoftheLinux+*distributionformoredetails.+*/++/* default debug level for output */+#define CKPT_DFLAG CKPT_DSYS++#include<asm/desc.h>+#include<asm/i387.h>++#include<linux/checkpoint.h>+#include<linux/checkpoint_hdr.h>++/*+*helperstoencode/decode/validateregisters/segments/eflags+*/++staticintcheck_eflags(__u32eflags)+{+#define X86_EFLAGS_CKPT_MASK \+(X86_EFLAGS_CF|X86_EFLAGS_PF|X86_EFLAGS_AF|X86_EFLAGS_ZF|\+X86_EFLAGS_SF|X86_EFLAGS_TF|X86_EFLAGS_DF|X86_EFLAGS_OF|\+X86_EFLAGS_NT|X86_EFLAGS_AC|X86_EFLAGS_ID)++if((eflags&~X86_EFLAGS_CKPT_MASK)!=(X86_EFLAGS_IF|0x2))+return0;+return1;+}++staticintcheck_tls(structdesc_struct*desc)+{+if(!desc->a&&!desc->b)+return1;+if(desc->l!=0||desc->s!=1||desc->dpl!=3)+return0;+return1;+}++staticintcheck_segment(__u16seg)+{+intret=0;++switch(seg){+caseCKPT_X86_SEG_NULL:+caseCKPT_X86_SEG_USER32_CS:+caseCKPT_X86_SEG_USER32_DS:+return1;+}+if(seg&CKPT_X86_SEG_TLS){+seg&=~CKPT_X86_SEG_TLS;+if(seg<=GDT_ENTRY_TLS_MAX-GDT_ENTRY_TLS_MIN)+ret=1;+}elseif(seg&CKPT_X86_SEG_LDT){+seg&=~CKPT_X86_SEG_LDT;+if(seg<=0x1fff)+ret=1;+}+returnret;+}++static__u16encode_segment(unsignedshortseg)+{+if(seg==0)+returnCKPT_X86_SEG_NULL;+BUG_ON((seg&3)!=3);++if(seg==__USER_CS)+returnCKPT_X86_SEG_USER32_CS;+if(seg==__USER_DS)+returnCKPT_X86_SEG_USER32_DS;++if(seg&4)+returnCKPT_X86_SEG_LDT|(seg>>3);++seg>>=3;+if(GDT_ENTRY_TLS_MIN<=seg&&seg<=GDT_ENTRY_TLS_MAX)+returnCKPT_X86_SEG_TLS|(seg-GDT_ENTRY_TLS_MIN);++printk(KERN_ERR"c/r: (decode) bad segment %#hx\n",seg);+BUG();+}++staticunsignedshortdecode_segment(__u16seg)+{+if(seg==CKPT_X86_SEG_NULL)+return0;+if(seg==CKPT_X86_SEG_USER32_CS)+return__USER_CS;+if(seg==CKPT_X86_SEG_USER32_DS)+return__USER_DS;++if(seg&CKPT_X86_SEG_TLS){+seg&=~CKPT_X86_SEG_TLS;+return((GDT_ENTRY_TLS_MIN+seg)<<3)|3;+}+if(seg&CKPT_X86_SEG_LDT){+seg&=~CKPT_X86_SEG_LDT;+return(seg<<3)|7;+}+BUG();+}++#define CKPT_X86_TIF_UNSUPPORTED (_TIF_SECCOMP | _TIF_IO_BITMAP)++/**************************************************************************+*Checkpoint+*/++staticintmay_checkpoint_thread(structckpt_ctx*ctx,structtask_struct*t)+{+if(t->thread.vm86_info){+ckpt_write_err(ctx,"task %d (%s) in VM86 mode",+task_pid_vnr(t),t->comm);+return-EBUSY;+}+if(task_thread_info(t)->flags&CKPT_X86_TIF_UNSUPPORTED){+ckpt_write_err(ctx,"task %d (%s) uncool thread flags %#lx",+task_pid_vnr(t),t->comm,+task_thread_info(t)->flags);+return-EBUSY;+}+return0;+}++/* dump the thread_struct of a given task */+intcheckpoint_thread(structckpt_ctx*ctx,structtask_struct*t)+{+structckpt_hdr_thread*h;+inttls_size;+intret;++ret=may_checkpoint_thread(ctx,t);+if(ret<0)+returnret;++tls_size=sizeof(t->thread.tls_array);++h=ckpt_hdr_get_type(ctx,sizeof(*h)+tls_size,CKPT_HDR_THREAD);+if(!h)+return-ENOMEM;++h->thread_info_flags=+task_thread_info(t)->flags&~CKPT_X86_TIF_UNSUPPORTED;+h->gdt_entry_tls_entries=GDT_ENTRY_TLS_ENTRIES;+h->sizeof_tls_array=tls_size;++/* For simplicity dump the entire array */+memcpy(h+1,t->thread.tls_array,tls_size);++ret=ckpt_write_obj(ctx,&h->h);+ckpt_hdr_put(ctx,h);+returnret;+}++#ifdef CONFIG_X86_32++staticvoidsave_cpu_regs(structckpt_hdr_cpu*h,structtask_struct*t)+{+structthread_struct*thread=&t->thread;+structpt_regs*regs=task_pt_regs(t);+unsignedlong_gs;++h->bp=regs->bp;+h->bx=regs->bx;+h->ax=regs->ax;+h->cx=regs->cx;+h->dx=regs->dx;+h->si=regs->si;+h->di=regs->di;+h->orig_ax=regs->orig_ax;+h->ip=regs->ip;++h->flags=regs->flags;+h->sp=regs->sp;++h->cs=encode_segment(regs->cs);+h->ss=encode_segment(regs->ss);+h->ds=encode_segment(regs->ds);+h->es=encode_segment(regs->es);++/*+*forcheckpointinprocesscontext(fromwithinacontainer)+*theGSsegmentregistershouldbesavedfromthehardware;+*otherwiseitisalreadysavedonthethreadstructure+*/+if(t==current)+_gs=get_user_gs(regs);+else+_gs=thread->gs;++h->fsindex=encode_segment(regs->fs);+h->gsindex=encode_segment(_gs);++/*+*forcheckpointinprocesscontext(fromwithinacontainer),+*theactualsyscallistakingplaceatthisverymoment;so+*we(optimistically)subtitutethefuturereturnvalue(0)of+*thissyscallintotheorig_eax,sothatuponrestartitwill+*succeed(oritwillendlesslyretrycheckpoint...)+*/+if(t==current){+BUG_ON(h->orig_ax<0);+h->ax=0;+}+}++staticvoidsave_cpu_debug(structckpt_hdr_cpu*h,structtask_struct*t)+{+structthread_struct*thread=&t->thread;++/* debug regs */++/*+*forcheckpointinprocesscontext(fromwithinacontainer),+*gettheactualregisters;otherwisegetthesavedvalues.+*/++if(t==current){+get_debugreg(h->debugreg0,0);+get_debugreg(h->debugreg1,1);+get_debugreg(h->debugreg2,2);+get_debugreg(h->debugreg3,3);+get_debugreg(h->debugreg6,6);+get_debugreg(h->debugreg7,7);+}else{+h->debugreg0=thread->debugreg0;+h->debugreg1=thread->debugreg1;+h->debugreg2=thread->debugreg2;+h->debugreg3=thread->debugreg3;+h->debugreg6=thread->debugreg6;+h->debugreg7=thread->debugreg7;+}+}++staticvoidsave_cpu_fpu(structckpt_hdr_cpu*h,structtask_struct*t)+{+h->used_math=tsk_used_math(t)?1:0;+}++staticintcheckpoint_cpu_fpu(structckpt_ctx*ctx,structtask_struct*t)+{+structckpt_hdr*h;+intret;++h=ckpt_hdr_get_type(ctx,xstate_size+sizeof(*h),+CKPT_HDR_CPU_FPU);+if(!h)+return-ENOMEM;++/* i387 + MMU + SSE logic */+preempt_disable();/* needed it (t == current) */++/*+*normally,noneedtounlazy_fpu(),sinceTS_USEDFPUflag+*wasclearedwhentaskwascontext-switchedout...+*exceptifweareinprocesscontext,inwhichcasewedo+*/+if(t==current&&(task_thread_info(t)->status&TS_USEDFPU))+unlazy_fpu(current);++/*+*Forsimplicitydumptheentirestructure.+*FIX:needtobedeliberateaboutwhatregistersweare+*dumpingfortraceabilityandcompatibility.+*/+memcpy(h+1,t->thread.xstate,xstate_size);+preempt_enable();/* needed if (t == current) */++ret=ckpt_write_obj(ctx,h);+ckpt_hdr_put(ctx,h);++returnret;+}++#endif /* CONFIG_X86_32 */++/* dump the cpu state and registers of a given task */+intcheckpoint_cpu(structckpt_ctx*ctx,structtask_struct*t)+{+structckpt_hdr_cpu*h;+intret;++h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_CPU);+if(!h)+return-ENOMEM;++save_cpu_regs(h,t);+save_cpu_debug(h,t);+save_cpu_fpu(h,t);++ckpt_debug("math %d debug %d\n",h->used_math,!!h->debugreg7);++ret=ckpt_write_obj(ctx,&h->h);+if(ret<0)+gotoout;++if(h->used_math)+ret=checkpoint_cpu_fpu(ctx,t);+out:+ckpt_hdr_put(ctx,h);+returnret;+}++intcheckpoint_write_header_arch(structckpt_ctx*ctx)+{+structckpt_hdr_header_arch*h;+intret;++h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_HEADER_ARCH);+if(!h)+return-ENOMEM;++/* FPU capabilities */+h->has_fxsr=cpu_has_fxsr;+h->has_xsave=cpu_has_xsave;+h->xstate_size=xstate_size;++ret=ckpt_write_obj(ctx,&h->h);+ckpt_hdr_put(ctx,h);++returnret;+}++/**************************************************************************+*Restart+*/++/* read the thread_struct into the current task */+intrestore_thread(structckpt_ctx*ctx)+{+structckpt_hdr_thread*h;+structthread_struct*thread=¤t->thread;+structdesc_struct*desc;+inttls_size;+inti,cpu,ret;++tls_size=sizeof(thread->tls_array);++h=ckpt_read_obj_type(ctx,sizeof(*h)+tls_size,CKPT_HDR_THREAD);+if(IS_ERR(h))+returnPTR_ERR(h);++ret=-EINVAL;+if(h->thread_info_flags&CKPT_X86_TIF_UNSUPPORTED)+gotoout;+if(h->gdt_entry_tls_entries!=GDT_ENTRY_TLS_ENTRIES)+gotoout;+if(h->sizeof_tls_array!=tls_size)+gotoout;++/*+*restoreTLSbyhand:whyconverttostructuser_descif+*sys_set_thread_entry()willconvertitback?+*/+desc=(structdesc_struct*)(h+1);++for(i=0;i<GDT_ENTRY_TLS_ENTRIES;i++){+if(!check_tls(&desc[i]))+gotoout;+}++cpu=get_cpu();+memcpy(thread->tls_array,desc,tls_size);+load_TLS(thread,cpu);+put_cpu();++/* TODO: restore TIF flags as necessary (e.g. TIF_NOTSC) */++ret=0;+out:+ckpt_hdr_put(ctx,h);+returnret;+}++#ifdef CONFIG_X86_32++staticintload_cpu_regs(structckpt_hdr_cpu*h,structtask_struct*t)+{+structthread_struct*thread=&t->thread;+structpt_regs*regs=task_pt_regs(t);++if(!check_eflags(h->flags))+return-EINVAL;+if(h->cs==CKPT_X86_SEG_NULL)+return-EINVAL;+if(!check_segment(h->cs)||!check_segment(h->ds)||+!check_segment(h->es)||!check_segment(h->ss)||+!check_segment(h->fsindex)||!check_segment(h->gsindex))+return-EINVAL;++regs->bp=h->bp;+regs->bx=h->bx;+regs->ax=h->ax;+regs->cx=h->cx;+regs->dx=h->dx;+regs->si=h->si;+regs->di=h->di;+regs->orig_ax=h->orig_ax;+regs->ip=h->ip;++regs->flags=h->flags;+regs->sp=h->sp;++regs->ds=decode_segment(h->ds);+regs->es=decode_segment(h->es);+regs->cs=decode_segment(h->cs);+regs->ss=decode_segment(h->ss);++regs->fs=decode_segment(h->fsindex);+regs->gs=decode_segment(h->gsindex);++thread->gs=regs->gs;+lazy_load_gs(regs->gs);++return0;+}++staticintload_cpu_debug(structckpt_hdr_cpu*h,structtask_struct*t)+{+intret;++ret=ptrace_check_debugreg(1,h->debugreg0,h->debugreg1,h->debugreg2,+h->debugreg3,h->debugreg6,h->debugreg7);+if(ret<0)+returnret;++set_debugreg(h->debugreg0,0);+set_debugreg(h->debugreg1,1);+/* ignore 4, 5 */+set_debugreg(h->debugreg2,2);+set_debugreg(h->debugreg3,3);+set_debugreg(h->debugreg6,6);+set_debugreg(h->debugreg7,7);++if(h->debugreg7)+set_tsk_thread_flag(t,TIF_DEBUG);+else+clear_tsk_thread_flag(t,TIF_DEBUG);++return0;+}++staticintload_cpu_fpu(structckpt_hdr_cpu*h,structtask_struct*t)+{+preempt_disable();++__clear_fpu(t);/* in case we used FPU in user mode */++if(!h->used_math)+clear_used_math();++preempt_enable();+return0;+}++staticintrestore_cpu_fpu(structckpt_ctx*ctx,structtask_struct*t)+{+structckpt_hdr*h;+intret;++/* init_fpu() eventually also calls set_used_math() */+ret=init_fpu(current);+if(ret<0)+returnret;++h=ckpt_read_obj_type(ctx,xstate_size+sizeof(*h),+CKPT_HDR_CPU_FPU);+if(IS_ERR(h))+returnPTR_ERR(h);++memcpy(t->thread.xstate,h+1,xstate_size);++ckpt_hdr_put(ctx,h);+returnret;+}++#endif /* CONFIG_X86_32 */++/* read the cpu state and registers for the current task */+intrestore_cpu(structckpt_ctx*ctx)+{+structckpt_hdr_cpu*h;+structtask_struct*t=current;+intret;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_CPU);+if(IS_ERR(h))+returnPTR_ERR(h);++ckpt_debug("math %d debug %d\n",h->used_math,!!h->debugreg7);++ret=load_cpu_regs(h,t);+if(ret<0)+gotoout;+ret=load_cpu_debug(h,t);+if(ret<0)+gotoout;+ret=load_cpu_fpu(h,t);+if(ret<0)+gotoout;++if(h->used_math)+ret=restore_cpu_fpu(ctx,t);+out:+ckpt_hdr_put(ctx,h);+returnret;+}++intrestore_read_header_arch(structckpt_ctx*ctx)+{+structckpt_hdr_header_arch*h;+intret=0;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_HEADER_ARCH);+if(IS_ERR(h))+returnPTR_ERR(h);++/* FIX: verify compatibility of architecture features */++/* verify FPU capabilities */+if(h->has_fxsr!=cpu_has_fxsr||+h->has_xsave!=cpu_has_xsave||+h->xstate_size!=xstate_size)+ret=-EINVAL;++ckpt_hdr_put(ctx,h);+returnret;+}
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Add those interfaces, as well as helpers needed to easily manage the
file format. The code is roughly broken out as follows:
checkpoint/sys.c - user/kernel data transfer, as well as setup of the
c/r context (a per-checkpoint data structure for housekeeping)
checkpoint/checkpoint.c - output wrappers and basic checkpoint handling
checkpoint/restart.c - input wrappers and basic restart handling
checkpoint/process.c - c/r of task data
For now, we can only checkpoint the 'current' task ("self" checkpoint),
and the 'pid' argument to the syscall is ignored.
Patches to add the per-architecture support as well as the actual
work to do the memory checkpoint follow in subsequent patches.
Changelog[v17]:
- Fix compilation for architectures that don't support checkpoint
- Save/restore t->{set,clear}_child_tid
- Restart(2) isn't idempotent: must return -EINTR if interrupted
- ckpt_debug does not depend on DYNAMIC_DEBUG, on by default
- Export generic checkpoint headers to userespace
- Fix comment for prototype of sys_restart
- Have ckpt_debug() print global-pid and __LINE__
- Only save and test kernel constants once (in header)
Changelog[v16]:
- Split ctx->flags to ->uflags (user flags) and ->kflags (kernel flags)
- Introduce __ckpt_write_err() and ckpt_write_err() to report errors
- Allow @ptr == NULL to write (or read) header only without payload
- Introduce _ckpt_read_obj_type()
Changelog[v15]:
- Replace header buffer in ckpt_ctx (hbuf,hpos) with kmalloc/kfree()
Changelog[v14]:
- Cleanup interface to get/put hdr buffers
- Merge checkpoint and restart code into a single file (per subsystem)
- Take uts_sem around access to uts->{release,version,machine}
- Embed ckpt_hdr in all ckpt_hdr_...., cleanup read/write helpers
- Define sys_checkpoint(0,...) as asking for a self-checkpoint (Serge)
- Revert use of 'pr_fmt' to avoid tainting whom includes us (Nathan Lynch)
- Explicitly indicate length of UTS fields in header
- Discard field 'h->parent' from ckpt_hdr
Changelog[v12]:
- ckpt_kwrite/ckpt_kread() again use vfs_read(), vfs_write() (safer)
- Split ckpt_write/ckpt_read() to two parts: _ckpt_write/read() helper
- Befriend with sparse : explicit conversion to 'void __user *'
- Redfine 'pr_fmt' instead of using special ckpt_debug()
Changelog[v10]:
- add ckpt_write_buffer(), ckpt_read_buffer() and ckpt_read_buf_type()
- force end-of-string in ckpt_read_string() (fix possible DoS)
Changelog[v9]:
- ckpt_kwrite/ckpt_kread() use file->f_op->write() directly
- Drop ckpt_uwrite/ckpt_uread() since they aren't used anywhere
Changelog[v6]:
- Balance all calls to ckpt_hbuf_get() with matching ckpt_hbuf_put()
(although it's not really needed)
Changelog[v5]:
- Rename headers files s/ckpt/checkpoint/
Changelog[v2]:
- Added utsname->{release,version,machine} to checkpoint header
- Pad header structures to 64 bits to ensure compatibility
Signed-off-by: Oren Laadan <redacted>
---
Makefile | 2 +-
checkpoint/Makefile | 6 +-
checkpoint/checkpoint.c | 272 +++++++++++++++++++++++++++++++
checkpoint/process.c | 99 +++++++++++
checkpoint/restart.c | 333 ++++++++++++++++++++++++++++++++++++++
checkpoint/sys.c | 247 ++++++++++++++++++++++++++++-
include/linux/Kbuild | 3 +
include/linux/checkpoint.h | 101 ++++++++++++
include/linux/checkpoint_hdr.h | 109 +++++++++++++
include/linux/checkpoint_types.h | 34 ++++
include/linux/magic.h | 4 +
lib/Kconfig.debug | 13 ++
12 files changed, 1219 insertions(+), 4 deletions(-)
create mode 100644 checkpoint/checkpoint.c
create mode 100644 checkpoint/process.c
create mode 100644 checkpoint/restart.c
create mode 100644 include/linux/checkpoint.h
create mode 100644 include/linux/checkpoint_hdr.h
create mode 100644 include/linux/checkpoint_types.h
@@ -2,4 +2,8 @@# Makefile for linux checkpoint/restart.#-obj-$(CONFIG_CHECKPOINT)+=sys.o+obj-$(CONFIG_CHECKPOINT)+=\+sys.o\+checkpoint.o\+restart.o\+process.o
@@ -0,0 +1,99 @@+/*+*Checkpointtaskstructure+*+*Copyright(C)2008-2009OrenLaadan+*+*ThisfileissubjecttothetermsandconditionsoftheGNUGeneralPublic+*License.SeethefileCOPYINGinthemaindirectoryoftheLinux+*distributionformoredetails.+*/++/* default debug level for output */+#define CKPT_DFLAG CKPT_DSYS++#include<linux/sched.h>+#include<linux/checkpoint.h>+#include<linux/checkpoint_hdr.h>++/***********************************************************************+*Checkpoint+*/++/* dump the task_struct of a given task */+staticintcheckpoint_task_struct(structckpt_ctx*ctx,structtask_struct*t)+{+structckpt_hdr_task*h;+intret;++h=ckpt_hdr_get_type(ctx,sizeof(*h),CKPT_HDR_TASK);+if(!h)+return-ENOMEM;++h->state=t->state;+h->exit_state=t->exit_state;+h->exit_code=t->exit_code;+h->exit_signal=t->exit_signal;++h->set_child_tid=t->set_child_tid;+h->clear_child_tid=t->clear_child_tid;++/* FIXME: save remaining relevant task_struct fields */++ret=ckpt_write_obj(ctx,&h->h);+ckpt_hdr_put(ctx,h);+if(ret<0)+returnret;++returnckpt_write_string(ctx,t->comm,TASK_COMM_LEN);+}++/* dump the entire state of a given task */+intcheckpoint_task(structckpt_ctx*ctx,structtask_struct*t)+{+intret;++ret=checkpoint_task_struct(ctx,t);+ckpt_debug("task %d\n",ret);++returnret;+}++/***********************************************************************+*Restart+*/++/* read the task_struct into the current task */+staticintrestore_task_struct(structckpt_ctx*ctx)+{+structckpt_hdr_task*h;+structtask_struct*t=current;+intret;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_TASK);+if(IS_ERR(h))+returnPTR_ERR(h);++memset(t->comm,0,TASK_COMM_LEN);+ret=_ckpt_read_string(ctx,t->comm,TASK_COMM_LEN);+if(ret<0)+gotoout;++t->set_child_tid=h->set_child_tid;+t->clear_child_tid=h->clear_child_tid;++/* FIXME: restore remaining relevant task_struct fields */+out:+ckpt_hdr_put(ctx,h);+returnret;+}++/* read the entire state of the current task */+intrestore_task(structckpt_ctx*ctx)+{+intret;++ret=restore_task_struct(ctx);+ckpt_debug("task %d\n",ret);++returnret;+}
@@ -0,0 +1,333 @@+/*+*Restartlogicandhelpers+*+*Copyright(C)2008-2009OrenLaadan+*+*ThisfileissubjecttothetermsandconditionsoftheGNUGeneralPublic+*License.SeethefileCOPYINGinthemaindirectoryoftheLinux+*distributionformoredetails.+*/++/* default debug level for output */+#define CKPT_DFLAG CKPT_DSYS++#include<linux/version.h>+#include<linux/sched.h>+#include<linux/file.h>+#include<linux/magic.h>+#include<linux/utsname.h>+#include<linux/checkpoint.h>+#include<linux/checkpoint_hdr.h>++/**+*_ckpt_read_obj-readanobject(ckpt_hdrfollowedbypayload)+*@ctx:checkpointcontext+*@h:desiredckpt_hdr+*@ptr:desiredbuffer+*@len:desiredpayloadlength(if0,flexible)+*@max:maximumpayloadlength+*+*If@ptrisNULL,thenreadonlytheheader(payloadtofollow)+*/+staticint_ckpt_read_obj(structckpt_ctx*ctx,structckpt_hdr*h,+void*ptr,intlen,intmax)+{+intret;++ret=ckpt_kread(ctx,h,sizeof(*h));+if(ret<0)+returnret;+_ckpt_debug(CKPT_DRW,"type %d len %d(%d,%d)\n",+h->type,h->len,len,max);+if(h->len<sizeof(*h))+return-EINVAL;+/* if len specified, enforce, else if maximum specified, enforce */+if((len&&h->len!=len)||(!len&&max&&h->len>max))+return-EINVAL;++if(ptr)+ret=ckpt_kread(ctx,ptr,h->len-sizeof(structckpt_hdr));+returnret;+}++/**+*_ckpt_read_nbuffer-readanobjectoftypebuffer(variablelength)+*@ctx:checkpointcontext+*@ptr:providedbuffer+*@len:bufferlength+*+*If@ptrisNULL,thenreadonlytheheader(payloadtofollow)+*Returns:actualbufferlength(boundedby@len)+*/+int_ckpt_read_nbuffer(structckpt_ctx*ctx,void*ptr,intlen)+{+structckpt_hdrh;+intret;++BUG_ON(!len);++len+=sizeof(structckpt_hdr);+ret=_ckpt_read_obj(ctx,&h,ptr,0,len);+if(ret<0)+returnret;+_ckpt_debug(CKPT_DRW,"type %d len %d\n",h.type,h.len);+if(h.type!=CKPT_HDR_BUFFER)+return-EINVAL;+returnh.len;+}++/**+*_ckpt_read_obj_type-readanobjectofsometype(setlength)+*@ctx:checkpointcontext+*@ptr:providedbuffer+*@len:bufferlength+*@type:buffertype+*+*If@ptrisNULL,thenreadonlytheheader(payloadtofollow)+*/+int_ckpt_read_obj_type(structckpt_ctx*ctx,void*ptr,intlen,inttype)+{+structckpt_hdrh;+intret;++len+=sizeof(structckpt_hdr);+ret=_ckpt_read_obj(ctx,&h,ptr,len,len);+if(ret<0)+returnret;+if(h.type!=type)+return-EINVAL;+return0;+}++/**+*_ckpt_read_buffer-readanobjectoftypebuffer(setlength)+*@ctx:checkpointcontext+*@ptr:providedbuffer+*@len:bufferlength+*+*If@ptrisNULL,thenreadonlytheheader(payloadtofollow)+*/+int_ckpt_read_buffer(structckpt_ctx*ctx,void*ptr,intlen)+{+BUG_ON(!len);+return_ckpt_read_obj_type(ctx,ptr,len,CKPT_HDR_BUFFER);+}++/**+*_ckpt_read_string-readanobjectoftypestring(setlength)+*@ctx:checkpointcontext+*@ptr:providedbuffer+*@len:stringlength+*+*If@ptrisNULL,thenreadonlytheheader(payloadtofollow)+*/+int_ckpt_read_string(structckpt_ctx*ctx,void*ptr,intlen)+{+intret;++BUG_ON(!len);++ret=_ckpt_read_obj_type(ctx,ptr,len,CKPT_HDR_STRING);+if(ret<0)+returnret;+if(ptr)+((char*)ptr)[len-1]='\0';/* always play it safe */+return0;+}++/**+*ckpt_read_obj-allocateandreadanobject(ckpt_hdrfollowedbypayload)+*@ctx:checkpointcontext+*@h:objectdescriptor+*@len:desiredpayloadlength(if0,flexible)+*@max:maximumpayloadlength+*+*Return:newbufferallocatedonsuccess,errorpointerotherwise+*/+staticvoid*ckpt_read_obj(structckpt_ctx*ctx,intlen,intmax)+{+structckpt_hdrhh;+structckpt_hdr*h;+intret;++ret=ckpt_kread(ctx,&hh,sizeof(hh));+if(ret<0)+returnERR_PTR(ret);+_ckpt_debug(CKPT_DRW,"type %d len %d(%d,%d)\n",+hh.type,hh.len,len,max);+if(hh.len<sizeof(*h))+returnERR_PTR(-EINVAL);+/* if len specified, enforce, else if maximum specified, enforce */+if((len&&hh.len!=len)||(!len&&max&&hh.len>max))+returnERR_PTR(-EINVAL);++h=ckpt_hdr_get(ctx,hh.len);+if(!h)+returnERR_PTR(-ENOMEM);++*h=hh;/* yay ! */++ret=ckpt_kread(ctx,(h+1),hh.len-sizeof(structckpt_hdr));+if(ret<0){+ckpt_hdr_put(ctx,h);+h=ERR_PTR(ret);+}++returnh;+}++/**+*ckpt_read_obj_type-allocateandreadanobjectofsometype+*@ctx:checkpointcontext+*@len:desiredobjectlength+*@type:desiredobjecttype+*+*Return:newbufferallocatedonsuccess,errorpointerotherwise+*/+void*ckpt_read_obj_type(structckpt_ctx*ctx,intlen,inttype)+{+structckpt_hdr*h;++BUG_ON(!len);++h=ckpt_read_obj(ctx,len,len);+if(IS_ERR(h))+returnh;++if(h->type!=type){+ckpt_hdr_put(ctx,h);+h=ERR_PTR(-EINVAL);+}++returnh;+}++/**+*ckpt_read_buf_type-allocateandreadanobjectofsometype(flxible)+*@ctx:checkpointcontext+*@len:maximumobjectlength+*@type:desiredobjecttype+*+*Thisdiffersfromckpt_read_obj_type()inthatthelengthofthe+*incomingobjectisflexible(uptothemaximumspecifiedby@len),+*asdeterminedbytheckpt_hdrdata.+*+*Return:newbufferallocatedonsuccess,errorpointerotherwise+*/+void*ckpt_read_buf_type(structckpt_ctx*ctx,intlen,inttype)+{+structckpt_hdr*h;++h=ckpt_read_obj(ctx,0,len);+if(IS_ERR(h))+returnh;++if(h->type!=type){+ckpt_hdr_put(ctx,h);+h=ERR_PTR(-EINVAL);+}++returnh;+}++/***********************************************************************+*Restart+*/++staticintcheck_kernel_const(structckpt_hdr_const*h)+{+structtask_struct*tsk;+structnew_utsname*uts;++/* task */+if(h->task_comm_len!=sizeof(tsk->comm))+return-EINVAL;+/* uts */+if(h->uts_release_len!=sizeof(uts->release))+return-EINVAL;+if(h->uts_version_len!=sizeof(uts->version))+return-EINVAL;+if(h->uts_machine_len!=sizeof(uts->machine))+return-EINVAL;++return0;+}++/* read the checkpoint header */+staticintrestore_read_header(structckpt_ctx*ctx)+{+structckpt_hdr_header*h;+structnew_utsname*uts=NULL;+intret;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_HEADER);+if(IS_ERR(h))+returnPTR_ERR(h);++ret=-EINVAL;+if(h->magic!=CHECKPOINT_MAGIC_HEAD||+h->rev!=CHECKPOINT_VERSION||+h->major!=((LINUX_VERSION_CODE>>16)&0xff)||+h->minor!=((LINUX_VERSION_CODE>>8)&0xff)||+h->patch!=((LINUX_VERSION_CODE)&0xff))+gotoout;+if(h->uflags)+gotoout;++ret=check_kernel_const(&h->constants);+if(ret<0)+gotoout;++ret=-ENOMEM;+uts=kmalloc(sizeof(*uts),GFP_KERNEL);+if(!uts)+gotoout;++ctx->oflags=h->uflags;++/* FIX: verify compatibility of release, version and machine */+ret=_ckpt_read_buffer(ctx,uts->release,sizeof(uts->release));+if(ret<0)+gotoout;+ret=_ckpt_read_buffer(ctx,uts->version,sizeof(uts->version));+if(ret<0)+gotoout;+ret=_ckpt_read_buffer(ctx,uts->machine,sizeof(uts->machine));+out:+kfree(uts);+ckpt_hdr_put(ctx,h);+returnret;+}++/* read the checkpoint trailer */+staticintrestore_read_tail(structckpt_ctx*ctx)+{+structckpt_hdr_tail*h;+intret=0;++h=ckpt_read_obj_type(ctx,sizeof(*h),CKPT_HDR_TAIL);+if(IS_ERR(h))+returnPTR_ERR(h);++if(h->magic!=CHECKPOINT_MAGIC_TAIL)+ret=-EINVAL;++ckpt_hdr_put(ctx,h);+returnret;+}++longdo_restart(structckpt_ctx*ctx,pid_tpid)+{+longret;++ret=restore_read_header(ctx);+if(ret<0)+returnret;+ret=restore_task(ctx);+if(ret<0)+returnret;+ret=restore_read_tail(ctx);++/* on success, adjust the return value if needed [TODO] */+returnret;+}
@@ -23,7 +206,26 @@*/SYSCALL_DEFINE3(checkpoint,pid_t,pid,int,fd,unsignedlong,flags){-return-ENOSYS;+structckpt_ctx*ctx;+longret;++/* no flags for now */+if(flags)+return-EINVAL;++if(pid==0)+pid=task_pid_vnr(current);+ctx=ckpt_ctx_alloc(fd,flags,CKPT_CTX_CHECKPOINT);+if(IS_ERR(ctx))+returnPTR_ERR(ctx);++ret=do_checkpoint(ctx,pid);++if(!ret)+ret=ctx->crid;++ckpt_ctx_free(ctx);+returnret;}/**
@@ -37,5 +239,46 @@ SYSCALL_DEFINE3(checkpoint, pid_t, pid, int, fd, unsigned long, flags)*/SYSCALL_DEFINE3(restart,pid_t,pid,int,fd,unsignedlong,flags){-return-ENOSYS;+structckpt_ctx*ctx=NULL;+longret;++/* no flags for now */+if(flags)+return-EINVAL;++ctx=ckpt_ctx_alloc(fd,flags,CKPT_CTX_RESTART);+if(IS_ERR(ctx))+returnPTR_ERR(ctx);++ret=do_restart(ctx,pid);++/* restart(2) isn't idempotent: can't restart syscall */+if(ret==-ERESTARTSYS||ret==-ERESTARTNOINTR||+ret==-ERESTARTNOHAND||ret==-ERESTART_RESTARTBLOCK)+ret=-EINTR;++ckpt_ctx_free(ctx);+returnret;+}+++/* 'ckpt_debug_level' controls the verbosity level of c/r code */+#ifdef CONFIG_CHECKPOINT_DEBUG++/* FIX: allow to change during runtime */+unsignedlong__read_mostlyckpt_debug_level=CKPT_DDEFAULT;++static__initintckpt_debug_setup(char*s)+{+longval,ret;++ret=strict_strtoul(s,10,&val);+if(ret<0)+returnret;+ckpt_debug_level=val;+return0;}++__setup("ckpt_debug=",ckpt_debug_setup);++#endif /* CONFIG_CHECKPOINT_DEBUG */
--
1.6.0.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Serge E. Hallyn <hidden> Date: 2009-07-22 17:57:44
Quoting Oren Laadan (orenl@librato.com):
Now we can do "external" checkpoint, i.e. act on another task.
...
long do_checkpoint(struct ckpt_ctx *ctx, pid_t pid)
{
long ret;
+ ret = init_checkpoint_ctx(ctx, pid);
+ if (ret < 0)
+ return ret;
+
+ if (ctx->root_freezer) {
+ ret = cgroup_freezer_begin_checkpoint(ctx->root_freezer);
+ if (ret < 0)
+ return ret;
+ }
Self-checkpoint of a task in root freezer is now denied, though.
Was that intentional?
-serge
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
I'm not sure but how about renaming this to
file_pos()
set_file_pos()
at moving this to global include file ?
Thanks,
-Kame
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
[Oren Laadan - Wed, Jul 22, 2009 at 06:00:14AM -0400]
...
| +static struct sem *restore_sem_array(struct ckpt_ctx *ctx, int nsems)
| +{
| + struct sem *sma;
| + int i, ret;
| +
| + sma = kmalloc(nsems * sizeof(*sma), GFP_KERNEL);
Forgot to
if (!sma)
return -ENOMEM;
right?
Yep ! thanks... (fixed commit to branch ckpt-v17-dev)
Oren.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Now we can do "external" checkpoint, i.e. act on another task.
...
quoted
long do_checkpoint(struct ckpt_ctx *ctx, pid_t pid)
{
long ret;
+ ret = init_checkpoint_ctx(ctx, pid);
+ if (ret < 0)
+ return ret;
+
+ if (ctx->root_freezer) {
+ ret = cgroup_freezer_begin_checkpoint(ctx->root_freezer);
+ if (ret < 0)
+ return ret;
+ }
Self-checkpoint of a task in root freezer is now denied, though.
Was that intentional?
Yes.
"root freezer" is an arbitrary task in the checkpoint subtree or
container. It is used to verify that all checkpointed tasks - except
for current, if doing self-checkpoint - belong to the same freezer
group.
Since current is busy calling checkpoint(2), and since we only permit
checkpoint of (cgroup-) frozen tasks, then - by definition - it cannot
possibly belong to the same group. If it did, it would itself be frozen
like its fellows and unable to call checkpoint(2).
Oren.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Now we can do "external" checkpoint, i.e. act on another task.
...
quoted
long do_checkpoint(struct ckpt_ctx *ctx, pid_t pid)
{
long ret;
+ ret = init_checkpoint_ctx(ctx, pid);
+ if (ret < 0)
+ return ret;
+
+ if (ctx->root_freezer) {
+ ret = cgroup_freezer_begin_checkpoint(ctx->root_freezer);
+ if (ret < 0)
+ return ret;
+ }
Self-checkpoint of a task in root freezer is now denied, though.
Was that intentional?
Yes.
"root freezer" is an arbitrary task in the checkpoint subtree or
container. It is used to verify that all checkpointed tasks - except
for current, if doing self-checkpoint - belong to the same freezer
group.
Since current is busy calling checkpoint(2), and since we only permit
checkpoint of (cgroup-) frozen tasks, then - by definition - it cannot
possibly belong to the same group. If it did, it would itself be frozen
like its fellows and unable to call checkpoint(2).
So then you're saying that regular self-checkpoint no longer works,
but the documentation still shows self.c and claims it should just
work.
Mind you I prefer this as it is more consistent, but I thought it
was something you wanted to support.
-serge
Now we can do "external" checkpoint, i.e. act on another task.
...
quoted
long do_checkpoint(struct ckpt_ctx *ctx, pid_t pid)
{
long ret;
+ ret = init_checkpoint_ctx(ctx, pid);
+ if (ret < 0)
+ return ret;
+
+ if (ctx->root_freezer) {
+ ret = cgroup_freezer_begin_checkpoint(ctx->root_freezer);
+ if (ret < 0)
+ return ret;
+ }
Self-checkpoint of a task in root freezer is now denied, though.
Was that intentional?
Yes.
"root freezer" is an arbitrary task in the checkpoint subtree or
container. It is used to verify that all checkpointed tasks - except
for current, if doing self-checkpoint - belong to the same freezer
group.
Since current is busy calling checkpoint(2), and since we only permit
checkpoint of (cgroup-) frozen tasks, then - by definition - it cannot
possibly belong to the same group. If it did, it would itself be frozen
like its fellows and unable to call checkpoint(2).
So then you're saying that regular self-checkpoint no longer works,
but the documentation still shows self.c and claims it should just
work.
I'm unsure why you say that self-checkpoint no longer works ?
In fact, I just double checked that it does.
Self-checkpoint has two immediate use-cases:
1) Single process that checkpoints itself - ctx->root_freezer remains
NULL, which causes cgroup_freezer_begin_checkpoint() to be skipped.
2) Process P that belongs to a hierarchy (subtree or container), and
P calls checkpoint(2) to checkpoint the hierarchy.
For this to work, all other processes in the hierarchy must be frozen.
Therefore, they also belong to a freezer cgroup (perhaps more than one -
but that is not permitted).
In this case, ctx->root will point to a process from the freezer cgroup,
and the code tests all other processes (excluding P, which is current)
to confirm that they belong to the same freezer cgroup.
P itself can not possibly belong to it, otherwise it would have been
frozen and not executing the checkpoint(2) syscall.
IOW, for case 2 to work, one must arrange for all tasks in the target
hierarchy, except for P (- current, the checkpointer), to belong to
a single freezer cgroup, and for that cgroup to be frozen.
quoted
quoted
Self-checkpoint of a task in root freezer is now denied, though.
Maybe I didn't really understand what you meant by that, and by
"root freezer" ?
Mind you I prefer this as it is more consistent, but I thought it
was something you wanted to support.
Self-checkpoint simply allows a process to checkpoint itself (and
perhaps additional processes too). I never quite understood why you
view it as a source of inconsistency ...
Nevertheless, it still works.
Oren.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
+Security
+========
+
+The main question is whether sys_checkpoint() and sys_restart()
+require privileged or unprivileged operation.
+
+Early versions checked capable(CAP_SYS_ADMIN) assuming that we would
+attempt to remove the need for privilege, so that all users could
+safely use it. Arnd Bergmann pointed out that it'd make more sense to
+let unprivileged users use them now, so that we'll be more careful
+about the security as patches roll in.
+
+Checkpoint: the main concern is whether a task that performs the
+checkpoint of another task has sufficient privileges to access its
+state. We address this by requiring that the checkpointer task will be
+able to ptrace the target task, by means of ptrace_may_access() with
+read mode.
with access mode now, actually.
+Restart: the main concern is that we may allow an unprivileged user to
+feed the kernel with random data. To this end, the restart works in a
+way that does not skip the usual security checks. Task credentials,
+i.e. euid, reuid, and LSM security contexts currently come from the
+caller, not the checkpoint image. When restoration of credentials
+becomes supported, then definitely the ability of the task that calls
+sys_restore() to setresuid/setresgid to those values must be checked.
That is now possible, and this is done.
quoted hunk
+Keeping the restart procedure to operate within the limits of the
+caller's credentials means that there various scenarios that cannot
+be supported. For instance, a setuid program that opened a protected
+log file and then dropped privileges will fail the restart, because
+the user won't have enough credentials to reopen the file. In these
+cases, we should probably treat restarting like inserting a kernel
+module: surely the user can cause havoc by providing incorrect data,
+but then again we must trust the root account.
+
+So that's why we don't want CAP_SYS_ADMIN required up-front. That way
+we will be forced to more carefully review each of those features.
+However, this can be controlled with a sysctl-variable.
+
+
@@ -0,0 +1,193 @@++ How to use Checkpoint-Restart+ =========================================+++API+===++The API consists of two new system calls:++* int checkpoint(pid_t pid, int fd, unsigned long flag);++ Checkpoint a (sub-)container whose root task is identified by @pid,+ to the open file indicated by @fd. @flags may be on or more of:+ - CHECKPOINT_SUBTREE : allow checkpoint of sub-container+ (other value are not allowed).++ Returns: a positive checkpoint identifier (ckptid) upon success, 0 if+ it returns from a restart, and -1 if an error occurs. The ckptid will+ uniquely identify a checkpoint image, for as long as the checkpoint+ is kept in the kernel (e.g. if one wishes to keep a checkpoint, or a+ partial checkpoint, residing in kernel memory).++* int sys_restart(pid_t pid, int fd, unsigned long flags);++ Restart a process hierarchy from a checkpoint image that is read from+ the blob stored in the file indicated by @fd. The @flags' will have+ future meaning (must be 0 for now). @pid indicates the root of the+ hierarchy as seen in the coordinator's pid-namespace, and is expected+ to be a child of the coordinator. (Note that this argument may mean+ 'ckptid' to identify an in-kernel checkpoint image, with some @flags+ in the future).++ Returns: -1 if an error occurs, 0 on success when restarting from a+ "self" checkpoint, and return value of system call at the time of the+ checkpoint when restarting from an "external" checkpoint.
Return value of the checkpointed (init) task's syscall at the time of
external checkpoint? If so, what's the use for this, as opposed to
returning 0 as in the case of self-checkpoint?
+ TODO: upon successful "external" restart, the container will end up
+ in a frozen state.
Should clone_with_pids() be mentioned here?
thanks,
-serge
--
To unsubscribe from this list: send the line "unsubscribe linux-api" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Now we can do "external" checkpoint, i.e. act on another task.
...
quoted
long do_checkpoint(struct ckpt_ctx *ctx, pid_t pid)
{
long ret;
+ ret = init_checkpoint_ctx(ctx, pid);
+ if (ret < 0)
+ return ret;
+
+ if (ctx->root_freezer) {
+ ret = cgroup_freezer_begin_checkpoint(ctx->root_freezer);
+ if (ret < 0)
+ return ret;
+ }
Self-checkpoint of a task in root freezer is now denied, though.
Was that intentional?
Yes.
"root freezer" is an arbitrary task in the checkpoint subtree or
container. It is used to verify that all checkpointed tasks - except
for current, if doing self-checkpoint - belong to the same freezer
group.
Since current is busy calling checkpoint(2), and since we only permit
checkpoint of (cgroup-) frozen tasks, then - by definition - it cannot
possibly belong to the same group. If it did, it would itself be frozen
like its fellows and unable to call checkpoint(2).
Oren.
Ok, well I don't know what was happening yesterday. Today it's
restart that is failing, and as you pointed out on irc that's
on s390 only. I'll send out a patch this afternoon to fix that.
Yesterday I must not have read the output right I guess...
thanks,
-serge
--
To unsubscribe from this list: send the line "unsubscribe linux-api" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
From: Serge E. Hallyn <hidden> Date: 2009-07-23 15:23:46
Quoting Oren Laadan (orenl@librato.com):
+/* setup checkpoint-specific parts of ctx */
+static int init_checkpoint_ctx(struct ckpt_ctx *ctx, pid_t pid)
+{
+ struct task_struct *task;
+ struct nsproxy *nsproxy;
+ int ret;
+
+ /*
+ * No need for explicit cleanup here, because if an error
+ * occurs then ckpt_ctx_free() is eventually called.
+ */
+
+ ctx->root_pid = pid;
+
+ /* root task */
+ read_lock(&tasklist_lock);
+ task = find_task_by_vpid(pid);
+ if (task)
+ get_task_struct(task);
+ read_unlock(&tasklist_lock);
+ if (!task)
+ return -ESRCH;
+ else
+ ctx->root_task = task;
+
+ /* root nsproxy */
+ rcu_read_lock();
+ nsproxy = task_nsproxy(task);
+ if (nsproxy)
+ get_nsproxy(nsproxy);
+ rcu_read_unlock();
+ if (!nsproxy)
+ return -ESRCH;
+ else
+ ctx->root_nsproxy = nsproxy;
+
+ /* root freezer */
+ ctx->root_freezer = task;
+ geT_task_struct(task);
+
+ ret = may_checkpoint_task(ctx, task);
+ if (ret) {
+ ckpt_write_err(ctx, NULL);
+ put_task_struct(task);
+ put_task_struct(task);
+ put_nsproxy(nsproxy);
I don't think this is safe - the ckpt_ctx_free() will
free them a second time because you're not setting them
to NULL, right?
+ return ret;
+ }
+
+ return 0;
+}
+
-serge
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
+Security
+========
+
+The main question is whether sys_checkpoint() and sys_restart()
+require privileged or unprivileged operation.
+
+Early versions checked capable(CAP_SYS_ADMIN) assuming that we would
+attempt to remove the need for privilege, so that all users could
+safely use it. Arnd Bergmann pointed out that it'd make more sense to
+let unprivileged users use them now, so that we'll be more careful
+about the security as patches roll in.
+
+Checkpoint: the main concern is whether a task that performs the
+checkpoint of another task has sufficient privileges to access its
+state. We address this by requiring that the checkpointer task will be
+able to ptrace the target task, by means of ptrace_may_access() with
+read mode.
with access mode now, actually.
Yes...
quoted
+Restart: the main concern is that we may allow an unprivileged user to
+feed the kernel with random data. To this end, the restart works in a
+way that does not skip the usual security checks. Task credentials,
+i.e. euid, reuid, and LSM security contexts currently come from the
+caller, not the checkpoint image. When restoration of credentials
+becomes supported, then definitely the ability of the task that calls
+sys_restore() to setresuid/setresgid to those values must be checked.
That is now possible, and this is done.
Yes again.
quoted
+Keeping the restart procedure to operate within the limits of the
+caller's credentials means that there various scenarios that cannot
+be supported. For instance, a setuid program that opened a protected
+log file and then dropped privileges will fail the restart, because
+the user won't have enough credentials to reopen the file. In these
+cases, we should probably treat restarting like inserting a kernel
+module: surely the user can cause havoc by providing incorrect data,
+but then again we must trust the root account.
+
+So that's why we don't want CAP_SYS_ADMIN required up-front. That way
+we will be forced to more carefully review each of those features.
+However, this can be controlled with a sysctl-variable.
+
+
@@ -0,0 +1,193 @@++ How to use Checkpoint-Restart+ =========================================+++API+===++The API consists of two new system calls:++* int checkpoint(pid_t pid, int fd, unsigned long flag);++ Checkpoint a (sub-)container whose root task is identified by @pid,+ to the open file indicated by @fd. @flags may be on or more of:+ - CHECKPOINT_SUBTREE : allow checkpoint of sub-container+ (other value are not allowed).++ Returns: a positive checkpoint identifier (ckptid) upon success, 0 if+ it returns from a restart, and -1 if an error occurs. The ckptid will+ uniquely identify a checkpoint image, for as long as the checkpoint+ is kept in the kernel (e.g. if one wishes to keep a checkpoint, or a+ partial checkpoint, residing in kernel memory).++* int sys_restart(pid_t pid, int fd, unsigned long flags);++ Restart a process hierarchy from a checkpoint image that is read from+ the blob stored in the file indicated by @fd. The @flags' will have+ future meaning (must be 0 for now). @pid indicates the root of the+ hierarchy as seen in the coordinator's pid-namespace, and is expected+ to be a child of the coordinator. (Note that this argument may mean+ 'ckptid' to identify an in-kernel checkpoint image, with some @flags+ in the future).++ Returns: -1 if an error occurs, 0 on success when restarting from a+ "self" checkpoint, and return value of system call at the time of the+ checkpoint when restarting from an "external" checkpoint.
Return value of the checkpointed (init) task's syscall at the time of
external checkpoint? If so, what's the use for this, as opposed to
returning 0 as in the case of self-checkpoint?
When you restart from a regular ("external") syscall, the checkpointed
process was doing _something_:
If it was frozen for checkpoint while running in userspace, then it will
resume running in userspace exactly where it was interrupted.
If it was frozen while in kernel doing a syscall, it will return what
that syscall returned when it was interrupted - or completed - for the
freeze. It will proceed from there as if it had only been frozen and
then thawed.
In the special case that the process original self-checkpointed, then
once restart completes successfully, it will resume execution at the
first instruction after the original call to checkpoint(2), and the
return value from that syscall will be set to 0. (The caller uses
this retval to learn that it was restarted, and not just completed
a checkpoint).
quoted
+ TODO: upon successful "external" restart, the container will end up
+ in a frozen state.
Heh .. this is also done :)
Should clone_with_pids() be mentioned here?
It's not a c/r interface per-se, but you're probably right that a few
words there won't hurt.
Thanks.
Oren.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
+/* setup checkpoint-specific parts of ctx */
+static int init_checkpoint_ctx(struct ckpt_ctx *ctx, pid_t pid)
+{
+ struct task_struct *task;
+ struct nsproxy *nsproxy;
+ int ret;
+
+ /*
+ * No need for explicit cleanup here, because if an error
+ * occurs then ckpt_ctx_free() is eventually called.
+ */
+
+ ctx->root_pid = pid;
+
+ /* root task */
+ read_lock(&tasklist_lock);
+ task = find_task_by_vpid(pid);
+ if (task)
+ get_task_struct(task);
+ read_unlock(&tasklist_lock);
+ if (!task)
+ return -ESRCH;
+ else
+ ctx->root_task = task;
+
+ /* root nsproxy */
+ rcu_read_lock();
+ nsproxy = task_nsproxy(task);
+ if (nsproxy)
+ get_nsproxy(nsproxy);
+ rcu_read_unlock();
+ if (!nsproxy)
+ return -ESRCH;
+ else
+ ctx->root_nsproxy = nsproxy;
+
+ /* root freezer */
+ ctx->root_freezer = task;
+ geT_task_struct(task);
+
+ ret = may_checkpoint_task(ctx, task);
+ if (ret) {
+ ckpt_write_err(ctx, NULL);
+ put_task_struct(task);
+ put_task_struct(task);
+ put_nsproxy(nsproxy);
I don't think this is safe - the ckpt_ctx_free() will
free them a second time because you're not setting them
to NULL, right?
Yes. Fortunately this hole chunk is removed by the 3rd-next patch.
I'll make sure it's correct here too.
Thanks,
Oren.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Application checkpoint/restart (c/r) is the ability to save the state
of a running application so that it can later resume its execution
from the time at which it was checkpointed, on the same or a different
machine.
This version introduces 'clone_with_pids()' syscall to preset pid(s)
for a child process. It is used by restart(2) to recreate process
hierarchy with the same pids as at checkpoint time.
It also adds a freezer state CHECKPOINTING to safeguard processes
during a checkpoint. Other important changes include support for
threads and zombies, credentials, signal handling, and improved
restart logic. See below for a more detailed changelog.
Compiled and tested against v2.6.31-rc3.
With the s390 patch I recently sent on top of this set, all of my
c/r tests pass, and ltp behaves the same as on plain v2.6.31-rc3
(up to and including hanging on mallocstress).
-serge
--
To unsubscribe from this list: send the line "unsubscribe linux-api" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Ccing Oleg Nesterov, Eric Biederman, Mike Waychinson, Ying Han
Note that this is a variant of an earlier clone_with_pids() interface, sent
in Mar 2009 (http://lkml.org/lkml/2009/3/13/359). Linus' major objection
was about security, as unprivileged tasks could read /var/run data and try
to run with cached pids.
This variant addresses that concern by requiring CAP_SYS_ADMIN to specify
pids. This makes sense since CAP_SYS_ADMIN is required to create a new
pid namespace anyway.
Sukadev
Oren Laadan [orenl-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org] wrote:
| From: Sukadev Bhattiprolu [off-list ref]
|
| 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 'target_pid_set' 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).
|
| Unlike clone(), clone_with_pids() needs CAP_SYS_ADMIN, at least for now, to
| prevent unprivileged processes from misusing this interface.
|
| Call clone_with_pids as follows:
|
| pid_t pids[] = { 0, 77, 99 };
| struct target_pid_set pid_set;
|
| pid_set.num_pids = sizeof(pids) / sizeof(int);
| pid_set.target_pids = &pids;
|
| syscall(__NR_clone_with_pids, flags, stack, NULL, NULL, NULL, &pid_set);
|
| If a target-pid is 0, the kernel continues to assign a pid for the process in
| that namespace. In the above example, pids[0] is 0, meaning the kernel will
| assign next available pid to the process in init_pid_ns. But kernel will assign
| pid 77 in the child pid namespace 1 and pid 99 in pid namespace 2. If either
| 77 or 99 are taken, the system call fails with -EBUSY.
|
| If 'pid_set.num_pids' exceeds the current nesting level of pid namespaces,
| the system call fails with -EINVAL.
|
| Its mostly an exploratory patch seeking feedback on the interface.
|
| NOTE:
| Compared to clone(), clone_with_pids() needs to pass in two more
| pieces of information:
|
| - number of pids in the set
| - user buffer containing the list of pids.
|
| But since clone() already takes 5 parameters, use a 'struct
| target_pid_set'.
|
| TODO:
| - Gently tested.
| - May need additional sanity checks in do_fork_with_pids().
|
| 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)
|
| Signed-off-by: Sukadev Bhattiprolu [off-list ref]
| ---
| arch/x86/include/asm/syscalls.h | 2 +
| arch/x86/include/asm/unistd_32.h | 1 +
| arch/x86/kernel/entry_32.S | 1 +
| arch/x86/kernel/process_32.c | 21 +++++++
| arch/x86/kernel/syscall_table_32.S | 1 +
| kernel/fork.c | 108 +++++++++++++++++++++++++++++++++++-
| 6 files changed, 133 insertions(+), 1 deletions(-)
|
| diff --git a/arch/x86/include/asm/syscalls.h b/arch/x86/include/asm/syscalls.h
| index 372b76e..df3c4a8 100644
| --- a/arch/x86/include/asm/syscalls.h
| +++ b/arch/x86/include/asm/syscalls.h
| @@ -40,6 +40,8 @@ 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_vfork(struct pt_regs *);
| int sys_execve(struct pt_regs *);
|
| /* kernel/signal.c */
| diff --git a/arch/x86/include/asm/unistd_32.h b/arch/x86/include/asm/unistd_32.h
| index 732a307..f65b750 100644
| --- a/arch/x86/include/asm/unistd_32.h
| +++ b/arch/x86/include/asm/unistd_32.h
| @@ -342,6 +342,7 @@
| #define __NR_pwritev 334
| #define __NR_rt_tgsigqueueinfo 335
| #define __NR_perf_counter_open 336
| +#define __NR_clone_with_pids 337
|
| #ifdef __KERNEL__
|
| diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S
| index c097e7d..c7bd1f6 100644
| --- a/arch/x86/kernel/entry_32.S
| +++ b/arch/x86/kernel/entry_32.S
| @@ -718,6 +718,7 @@ ptregs_##name: \
| PTREGSCALL(iopl)
| PTREGSCALL(fork)
| PTREGSCALL(clone)
| +PTREGSCALL(clone_with_pids)
| PTREGSCALL(vfork)
| PTREGSCALL(execve)
| PTREGSCALL(sigaltstack)
| diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c
| index 59f4524..9965c06 100644
| --- a/arch/x86/kernel/process_32.c
| +++ b/arch/x86/kernel/process_32.c
| @@ -443,6 +443,27 @@ 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)
| +{
| + unsigned long clone_flags;
| + unsigned long newsp;
| + int __user *parent_tidptr;
| + int __user *child_tidptr;
| + void __user *upid_setp;
| +
| + clone_flags = regs->bx;
| + newsp = regs->cx;
| + parent_tidptr = (int __user *)regs->dx;
| + child_tidptr = (int __user *)regs->di;
| + upid_setp = (void __user *)regs->bp;
| +
| + if (!newsp)
| + newsp = regs->sp;
| +
| + return do_fork_with_pids(clone_flags, newsp, regs, 0, parent_tidptr,
| + child_tidptr, upid_setp);
| +}
| +
| /*
| * sys_execve() executes a new program.
| */
| diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S
| index d51321d..879e5ec 100644
| --- a/arch/x86/kernel/syscall_table_32.S
| +++ b/arch/x86/kernel/syscall_table_32.S
| @@ -336,3 +336,4 @@ ENTRY(sys_call_table)
| .long sys_pwritev
| .long sys_rt_tgsigqueueinfo /* 335 */
| .long sys_perf_counter_open
| + .long ptregs_clone_with_pids
| diff --git a/kernel/fork.c b/kernel/fork.c
| index 64d53d9..29c66f0 100644
| --- a/kernel/fork.c
| +++ b/kernel/fork.c
| @@ -1336,6 +1336,97 @@ struct task_struct * __cpuinit fork_idle(int cpu)
| }
|
| /*
| + * If user specified any 'target-pids' in @upid_setp, copy them from
| + * user and return a pointer to a local copy of the list of pids. The
| + * caller must free the list, when they are done using it.
| + *
| + * If user did not specify any target pids, return NULL (caller should
| + * treat this like normal clone).
| + *
| + * On any errors, return the error code
| + */
| +static pid_t *copy_target_pids(void __user *upid_setp)
| +{
| + int j;
| + int rc;
| + int size;
| + int unum_pids; /* # of pids specified by user */
| + int knum_pids; /* # of pids needed in kernel */
| + pid_t *target_pids;
| + struct target_pid_set pid_set;
| +
| + if (!upid_setp)
| + return NULL;
| +
| + rc = copy_from_user(&pid_set, upid_setp, sizeof(pid_set));
| + if (rc)
| + return ERR_PTR(-EFAULT);
| +
| + unum_pids = pid_set.num_pids;
| + knum_pids = task_pid(current)->level + 1;
| +
| + if (!unum_pids)
| + return NULL;
| +
| + if (unum_pids < 0 || unum_pids > knum_pids)
| + return ERR_PTR(-EINVAL);
| +
| + /*
| + * To keep alloc_pid() simple, allocate an extra pid_t in target_pids[]
| + * and set it to 0. This last entry in target_pids[] corresponds to the
| + * (yet-to-be-created) descendant pid-namespace if CLONE_NEWPID was
| + * specified. If CLONE_NEWPID was not specified, this last entry will
| + * simply be ignored.
| + */
| + target_pids = kzalloc((knum_pids + 1) * sizeof(pid_t), GFP_KERNEL);
| + if (!target_pids)
| + return ERR_PTR(-ENOMEM);
| +
| + /*
| + * A process running in a level 2 pid namespace has three pid namespaces
| + * and hence three pid numbers. If this process is checkpointed,
| + * information about these three namespaces are saved. We refer to these
| + * namespaces as 'known namespaces'.
| + *
| + * If this checkpointed process is however restarted in a level 3 pid
| + * namespace, the restarted process has an extra ancestor pid namespace
| + * (i.e 'unknown namespace') and 'knum_pids' exceeds 'unum_pids'.
| + *
| + * During restart, the process requests specific pids for its 'known
| + * namespaces' and lets kernel assign pids to its 'unknown namespaces'.
| + *
| + * Since the requested-pids correspond to 'known namespaces' and since
| + * 'known-namespaces' are younger than (i.e descendants of) 'unknown-
| + * namespaces', copy requested pids to the back-end of target_pids[]
| + * (i.e before the last entry for CLONE_NEWPID mentioned above).
| + * Any entries in target_pids[] not corresponding to a requested pid
| + * will be set to zero and kernel assigns a pid in those namespaces.
| + *
| + * NOTE: The order of pids in target_pids[] is oldest pid namespace to
| + * youngest (target_pids[0] corresponds to init_pid_ns). i.e.
| + * the order is:
| + *
| + * - pids for 'unknown-namespaces' (if any)
| + * - pids for 'known-namespaces' (requested pids)
| + * - 0 in the last entry (for CLONE_NEWPID).
| + */
| + j = knum_pids - unum_pids;
| + size = unum_pids * sizeof(pid_t);
| +
| + rc = copy_from_user(&target_pids[j], pid_set.target_pids, size);
| + if (rc) {
| + rc = -EFAULT;
| + goto out_free;
| + }
| +
| + return target_pids;
| +
| +out_free:
| + kfree(target_pids);
| + return ERR_PTR(rc);
| +}
| +
| +/*
| * Ok, this is the main fork-routine.
| *
| * It copies the process, and if successful kick-starts
| @@ -1352,7 +1443,7 @@ long do_fork_with_pids(unsigned long clone_flags,
| struct task_struct *p;
| int trace = 0;
| long nr;
| - pid_t *target_pids = NULL;
| + pid_t *target_pids;
|
| /*
| * Do some preliminary argument and permissions checking before we
| @@ -1386,6 +1477,17 @@ long do_fork_with_pids(unsigned long clone_flags,
| }
| }
|
| + target_pids = copy_target_pids(pid_setp);
| +
| + if (target_pids) {
| + if (IS_ERR(target_pids))
| + return PTR_ERR(target_pids);
| +
| + nr = -EPERM;
| + if (!capable(CAP_SYS_ADMIN))
| + goto out_free;
| + }
| +
| /*
| * When called from kernel_thread, don't do user tracing stuff.
| */
| @@ -1453,6 +1555,10 @@ long do_fork_with_pids(unsigned long clone_flags,
| } else {
| nr = PTR_ERR(p);
| }
| +
| +out_free:
| + kfree(target_pids);
| +
| return nr;
| }
|
| --
| 1.6.0.4
--
To unsubscribe from this list: send the line "unsubscribe linux-api" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html