Author: axeld Date: 2009-11-25 10:37:31 +0100 (Wed, 25 Nov 2009) New Revision: 34233 Changeset: http://dev.haiku-os.org/changeset/34233/haiku Ticket: http://dev.haiku-os.org/ticket/5027 Modified: haiku/trunk/src/system/kernel/syscalls.cpp Log: * Added a use counter for the syscalls, so that they are no longer removed while a hook is called. * This closes ticket #5027. Modified: haiku/trunk/src/system/kernel/syscalls.cpp =================================================================== --- haiku/trunk/src/system/kernel/syscalls.cpp 2009-11-25 06:03:44 UTC (rev 34232) +++ haiku/trunk/src/system/kernel/syscalls.cpp 2009-11-25 09:37:31 UTC (rev 34233) @@ -1,11 +1,17 @@ /* * Copyright 2008, Ingo Weinhold, ingo_weinhold@xxxxxxx - * Copyright 2004-2006, Haiku Inc. All rights reserved. + * Copyright 2004-2009, Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. */ -/*! Big case statement for dispatching syscalls */ +/*! Big case statement for dispatching syscalls, as well as the generic + syscall interface. +*/ + + +#include <syscalls.h> + #include <stdlib.h> #include <string.h> @@ -34,12 +40,12 @@ #include <safemode.h> #include <sem.h> #include <sys/resource.h> -#include <syscalls.h> #include <system_profiler.h> #include <thread.h> #include <tracing.h> #include <user_atomic.h> #include <usergroup.h> +#include <util/AutoLock.h> #include <vfs.h> #include <vm.h> #include <wait_for_objects.h> @@ -55,6 +61,7 @@ syscall_hook hook; uint32 version; uint32 flags; + int32 use_count; generic_syscall *previous; }; @@ -84,20 +91,17 @@ } -/** Calls the generic syscall subsystem if any. - * Also handles the special generic syscall function \c B_SYSCALL_INFO. - * Returns \c B_NAME_NOT_FOUND if either the subsystem was not found, or - * the subsystem does not support the requested function. - * All other return codes are depending on the generic syscall implementation. - */ - +/*! Calls the generic syscall subsystem if any. + Also handles the special generic syscall function \c B_SYSCALL_INFO. + Returns \c B_NAME_NOT_FOUND if either the subsystem was not found, or + the subsystem does not support the requested function. + All other return codes are depending on the generic syscall implementation. +*/ static inline status_t _user_generic_syscall(const char *userSubsystem, uint32 function, void *buffer, size_t bufferSize) { char subsystem[B_FILE_NAME_LENGTH]; - generic_syscall *syscall; - status_t status = B_NAME_NOT_FOUND; if (!IS_USER_ADDRESS(userSubsystem) || user_strlcpy(subsystem, userSubsystem, sizeof(subsystem)) < B_OK) @@ -105,61 +109,58 @@ //dprintf("generic_syscall(subsystem = \"%s\", function = %lu)\n", subsystem, function); - mutex_lock(&sGenericSyscallLock); + MutexLocker locker(sGenericSyscallLock); - syscall = find_generic_syscall(subsystem); + generic_syscall* syscall = find_generic_syscall(subsystem); if (syscall == NULL) - goto out; + return B_NAME_NOT_FOUND; if (function >= B_RESERVED_SYSCALL_BASE) { if (function != B_SYSCALL_INFO) { // this is all we know - status = B_NAME_NOT_FOUND; - goto out; + return B_NAME_NOT_FOUND; } // special info syscall if (bufferSize != sizeof(uint32)) - status = B_BAD_VALUE; - else { - uint32 requestedVersion; + return B_BAD_VALUE; - // retrieve old version - status = user_memcpy(&requestedVersion, buffer, sizeof(uint32)); - if (status == B_OK && requestedVersion != 0 && requestedVersion < syscall->version) - status = B_BAD_TYPE; + uint32 requestedVersion; - // return current version - if (status == B_OK) - status = user_memcpy(buffer, &syscall->version, sizeof(uint32)); - } - } else { - while (syscall != NULL) { - generic_syscall *next; + // retrieve old version + if (user_memcpy(&requestedVersion, buffer, sizeof(uint32)) != B_OK) + return B_BAD_ADDRESS; + if (requestedVersion != 0 && requestedVersion < syscall->version) + return B_BAD_TYPE; - mutex_unlock(&sGenericSyscallLock); + // return current version + return user_memcpy(buffer, &syscall->version, sizeof(uint32)); + } - status = syscall->hook(subsystem, function, buffer, bufferSize); + while (syscall != NULL) { + generic_syscall *next; - mutex_lock(&sGenericSyscallLock); - if (status != B_BAD_HANDLER) - break; + syscall->use_count++; + locker.Unlock(); - // the syscall may have been removed in the mean time - next = find_generic_syscall(subsystem); - if (next == syscall) - syscall = syscall->previous; - else - syscall = next; - } + status_t status + = syscall->hook(subsystem, function, buffer, bufferSize); - if (syscall == NULL) - status = B_NAME_NOT_FOUND; + locker.Lock(); + syscall->use_count--; + + if (status != B_BAD_HANDLER) + return status; + + // the syscall may have been removed in the mean time + next = find_generic_syscall(subsystem); + if (next == syscall) + syscall = syscall->previous; + else + syscall = next; } -out: - mutex_unlock(&sGenericSyscallLock); - return status; + return B_NAME_NOT_FOUND; } @@ -169,8 +170,8 @@ return 1; } -// map to the arch specific call +//! Map to the arch specific call static inline int64 _user_restore_signal_frame() { @@ -231,82 +232,77 @@ } -// #pragma mark - -// public API +// #pragma mark - public API status_t register_generic_syscall(const char *subsystem, syscall_hook hook, uint32 version, uint32 flags) { - struct generic_syscall *previous, *syscall; - status_t status; - if (hook == NULL) return B_BAD_VALUE; - mutex_lock(&sGenericSyscallLock); + MutexLocker _(sGenericSyscallLock); - previous = find_generic_syscall(subsystem); + generic_syscall* previous = find_generic_syscall(subsystem); if (previous != NULL) { if ((flags & B_DO_NOT_REPLACE_SYSCALL) != 0 || version < previous->version) { - status = B_NAME_IN_USE; - goto out; + return B_NAME_IN_USE; } - if (previous->flags & B_SYSCALL_NOT_REPLACEABLE) { - status = B_NOT_ALLOWED; - goto out; - } + if ((previous->flags & B_SYSCALL_NOT_REPLACEABLE) != 0) + return B_NOT_ALLOWED; } - syscall = (generic_syscall *)malloc(sizeof(struct generic_syscall)); - if (syscall == NULL) { - status = B_NO_MEMORY; - goto out; - } + generic_syscall* syscall + = (generic_syscall*)malloc(sizeof(struct generic_syscall)); + if (syscall == NULL) + return B_NO_MEMORY; strlcpy(syscall->subsystem, subsystem, sizeof(syscall->subsystem)); syscall->hook = hook; syscall->version = version; syscall->flags = flags; + syscall->use_count = 0; syscall->previous = previous; list_add_item(&sGenericSyscalls, syscall); if (previous != NULL) list_remove_link(&previous->link); - status = B_OK; - -out: - mutex_unlock(&sGenericSyscallLock); - return status; + return B_OK; } status_t unregister_generic_syscall(const char *subsystem, uint32 version) { - // ToDo: we should only remove the syscall with the matching version - generic_syscall *syscall; - status_t status; + // TODO: we should only remove the syscall with the matching version - mutex_lock(&sGenericSyscallLock); + while (true) { + MutexLocker locker(sGenericSyscallLock); - syscall = find_generic_syscall(subsystem); - if (syscall != NULL) { + generic_syscall* syscall = find_generic_syscall(subsystem); + if (syscall == NULL) + return B_NAME_NOT_FOUND; + + if (syscall->use_count != 0) { + // TODO: we could use a condition variable here instead + locker.Unlock(); + snooze(6000); + continue; + } + if (syscall->previous != NULL) { // reestablish the old syscall list_add_item(&sGenericSyscalls, syscall->previous); } + list_remove_link(&syscall->link); free(syscall); - status = B_OK; - } else - status = B_NAME_NOT_FOUND; - mutex_unlock(&sGenericSyscallLock); - return status; + return B_OK; + } }