axeld + bonefish:
* Implemented automatic syscall restarts:
- A syscall can indicate that it has been interrupted and can be
restarted by setting a respective bit in thread::flags. It can
store parameters it wants to be preserved for the restart in
thread::syscall_restart::parameters. Another thread::flags bit
indicates whether it has been restarted.
- handle_signals() clears the restart flag, if the handled signal
has a handler function installed and SA_RESTART is not set. Another
thread flag (THREAD_FLAGS_DONT_RESTART_SYSCALL) can prevent syscalls
from being restarted, even if they could be (not used yet, but we
might want to use it in resume_thread(), so that we stay
behaviorally compatible with BeOS).
- The architecture specific syscall handler restarts the syscall, if
the restart flag is set. Implemented for x86 only.
- Added some support functions in the private <syscall_restart.h> to
simplify the syscall restart code in the syscalls.
- Adjusted all syscalls that can potentially be restarted accordingly.
- _user_ioctl() sets new thread flag THREAD_FLAGS_IOCTL_SYSCALL while
calling the underlying FS's/driver's hook, so that syscall restarts
can also be supported there.
* thread_at_kernel_exit() invokes handle_signals() in a loop now, as
long as the latter indicates that the thread shall be suspended, so
that after waking up signals received in the meantime will be handled
before the thread returns to userland. Adjusted handle_signals()
accordingly -- when encountering a suspending signal we don't check
for further signals.
* Fixed sigsuspend(): Suspending the thread and rescheduling doesn't
result in the correct behavior. Instead we employ a temporary
condition variable and interruptably wait on it. The POSIX test
suite test passes, now.
* Made the switch_sem[_etc]() behavior on interruption consistent.
Depending on when the signal arrived (before the call or when already
waiting) the first semaphore would or wouldn't be released. Now we
consistently release it.
* Refactored _user_{read,write}[v]() syscalls. Use a common function for
either pair. The iovec version doesn't fail anymore, if anything could
be read/written at all. It also checks whether a complete vector
could be read/written, so that we won't skip data, if the underlying
FS/driver couldn't read/write more ATM.
* Some refactoring in the x86 syscall handler: The int 99 and sysenter
handlers use a common subroutine to avoid code duplication.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23983 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -41,7 +41,6 @@ bool arch_on_signal_stack(struct thread *thread);
|
||||
status_t arch_setup_signal_frame(struct thread *t, struct sigaction *sa,
|
||||
int signal, int signalMask);
|
||||
int64 arch_restore_signal_frame(void);
|
||||
void arch_check_syscall_restart(struct thread *t);
|
||||
|
||||
void arch_store_fork_frame(struct arch_fork_arg *arg);
|
||||
void arch_restore_fork_frame(struct arch_fork_arg *arg);
|
||||
|
||||
@@ -19,6 +19,8 @@ extern "C" {
|
||||
struct iframe *i386_get_user_iframe(void);
|
||||
void *x86_next_page_directory(struct thread *from, struct thread *to);
|
||||
|
||||
void x86_restart_syscall(struct iframe* frame);
|
||||
|
||||
void i386_return_from_signal();
|
||||
void i386_end_return_from_signal();
|
||||
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2008, Haiku Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef _KERNEL_SYSCALL_RESTART_H
|
||||
#define _KERNEL_SYSCALL_RESTART_H
|
||||
|
||||
#include <OS.h>
|
||||
|
||||
#include <thread.h>
|
||||
|
||||
|
||||
static inline void
|
||||
syscall_restart_handle_timeout_pre(bigtime_t& timeout)
|
||||
{
|
||||
// If restarted, get the timeout from the restart parameters. Otherwise
|
||||
// convert relative timeout to an absolute one.
|
||||
struct thread* thread = thread_get_current_thread();
|
||||
if ((thread->flags & THREAD_FLAGS_SYSCALL_RESTARTED) != 0)
|
||||
timeout = *(bigtime_t*)thread->syscall_restart.parameters;
|
||||
else if (timeout >= 0) {
|
||||
timeout += system_time();
|
||||
if (timeout < 0)
|
||||
timeout = B_INFINITE_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
syscall_restart_handle_timeout_pre(uint32& flags, bigtime_t& timeout)
|
||||
{
|
||||
// If restarted, get the timeout from the restart parameters. Otherwise
|
||||
// convert relative timeout to an absolute one.
|
||||
struct thread* thread = thread_get_current_thread();
|
||||
if ((thread->flags & THREAD_FLAGS_SYSCALL_RESTARTED) != 0)
|
||||
timeout = *(bigtime_t*)thread->syscall_restart.parameters;
|
||||
else if ((flags & B_RELATIVE_TIMEOUT) != 0) {
|
||||
timeout += system_time();
|
||||
if (timeout < 0)
|
||||
timeout = B_INFINITE_TIMEOUT;
|
||||
}
|
||||
|
||||
// any timeout is absolute at this point
|
||||
if ((flags & B_RELATIVE_TIMEOUT) != 0)
|
||||
flags = (flags & ~B_RELATIVE_TIMEOUT) | B_ABSOLUTE_TIMEOUT;
|
||||
}
|
||||
|
||||
|
||||
static inline status_t
|
||||
syscall_restart_handle_timeout_post(status_t error, bigtime_t timeout)
|
||||
{
|
||||
if (error == B_INTERRUPTED) {
|
||||
// interrupted -- store timeout and set flag for syscall restart
|
||||
struct thread* thread = thread_get_current_thread();
|
||||
*(bigtime_t*)thread->syscall_restart.parameters = timeout;
|
||||
atomic_or(&thread->flags, THREAD_FLAGS_RESTART_SYSCALL);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
static inline status_t
|
||||
syscall_restart_handle_post(status_t error)
|
||||
{
|
||||
if (error == B_INTERRUPTED) {
|
||||
// interrupted -- set flag for syscall restart
|
||||
struct thread* thread = thread_get_current_thread();
|
||||
atomic_or(&thread->flags, THREAD_FLAGS_RESTART_SYSCALL);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
static inline bool
|
||||
syscall_restart_ioctl_is_restarted()
|
||||
{
|
||||
struct thread* thread = thread_get_current_thread();
|
||||
|
||||
return (thread->flags & THREAD_FLAGS_IOCTL_SYSCALL) != 0
|
||||
&& (thread->flags & THREAD_FLAGS_SYSCALL_RESTARTED) != 0;
|
||||
}
|
||||
|
||||
|
||||
static inline status_t
|
||||
syscall_restart_ioctl_handle_post(status_t error)
|
||||
{
|
||||
if (error == B_INTERRUPTED) {
|
||||
// interrupted -- set flag for syscall restart
|
||||
struct thread* thread = thread_get_current_thread();
|
||||
if ((thread->flags & THREAD_FLAGS_IOCTL_SYSCALL) != 0)
|
||||
atomic_or(&thread->flags, THREAD_FLAGS_RESTART_SYSCALL);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
#endif // _KERNEL_SYSCALL_RESTART_H
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-2007, Haiku Inc.
|
||||
* Copyright 2004-2008, Haiku Inc.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Thread definition and structures
|
||||
@@ -205,6 +205,10 @@ struct thread {
|
||||
size_t signal_stack_size;
|
||||
bool signal_stack_enabled;
|
||||
|
||||
struct {
|
||||
uint8 parameters[32];
|
||||
} syscall_restart;
|
||||
|
||||
bool in_kernel;
|
||||
bool was_yielded;
|
||||
|
||||
@@ -278,12 +282,16 @@ struct thread_queue {
|
||||
|
||||
|
||||
// bits for the thread::flags field
|
||||
#define THREAD_FLAGS_SIGNALS_PENDING 0x01
|
||||
#define THREAD_FLAGS_DEBUG_THREAD 0x02
|
||||
#define THREAD_FLAGS_DEBUGGER_INSTALLED 0x04
|
||||
#define THREAD_FLAGS_BREAKPOINTS_DEFINED 0x08
|
||||
#define THREAD_FLAGS_BREAKPOINTS_INSTALLED 0x10
|
||||
#define THREAD_FLAGS_64_BIT_SYSCALL_RETURN 0x20
|
||||
#define THREAD_FLAGS_SIGNALS_PENDING 0x0001
|
||||
#define THREAD_FLAGS_DEBUG_THREAD 0x0002
|
||||
#define THREAD_FLAGS_DEBUGGER_INSTALLED 0x0004
|
||||
#define THREAD_FLAGS_BREAKPOINTS_DEFINED 0x0008
|
||||
#define THREAD_FLAGS_BREAKPOINTS_INSTALLED 0x0010
|
||||
#define THREAD_FLAGS_64_BIT_SYSCALL_RETURN 0x0020
|
||||
#define THREAD_FLAGS_RESTART_SYSCALL 0x0040
|
||||
#define THREAD_FLAGS_DONT_RESTART_SYSCALL 0x0080
|
||||
#define THREAD_FLAGS_SYSCALL_RESTARTED 0x0100
|
||||
#define THREAD_FLAGS_IOCTL_SYSCALL 0x0200
|
||||
|
||||
|
||||
#endif /* _KERNEL_THREAD_TYPES_H */
|
||||
|
||||
Reference in New Issue
Block a user