Atomic insertion/removal of groups of system call vectors at runtime with

a basic facility for rollback.

Proposed on tech-kern@.
This commit is contained in:
ad 2008-11-12 14:29:31 +00:00
parent 307004b54f
commit 119366618e
5 changed files with 189 additions and 9 deletions

View File

@ -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 <sys/cdefs.h>
__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 <sys/kernel.h>
#include <sys/proc.h>
#include <sys/signalvar.h>
#include <sys/syscall.h>
#include <sys/syscallargs.h>
#include <sys/syscallvar.h>
#include <sys/ktrace.h>
#include <sys/intr.h>
#include <sys/cpu.h>
#include <sys/module.h>
/*
* 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).
*/

View File

@ -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 <sys/cdefs.h>
__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 <sys/fcntl.h>
#include <sys/kauth.h>
#include <sys/vnode.h>
#include <sys/syscallvar.h>
#include <sys/xcall.h>
#include <sys/module.h>
#include <uvm/uvm_extern.h>
@ -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;
}

View File

@ -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 ")";

View File

@ -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 <sys/systm.h>
#include <sys/proc.h>
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)

View File

@ -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)));