diff --git a/lib/libc/sys/Symbol.map b/lib/libc/sys/Symbol.map index ce6f32a..4f16b5d 100644 --- a/lib/libc/sys/Symbol.map +++ b/lib/libc/sys/Symbol.map @@ -360,6 +360,11 @@ FBSD_1.1 { unlinkat; }; +FBSD_1.2 { + thr_cancel_deferred; + thr_cancel_test; +}; + FBSDprivate_1.0 { ___acl_aclcheck_fd; __sys___acl_aclcheck_fd; @@ -952,6 +957,10 @@ FBSDprivate_1.0 { __sys_thr_kill; _thr_kill2; __sys_thr_kill2; + __sys_thr_cancel_deferred; + _thr_cancel_deferred; + __sys_thr_cancel_test; + _thr_cancel_test; _thr_new; __sys_thr_new; _thr_self; diff --git a/lib/libthr/thread/thr_cancel.c b/lib/libthr/thread/thr_cancel.c index bf93fdc..d5d2034 100644 --- a/lib/libthr/thread/thr_cancel.c +++ b/lib/libthr/thread/thr_cancel.c @@ -49,6 +49,7 @@ testcancel(struct pthread *curthread) void _thr_testcancel(struct pthread *curthread) { + testcancel(curthread); } @@ -80,7 +81,7 @@ int _pthread_setcancelstate(int state, int *oldstate) { struct pthread *curthread = _get_curthread(); - int oldval; + int error, oldval; oldval = curthread->cancel_enable; switch (state) { @@ -97,19 +98,28 @@ _pthread_setcancelstate(int state, int *oldstate) default: return (EINVAL); } + error = 0; + if (!curthread->cancel_async) { + if (oldval && !curthread->cancel_enable) + error = thr_cancel_deferred(THR_CANCEL_DEFERRED_DISABLE, + NULL); + else if (!oldval && curthread->cancel_enable) + error = thr_cancel_deferred(THR_CANCEL_DEFERRED_ENABLE, + NULL); + } if (oldstate) { *oldstate = oldval ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE; } - return (0); + return (error); } int _pthread_setcanceltype(int type, int *oldtype) { - struct pthread *curthread = _get_curthread(); - int oldval; + struct pthread *curthread = _get_curthread(); + int error, oldval; oldval = curthread->cancel_async; switch (type) { @@ -123,12 +133,21 @@ _pthread_setcanceltype(int type, int *oldtype) default: return (EINVAL); } + error = 0; + if (curthread->cancel_enable) { + if (oldval && !curthread->cancel_async) + error = thr_cancel_deferred(THR_CANCEL_DEFERRED_ENABLE, + NULL); + else if (!oldval && curthread->cancel_async) + error = thr_cancel_deferred(THR_CANCEL_DEFERRED_DISABLE, + NULL); + } if (oldtype) { *oldtype = oldval ? PTHREAD_CANCEL_ASYNCHRONOUS : PTHREAD_CANCEL_DEFERRED; } - return (0); + return (error); } void @@ -137,6 +156,8 @@ _pthread_testcancel(void) struct pthread *curthread = _get_curthread(); _thr_cancel_enter(curthread); + if (curthread->cancel_enable && !curthread->cancel_async) + thr_cancel_test(); _thr_cancel_leave(curthread); } diff --git a/lib/libthr/thread/thr_create.c b/lib/libthr/thread/thr_create.c index f73a6c9..7012106 100644 --- a/lib/libthr/thread/thr_create.c +++ b/lib/libthr/thread/thr_create.c @@ -263,6 +263,7 @@ thread_start(struct pthread *curthread) if (curthread->force_exit) _pthread_exit(PTHREAD_CANCELED); + thr_cancel_deferred(THR_CANCEL_DEFERRED_ENABLE, NULL); if (curthread->unblock_sigcancel) { sigset_t set1; diff --git a/lib/libthr/thread/thr_init.c b/lib/libthr/thread/thr_init.c index 1bfdd28..302488c 100644 --- a/lib/libthr/thread/thr_init.c +++ b/lib/libthr/thread/thr_init.c @@ -408,6 +408,7 @@ init_main_thread(struct pthread *thread) thread->cancel_enable = 1; thread->cancel_async = 0; thr_set_name(thread->tid, "initial thread"); + thr_cancel_deferred(THR_CANCEL_DEFERRED_ENABLE, NULL); /* Initialize the mutex queue: */ TAILQ_INIT(&thread->mutexq); diff --git a/lib/libthr/thread/thr_private.h b/lib/libthr/thread/thr_private.h index 415c1ec..18286f9 100644 --- a/lib/libthr/thread/thr_private.h +++ b/lib/libthr/thread/thr_private.h @@ -78,9 +78,6 @@ typedef TAILQ_HEAD(pthreadlist, pthread) pthreadlist; typedef TAILQ_HEAD(atfork_head, pthread_atfork) atfork_head; TAILQ_HEAD(mutex_queue, pthread_mutex); -/* Signal to do cancellation */ -#define SIGCANCEL 32 - /* * Kernel fatal error handler macro. */ diff --git a/sys/compat/freebsd32/syscalls.master b/sys/compat/freebsd32/syscalls.master index 4f1fc28..8726a54 100644 --- a/sys/compat/freebsd32/syscalls.master +++ b/sys/compat/freebsd32/syscalls.master @@ -41,6 +41,7 @@ ; NOPROTO same as STD except do not create structure or ; function prototype in sys/sysproto.h. Does add a ; definition to syscall.h besides adding a sysent. +; CANCEL syscall is the cancelation point ; #ifdef's, etc. may be included, and are copied to the output files. @@ -72,7 +73,7 @@ size_t nbyte); } 5 AUE_OPEN_RWTC NOPROTO { int open(char *path, int flags, \ int mode); } -6 AUE_CLOSE NOPROTO { int close(int fd); } +6 AUE_CLOSE NOPROTO|CANCEL { int close(int fd); } 7 AUE_WAIT4 STD { int freebsd32_wait4(int pid, int *status, \ int options, struct rusage32 *rusage); } 8 AUE_CREAT OBSOL old creat @@ -962,3 +963,6 @@ fd_set *ou, fd_set *ex, \ const struct timespec32 *ts, \ const sigset_t *sm); } +523 AUE_NULL NOPROTO { int thr_cancel_deferred(uint32_t op, \ + uint32_t *val); } +524 AUE_NULL NOPROTO|CANCEL {int thr_cancel_test(void); } \ No newline at end of file diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index 8a322de..544edfa 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -1478,6 +1478,10 @@ kern_sigsuspend(struct thread *td, sigset_t mask) kern_sigprocmask(td, SIG_SETMASK, &mask, &td->td_oldsigmask, SIGPROCMASK_PROC_LOCKED); td->td_pflags |= TDP_OLDMASK; + if ((td->td_pflags & TDP_CANCELDF) != 0) { + td->td_pflags |= TDP_CANCELPOINT; + SIGDELSET(td->td_sigmask, SIGCANCEL); + } /* * Process signals now. Otherwise, we can get spurious wakeup @@ -1497,6 +1501,15 @@ kern_sigsuspend(struct thread *td, sigset_t mask) mtx_unlock(&p->p_sigacts->ps_mtx); } PROC_UNLOCK(p); + + /* + * This allows ast() to deliver SIGCANCEL even despite return + * code from the sigsuspend() is EJUSTRETURN, that prevents + * the check_cancel() call from syscall_enter().. + */ + if ((td->td_pflags & TDP_CANCELDF) != 0) + td->td_pflags |= TDP_CANCELPOINT; + return (EJUSTRETURN); } @@ -2535,6 +2548,16 @@ issignal(struct thread *td, int stop_allowed) SIGSETOR(sigpending, p->p_sigqueue.sq_signals); SIGSETNAND(sigpending, td->td_sigmask); + /* + * When in the deferred cancelation mode, SIGCANCEL is + * only delivered at the cancelation points. + * Otherwise, it is a normal signal. + * If the debugger decides to send SIGCANCEL, so be it. + */ + if ((td->td_pflags & TDP_CANCELDF) != 0 && + (td->td_pflags & TDP_CANCELPOINT) == 0) + SIGDELSET(sigpending, SIGCANCEL); + if (p->p_flag & P_PPWAIT) SIG_STOPSIGMASK(sigpending); if (SIGISEMPTY(sigpending)) /* no signal to send */ @@ -2556,6 +2579,7 @@ issignal(struct thread *td, int stop_allowed) sigqueue_delete(&p->p_sigqueue, sig); continue; } + if (p->p_flag & P_TRACED && (p->p_flag & P_PPWAIT) == 0) { /* * If traced, always stop. diff --git a/sys/kern/kern_thr.c b/sys/kern/kern_thr.c index d8f7a8e..4bd96df 100644 --- a/sys/kern/kern_thr.c +++ b/sys/kern/kern_thr.c @@ -522,3 +522,54 @@ thr_set_name(struct thread *td, struct thr_set_name_args *uap) PROC_UNLOCK(p); return (error); } + +int +thr_cancel_deferred(struct thread *td, struct thr_cancel_deferred_args *uap) +{ + struct proc *p; + int error; + + error = 0; + p = td->td_proc; + switch (uap->op) { + case THR_CANCEL_DEFERRED_ENABLE: + if (uap->val != NULL) { + error = suword32(uap->val, !!(td->td_pflags & + TDP_CANCELDF)); + if (error != 0) + break; + } + td->td_pflags |= TDP_CANCELDF; + PROC_LOCK(p); + SIGADDSET(td->td_sigmask, SIGCANCEL); + PROC_UNLOCK(p); + break; + + case THR_CANCEL_DEFERRED_DISABLE: + if (uap->val != NULL) { + error = suword32(uap->val, !!(td->td_pflags & + TDP_CANCELDF)); + if (error != 0) + break; + } + td->td_pflags &= ~TDP_CANCELDF; + PROC_LOCK(p); + SIGDELSET(td->td_sigmask, SIGCANCEL); + PROC_UNLOCK(p); + break; + + default: + error = EINVAL; + break; + } + + return (error); +} + +int +thr_cancel_test(struct thread *td __unused, + struct thr_cancel_test_args *uap __unused) +{ + + return (0); +} diff --git a/sys/kern/makesyscalls.sh b/sys/kern/makesyscalls.sh index 46e04fc..af37cc1 100644 --- a/sys/kern/makesyscalls.sh +++ b/sys/kern/makesyscalls.sh @@ -257,6 +257,10 @@ s/\$//g if (flag("NOTSTATIC")) { thr_flag = "SY_THR_ABSENT" } + sy_flags = "0" + if (flag("CANCEL")) { + sy_flags = "SY_FLAG_CANCEL_POINT" + } if ($NF != "}") { funcalias=$(NF-2) argalias=$(NF-1) @@ -347,13 +351,6 @@ s/\$//g auditev = $2; } - # - # The currently-empty flags field. - # - { - flags = "0"; - } - type("STD") || type("NODEF") || type("NOARGS") || type("NOPROTO") \ || type("NOSTD") { parseline() @@ -408,8 +405,8 @@ s/\$//g printf("%s },", "lkmressys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT") > sysent column = column + length("lkmressys") + length("AUE_NULL") + 3 } else { - printf("%s, %s, NULL, 0, 0, %s, %s },", funcname, auditev, flags, thr_flag) > sysent - column = column + length(funcname) + length(auditev) + length(flags) + 3 + printf("%s, %s, NULL, 0, 0, %s, %s },", funcname, auditev, sy_flags, thr_flag) > sysent + column = column + length(funcname) + length(auditev) + length(sy_flags) + 3 } align_sysent_comment(column) printf("/* %d = %s */\n", syscall, funcalias) > sysent @@ -482,10 +479,10 @@ s/\$//g length("lkmressys") + length("AUE_NULL") + 3) } else { printf("\t{ %s(%s,%s), %s, NULL, 0, 0, %s, %s },", - wrap, argssize, funcname, auditev, flags, thr_flag) > sysent + wrap, argssize, funcname, auditev, sy_flags, thr_flag) > sysent align_sysent_comment(8 + 9 + length(argssize) + 1 + \ length(funcname) + length(auditev) + \ - length(flags) + 4) + length(sy_flags) + 4) } printf("/* %d = %s %s */\n", syscall, descr, funcalias) > sysent printf("\t\"%s.%s\",\t\t/* %d = %s %s */\n", diff --git a/sys/kern/subr_trap.c b/sys/kern/subr_trap.c index 5c90d95..dda1aa9 100644 --- a/sys/kern/subr_trap.c +++ b/sys/kern/subr_trap.c @@ -235,12 +235,44 @@ ast(struct trapframe *framep) td->td_pflags &= ~TDP_OLDMASK; kern_sigprocmask(td, SIG_SETMASK, &td->td_oldsigmask, NULL, 0); } + if ((td->td_pflags & TDP_CANCELDF) != 0) { + PROC_LOCK(p); + SIGADDSET(td->td_sigmask, SIGCANCEL); + PROC_UNLOCK(p); + } + td->td_pflags &= ~TDP_CANCELPOINT; userret(td, framep); mtx_assert(&Giant, MA_NOTOWNED); } #ifdef HAVE_SYSCALL_ARGS_DEF +static int +check_cancel(struct thread *td, struct sysent *callp, int serror) +{ + struct proc *p; + int error; + + if ((td->td_pflags & TDP_CANCELDF) == 0 || + (callp->sy_flags & SY_FLAG_CANCEL_POINT) == 0 || + !(serror == EINTR || serror == ERESTART)) { + td->td_pflags &= ~TDP_CANCELPOINT; + return (0); + } + + p = td->td_proc; + PROC_LOCK(p); + SIGDELSET(td->td_sigmask, SIGCANCEL); + if (SIGISMEMBER(td->td_siglist, SIGCANCEL)) { + error = EINTR; + signotify(td); + } else + error = 0; + PROC_UNLOCK(p); + td->td_pflags |= TDP_CANCELPOINT; + return (error); +} + const char * syscallname(struct proc *p, u_int code) { @@ -300,6 +332,9 @@ syscallenter(struct thread *td, struct syscall_args *sa) if (error != 0) goto retval; } + error = check_cancel(td, sa->callp, EINTR); + if (error != 0) + goto retval; error = syscall_thread_enter(td, sa->callp); if (error != 0) goto retval; @@ -343,6 +378,7 @@ syscallenter(struct thread *td, struct syscall_args *sa) PROC_UNLOCK(p); } (p->p_sysent->sv_set_syscall_retval)(td, error); + check_cancel(td, sa->callp, error); return (error); } diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index 8e11134..44760e6 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -41,6 +41,7 @@ ; function prototype in sys/sysproto.h. Does add a ; definition to syscall.h besides adding a sysent. ; NONSTATIC syscall is loadable +; CANCEL syscall is the cancelation point ; ; Please copy any additions and changes to the following compatability tables: ; sys/compat/freebsd32/syscalls.master @@ -69,7 +70,7 @@ ; XXX should be { int open(const char *path, int flags, ...); } ; but we're not ready for `const' or varargs. ; XXX man page says `mode_t mode'. -6 AUE_CLOSE STD { int close(int fd); } +6 AUE_CLOSE STD|CANCEL { int close(int fd); } 7 AUE_WAIT4 STD { int wait4(int pid, int *status, \ int options, struct rusage *rusage); } \ wait4 wait_args int @@ -922,5 +923,8 @@ fd_set *ou, fd_set *ex, \ const struct timespec *ts, \ const sigset_t *sm); } +523 AUE_NULL STD { int thr_cancel_deferred(uint32_t op, \ + uint32_t *val); } +524 AUE_NULL STD|CANCEL {int thr_cancel_test(void); } ; Please copy any additions and changes to the following compatability tables: ; sys/compat/freebsd32/syscalls.master diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 336279e..90a3a48 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -383,11 +383,11 @@ do { \ #define TDP_COWINPROGRESS 0x00000010 /* Snapshot copy-on-write in progress. */ #define TDP_ALTSTACK 0x00000020 /* Have alternate signal stack. */ #define TDP_DEADLKTREAT 0x00000040 /* Lock aquisition - deadlock treatment. */ -#define TDP_UNUSED80 0x00000080 /* available. */ +#define TDP_CANCELPOINT 0x00000080 /* Deliver SIGCANCEL. */ #define TDP_NOSLEEPING 0x00000100 /* Thread is not allowed to sleep on a sq. */ #define TDP_OWEUPC 0x00000200 /* Call addupc() at next AST. */ #define TDP_ITHREAD 0x00000400 /* Thread is an interrupt thread. */ -#define TDP_UNUSED800 0x00000800 /* available. */ +#define TDP_CANCELDF 0x00000800 /* In deferred cancel mode. */ #define TDP_SCHED1 0x00001000 /* Reserved for scheduler private use */ #define TDP_SCHED2 0x00002000 /* Reserved for scheduler private use */ #define TDP_SCHED3 0x00004000 /* Reserved for scheduler private use */ diff --git a/sys/sys/signal.h b/sys/sys/signal.h index a7ca96e..dc5a36f 100644 --- a/sys/sys/signal.h +++ b/sys/sys/signal.h @@ -111,6 +111,7 @@ #if __BSD_VISIBLE #define SIGTHR 32 /* reserved by thread library. */ #define SIGLWP SIGTHR +#define SIGCANCEL SIGTHR /* Signal to do cancellation */ #endif #define SIGRTMIN 65 diff --git a/sys/sys/sysent.h b/sys/sys/sysent.h index 4754f54..8546b12 100644 --- a/sys/sys/sysent.h +++ b/sys/sys/sysent.h @@ -64,6 +64,8 @@ struct sysent { /* system call table */ u_int32_t sy_thrcnt; }; +#define SY_FLAG_CANCEL_POINT 0x0001 + #define SY_THR_FLAGMASK 0x7 #define SY_THR_STATIC 0x1 #define SY_THR_DRAINING 0x2 diff --git a/sys/sys/thr.h b/sys/sys/thr.h index 7ccc872..dcfe81f 100644 --- a/sys/sys/thr.h +++ b/sys/sys/thr.h @@ -58,6 +58,9 @@ struct thr_param { void *spare[3]; /* TODO: cpu affinity mask etc. */ }; +#define THR_CANCEL_DEFERRED_ENABLE 1 +#define THR_CANCEL_DEFERRED_DISABLE 2 + /* * See pthread_* */ @@ -79,6 +82,8 @@ int thr_kill2(pid_t pid, long id, int sig); int thr_suspend(const struct timespec *timeout); int thr_wake(long id); int thr_set_name(long id, const char *name); +int thr_cancel_deferred(uint32_t op, uint32_t *val); +int thr_cancel_test(void); __END_DECLS #endif /* !_KERNEL */