diff --git a/sys/kern/kern_stub.c b/sys/kern/kern_stub.c index 12df5f5b9f9a..a45c15e27145 100644 --- a/sys/kern/kern_stub.c +++ b/sys/kern/kern_stub.c @@ -1,4 +1,4 @@ -/* $NetBSD: kern_stub.c,v 1.11 2008/10/15 16:03:29 wrstuden Exp $ */ +/* $NetBSD: kern_stub.c,v 1.12 2008/11/12 14:29:31 ad Exp $ */ /*- * Copyright (c) 2007, 2008 The NetBSD Foundation, Inc. @@ -62,7 +62,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: kern_stub.c,v 1.11 2008/10/15 16:03:29 wrstuden Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_stub.c,v 1.12 2008/11/12 14:29:31 ad Exp $"); #include "opt_ptrace.h" #include "opt_ktrace.h" @@ -72,10 +72,13 @@ __KERNEL_RCSID(0, "$NetBSD: kern_stub.c,v 1.11 2008/10/15 16:03:29 wrstuden Exp #include #include #include +#include #include +#include #include #include #include +#include /* * Nonexistent system call-- signal process (may want to handle it). Flag @@ -160,7 +163,6 @@ cpu_kpreempt_disabled(void) # endif #endif /* !__HAVE_PREEMPTION */ -/* ARGSUSED */ int sys_nosys(struct lwp *l, const void *v, register_t *retval) { @@ -171,6 +173,65 @@ sys_nosys(struct lwp *l, const void *v, register_t *retval) return ENOSYS; } +int +sys_nomodule(struct lwp *l, const void *v, register_t *retval) +{ +#ifdef MODULAR + static struct { + u_int al_code; + const char *al_module; + } const autoload[] = { + { SYS__ksem_init, "ksem" }, + { SYS__ksem_open, "ksem" }, + { SYS__ksem_unlink, "ksem" }, + { SYS__ksem_close, "ksem" }, + { SYS__ksem_post, "ksem" }, + { SYS__ksem_wait, "ksem" }, + { SYS__ksem_trywait, "ksem" }, + { SYS__ksem_getvalue, "ksem" }, + { SYS__ksem_destroy, "ksem" }, + }; + const struct sysent *sy; + const struct emul *em; + int code, i; + + /* + * Restart the syscall if we interrupted a module unload that + * failed. Acquiring module_lock delays us until any unload + * has been completed or rolled back. + */ + mutex_enter(&module_lock); + sy = l->l_sysent; + if (sy->sy_call != sys_nomodule) { + mutex_exit(&module_lock); + return ERESTART; + } + /* + * Try to autoload a module to satisfy the request. If it + * works, retry the request. + */ + em = l->l_proc->p_emul; + if (em == &emul_netbsd) { + code = sy - em->e_sysent; + for (i = 0; i < __arraycount(autoload); i++) { + if (autoload[i].al_code != code) { + continue; + } + if (module_autoload(autoload[i].al_module, + MODULE_CLASS_ANY) != 0 || + sy->sy_call == sys_nomodule) { + break; + } + mutex_exit(&module_lock); + return ERESTART; + } + } + mutex_exit(&module_lock); +#endif /* MODULAR */ + + return sys_nosys(l, v, retval); +} + /* * Unsupported device function (e.g. writing to read-only device). */ diff --git a/sys/kern/kern_subr.c b/sys/kern/kern_subr.c index 1c44249099f8..e8d5ecc4a220 100644 --- a/sys/kern/kern_subr.c +++ b/sys/kern/kern_subr.c @@ -1,4 +1,4 @@ -/* $NetBSD: kern_subr.c,v 1.193 2008/11/11 06:46:44 dyoung Exp $ */ +/* $NetBSD: kern_subr.c,v 1.194 2008/11/12 14:29:31 ad Exp $ */ /*- * Copyright (c) 1997, 1998, 1999, 2002, 2007, 2008 The NetBSD Foundation, Inc. @@ -79,7 +79,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: kern_subr.c,v 1.193 2008/11/11 06:46:44 dyoung Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_subr.c,v 1.194 2008/11/12 14:29:31 ad Exp $"); #include "opt_ddb.h" #include "opt_md.h" @@ -105,6 +105,9 @@ __KERNEL_RCSID(0, "$NetBSD: kern_subr.c,v 1.193 2008/11/11 06:46:44 dyoung Exp $ #include #include #include +#include +#include +#include #include @@ -1330,3 +1333,98 @@ trace_exit(register_t code, register_t rval[], int error) process_stoptrace(); #endif } + +int +syscall_establish(const struct emul *em, const struct syscall_package *sp) +{ + struct sysent *sy; + int i; + + KASSERT(mutex_owned(&module_lock)); + + if (em == NULL) { + em = &emul_netbsd; + } + sy = em->e_sysent; + + /* + * Ensure that all preconditions are valid, since this is + * an all or nothing deal. Once a system call is entered, + * it can become busy and we could be unable to remove it + * on error. + */ + for (i = 0; sp[i].sp_call != NULL; i++) { + if (sy[sp[i].sp_code].sy_call != sys_nomodule) { + return EBUSY; + } + } + /* Everything looks good, patch them in. */ + for (i = 0; sp[i].sp_call != NULL; i++) { + sy[sp[i].sp_code].sy_call = sp[i].sp_call; + } + + return 0; +} + +int +syscall_disestablish(const struct emul *em, const struct syscall_package *sp) +{ + struct sysent *sy; + uint64_t where; + lwp_t *l; + int i; + + KASSERT(mutex_owned(&module_lock)); + + if (em == NULL) { + em = &emul_netbsd; + } + sy = em->e_sysent; + + /* + * First, patch the system calls to sys_nomodule to gate further + * activity. + */ + for (i = 0; sp[i].sp_call != NULL; i++) { + KASSERT(sy[sp[i].sp_code].sy_call == sp[i].sp_call); + sy[sp[i].sp_code].sy_call = sys_nomodule; + } + + /* + * Run a cross call to cycle through all CPUs. This does two + * things: lock activity provides a barrier and makes our update + * of sy_call visible to all CPUs, and upon return we can be sure + * that we see pertinent values of l_sysent posted by remote CPUs. + */ + where = xc_broadcast(0, (xcfunc_t)nullop, NULL, NULL); + xc_wait(where); + + /* + * Now it's safe to check l_sysent. Run through all LWPs and see + * if anyone is still using the system call. + */ + for (i = 0; sp[i].sp_call != NULL; i++) { + mutex_enter(proc_lock); + LIST_FOREACH(l, &alllwp, l_list) { + if (l->l_sysent == &sy[sp[i].sp_code]) { + break; + } + } + mutex_exit(proc_lock); + if (l == NULL) { + continue; + } + /* + * We lose: one or more calls are still in use. Put back + * the old entrypoints and act like nothing happened. + * When we drop module_lock, any system calls held in + * sys_nomodule() will be restarted. + */ + for (i = 0; sp[i].sp_call != NULL; i++) { + sy[sp[i].sp_code].sy_call = sp[i].sp_call; + } + return EBUSY; + } + + return 0; +} diff --git a/sys/kern/makesyscalls.sh b/sys/kern/makesyscalls.sh index b3df0949d687..8119834ad92d 100644 --- a/sys/kern/makesyscalls.sh +++ b/sys/kern/makesyscalls.sh @@ -1,5 +1,5 @@ #! /bin/sh - -# $NetBSD: makesyscalls.sh,v 1.73 2008/10/13 18:16:33 pooka Exp $ +# $NetBSD: makesyscalls.sh,v 1.74 2008/11/12 14:29:31 ad Exp $ # # Copyright (c) 1994, 1996, 2000 Christopher G. Demetriou # All rights reserved. @@ -345,6 +345,12 @@ function parseline() { sycall_flags = "SYCALL_INDIRECT | " sycall_flags f++ } + if ($f == "MODULAR") { # registered at runtime + modular = 1 + f++ + } else { + modular = 0; + } if ($f == "RUMP") { rumpable = 1 f++ @@ -497,7 +503,9 @@ function putent(type, compatwrap) { } else { printf("ns(struct %s%s_args), ", compatwrap_, funcname) > sysent } - if (compatwrap == "") + if (modular) + wfn = "(sy_call_t *)sys_nomodule"; + else if (compatwrap == "") wfn = "(sy_call_t *)" funcname; else wfn = "(sy_call_t *)" compatwrap "(" funcname ")"; diff --git a/sys/sys/syscallvar.h b/sys/sys/syscallvar.h index c42ff89dc395..8c574c7d2e39 100644 --- a/sys/sys/syscallvar.h +++ b/sys/sys/syscallvar.h @@ -1,4 +1,4 @@ -/* $NetBSD: syscallvar.h,v 1.2 2008/10/21 12:22:00 ad Exp $ */ +/* $NetBSD: syscallvar.h,v 1.3 2008/11/12 14:29:31 ad Exp $ */ /*- * Copyright (c) 2008 The NetBSD Foundation, Inc. @@ -39,6 +39,18 @@ #include #include +extern const struct emul emul_netbsd; + +struct syscall_package { + u_short sp_code; + u_short sp_flags; + sy_call_t *sp_call; +}; + +void syscall_init(void); +int syscall_establish(const struct emul *, const struct syscall_package *); +int syscall_disestablish(const struct emul *, const struct syscall_package *); + static inline int sy_call(const struct sysent *sy, struct lwp *l, const void *uap, register_t *rval) diff --git a/sys/sys/systm.h b/sys/sys/systm.h index f96c5fb05129..342519ad8107 100644 --- a/sys/sys/systm.h +++ b/sys/sys/systm.h @@ -1,4 +1,4 @@ -/* $NetBSD: systm.h,v 1.229 2008/11/12 12:36:28 ad Exp $ */ +/* $NetBSD: systm.h,v 1.230 2008/11/12 14:29:31 ad Exp $ */ /*- * Copyright (c) 1982, 1988, 1991, 1993 @@ -157,6 +157,7 @@ void *hashinit(u_int, enum hashtype, bool, u_long *); void hashdone(void *, enum hashtype, u_long); int seltrue(dev_t, int, struct lwp *); int sys_nosys(struct lwp *, const void *, register_t *); +int sys_nomodule(struct lwp *, const void *, register_t *); void aprint_normal(const char *, ...) __attribute__((__format__(__printf__,1,2)));