summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_syscalls.c
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2010-06-28 18:06:46 +0000
committerkib <kib@FreeBSD.org>2010-06-28 18:06:46 +0000
commitb6d8416eac525c06902bf03315b88189e10545e3 (patch)
tree2ffebda973ea179d4a39ddccaf5d3e9279811ad0 /sys/kern/kern_syscalls.c
parent2774f04de78aab6af0f10e1129f45bf8be19aae5 (diff)
downloadFreeBSD-src-b6d8416eac525c06902bf03315b88189e10545e3.zip
FreeBSD-src-b6d8416eac525c06902bf03315b88189e10545e3.tar.gz
Count number of threads that enter and leave dynamically registered
syscalls. On the dynamic syscall deregistration, wait until all threads leave the syscall code. This somewhat increases the safety of the loadable modules unloading. Reviewed by: jhb Tested by: pho MFC after: 1 month
Diffstat (limited to 'sys/kern/kern_syscalls.c')
-rw-r--r--sys/kern/kern_syscalls.c56
1 files changed, 55 insertions, 1 deletions
diff --git a/sys/kern/kern_syscalls.c b/sys/kern/kern_syscalls.c
index 9cb7b68..d1a5c0d 100644
--- a/sys/kern/kern_syscalls.c
+++ b/sys/kern/kern_syscalls.c
@@ -28,12 +28,15 @@
__FBSDID("$FreeBSD$");
#include <sys/param.h>
+#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/module.h>
#include <sys/sx.h>
#include <sys/syscall.h>
#include <sys/sysent.h>
#include <sys/sysproto.h>
+#include <sys/systm.h>
+#include <machine/atomic.h>
/*
* Acts like "nosys" but can be identified in sysent for dynamic call
@@ -55,6 +58,51 @@ lkmressys(struct thread *td, struct nosys_args *args)
return (nosys(td, args));
}
+static void
+syscall_thread_drain(struct sysent *se)
+{
+ u_int32_t cnt, oldcnt;
+
+ do {
+ oldcnt = se->sy_thrcnt;
+ KASSERT((oldcnt & SY_THR_STATIC) == 0,
+ ("drain on static syscall"));
+ cnt = oldcnt | SY_THR_DRAINING;
+ } while (atomic_cmpset_acq_32(&se->sy_thrcnt, oldcnt, cnt) == 0);
+ while (atomic_cmpset_32(&se->sy_thrcnt, SY_THR_DRAINING,
+ SY_THR_ABSENT) == 0)
+ pause("scdrn", hz/2);
+}
+
+int
+syscall_thread_enter(struct thread *td, struct sysent *se)
+{
+ u_int32_t cnt, oldcnt;
+
+ do {
+ oldcnt = se->sy_thrcnt;
+ if ((oldcnt & SY_THR_STATIC) != 0)
+ return (0);
+ if ((oldcnt & (SY_THR_DRAINING | SY_THR_ABSENT)) != 0)
+ return (ENOSYS);
+ cnt = oldcnt + SY_THR_INCR;
+ } while (atomic_cmpset_acq_32(&se->sy_thrcnt, oldcnt, cnt) == 0);
+ return (0);
+}
+
+void
+syscall_thread_exit(struct thread *td, struct sysent *se)
+{
+ u_int32_t cnt, oldcnt;
+
+ do {
+ oldcnt = se->sy_thrcnt;
+ if ((oldcnt & SY_THR_STATIC) != 0)
+ return;
+ cnt = oldcnt - SY_THR_INCR;
+ } while (atomic_cmpset_rel_32(&se->sy_thrcnt, oldcnt, cnt) == 0);
+}
+
int
syscall_register(int *offset, struct sysent *new_sysent,
struct sysent *old_sysent)
@@ -74,8 +122,12 @@ syscall_register(int *offset, struct sysent *new_sysent,
sysent[*offset].sy_call != (sy_call_t *)lkmressys)
return (EEXIST);
+ KASSERT(sysent[*offset].sy_thrcnt == SY_THR_ABSENT,
+ ("dynamic syscall is not protected"));
*old_sysent = sysent[*offset];
+ new_sysent->sy_thrcnt = SY_THR_ABSENT;
sysent[*offset] = *new_sysent;
+ atomic_store_rel_32(&sysent[*offset].sy_thrcnt, 0);
return (0);
}
@@ -83,8 +135,10 @@ int
syscall_deregister(int *offset, struct sysent *old_sysent)
{
- if (*offset)
+ if (*offset) {
+ syscall_thread_drain(&sysent[*offset]);
sysent[*offset] = *old_sysent;
+ }
return (0);
}
OpenPOWER on IntegriCloud