diff options
author | Julian Elischer <julian@FreeBSD.org> | 1998-12-19 02:55:34 +0000 |
---|---|---|
committer | Julian Elischer <julian@FreeBSD.org> | 1998-12-19 02:55:34 +0000 |
commit | 6626c6045c38a22c2dc57621a840ae612da0e2e3 (patch) | |
tree | b5baf90edc2cac8af964657b46d2d820dce40d35 /sys/compat/linux/linux_misc.c | |
parent | 02489dd7373f6eb94fc23050f36acaaa2b71167e (diff) | |
download | src-6626c6045c38a22c2dc57621a840ae612da0e2e3.tar.gz src-6626c6045c38a22c2dc57621a840ae612da0e2e3.zip |
Reviewed by: Luoqi Chen, Jordan Hubbard
Submitted by: "Richard Seaman, Jr." <lists@tar.com>
Obtained from: linux :-)
Code to allow Linux Threads to run under FreeBSD.
By default not enabled
This code is dependent on the conditional
COMPAT_LINUX_THREADS (suggested by Garret)
This is not yet a 'real' option but will be within some number of hours.
Notes
Notes:
svn path=/head/; revision=41931
Diffstat (limited to 'sys/compat/linux/linux_misc.c')
-rw-r--r-- | sys/compat/linux/linux_misc.c | 174 |
1 files changed, 173 insertions, 1 deletions
diff --git a/sys/compat/linux/linux_misc.c b/sys/compat/linux/linux_misc.c index 68fee0f657d6..4b441fd81370 100644 --- a/sys/compat/linux/linux_misc.c +++ b/sys/compat/linux/linux_misc.c @@ -25,7 +25,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: linux_misc.c,v 1.46 1998/12/04 22:54:50 archie Exp $ + * $Id: linux_misc.c,v 1.47 1998/12/10 13:47:18 jkh Exp $ */ #include <sys/param.h> @@ -41,6 +41,9 @@ #include <sys/resourcevar.h> #include <sys/stat.h> #include <sys/sysctl.h> +#ifdef COMPAT_LINUX_THREADS +#include <sys/unistd.h> +#endif /* COMPAT_LINUX_THREADS */ #include <sys/vnode.h> #include <sys/wait.h> #include <sys/time.h> @@ -560,6 +563,84 @@ linux_fork(struct proc *p, struct linux_fork_args *args) return 0; } +#ifndef COMPAT_LINUX_THREADS +int +linux_clone(struct proc *p, struct linux_clone_args *args) +{ + printf("linux_clone(%d): Not enabled\n", p->p_pid); + return (EOPNOTSUPP); +} + +#else +#define CLONE_VM 0x100 +#define CLONE_FS 0x200 +#define CLONE_FILES 0x400 +#define CLONE_SIGHAND 0x800 +#define CLONE_PID 0x1000 + +int +linux_clone(struct proc *p, struct linux_clone_args *args) +{ + int error, ff = RFPROC; + struct proc *p2; + int growable; + int initstacksize; + int maxstacksize; + int exit_signal; + vm_map_entry_t entry; + vm_map_t map; + vm_offset_t start; + struct rfork_args rf_args; + +#ifdef SMP + printf("linux_clone(%d): does not work with SMP yet\n", p->p_pid); + return (EOPNOTSUPP); +#endif +#ifdef DEBUG + if (args->flags & CLONE_PID) + printf("linux_clone(%d): CLONE_PID not yet supported\n", p->p_pid); + printf ("linux_clone(%d): invoked with flags %x and stack %x\n", p->p_pid, + (unsigned int)args->flags, (unsigned int)args->stack); +#endif + + if (!args->stack) + return (EINVAL); + exit_signal = args->flags & 0x000000ff; + if (exit_signal >= LINUX_NSIG) + return EINVAL; + exit_signal = linux_to_bsd_signal[exit_signal]; + + /* RFTHREAD probably not necessary here, but it shouldn't hurt either */ + ff |= RFTHREAD; + + if (args->flags & CLONE_VM) + ff |= RFMEM; + if (args->flags & CLONE_SIGHAND) + ff |= RFSIGSHARE; + if (!(args->flags & CLONE_FILES)) + ff |= RFFDG; + + error = 0; + start = 0; + + rf_args.flags = ff; + if (error = rfork(p, &rf_args)) + return error; + + p2 = pfind(p->p_retval[0]); + if (p2 == 0) + return ESRCH; + + p2->p_sigparent = exit_signal; + p2->p_md.md_regs->tf_esp = (unsigned int)args->stack; + +#ifdef DEBUG + printf ("linux_clone(%d): successful rfork to %d\n", p->p_pid, p2->p_pid); +#endif + return 0; +} + +#endif /* COMPAT_LINUX_THREADS */ /* XXX move */ struct linux_mmap_argv { linux_caddr_t addr; @@ -570,6 +651,11 @@ struct linux_mmap_argv { int pos; }; +#ifdef COMPAT_LINUX_THREADS +#define STACK_SIZE (2 * 1024 * 1024) +#define GUARD_SIZE (4 * PAGE_SIZE) + +#endif /* COMPAT_LINUX_THREADS */ int linux_mmap(struct proc *p, struct linux_mmap_args *args) { @@ -602,8 +688,70 @@ linux_mmap(struct proc *p, struct linux_mmap_args *args) bsd_args.flags |= MAP_FIXED; if (linux_args.flags & LINUX_MAP_ANON) bsd_args.flags |= MAP_ANON; +#ifndef COMPAT_LINUX_THREADS bsd_args.addr = linux_args.addr; bsd_args.len = linux_args.len; +#else + +#if !defined(USE_VM_STACK) && !defined(USE_VM_STACK_FOR_EXEC) + /* Linux Threads will map into the proc stack space, unless + we prevent it. This causes problems if we're not using + our VM_STACK options. + */ + if ((unsigned int)linux_args.addr + linux_args.len > (USRSTACK - MAXSSIZ)) + return (EINVAL); +#endif + + if (linux_args.flags & LINUX_MAP_GROWSDOWN) { + +#ifdef USE_VM_STACK + /* USE_VM_STACK is defined (or not) in vm/vm_map.h */ + bsd_args.flags |= MAP_STACK; +#endif + + /* The linux MAP_GROWSDOWN option does not limit auto + growth of the region. Linux mmap with this option + takes as addr the inital BOS, and as len, the initial + region size. It can then grow down from addr without + limit. However, linux threads has an implicit internal + limit to stack size of STACK_SIZE. Its just not + enforced explicitly in linux. But, here we impose + a limit of (STACK_SIZE - GUARD_SIZE) on the stack + region, since we can do this with our mmap. + + Our mmap with MAP_STACK takes addr as the maximum + downsize limit on BOS, and as len the max size of + the region. It them maps the top SGROWSIZ bytes, + and autgrows the region down, up to the limit + in addr. + + If we don't use the MAP_STACK option, the effect + of this code is to allocate a stack region of a + fixed size of (STACK_SIZE - GUARD_SIZE). + */ + + /* This gives us TOS */ + bsd_args.addr = linux_args.addr + linux_args.len; + + /* This gives us our maximum stack size */ + if (linux_args.len > STACK_SIZE - GUARD_SIZE) + bsd_args.len = linux_args.len; + else + bsd_args.len = STACK_SIZE - GUARD_SIZE; + + /* This gives us a new BOS. If we're using VM_STACK, then + mmap will just map the top SGROWSIZ bytes, and let + the stack grow down to the limit at BOS. If we're + not using VM_STACK we map the full stack, since we + don't have a way to autogrow it. + */ + bsd_args.addr -= bsd_args.len; + + } else { + bsd_args.addr = linux_args.addr; + bsd_args.len = linux_args.len; + } +#endif /* COMPAT_LINUX_THREADS */ bsd_args.prot = linux_args.prot | PROT_READ; /* always required */ bsd_args.fd = linux_args.fd; bsd_args.pos = linux_args.pos; @@ -830,11 +978,25 @@ linux_waitpid(struct proc *p, struct linux_waitpid_args *args) #endif tmp.pid = args->pid; tmp.status = args->status; +#ifndef COMPAT_LINUX_THREADS tmp.options = args->options; +#else + /* This filters out the linux option _WCLONE. I don't + think we need it, but I could be wrong. If we need + it, we need to fix wait4, since it will give us an + error return of EINVAL if we pass in _WCLONE, and + of course, it won't do anything with it. + */ + tmp.options = (args->options & (WNOHANG | WUNTRACED)); +#endif /* COMPAT_LINUX_THREADS */ tmp.rusage = NULL; if (error = wait4(p, &tmp)) +#ifndef COMPAT_LINUX_THREADS return error; +#else + return error; +#endif /* COMPAT_LINUX_THREADS */ if (args->status) { if (error = copyin(args->status, &tmpstat, sizeof(int))) return error; @@ -867,7 +1029,17 @@ linux_wait4(struct proc *p, struct linux_wait4_args *args) #endif tmp.pid = args->pid; tmp.status = args->status; +#ifndef COMPAT_LINUX_THREADS tmp.options = args->options; +#else + /* This filters out the linux option _WCLONE. I don't + think we need it, but I could be wrong. If we need + it, we need to fix wait4, since it will give us an + error return of EINVAL if we pass in _WCLONE, and + of course, it won't do anything with it. + */ + tmp.options = (args->options & (WNOHANG | WUNTRACED)); +#endif /* COMPAT_LINUX_THREADS */ tmp.rusage = args->rusage; if (error = wait4(p, &tmp)) |