void xsi_sem_init() { // Initialize hash tables status_t status = sIpcHashTable.Init(); if (status != B_OK) panic("xsi_sem_init() failed to initialize ipc hash table\n"); status = sSemaphoreHashTable.Init(); if (status != B_OK) panic("xsi_sem_init() failed to initialize semaphore hash table\n"); mutex_init(&sIpcLock, "global POSIX semaphore IPC table"); mutex_init(&sXsiSemaphoreSetLock, "global POSIX xsi sem table"); }
extern "C" status_t vboxsf_new_vnode(PVBSFMAP map, PSHFLSTRING path, PSHFLSTRING name, vboxsf_vnode** p) { vboxsf_vnode* vn = (vboxsf_vnode*)malloc(sizeof(vboxsf_vnode)); if (vn == NULL) { return B_NO_MEMORY; } dprintf("creating new vnode at %p with path=%p (%s)\n", vn, path->String.utf8, path->String.utf8); vn->map = map; vn->path = path; if (name) { vn->name = name; } else { const char* cname = strrchr((char*)path->String.utf8, '/'); if (!cname) vn->name = path; // no slash, assume this *is* the filename else vn->name = make_shflstring(cname); } if (mutex_lock(&g_vnodeCacheLock) < B_OK) { free(vn); return B_ERROR; } vn->vnode = g_nextVnid++; *p = vn; dprintf("vboxsf: allocated %p (path=%p name=%p)\n", vn, vn->path, vn->name); status_t rv = g_cache.Insert(vn); mutex_unlock(&g_vnodeCacheLock); return rv; }
extern "C" status_t vboxsf_get_vnode(fs_volume* volume, ino_t id, fs_vnode* vnode, int* _type, uint32* _flags, bool reenter) { vboxsf_vnode* vn = g_cache.Lookup(id); if (vn) { vnode->private_node = vn; return B_OK; } else { return B_ERROR; } }
void XsiSemaphoreSet::SetID() { fID = real_time_clock(); // The lock is held before calling us while (true) { if (sSemaphoreHashTable.Lookup(fID) == NULL) break; fID = (fID + 1) % INT_MAX; } sGlobalSequenceNumber = (sGlobalSequenceNumber + 1) % UINT_MAX; fSequenceNumber = sGlobalSequenceNumber; }
int _user_xsi_semctl(int semaphoreID, int semaphoreNumber, int command, union semun *args) { TRACE(("xsi_semctl: semaphoreID = %d, semaphoreNumber = %d, command = %d\n", semaphoreID, semaphoreNumber, command)); MutexLocker ipcHashLocker(sIpcLock); MutexLocker setHashLocker(sXsiSemaphoreSetLock); XsiSemaphoreSet *semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreID); if (semaphoreSet == NULL) { TRACE_ERROR(("xsi_semctl: semaphore set id %d not valid\n", semaphoreID)); return EINVAL; } if (semaphoreNumber < 0 || semaphoreNumber > semaphoreSet->NumberOfSemaphores()) { TRACE_ERROR(("xsi_semctl: semaphore number %d not valid for " "semaphore %d\n", semaphoreNumber, semaphoreID)); return EINVAL; } if (args != 0 && !IS_USER_ADDRESS(args)) { TRACE_ERROR(("xsi_semctl: semun address is not valid\n")); return B_BAD_ADDRESS; } // Lock the semaphore set itself and release both the semaphore // set hash table lock and the ipc hash table lock _only_ if // the command it's not IPC_RMID, this prevents undesidered // situation from happening while (hopefully) improving the // concurrency. MutexLocker setLocker; if (command != IPC_RMID) { setLocker.SetTo(&semaphoreSet->Lock(), false); setHashLocker.Unlock(); ipcHashLocker.Unlock(); } else // We are about to delete the set along with its mutex, so // we can't use the MutexLocker class, as the mutex itself // won't exist on function exit mutex_lock(&semaphoreSet->Lock()); int result = 0; XsiSemaphore *semaphore = semaphoreSet->Semaphore(semaphoreNumber); switch (command) { case GETVAL: { if (!semaphoreSet->HasReadPermission()) { TRACE_ERROR(("xsi_semctl: calling process has not permission " "on semaphore %d, key %d\n", semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); result = EACCES; } else result = semaphore->Value(); break; } case SETVAL: { if (!semaphoreSet->HasPermission()) { TRACE_ERROR(("xsi_semctl: calling process has not permission " "on semaphore %d, key %d\n", semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); result = EACCES; } else { int value; if (user_memcpy(&value, &args->val, sizeof(int)) < B_OK) { TRACE_ERROR(("xsi_semctl: user_memcpy failed\n")); result = B_BAD_ADDRESS; } else if (value > USHRT_MAX) { TRACE_ERROR(("xsi_semctl: value %d out of range\n", value)); result = ERANGE; } else { semaphore->SetValue(value); semaphoreSet->ClearUndo(semaphoreNumber); } } break; } case GETPID: { if (!semaphoreSet->HasReadPermission()) { TRACE_ERROR(("xsi_semctl: calling process has not permission " "on semaphore %d, key %d\n", semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); result = EACCES; } else result = semaphore->LastPid(); break; } case GETNCNT: { if (!semaphoreSet->HasReadPermission()) { TRACE_ERROR(("xsi_semctl: calling process has not permission " "on semaphore %d, key %d\n", semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); result = EACCES; } else result = semaphore->ThreadsWaitingToIncrease(); break; } case GETZCNT: { if (!semaphoreSet->HasReadPermission()) { TRACE_ERROR(("xsi_semctl: calling process has not permission " "on semaphore %d, key %d\n", semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); result = EACCES; } else result = semaphore->ThreadsWaitingToBeZero(); break; } case GETALL: { if (!semaphoreSet->HasReadPermission()) { TRACE_ERROR(("xsi_semctl: calling process has not read " "permission on semaphore %d, key %d\n", semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); result = EACCES; } else for (int i = 0; i < semaphoreSet->NumberOfSemaphores(); i++) { semaphore = semaphoreSet->Semaphore(i); unsigned short value = semaphore->Value(); if (user_memcpy(&args->array[i], &value, sizeof(unsigned short)) < B_OK) { TRACE_ERROR(("xsi_semctl: user_memcpy failed\n")); result = B_BAD_ADDRESS; break; } } break; } case SETALL: { if (!semaphoreSet->HasPermission()) { TRACE_ERROR(("xsi_semctl: calling process has not permission " "on semaphore %d, key %d\n", semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); result = EACCES; } else { bool doClear = true; for (int i = 0; i < semaphoreSet->NumberOfSemaphores(); i++) { semaphore = semaphoreSet->Semaphore(i); unsigned short value; if (user_memcpy(&value, &args->array[i], sizeof(unsigned short)) < B_OK) { TRACE_ERROR(("xsi_semctl: user_memcpy failed\n")); result = B_BAD_ADDRESS; doClear = false; break; } else semaphore->SetValue(value); } if (doClear) semaphoreSet->ClearUndos(); } break; } case IPC_STAT: { if (!semaphoreSet->HasReadPermission()) { TRACE_ERROR(("xsi_semctl: calling process has not read " "permission on semaphore %d, key %d\n", semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); result = EACCES; } else { struct semid_ds sem; sem.sem_perm = semaphoreSet->IpcPermission(); sem.sem_nsems = semaphoreSet->NumberOfSemaphores(); sem.sem_otime = semaphoreSet->LastSemopTime(); sem.sem_ctime = semaphoreSet->LastSemctlTime(); if (user_memcpy(args->buf, &sem, sizeof(struct semid_ds)) < B_OK) { TRACE_ERROR(("xsi_semctl: user_memcpy failed\n")); result = B_BAD_ADDRESS; } } break; } case IPC_SET: { if (!semaphoreSet->HasPermission()) { TRACE_ERROR(("xsi_semctl: calling process has not " "permission on semaphore %d, key %d\n", semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); result = EACCES; } else { struct semid_ds sem; if (user_memcpy(&sem, args->buf, sizeof(struct semid_ds)) < B_OK) { TRACE_ERROR(("xsi_semctl: user_memcpy failed\n")); result = B_BAD_ADDRESS; } else semaphoreSet->DoIpcSet(&sem); } break; } case IPC_RMID: { // If this was the command, we are still holding // the semaphore set hash table lock along with the // ipc hash table lock and the semaphore set lock // itself, this way we are sure there is not // one waiting in the queue of the mutex. if (!semaphoreSet->HasPermission()) { TRACE_ERROR(("xsi_semctl: calling process has not " "permission on semaphore %d, key %d\n", semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); return EACCES; } key_t key = semaphoreSet->IpcKey(); Ipc *ipcKey = NULL; if (key != -1) { ipcKey = sIpcHashTable.Lookup(key); sIpcHashTable.Remove(ipcKey); } sSemaphoreHashTable.Remove(semaphoreSet); // Wake up of threads waiting on this set // happens in the destructor if (key != -1) delete ipcKey; atomic_add(&sXsiSemaphoreCount, -semaphoreSet->NumberOfSemaphores()); atomic_add(&sXsiSemaphoreSetCount, -1); // Remove any sem_undo request while (struct sem_undo *entry = semaphoreSet->GetUndoList().RemoveHead()) { MutexLocker _(entry->team->xsi_sem_context->lock); entry->team->xsi_sem_context->undo_list.Remove(entry); delete entry; } delete semaphoreSet; return 0; } default: TRACE_ERROR(("xsi_semctl: command %d not valid\n", command)); result = EINVAL; } return result; }
int _user_xsi_semget(key_t key, int numberOfSemaphores, int flags) { TRACE(("xsi_semget: key = %d, numberOfSemaphores = %d, flags = %d\n", (int)key, numberOfSemaphores, flags)); XsiSemaphoreSet *semaphoreSet = NULL; Ipc *ipcKey = NULL; // Default assumptions bool isPrivate = true; bool create = true; MutexLocker _(sIpcLock); if (key != IPC_PRIVATE) { isPrivate = false; // Check if key already exist, if it does it already has a semaphore // set associated with it ipcKey = sIpcHashTable.Lookup(key); if (ipcKey == NULL) { // The ipc key does not exist. Create it and add it to the system if (!(flags & IPC_CREAT)) { TRACE_ERROR(("xsi_semget: key %d does not exist, but the " "caller did not ask for creation\n",(int)key)); return ENOENT; } ipcKey = new(std::nothrow) Ipc(key); if (ipcKey == NULL) { TRACE_ERROR(("xsi_semget: failed to create new Ipc object " "for key %d\n", (int)key)); return ENOMEM; } sIpcHashTable.Insert(ipcKey); } else { // The IPC key exist and it already has a semaphore if ((flags & IPC_CREAT) && (flags & IPC_EXCL)) { TRACE_ERROR(("xsi_semget: key %d already exist\n", (int)key)); return EEXIST; } int semaphoreSetID = ipcKey->SemaphoreSetID(); MutexLocker _(sXsiSemaphoreSetLock); semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreSetID); if (!semaphoreSet->HasPermission()) { TRACE_ERROR(("xsi_semget: calling process has not permission " "on semaphore %d, key %d\n", semaphoreSet->ID(), (int)key)); return EACCES; } if (numberOfSemaphores > semaphoreSet->NumberOfSemaphores() && numberOfSemaphores != 0) { TRACE_ERROR(("xsi_semget: numberOfSemaphores greater than the " "one associated with semaphore %d, key %d\n", semaphoreSet->ID(), (int)key)); return EINVAL; } create = false; } } if (create) { // Create a new sempahore set for this key if (numberOfSemaphores <= 0 || numberOfSemaphores >= MAX_XSI_SEMS_PER_TEAM) { TRACE_ERROR(("xsi_semget: numberOfSemaphores out of range\n")); return EINVAL; } if (sXsiSemaphoreCount >= MAX_XSI_SEMAPHORE || sXsiSemaphoreSetCount >= MAX_XSI_SEMAPHORE_SET) { TRACE_ERROR(("xsi_semget: reached limit of maximum number of " "semaphores allowed\n")); return ENOSPC; } semaphoreSet = new(std::nothrow) XsiSemaphoreSet(numberOfSemaphores, flags); if (semaphoreSet == NULL || !semaphoreSet->InitOK()) { TRACE_ERROR(("xsi_semget: failed to allocate a new xsi " "semaphore set\n")); delete semaphoreSet; return ENOMEM; } atomic_add(&sXsiSemaphoreCount, numberOfSemaphores); atomic_add(&sXsiSemaphoreSetCount, 1); MutexLocker _(sXsiSemaphoreSetLock); semaphoreSet->SetID(); if (isPrivate) semaphoreSet->SetIpcKey((key_t)-1); else { semaphoreSet->SetIpcKey(key); ipcKey->SetSemaphoreSetID(semaphoreSet); } sSemaphoreHashTable.Insert(semaphoreSet); TRACE(("semget: new set = %d created, sequence = %ld\n", semaphoreSet->ID(), semaphoreSet->SequenceNumber())); } return semaphoreSet->ID(); }
status_t _user_xsi_semop(int semaphoreID, struct sembuf *ops, size_t numOps) { TRACE(("xsi_semop: semaphoreID = %d, ops = %p, numOps = %ld\n", semaphoreID, ops, numOps)); MutexLocker setHashLocker(sXsiSemaphoreSetLock); XsiSemaphoreSet *semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreID); if (semaphoreSet == NULL) { TRACE_ERROR(("xsi_semop: semaphore set id %d not valid\n", semaphoreID)); return EINVAL; } MutexLocker setLocker(semaphoreSet->Lock()); setHashLocker.Unlock(); if (!IS_USER_ADDRESS(ops)) { TRACE_ERROR(("xsi_semop: sembuf address is not valid\n")); return B_BAD_ADDRESS; } if (numOps < 0 || numOps >= MAX_XSI_SEMS_PER_TEAM) { TRACE_ERROR(("xsi_semop: numOps out of range\n")); return EINVAL; } struct sembuf *operations = (struct sembuf *)malloc(sizeof(struct sembuf) * numOps); if (operations == NULL) { TRACE_ERROR(("xsi_semop: failed to allocate sembuf struct\n")); return B_NO_MEMORY; } MemoryDeleter operationsDeleter(operations); if (user_memcpy(operations, ops, (sizeof(struct sembuf) * numOps)) < B_OK) { TRACE_ERROR(("xsi_semop: user_memcpy failed\n")); return B_BAD_ADDRESS; } // We won't do partial request, that is operations // only on some sempahores belonging to the set and then // going to sleep. If we must wait on a semaphore, we undo // all the operations already done and go to sleep, otherwise // we may caused some unwanted deadlock among threads // fighting for the same set. bool notDone = true; status_t result = 0; while (notDone) { XsiSemaphore *semaphore = NULL; short numberOfSemaphores = semaphoreSet->NumberOfSemaphores(); bool goToSleep = false; uint32 i = 0; for (; i < numOps; i++) { short semaphoreNumber = operations[i].sem_num; if (semaphoreNumber >= numberOfSemaphores) { TRACE_ERROR(("xsi_semop: %" B_PRIu32 " invalid semaphore number" "\n", i)); result = EINVAL; break; } semaphore = semaphoreSet->Semaphore(semaphoreNumber); unsigned short value = semaphore->Value(); short operation = operations[i].sem_op; TRACE(("xsi_semop: semaphoreNumber = %d, value = %d\n", semaphoreNumber, value)); if (operation < 0) { if (semaphore->Add(operation)) { if (operations[i].sem_flg & IPC_NOWAIT) result = EAGAIN; else goToSleep = true; break; } } else if (operation == 0) { if (value == 0) continue; else if (operations[i].sem_flg & IPC_NOWAIT) { result = EAGAIN; break; } else { goToSleep = true; break; } } else { // Operation must be greater than zero, // just add the value and continue semaphore->Add(operation); } } // Either we have to wait or an error occured if (goToSleep || result != 0) { // Undo all previously done operations for (uint32 j = 0; j < i; j++) { short semaphoreNumber = operations[j].sem_num; semaphore = semaphoreSet->Semaphore(semaphoreNumber); short operation = operations[j].sem_op; if (operation != 0) semaphore->Revert(operation); } if (result != 0) return result; // We have to wait: first enqueue the thread // in the appropriate set waiting list, then // unlock the set itself and block the thread. bool waitOnZero = true; if (operations[i].sem_op != 0) waitOnZero = false; Thread *thread = thread_get_current_thread(); queued_thread queueEntry(thread, (int32)operations[i].sem_op); semaphore->Enqueue(&queueEntry, waitOnZero); uint32 sequenceNumber = semaphoreSet->SequenceNumber(); TRACE(("xsi_semop: thread %d going to sleep\n", (int)thread->id)); result = semaphore->BlockAndUnlock(thread, &setLocker); TRACE(("xsi_semop: thread %d back to life\n", (int)thread->id)); // We are back to life. Find out why! // Make sure the set hasn't been deleted or worst yet // replaced. setHashLocker.Lock(); semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreID); if (result == EIDRM || semaphoreSet == NULL || (semaphoreSet != NULL && sequenceNumber != semaphoreSet->SequenceNumber())) { TRACE_ERROR(("xsi_semop: semaphore set id %d (sequence = " "%" B_PRIu32 ") got destroyed\n", semaphoreID, sequenceNumber)); notDone = false; result = EIDRM; } else if (result == B_INTERRUPTED) { TRACE_ERROR(("xsi_semop: thread %d got interrupted while " "waiting on semaphore set id %d\n",(int)thread->id, semaphoreID)); semaphore->Deque(&queueEntry, waitOnZero); result = EINTR; notDone = false; } else { setLocker.Lock(); setHashLocker.Unlock(); } } else { // everything worked like a charm (so far) notDone = false; TRACE(("xsi_semop: semaphore acquired succesfully\n")); // We acquired the semaphore, now records the sem_undo // requests XsiSemaphore *semaphore = NULL; uint32 i = 0; for (; i < numOps; i++) { short semaphoreNumber = operations[i].sem_num; semaphore = semaphoreSet->Semaphore(semaphoreNumber); short operation = operations[i].sem_op; if (operations[i].sem_flg & SEM_UNDO) if (semaphoreSet->RecordUndo(semaphoreNumber, operation) != B_OK) { // Unlikely scenario, but we might get here. // Undo everything! // Start with semaphore operations for (uint32 j = 0; j < numOps; j++) { short semaphoreNumber = operations[j].sem_num; semaphore = semaphoreSet->Semaphore(semaphoreNumber); short operation = operations[j].sem_op; if (operation != 0) semaphore->Revert(operation); } // Remove all previously registered sem_undo request for (uint32 j = 0; j < i; j++) { if (operations[j].sem_flg & SEM_UNDO) semaphoreSet->RevertUndo(operations[j].sem_num, operations[j].sem_op); } result = ENOSPC; } } } } // We did it. Set the pid of all semaphores used if (result == 0) { for (uint32 i = 0; i < numOps; i++) { short semaphoreNumber = operations[i].sem_num; XsiSemaphore *semaphore = semaphoreSet->Semaphore(semaphoreNumber); semaphore->SetPid(getpid()); } } return result; }
extern "C" status_t vboxsf_put_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter) { g_cache.Remove((vboxsf_vnode*)vnode->private_node); }