vfs: check userland buffers before reading them.
[haiku.git] / src / system / kernel / posix / xsi_semaphore.cpp
blob918d58a8f5a68c4ef6001095d0f3c1ee15527560
1 /*
2 * Copyright 2008-2011, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Salvatore Benedetto <salvatore.benedetto@gmail.com>
7 */
9 #include <posix/xsi_semaphore.h>
11 #include <new>
13 #include <sys/ipc.h>
14 #include <sys/types.h>
16 #include <OS.h>
18 #include <kernel.h>
19 #include <syscall_restart.h>
21 #include <util/atomic.h>
22 #include <util/AutoLock.h>
23 #include <util/DoublyLinkedList.h>
24 #include <util/OpenHashTable.h>
25 #include <AutoDeleter.h>
28 //#define TRACE_XSI_SEM
29 #ifdef TRACE_XSI_SEM
30 # define TRACE(x) dprintf x
31 # define TRACE_ERROR(x) dprintf x
32 #else
33 # define TRACE(x) /* nothing */
34 # define TRACE_ERROR(x) dprintf x
35 #endif
38 namespace {
40 // Queue for holding blocked threads
41 struct queued_thread : DoublyLinkedListLinkImpl<queued_thread> {
42 queued_thread(Thread *thread, int32 count)
44 thread(thread),
45 count(count),
46 queued(false)
50 Thread *thread;
51 int32 count;
52 bool queued;
55 typedef DoublyLinkedList<queued_thread> ThreadQueue;
57 class XsiSemaphoreSet;
59 struct sem_undo : DoublyLinkedListLinkImpl<sem_undo> {
60 sem_undo(XsiSemaphoreSet *semaphoreSet, Team *team, int16 *undoValues)
62 semaphore_set(semaphoreSet),
63 team(team),
64 undo_values(undoValues)
68 DoublyLinkedListLink<sem_undo> team_link;
69 XsiSemaphoreSet *semaphore_set;
70 Team *team;
71 int16 *undo_values;
74 typedef DoublyLinkedList<sem_undo> UndoList;
75 typedef DoublyLinkedList<sem_undo,
76 DoublyLinkedListMemberGetLink<sem_undo, &sem_undo::team_link> > TeamList;
78 } // namespace
81 // Forward declared in global namespace.
82 struct xsi_sem_context {
83 xsi_sem_context()
85 mutex_init(&lock, "Private team undo_list lock");
88 ~xsi_sem_context()
90 mutex_destroy(&lock);
93 TeamList undo_list;
94 mutex lock;
98 namespace {
100 // Xsi semaphore definition
101 class XsiSemaphore {
102 public:
103 XsiSemaphore()
104 : fLastPidOperation(0),
105 fThreadsWaitingToIncrease(0),
106 fThreadsWaitingToBeZero(0),
107 fValue(0)
111 ~XsiSemaphore()
113 // For some reason the semaphore is getting destroyed.
114 // Wake up any remaing awaiting threads
115 while (queued_thread *entry = fWaitingToIncreaseQueue.RemoveHead()) {
116 entry->queued = false;
117 thread_unblock(entry->thread, EIDRM);
119 while (queued_thread *entry = fWaitingToBeZeroQueue.RemoveHead()) {
120 entry->queued = false;
121 thread_unblock(entry->thread, EIDRM);
123 // No need to remove any sem_undo request still
124 // hanging. When the process exit and doesn't found
125 // the semaphore set, it'll just ignore the sem_undo
126 // request. That's better than iterating trough the
127 // whole sUndoList. Beside we don't know our semaphore
128 // number nor our semaphore set id.
131 // We return true in case the operation causes the
132 // caller to wait, so it can undo all the operations
133 // previously done
134 bool Add(short value)
136 if ((int)(fValue + value) < 0) {
137 TRACE(("XsiSemaphore::Add: potentially going to sleep\n"));
138 return true;
139 } else {
140 fValue += value;
141 if (fValue == 0 && fThreadsWaitingToBeZero > 0)
142 WakeUpThread(true);
143 else if (fValue > 0 && fThreadsWaitingToIncrease > 0)
144 WakeUpThread(false);
145 return false;
149 status_t BlockAndUnlock(Thread *thread, MutexLocker *setLocker)
151 thread_prepare_to_block(thread, B_CAN_INTERRUPT,
152 THREAD_BLOCK_TYPE_OTHER, (void*)"xsi semaphore");
153 // Unlock the set before blocking
154 setLocker->Unlock();
156 // TODO: We've got a serious race condition: If BlockAndUnlock() returned due to
157 // interruption, we will still be queued. A WakeUpThread() at this point will
158 // call thread_unblock() and might thus screw with our trying to re-lock the
159 // mutex.
160 return thread_block();
163 void Deque(queued_thread *queueEntry, bool waitForZero)
165 if (queueEntry->queued) {
166 if (waitForZero) {
167 fWaitingToBeZeroQueue.Remove(queueEntry);
168 fThreadsWaitingToBeZero--;
169 } else {
170 fWaitingToIncreaseQueue.Remove(queueEntry);
171 fThreadsWaitingToIncrease--;
176 void Enqueue(queued_thread *queueEntry, bool waitForZero)
178 if (waitForZero) {
179 fWaitingToBeZeroQueue.Add(queueEntry);
180 fThreadsWaitingToBeZero++;
181 } else {
182 fWaitingToIncreaseQueue.Add(queueEntry);
183 fThreadsWaitingToIncrease++;
185 queueEntry->queued = true;
188 pid_t LastPid() const
190 return fLastPidOperation;
193 void Revert(short value)
195 fValue -= value;
196 if (fValue == 0 && fThreadsWaitingToBeZero > 0)
197 WakeUpThread(true);
198 else if (fValue > 0 && fThreadsWaitingToIncrease > 0)
199 WakeUpThread(false);
202 void SetPid(pid_t pid)
204 fLastPidOperation = pid;
207 void SetValue(ushort value)
209 fValue = value;
212 ushort ThreadsWaitingToIncrease() const
214 return fThreadsWaitingToIncrease;
217 ushort ThreadsWaitingToBeZero() const
219 return fThreadsWaitingToBeZero;
222 ushort Value() const
224 return fValue;
227 void WakeUpThread(bool waitingForZero)
229 if (waitingForZero) {
230 // Wake up all threads waiting on zero
231 while (queued_thread *entry = fWaitingToBeZeroQueue.RemoveHead()) {
232 entry->queued = false;
233 fThreadsWaitingToBeZero--;
234 thread_unblock(entry->thread, 0);
236 } else {
237 // Wake up all threads even though they might go back to sleep
238 while (queued_thread *entry = fWaitingToIncreaseQueue.RemoveHead()) {
239 entry->queued = false;
240 fThreadsWaitingToIncrease--;
241 thread_unblock(entry->thread, 0);
246 private:
247 pid_t fLastPidOperation; // sempid
248 ushort fThreadsWaitingToIncrease; // semncnt
249 ushort fThreadsWaitingToBeZero; // semzcnt
250 ushort fValue; // semval
252 ThreadQueue fWaitingToIncreaseQueue;
253 ThreadQueue fWaitingToBeZeroQueue;
256 #define MAX_XSI_SEMS_PER_TEAM 128
258 // Xsi semaphore set definition (semid_ds)
259 class XsiSemaphoreSet {
260 public:
261 XsiSemaphoreSet(int numberOfSemaphores, int flags)
262 : fInitOK(false),
263 fLastSemctlTime((time_t)real_time_clock()),
264 fLastSemopTime(0),
265 fNumberOfSemaphores(numberOfSemaphores),
266 fSemaphores(0)
268 mutex_init(&fLock, "XsiSemaphoreSet private mutex");
269 SetIpcKey((key_t)-1);
270 SetPermissions(flags);
271 fSemaphores = new(std::nothrow) XsiSemaphore[numberOfSemaphores];
272 if (fSemaphores == NULL) {
273 TRACE_ERROR(("XsiSemaphoreSet::XsiSemaphore(): failed to allocate "
274 "XsiSemaphore object\n"));
275 } else
276 fInitOK = true;
279 ~XsiSemaphoreSet()
281 TRACE(("XsiSemaphoreSet::~XsiSemaphoreSet(): removing semaphore "
282 "set %d\n", fID));
283 mutex_destroy(&fLock);
284 delete[] fSemaphores;
287 void ClearUndo(unsigned short semaphoreNumber)
289 Team *team = thread_get_current_thread()->team;
290 UndoList::Iterator iterator = fUndoList.GetIterator();
291 while (iterator.HasNext()) {
292 struct sem_undo *current = iterator.Next();
293 if (current->team == team) {
294 TRACE(("XsiSemaphoreSet::ClearUndo: teamID = %d, "
295 "semaphoreSetID = %d, semaphoreNumber = %d\n",
296 fID, semaphoreNumber, (int)team->id));
297 MutexLocker _(team->xsi_sem_context->lock);
298 current->undo_values[semaphoreNumber] = 0;
299 return;
304 void ClearUndos()
306 // Clear all undo_values (POSIX semadj equivalent)
307 // of the calling team. This happens only on semctl SETALL.
308 Team *team = thread_get_current_thread()->team;
309 DoublyLinkedList<sem_undo>::Iterator iterator = fUndoList.GetIterator();
310 while (iterator.HasNext()) {
311 struct sem_undo *current = iterator.Next();
312 if (current->team == team) {
313 TRACE(("XsiSemaphoreSet::ClearUndos: teamID = %d, "
314 "semaphoreSetID = %d\n", (int)team->id, fID));
315 MutexLocker _(team->xsi_sem_context->lock);
316 memset(current->undo_values, 0,
317 sizeof(int16) * fNumberOfSemaphores);
318 return;
323 void DoIpcSet(struct semid_ds *result)
325 fPermissions.uid = result->sem_perm.uid;
326 fPermissions.gid = result->sem_perm.gid;
327 fPermissions.mode = (fPermissions.mode & ~0x01ff)
328 | (result->sem_perm.mode & 0x01ff);
331 bool HasPermission() const
333 if ((fPermissions.mode & S_IWOTH) != 0)
334 return true;
336 uid_t uid = geteuid();
337 if (uid == 0 || (uid == fPermissions.uid
338 && (fPermissions.mode & S_IWUSR) != 0))
339 return true;
341 gid_t gid = getegid();
342 if (gid == fPermissions.gid && (fPermissions.mode & S_IWGRP) != 0)
343 return true;
345 return false;
348 bool HasReadPermission() const
350 // TODO: fix this
351 return HasPermission();
354 int ID() const
356 return fID;
359 bool InitOK()
361 return fInitOK;
364 key_t IpcKey() const
366 return fPermissions.key;
369 struct ipc_perm IpcPermission() const
371 return fPermissions;
374 time_t LastSemctlTime() const
376 return fLastSemctlTime;
379 time_t LastSemopTime() const
381 return fLastSemopTime;
384 mutex &Lock()
386 return fLock;
389 ushort NumberOfSemaphores() const
391 return fNumberOfSemaphores;
394 // Record the sem_undo operation into our private fUndoList and
395 // the team undo_list. The only limit here is the memory needed
396 // for creating a new sem_undo structure.
397 int RecordUndo(short semaphoreNumber, short value)
399 // Look if there is already a record from the team caller
400 // for the same semaphore set
401 bool notFound = true;
402 Team *team = thread_get_current_thread()->team;
403 DoublyLinkedList<sem_undo>::Iterator iterator = fUndoList.GetIterator();
404 while (iterator.HasNext()) {
405 struct sem_undo *current = iterator.Next();
406 if (current->team == team) {
407 // Update its undo value
408 MutexLocker _(team->xsi_sem_context->lock);
409 int newValue = current->undo_values[semaphoreNumber] + value;
410 if (newValue > USHRT_MAX || newValue < -USHRT_MAX) {
411 TRACE_ERROR(("XsiSemaphoreSet::RecordUndo: newValue %d "
412 "out of range\n", newValue));
413 return ERANGE;
415 current->undo_values[semaphoreNumber] = newValue;
416 notFound = false;
417 TRACE(("XsiSemaphoreSet::RecordUndo: found record. Team = %d, "
418 "semaphoreSetID = %d, semaphoreNumber = %d, value = %d\n",
419 (int)team->id, fID, semaphoreNumber,
420 current->undo_values[semaphoreNumber]));
421 break;
425 if (notFound) {
426 // First sem_undo request from this team for this
427 // semaphore set
428 int16 *undoValues
429 = (int16 *)malloc(sizeof(int16) * fNumberOfSemaphores);
430 if (undoValues == NULL)
431 return B_NO_MEMORY;
432 struct sem_undo *request
433 = new(std::nothrow) sem_undo(this, team, undoValues);
434 if (request == NULL) {
435 free(undoValues);
436 return B_NO_MEMORY;
438 memset(request->undo_values, 0, sizeof(int16) * fNumberOfSemaphores);
439 request->undo_values[semaphoreNumber] = value;
441 // Check if it's the very first sem_undo request for this team
442 xsi_sem_context *context = atomic_pointer_get(&team->xsi_sem_context);
443 if (context == NULL) {
444 // Create the context
445 context = new(std::nothrow) xsi_sem_context;
446 if (context == NULL) {
447 free(request->undo_values);
448 delete request;
449 return B_NO_MEMORY;
451 // Since we don't hold any global lock, someone
452 // else could have been quicker than us, so we have
453 // to delete the one we just created and use the one
454 // in place.
455 if (atomic_pointer_test_and_set(&team->xsi_sem_context, context,
456 (xsi_sem_context *)NULL) != NULL)
457 delete context;
460 // Add the request to both XsiSemaphoreSet and team list
461 fUndoList.Add(request);
462 MutexLocker _(team->xsi_sem_context->lock);
463 team->xsi_sem_context->undo_list.Add(request);
464 TRACE(("XsiSemaphoreSet::RecordUndo: new record added. Team = %d, "
465 "semaphoreSetID = %d, semaphoreNumber = %d, value = %d\n",
466 (int)team->id, fID, semaphoreNumber, value));
468 return B_OK;
471 void RevertUndo(short semaphoreNumber, short value)
473 // This can be called only when RecordUndo fails.
474 Team *team = thread_get_current_thread()->team;
475 DoublyLinkedList<sem_undo>::Iterator iterator = fUndoList.GetIterator();
476 while (iterator.HasNext()) {
477 struct sem_undo *current = iterator.Next();
478 if (current->team == team) {
479 MutexLocker _(team->xsi_sem_context->lock);
480 fSemaphores[semaphoreNumber].Revert(value);
481 break;
486 XsiSemaphore* Semaphore(int nth) const
488 return &fSemaphores[nth];
491 uint32 SequenceNumber() const
493 return fSequenceNumber;
496 // Implemented after sGlobalSequenceNumber is declared
497 void SetID();
499 void SetIpcKey(key_t key)
501 fPermissions.key = key;
504 void SetLastSemctlTime()
506 fLastSemctlTime = real_time_clock();
509 void SetLastSemopTime()
511 fLastSemopTime = real_time_clock();
514 void SetPermissions(int flags)
516 fPermissions.uid = fPermissions.cuid = geteuid();
517 fPermissions.gid = fPermissions.cgid = getegid();
518 fPermissions.mode = (flags & 0x01ff);
521 UndoList &GetUndoList()
523 return fUndoList;
526 XsiSemaphoreSet*& Link()
528 return fLink;
531 private:
532 int fID; // semaphore set id
533 bool fInitOK;
534 time_t fLastSemctlTime; // sem_ctime
535 time_t fLastSemopTime; // sem_otime
536 mutex fLock; // private lock
537 ushort fNumberOfSemaphores; // sem_nsems
538 struct ipc_perm fPermissions; // sem_perm
539 XsiSemaphore *fSemaphores; // array of semaphores
540 uint32 fSequenceNumber; // used as a second id
541 UndoList fUndoList; // undo list requests
543 XsiSemaphoreSet* fLink;
546 // Xsi semaphore set hash table
547 struct SemaphoreHashTableDefinition {
548 typedef int KeyType;
549 typedef XsiSemaphoreSet ValueType;
551 size_t HashKey (const int key) const
553 return (size_t)key;
556 size_t Hash(XsiSemaphoreSet *variable) const
558 return (size_t)variable->ID();
561 bool Compare(const int key, XsiSemaphoreSet *variable) const
563 return (int)key == (int)variable->ID();
566 XsiSemaphoreSet*& GetLink(XsiSemaphoreSet *variable) const
568 return variable->Link();
573 // IPC class
574 class Ipc {
575 public:
576 Ipc(key_t key)
577 : fKey(key),
578 fSemaphoreSetId(-1)
582 key_t Key() const
584 return fKey;
587 int SemaphoreSetID() const
589 return fSemaphoreSetId;
592 void SetSemaphoreSetID(XsiSemaphoreSet *semaphoreSet)
594 fSemaphoreSetId = semaphoreSet->ID();
597 Ipc*& Link()
599 return fLink;
602 private:
603 key_t fKey;
604 int fSemaphoreSetId;
605 Ipc* fLink;
609 struct IpcHashTableDefinition {
610 typedef key_t KeyType;
611 typedef Ipc ValueType;
613 size_t HashKey (const key_t key) const
615 return (size_t)(key);
618 size_t Hash(Ipc *variable) const
620 return (size_t)HashKey(variable->Key());
623 bool Compare(const key_t key, Ipc *variable) const
625 return (key_t)key == (key_t)variable->Key();
628 Ipc*& GetLink(Ipc *variable) const
630 return variable->Link();
634 } // namespace
637 // Arbitrary limit
638 #define MAX_XSI_SEMAPHORE 4096
639 #define MAX_XSI_SEMAPHORE_SET 2048
640 static BOpenHashTable<IpcHashTableDefinition> sIpcHashTable;
641 static BOpenHashTable<SemaphoreHashTableDefinition> sSemaphoreHashTable;
643 static mutex sIpcLock;
644 static mutex sXsiSemaphoreSetLock;
646 static uint32 sGlobalSequenceNumber = 1;
647 static int32 sXsiSemaphoreCount = 0;
648 static int32 sXsiSemaphoreSetCount = 0;
651 // #pragma mark -
654 void
655 XsiSemaphoreSet::SetID()
657 fID = real_time_clock();
658 // The lock is held before calling us
659 while (true) {
660 if (sSemaphoreHashTable.Lookup(fID) == NULL)
661 break;
662 fID = (fID + 1) % INT_MAX;
664 sGlobalSequenceNumber = (sGlobalSequenceNumber + 1) % UINT_MAX;
665 fSequenceNumber = sGlobalSequenceNumber;
669 // #pragma mark - Kernel exported API
672 void
673 xsi_sem_init()
675 // Initialize hash tables
676 status_t status = sIpcHashTable.Init();
677 if (status != B_OK)
678 panic("xsi_sem_init() failed to initialize ipc hash table\n");
679 status = sSemaphoreHashTable.Init();
680 if (status != B_OK)
681 panic("xsi_sem_init() failed to initialize semaphore hash table\n");
683 mutex_init(&sIpcLock, "global POSIX semaphore IPC table");
684 mutex_init(&sXsiSemaphoreSetLock, "global POSIX xsi sem table");
688 /*! Function called on team exit to process any sem_undo requests */
689 void
690 xsi_sem_undo(Team *team)
692 if (team->xsi_sem_context == NULL)
693 return;
695 // By acquiring first the semaphore hash table lock
696 // we make sure the semaphore set in our sem_undo
697 // list won't get removed by IPC_RMID call
698 MutexLocker _(sXsiSemaphoreSetLock);
700 // Process all sem_undo request in the team sem undo list
701 // if any
702 TeamList::Iterator iterator
703 = team->xsi_sem_context->undo_list.GetIterator();
704 while (iterator.HasNext()) {
705 struct sem_undo *current = iterator.Next();
706 XsiSemaphoreSet *semaphoreSet = current->semaphore_set;
707 // Acquire the set lock in order to prevent race
708 // condition with RecordUndo
709 MutexLocker setLocker(semaphoreSet->Lock());
710 MutexLocker _(team->xsi_sem_context->lock);
711 // Revert the changes done by this process
712 for (int i = 0; i < semaphoreSet->NumberOfSemaphores(); i++)
713 if (current->undo_values[i] != 0) {
714 TRACE(("xsi_sem_undo: TeamID = %d, SemaphoreSetID = %d, "
715 "SemaphoreNumber = %d, undo value = %d\n", (int)team->id,
716 semaphoreSet->ID(), i, (int)current->undo_values[i]));
717 semaphoreSet->Semaphore(i)->Revert(current->undo_values[i]);
720 // Remove and free the sem_undo structure from both lists
721 iterator.Remove();
722 semaphoreSet->GetUndoList().Remove(current);
723 delete current;
725 delete team->xsi_sem_context;
726 team->xsi_sem_context = NULL;
730 // #pragma mark - Syscalls
734 _user_xsi_semget(key_t key, int numberOfSemaphores, int flags)
736 TRACE(("xsi_semget: key = %d, numberOfSemaphores = %d, flags = %d\n",
737 (int)key, numberOfSemaphores, flags));
738 XsiSemaphoreSet *semaphoreSet = NULL;
739 Ipc *ipcKey = NULL;
740 // Default assumptions
741 bool isPrivate = true;
742 bool create = true;
744 MutexLocker _(sIpcLock);
745 if (key != IPC_PRIVATE) {
746 isPrivate = false;
747 // Check if key already exist, if it does it already has a semaphore
748 // set associated with it
749 ipcKey = sIpcHashTable.Lookup(key);
750 if (ipcKey == NULL) {
751 // The ipc key does not exist. Create it and add it to the system
752 if (!(flags & IPC_CREAT)) {
753 TRACE_ERROR(("xsi_semget: key %d does not exist, but the "
754 "caller did not ask for creation\n",(int)key));
755 return ENOENT;
757 ipcKey = new(std::nothrow) Ipc(key);
758 if (ipcKey == NULL) {
759 TRACE_ERROR(("xsi_semget: failed to create new Ipc object "
760 "for key %d\n", (int)key));
761 return ENOMEM;
763 sIpcHashTable.Insert(ipcKey);
764 } else {
765 // The IPC key exist and it already has a semaphore
766 if ((flags & IPC_CREAT) && (flags & IPC_EXCL)) {
767 TRACE_ERROR(("xsi_semget: key %d already exist\n", (int)key));
768 return EEXIST;
770 int semaphoreSetID = ipcKey->SemaphoreSetID();
772 MutexLocker _(sXsiSemaphoreSetLock);
773 semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreSetID);
774 if (!semaphoreSet->HasPermission()) {
775 TRACE_ERROR(("xsi_semget: calling process has not permission "
776 "on semaphore %d, key %d\n", semaphoreSet->ID(),
777 (int)key));
778 return EACCES;
780 if (numberOfSemaphores > semaphoreSet->NumberOfSemaphores()
781 && numberOfSemaphores != 0) {
782 TRACE_ERROR(("xsi_semget: numberOfSemaphores greater than the "
783 "one associated with semaphore %d, key %d\n",
784 semaphoreSet->ID(), (int)key));
785 return EINVAL;
787 create = false;
791 if (create) {
792 // Create a new sempahore set for this key
793 if (numberOfSemaphores <= 0
794 || numberOfSemaphores >= MAX_XSI_SEMS_PER_TEAM) {
795 TRACE_ERROR(("xsi_semget: numberOfSemaphores out of range\n"));
796 return EINVAL;
798 if (sXsiSemaphoreCount >= MAX_XSI_SEMAPHORE
799 || sXsiSemaphoreSetCount >= MAX_XSI_SEMAPHORE_SET) {
800 TRACE_ERROR(("xsi_semget: reached limit of maximum number of "
801 "semaphores allowed\n"));
802 return ENOSPC;
805 semaphoreSet = new(std::nothrow) XsiSemaphoreSet(numberOfSemaphores,
806 flags);
807 if (semaphoreSet == NULL || !semaphoreSet->InitOK()) {
808 TRACE_ERROR(("xsi_semget: failed to allocate a new xsi "
809 "semaphore set\n"));
810 delete semaphoreSet;
811 return ENOMEM;
813 atomic_add(&sXsiSemaphoreCount, numberOfSemaphores);
814 atomic_add(&sXsiSemaphoreSetCount, 1);
816 MutexLocker _(sXsiSemaphoreSetLock);
817 semaphoreSet->SetID();
818 if (isPrivate)
819 semaphoreSet->SetIpcKey((key_t)-1);
820 else {
821 semaphoreSet->SetIpcKey(key);
822 ipcKey->SetSemaphoreSetID(semaphoreSet);
824 sSemaphoreHashTable.Insert(semaphoreSet);
825 TRACE(("semget: new set = %d created, sequence = %ld\n",
826 semaphoreSet->ID(), semaphoreSet->SequenceNumber()));
829 return semaphoreSet->ID();
834 _user_xsi_semctl(int semaphoreID, int semaphoreNumber, int command,
835 union semun *args)
837 TRACE(("xsi_semctl: semaphoreID = %d, semaphoreNumber = %d, command = %d\n",
838 semaphoreID, semaphoreNumber, command));
839 MutexLocker ipcHashLocker(sIpcLock);
840 MutexLocker setHashLocker(sXsiSemaphoreSetLock);
841 XsiSemaphoreSet *semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreID);
842 if (semaphoreSet == NULL) {
843 TRACE_ERROR(("xsi_semctl: semaphore set id %d not valid\n",
844 semaphoreID));
845 return EINVAL;
847 if (semaphoreNumber < 0
848 || semaphoreNumber > semaphoreSet->NumberOfSemaphores()) {
849 TRACE_ERROR(("xsi_semctl: semaphore number %d not valid for "
850 "semaphore %d\n", semaphoreNumber, semaphoreID));
851 return EINVAL;
853 if (args != 0 && !IS_USER_ADDRESS(args)) {
854 TRACE_ERROR(("xsi_semctl: semun address is not valid\n"));
855 return B_BAD_ADDRESS;
858 // Lock the semaphore set itself and release both the semaphore
859 // set hash table lock and the ipc hash table lock _only_ if
860 // the command it's not IPC_RMID, this prevents undesidered
861 // situation from happening while (hopefully) improving the
862 // concurrency.
863 MutexLocker setLocker;
864 if (command != IPC_RMID) {
865 setLocker.SetTo(&semaphoreSet->Lock(), false);
866 setHashLocker.Unlock();
867 ipcHashLocker.Unlock();
868 } else
869 // We are about to delete the set along with its mutex, so
870 // we can't use the MutexLocker class, as the mutex itself
871 // won't exist on function exit
872 mutex_lock(&semaphoreSet->Lock());
874 int result = 0;
875 XsiSemaphore *semaphore = semaphoreSet->Semaphore(semaphoreNumber);
876 switch (command) {
877 case GETVAL: {
878 if (!semaphoreSet->HasReadPermission()) {
879 TRACE_ERROR(("xsi_semctl: calling process has not permission "
880 "on semaphore %d, key %d\n", semaphoreSet->ID(),
881 (int)semaphoreSet->IpcKey()));
882 result = EACCES;
883 } else
884 result = semaphore->Value();
885 break;
888 case SETVAL: {
889 if (!semaphoreSet->HasPermission()) {
890 TRACE_ERROR(("xsi_semctl: calling process has not permission "
891 "on semaphore %d, key %d\n", semaphoreSet->ID(),
892 (int)semaphoreSet->IpcKey()));
893 result = EACCES;
894 } else {
895 int value;
896 if (user_memcpy(&value, &args->val, sizeof(int)) < B_OK) {
897 TRACE_ERROR(("xsi_semctl: user_memcpy failed\n"));
898 result = B_BAD_ADDRESS;
899 } else if (value > USHRT_MAX) {
900 TRACE_ERROR(("xsi_semctl: value %d out of range\n", value));
901 result = ERANGE;
902 } else {
903 semaphore->SetValue(value);
904 semaphoreSet->ClearUndo(semaphoreNumber);
907 break;
910 case GETPID: {
911 if (!semaphoreSet->HasReadPermission()) {
912 TRACE_ERROR(("xsi_semctl: calling process has not permission "
913 "on semaphore %d, key %d\n", semaphoreSet->ID(),
914 (int)semaphoreSet->IpcKey()));
915 result = EACCES;
916 } else
917 result = semaphore->LastPid();
918 break;
921 case GETNCNT: {
922 if (!semaphoreSet->HasReadPermission()) {
923 TRACE_ERROR(("xsi_semctl: calling process has not permission "
924 "on semaphore %d, key %d\n", semaphoreSet->ID(),
925 (int)semaphoreSet->IpcKey()));
926 result = EACCES;
927 } else
928 result = semaphore->ThreadsWaitingToIncrease();
929 break;
932 case GETZCNT: {
933 if (!semaphoreSet->HasReadPermission()) {
934 TRACE_ERROR(("xsi_semctl: calling process has not permission "
935 "on semaphore %d, key %d\n", semaphoreSet->ID(),
936 (int)semaphoreSet->IpcKey()));
937 result = EACCES;
938 } else
939 result = semaphore->ThreadsWaitingToBeZero();
940 break;
943 case GETALL: {
944 if (!semaphoreSet->HasReadPermission()) {
945 TRACE_ERROR(("xsi_semctl: calling process has not read "
946 "permission on semaphore %d, key %d\n", semaphoreSet->ID(),
947 (int)semaphoreSet->IpcKey()));
948 result = EACCES;
949 } else
950 for (int i = 0; i < semaphoreSet->NumberOfSemaphores(); i++) {
951 semaphore = semaphoreSet->Semaphore(i);
952 unsigned short value = semaphore->Value();
953 if (user_memcpy(&args->array[i], &value,
954 sizeof(unsigned short)) < B_OK) {
955 TRACE_ERROR(("xsi_semctl: user_memcpy failed\n"));
956 result = B_BAD_ADDRESS;
957 break;
960 break;
963 case SETALL: {
964 if (!semaphoreSet->HasPermission()) {
965 TRACE_ERROR(("xsi_semctl: calling process has not permission "
966 "on semaphore %d, key %d\n", semaphoreSet->ID(),
967 (int)semaphoreSet->IpcKey()));
968 result = EACCES;
969 } else {
970 bool doClear = true;
971 for (int i = 0; i < semaphoreSet->NumberOfSemaphores(); i++) {
972 semaphore = semaphoreSet->Semaphore(i);
973 unsigned short value;
974 if (user_memcpy(&value, &args->array[i], sizeof(unsigned short))
975 < B_OK) {
976 TRACE_ERROR(("xsi_semctl: user_memcpy failed\n"));
977 result = B_BAD_ADDRESS;
978 doClear = false;
979 break;
980 } else
981 semaphore->SetValue(value);
983 if (doClear)
984 semaphoreSet->ClearUndos();
986 break;
989 case IPC_STAT: {
990 if (!semaphoreSet->HasReadPermission()) {
991 TRACE_ERROR(("xsi_semctl: calling process has not read "
992 "permission on semaphore %d, key %d\n", semaphoreSet->ID(),
993 (int)semaphoreSet->IpcKey()));
994 result = EACCES;
995 } else {
996 struct semid_ds sem;
997 sem.sem_perm = semaphoreSet->IpcPermission();
998 sem.sem_nsems = semaphoreSet->NumberOfSemaphores();
999 sem.sem_otime = semaphoreSet->LastSemopTime();
1000 sem.sem_ctime = semaphoreSet->LastSemctlTime();
1001 if (user_memcpy(args->buf, &sem, sizeof(struct semid_ds))
1002 < B_OK) {
1003 TRACE_ERROR(("xsi_semctl: user_memcpy failed\n"));
1004 result = B_BAD_ADDRESS;
1007 break;
1010 case IPC_SET: {
1011 if (!semaphoreSet->HasPermission()) {
1012 TRACE_ERROR(("xsi_semctl: calling process has not "
1013 "permission on semaphore %d, key %d\n",
1014 semaphoreSet->ID(), (int)semaphoreSet->IpcKey()));
1015 result = EACCES;
1016 } else {
1017 struct semid_ds sem;
1018 if (user_memcpy(&sem, args->buf, sizeof(struct semid_ds))
1019 < B_OK) {
1020 TRACE_ERROR(("xsi_semctl: user_memcpy failed\n"));
1021 result = B_BAD_ADDRESS;
1022 } else
1023 semaphoreSet->DoIpcSet(&sem);
1025 break;
1028 case IPC_RMID: {
1029 // If this was the command, we are still holding
1030 // the semaphore set hash table lock along with the
1031 // ipc hash table lock and the semaphore set lock
1032 // itself, this way we are sure there is not
1033 // one waiting in the queue of the mutex.
1034 if (!semaphoreSet->HasPermission()) {
1035 TRACE_ERROR(("xsi_semctl: calling process has not "
1036 "permission on semaphore %d, key %d\n",
1037 semaphoreSet->ID(), (int)semaphoreSet->IpcKey()));
1038 return EACCES;
1040 key_t key = semaphoreSet->IpcKey();
1041 Ipc *ipcKey = NULL;
1042 if (key != -1) {
1043 ipcKey = sIpcHashTable.Lookup(key);
1044 sIpcHashTable.Remove(ipcKey);
1046 sSemaphoreHashTable.Remove(semaphoreSet);
1047 // Wake up of threads waiting on this set
1048 // happens in the destructor
1049 if (key != -1)
1050 delete ipcKey;
1051 atomic_add(&sXsiSemaphoreCount, -semaphoreSet->NumberOfSemaphores());
1052 atomic_add(&sXsiSemaphoreSetCount, -1);
1053 // Remove any sem_undo request
1054 while (struct sem_undo *entry
1055 = semaphoreSet->GetUndoList().RemoveHead()) {
1056 MutexLocker _(entry->team->xsi_sem_context->lock);
1057 entry->team->xsi_sem_context->undo_list.Remove(entry);
1058 delete entry;
1061 delete semaphoreSet;
1062 return 0;
1065 default:
1066 TRACE_ERROR(("xsi_semctl: command %d not valid\n", command));
1067 result = EINVAL;
1070 return result;
1074 status_t
1075 _user_xsi_semop(int semaphoreID, struct sembuf *ops, size_t numOps)
1077 TRACE(("xsi_semop: semaphoreID = %d, ops = %p, numOps = %ld\n",
1078 semaphoreID, ops, numOps));
1079 MutexLocker setHashLocker(sXsiSemaphoreSetLock);
1080 XsiSemaphoreSet *semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreID);
1081 if (semaphoreSet == NULL) {
1082 TRACE_ERROR(("xsi_semop: semaphore set id %d not valid\n",
1083 semaphoreID));
1084 return EINVAL;
1086 MutexLocker setLocker(semaphoreSet->Lock());
1087 setHashLocker.Unlock();
1089 if (!IS_USER_ADDRESS(ops)) {
1090 TRACE_ERROR(("xsi_semop: sembuf address is not valid\n"));
1091 return B_BAD_ADDRESS;
1094 if (numOps < 0 || numOps >= MAX_XSI_SEMS_PER_TEAM) {
1095 TRACE_ERROR(("xsi_semop: numOps out of range\n"));
1096 return EINVAL;
1099 struct sembuf *operations
1100 = (struct sembuf *)malloc(sizeof(struct sembuf) * numOps);
1101 if (operations == NULL) {
1102 TRACE_ERROR(("xsi_semop: failed to allocate sembuf struct\n"));
1103 return B_NO_MEMORY;
1105 MemoryDeleter operationsDeleter(operations);
1107 if (user_memcpy(operations, ops,
1108 (sizeof(struct sembuf) * numOps)) < B_OK) {
1109 TRACE_ERROR(("xsi_semop: user_memcpy failed\n"));
1110 return B_BAD_ADDRESS;
1113 // We won't do partial request, that is operations
1114 // only on some sempahores belonging to the set and then
1115 // going to sleep. If we must wait on a semaphore, we undo
1116 // all the operations already done and go to sleep, otherwise
1117 // we may caused some unwanted deadlock among threads
1118 // fighting for the same set.
1119 bool notDone = true;
1120 status_t result = 0;
1121 while (notDone) {
1122 XsiSemaphore *semaphore = NULL;
1123 short numberOfSemaphores = semaphoreSet->NumberOfSemaphores();
1124 bool goToSleep = false;
1126 uint32 i = 0;
1127 for (; i < numOps; i++) {
1128 short semaphoreNumber = operations[i].sem_num;
1129 if (semaphoreNumber >= numberOfSemaphores) {
1130 TRACE_ERROR(("xsi_semop: %" B_PRIu32 " invalid semaphore number"
1131 "\n", i));
1132 result = EINVAL;
1133 break;
1135 semaphore = semaphoreSet->Semaphore(semaphoreNumber);
1136 unsigned short value = semaphore->Value();
1137 short operation = operations[i].sem_op;
1138 TRACE(("xsi_semop: semaphoreNumber = %d, value = %d\n",
1139 semaphoreNumber, value));
1140 if (operation < 0) {
1141 if (semaphore->Add(operation)) {
1142 if (operations[i].sem_flg & IPC_NOWAIT)
1143 result = EAGAIN;
1144 else
1145 goToSleep = true;
1146 break;
1148 } else if (operation == 0) {
1149 if (value == 0)
1150 continue;
1151 else if (operations[i].sem_flg & IPC_NOWAIT) {
1152 result = EAGAIN;
1153 break;
1154 } else {
1155 goToSleep = true;
1156 break;
1158 } else {
1159 // Operation must be greater than zero,
1160 // just add the value and continue
1161 semaphore->Add(operation);
1165 // Either we have to wait or an error occured
1166 if (goToSleep || result != 0) {
1167 // Undo all previously done operations
1168 for (uint32 j = 0; j < i; j++) {
1169 short semaphoreNumber = operations[j].sem_num;
1170 semaphore = semaphoreSet->Semaphore(semaphoreNumber);
1171 short operation = operations[j].sem_op;
1172 if (operation != 0)
1173 semaphore->Revert(operation);
1175 if (result != 0)
1176 return result;
1178 // We have to wait: first enqueue the thread
1179 // in the appropriate set waiting list, then
1180 // unlock the set itself and block the thread.
1181 bool waitOnZero = true;
1182 if (operations[i].sem_op != 0)
1183 waitOnZero = false;
1185 Thread *thread = thread_get_current_thread();
1186 queued_thread queueEntry(thread, (int32)operations[i].sem_op);
1187 semaphore->Enqueue(&queueEntry, waitOnZero);
1189 uint32 sequenceNumber = semaphoreSet->SequenceNumber();
1191 TRACE(("xsi_semop: thread %d going to sleep\n", (int)thread->id));
1192 result = semaphore->BlockAndUnlock(thread, &setLocker);
1193 TRACE(("xsi_semop: thread %d back to life\n", (int)thread->id));
1195 // We are back to life. Find out why!
1196 // Make sure the set hasn't been deleted or worst yet
1197 // replaced.
1198 setHashLocker.Lock();
1199 semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreID);
1200 if (result == EIDRM || semaphoreSet == NULL || (semaphoreSet != NULL
1201 && sequenceNumber != semaphoreSet->SequenceNumber())) {
1202 TRACE_ERROR(("xsi_semop: semaphore set id %d (sequence = "
1203 "%" B_PRIu32 ") got destroyed\n", semaphoreID,
1204 sequenceNumber));
1205 notDone = false;
1206 result = EIDRM;
1207 } else if (result == B_INTERRUPTED) {
1208 TRACE_ERROR(("xsi_semop: thread %d got interrupted while "
1209 "waiting on semaphore set id %d\n",(int)thread->id,
1210 semaphoreID));
1211 semaphore->Deque(&queueEntry, waitOnZero);
1212 result = EINTR;
1213 notDone = false;
1214 } else {
1215 setLocker.Lock();
1216 setHashLocker.Unlock();
1218 } else {
1219 // everything worked like a charm (so far)
1220 notDone = false;
1221 TRACE(("xsi_semop: semaphore acquired succesfully\n"));
1222 // We acquired the semaphore, now records the sem_undo
1223 // requests
1224 XsiSemaphore *semaphore = NULL;
1225 uint32 i = 0;
1226 for (; i < numOps; i++) {
1227 short semaphoreNumber = operations[i].sem_num;
1228 semaphore = semaphoreSet->Semaphore(semaphoreNumber);
1229 short operation = operations[i].sem_op;
1230 if (operations[i].sem_flg & SEM_UNDO)
1231 if (semaphoreSet->RecordUndo(semaphoreNumber, operation)
1232 != B_OK) {
1233 // Unlikely scenario, but we might get here.
1234 // Undo everything!
1235 // Start with semaphore operations
1236 for (uint32 j = 0; j < numOps; j++) {
1237 short semaphoreNumber = operations[j].sem_num;
1238 semaphore = semaphoreSet->Semaphore(semaphoreNumber);
1239 short operation = operations[j].sem_op;
1240 if (operation != 0)
1241 semaphore->Revert(operation);
1243 // Remove all previously registered sem_undo request
1244 for (uint32 j = 0; j < i; j++) {
1245 if (operations[j].sem_flg & SEM_UNDO)
1246 semaphoreSet->RevertUndo(operations[j].sem_num,
1247 operations[j].sem_op);
1249 result = ENOSPC;
1255 // We did it. Set the pid of all semaphores used
1256 if (result == 0) {
1257 for (uint32 i = 0; i < numOps; i++) {
1258 short semaphoreNumber = operations[i].sem_num;
1259 XsiSemaphore *semaphore = semaphoreSet->Semaphore(semaphoreNumber);
1260 semaphore->SetPid(getpid());
1263 return result;