* sem_entry no contains a pointer to the owning team, instead of only its

ID.
* Accidently, this should also fix bug #4839.
* Optimized _get_next_sem_info() a whole lot by iterating over the team's
  semaphore list.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@33773 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2009-10-26 15:09:08 +00:00
parent 36f51b0861
commit 0087f9409b
+83 -67
View File
@@ -80,7 +80,7 @@ struct sem_entry {
// count + acquisition count of all blocked // count + acquisition count of all blocked
// threads // threads
char* name; char* name;
team_id owner; struct team* owner;
select_info* select_infos; select_info* select_infos;
thread_id last_acquirer; thread_id last_acquirer;
#if DEBUG_SEM_LAST_ACQUIRER #if DEBUG_SEM_LAST_ACQUIRER
@@ -102,7 +102,7 @@ struct sem_entry {
ThreadQueue queue; // should be in u.used, but has a constructor ThreadQueue queue; // should be in u.used, but has a constructor
}; };
static const int32 kMaxSemaphores = 131072; static const int32 kMaxSemaphores = 65536;
static int32 sMaxSems = 4096; static int32 sMaxSems = 4096;
// Final value is computed based on the amount of available memory // Final value is computed based on the amount of available memory
static int32 sUsedSems = 0; static int32 sUsedSems = 0;
@@ -120,9 +120,9 @@ static spinlock sSemsSpinlock = B_SPINLOCK_INITIALIZER;
static int static int
dump_sem_list(int argc, char **argv) dump_sem_list(int argc, char** argv)
{ {
const char *name = NULL; const char* name = NULL;
team_id owner = -1; team_id owner = -1;
thread_id last = -1; thread_id last = -1;
int32 i; int32 i;
@@ -140,17 +140,19 @@ dump_sem_list(int argc, char **argv)
kprintf("sem id count team last name\n"); kprintf("sem id count team last name\n");
for (i = 0; i < sMaxSems; i++) { for (i = 0; i < sMaxSems; i++) {
struct sem_entry *sem = &sSems[i]; struct sem_entry* sem = &sSems[i];
if (sem->id < 0 if (sem->id < 0
|| (last != -1 && sem->u.used.last_acquirer != last) || (last != -1 && sem->u.used.last_acquirer != last)
|| (name != NULL && strstr(sem->u.used.name, name) == NULL) || (name != NULL && strstr(sem->u.used.name, name) == NULL)
|| (owner != -1 && sem->u.used.owner != owner)) || (owner != -1
&& (sem->u.used.owner == NULL
|| sem->u.used.owner->id != owner)))
continue; continue;
kprintf("%p %6ld %5ld %6ld " kprintf("%p %6ld %5ld %6ld "
"%6ld " "%6ld "
" %s\n", sem, sem->id, sem->u.used.count, " %s\n", sem, sem->id, sem->u.used.count,
sem->u.used.owner, sem->u.used.owner != NULL ? sem->u.used.owner->id : -1,
sem->u.used.last_acquirer > 0 ? sem->u.used.last_acquirer : 0, sem->u.used.last_acquirer > 0 ? sem->u.used.last_acquirer : 0,
sem->u.used.name); sem->u.used.name);
} }
@@ -160,13 +162,14 @@ dump_sem_list(int argc, char **argv)
static void static void
dump_sem(struct sem_entry *sem) dump_sem(struct sem_entry* sem)
{ {
kprintf("SEM: %p\n", sem); kprintf("SEM: %p\n", sem);
kprintf("id: %ld (%#lx)\n", sem->id, sem->id); kprintf("id: %ld (%#lx)\n", sem->id, sem->id);
if (sem->id >= 0) { if (sem->id >= 0) {
kprintf("name: '%s'\n", sem->u.used.name); kprintf("name: '%s'\n", sem->u.used.name);
kprintf("owner: %ld\n", sem->u.used.owner); kprintf("owner: %ld\n",
sem->u.used.owner != NULL ? sem->u.used.owner->id : -1);
kprintf("count: %ld\n", sem->u.used.count); kprintf("count: %ld\n", sem->u.used.count);
kprintf("queue: "); kprintf("queue: ");
if (!sem->queue.IsEmpty()) { if (!sem->queue.IsEmpty()) {
@@ -179,13 +182,14 @@ dump_sem(struct sem_entry *sem)
set_debug_variable("_sem", (addr_t)sem); set_debug_variable("_sem", (addr_t)sem);
set_debug_variable("_semID", sem->id); set_debug_variable("_semID", sem->id);
set_debug_variable("_owner", sem->u.used.owner); set_debug_variable("_owner",
sem->u.used.owner != NULL ? sem->u.used.owner->id : -1);
#if DEBUG_SEM_LAST_ACQUIRER #if DEBUG_SEM_LAST_ACQUIRER
kprintf("last acquired by: %ld, count: %ld\n", sem->u.used.last_acquirer, kprintf("last acquired by: %ld, count: %ld\n",
sem->u.used.last_acquire_count); sem->u.used.last_acquirer, sem->u.used.last_acquire_count);
kprintf("last released by: %ld, count: %ld\n", sem->u.used.last_releaser, kprintf("last released by: %ld, count: %ld\n",
sem->u.used.last_release_count); sem->u.used.last_releaser, sem->u.used.last_release_count);
if (sem->u.used.last_releaser != 0) if (sem->u.used.last_releaser != 0)
set_debug_variable("_releaser", sem->u.used.last_releaser); set_debug_variable("_releaser", sem->u.used.last_releaser);
@@ -199,7 +203,6 @@ dump_sem(struct sem_entry *sem)
set_debug_variable("_acquirer", sem->u.used.last_acquirer); set_debug_variable("_acquirer", sem->u.used.last_acquirer);
else else
unset_debug_variable("_acquirer"); unset_debug_variable("_acquirer");
} else { } else {
kprintf("next: %p\n", sem->u.unused.next); kprintf("next: %p\n", sem->u.unused.next);
kprintf("next_id: %ld\n", sem->u.unused.next_id); kprintf("next_id: %ld\n", sem->u.unused.next_id);
@@ -294,7 +297,7 @@ static void
fill_sem_info(struct sem_entry* sem, sem_info* info, size_t size) fill_sem_info(struct sem_entry* sem, sem_info* info, size_t size)
{ {
info->sem = sem->id; info->sem = sem->id;
info->team = sem->u.used.owner; info->team = sem->u.used.owner != NULL ? sem->u.used.owner->id : -1;
strlcpy(info->name, sem->u.used.name, sizeof(info->name)); strlcpy(info->name, sem->u.used.name, sizeof(info->name));
info->count = sem->u.used.count; info->count = sem->u.used.count;
info->latest_holder = sem->u.used.last_acquirer; info->latest_holder = sem->u.used.last_acquirer;
@@ -360,7 +363,7 @@ delete_sem_internal(sem_id id, bool checkPermission)
} }
if (checkPermission if (checkPermission
&& sSems[slot].u.used.owner == team_get_kernel_team_id()) { && sSems[slot].u.used.owner == team_get_kernel_team()) {
RELEASE_SEM_LOCK(sSems[slot]); RELEASE_SEM_LOCK(sSems[slot]);
RELEASE_TEAM_LOCK(); RELEASE_TEAM_LOCK();
restore_interrupts(state); restore_interrupts(state);
@@ -369,11 +372,11 @@ delete_sem_internal(sem_id id, bool checkPermission)
return B_NOT_ALLOWED; return B_NOT_ALLOWED;
} }
struct team* team = team_get_team_struct_locked(sSems[slot].u.used.owner); if (sSems[slot].u.used.owner != NULL) {
if (team != NULL)
list_remove_link(&sSems[slot].u.used.team_link); list_remove_link(&sSems[slot].u.used.team_link);
else sSems[slot].u.used.owner = NULL;
panic("team %ld missing", sSems[slot].u.used.owner); } else
panic("sem %ld has no owner", id);
RELEASE_TEAM_LOCK(); RELEASE_TEAM_LOCK();
@@ -402,8 +405,7 @@ haiku_sem_init(kernel_args *args)
// compute maximal number of semaphores depending on the available memory // compute maximal number of semaphores depending on the available memory
// 128 MB -> 16384 semaphores, 448 kB fixed array size // 128 MB -> 16384 semaphores, 448 kB fixed array size
// 256 MB -> 32768, 896 kB // 256 MB -> 32768, 896 kB
// 512 MB -> 65536, 1.75 MB // 512 MB and more-> 65536, 1.75 MB
// 1024 MB and more -> 131072, 3.5 MB
i = vm_page_num_pages() / 2; i = vm_page_num_pages() / 2;
while (sMaxSems < i && sMaxSems < kMaxSemaphores) while (sMaxSems < i && sMaxSems < kMaxSemaphores)
sMaxSems <<= 1; sMaxSems <<= 1;
@@ -477,11 +479,15 @@ create_sem_etc(int32 count, const char* name, team_id owner)
strlcpy(tempName, name, nameLength); strlcpy(tempName, name, nameLength);
struct team* team = NULL; struct team* team = NULL;
if (owner == team_get_kernel_team_id())
team = team_get_kernel_team();
else if (owner == team_get_current_team_id())
team = thread_get_current_thread()->team;
bool teamsLocked = false; bool teamsLocked = false;
state = disable_interrupts(); state = disable_interrupts();
if (owner != team_get_kernel_team_id() if (team == NULL) {
&& owner == team_get_current_team_id()) {
// We need to hold the team lock to make sure this one exists (and // We need to hold the team lock to make sure this one exists (and
// won't go away. // won't go away.
GRAB_TEAM_LOCK(); GRAB_TEAM_LOCK();
@@ -512,7 +518,7 @@ create_sem_etc(int32 count, const char* name, team_id owner)
sem->u.used.net_count = count; sem->u.used.net_count = count;
new(&sem->queue) ThreadQueue; new(&sem->queue) ThreadQueue;
sem->u.used.name = tempName; sem->u.used.name = tempName;
sem->u.used.owner = owner; sem->u.used.owner = team;
sem->u.used.select_infos = NULL; sem->u.used.select_infos = NULL;
id = sem->id; id = sem->id;
@@ -540,12 +546,8 @@ create_sem_etc(int32 count, const char* name, team_id owner)
GRAB_TEAM_LOCK(); GRAB_TEAM_LOCK();
GRAB_SEM_LOCK(sSems[slot]); GRAB_SEM_LOCK(sSems[slot]);
if (owner == team_get_kernel_team_id())
team = team_get_kernel_team();
else
team = thread_get_current_thread()->team;
list_add_item(&team->sem_list, &sem->u.used.team_link); list_add_item(&team->sem_list, &sem->u.used.team_link);
RELEASE_SEM_LOCK(sSems[slot]); RELEASE_SEM_LOCK(sSems[slot]);
teamsLocked = true; teamsLocked = true;
} }
@@ -580,7 +582,7 @@ select_sem(int32 id, struct select_info* info, bool kernel)
// bad sem ID // bad sem ID
error = B_BAD_SEM_ID; error = B_BAD_SEM_ID;
} else if (!kernel } else if (!kernel
&& sSems[slot].u.used.owner == team_get_kernel_team_id()) { && sSems[slot].u.used.owner == team_get_kernel_team()) {
// kernel semaphore, but call from userland // kernel semaphore, but call from userland
error = B_NOT_ALLOWED; error = B_NOT_ALLOWED;
} else { } else {
@@ -799,7 +801,7 @@ switch_sem_etc(sem_id semToBeReleased, sem_id id, int32 count,
// TODO: the B_CHECK_PERMISSION flag should be made private, as it // TODO: the B_CHECK_PERMISSION flag should be made private, as it
// doesn't have any use outside the kernel // doesn't have any use outside the kernel
if ((flags & B_CHECK_PERMISSION) != 0 if ((flags & B_CHECK_PERMISSION) != 0
&& sSems[slot].u.used.owner == team_get_kernel_team_id()) { && sSems[slot].u.used.owner == team_get_kernel_team()) {
dprintf("thread %ld tried to acquire kernel semaphore %ld.\n", dprintf("thread %ld tried to acquire kernel semaphore %ld.\n",
thread_get_current_thread_id(), id); thread_get_current_thread_id(), id);
status = B_NOT_ALLOWED; status = B_NOT_ALLOWED;
@@ -948,7 +950,7 @@ release_sem_etc(sem_id id, int32 count, uint32 flags)
// ToDo: the B_CHECK_PERMISSION flag should be made private, as it // ToDo: the B_CHECK_PERMISSION flag should be made private, as it
// doesn't have any use outside the kernel // doesn't have any use outside the kernel
if ((flags & B_CHECK_PERMISSION) != 0 if ((flags & B_CHECK_PERMISSION) != 0
&& sSems[slot].u.used.owner == team_get_kernel_team_id()) { && sSems[slot].u.used.owner == team_get_kernel_team()) {
dprintf("thread %ld tried to release kernel semaphore.\n", dprintf("thread %ld tried to release kernel semaphore.\n",
thread_get_current_thread_id()); thread_get_current_thread_id());
return B_NOT_ALLOWED; return B_NOT_ALLOWED;
@@ -1100,54 +1102,68 @@ _get_sem_info(sem_id id, struct sem_info *info, size_t size)
/*! Called by the get_next_sem_info() macro. */ /*! Called by the get_next_sem_info() macro. */
status_t status_t
_get_next_sem_info(team_id team, int32 *_cookie, struct sem_info *info, _get_next_sem_info(team_id teamID, int32 *_cookie, struct sem_info *info,
size_t size) size_t size)
{ {
int state;
int slot;
bool found = false;
if (!sSemsActive) if (!sSemsActive)
return B_NO_MORE_SEMS; return B_NO_MORE_SEMS;
if (_cookie == NULL || info == NULL || size != sizeof(sem_info)) if (_cookie == NULL || info == NULL || size != sizeof(sem_info))
return B_BAD_VALUE; return B_BAD_VALUE;
if (teamID < 0)
if (team == B_CURRENT_TEAM)
team = team_get_current_team_id();
/* prevents sSems[].owner == -1 >= means owned by a port */
if (team < 0 || !team_is_valid(team))
return B_BAD_TEAM_ID; return B_BAD_TEAM_ID;
slot = *_cookie; InterruptsSpinLocker locker(gTeamSpinlock);
if (slot >= sMaxSems)
return B_BAD_VALUE;
state = disable_interrupts(); struct team* team;
GRAB_SEM_LIST_LOCK(); if (teamID == B_CURRENT_TEAM)
team = thread_get_current_thread()->team;
else
team = team_get_team_struct_locked(teamID);
while (slot < sMaxSems) { if (team == NULL)
if (sSems[slot].id != -1 && sSems[slot].u.used.owner == team) { return B_BAD_TEAM_ID;
GRAB_SEM_LOCK(sSems[slot]);
if (sSems[slot].id != -1 && sSems[slot].u.used.owner == team) {
// found one!
fill_sem_info(&sSems[slot], info, size);
RELEASE_SEM_LOCK(sSems[slot]); int32 id = *_cookie;
slot++; sem_entry* sem = NULL;
found = true;
break; if (id != 0) {
} // shortcut to the first entry
RELEASE_SEM_LOCK(sSems[slot]); sem = &sSems[id % sMaxSems];
} GRAB_SEM_LOCK(*sem);
slot++;
// Check if the semaphore got deleted or reused in the mean time
if (sem->id != id)
sem = NULL;
RELEASE_SEM_LOCK(*sem);
}
if (sem == NULL)
sem = (sem_entry*)list_get_first_item(&team->sem_list);
bool found = false;
while (!found) {
// find the next entry to be returned
while (sem != NULL && id >= sem->id)
sem = (sem_entry*)list_get_next_item(&team->sem_list, sem);
if (sem == NULL)
return B_BAD_VALUE;
GRAB_SEM_LOCK(*sem);
if (sem->id != -1 && sem->u.used.owner == team) {
// found one!
fill_sem_info(sem, info, size);
id = sem->id;
found = true;
}
RELEASE_SEM_LOCK(*sem);
} }
RELEASE_SEM_LIST_LOCK();
restore_interrupts(state);
if (!found) if (!found)
return B_BAD_VALUE; return B_BAD_VALUE;
*_cookie = slot; *_cookie = id;
return B_OK; return B_OK;
} }
@@ -1180,7 +1196,7 @@ set_sem_owner(sem_id id, team_id newTeamID)
list_remove_link(&sSems[slot].u.used.team_link); list_remove_link(&sSems[slot].u.used.team_link);
list_add_item(&newTeam->sem_list, &sSems[slot].u.used.team_link); list_add_item(&newTeam->sem_list, &sSems[slot].u.used.team_link);
sSems[slot].u.used.owner = newTeamID; sSems[slot].u.used.owner = newTeam;
return B_OK; return B_OK;
} }