1 /*
2 * Copyright 2008-2023, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Salvatore Benedetto <salvatore.benedetto@gmail.com>
7 */
8
9 #include <posix/xsi_semaphore.h>
10
11 #include <new>
12
13 #include <sys/ipc.h>
14 #include <sys/types.h>
15
16 #include <OS.h>
17
18 #include <kernel.h>
19 #include <syscall_restart.h>
20
21 #include <util/atomic.h>
22 #include <util/AutoLock.h>
23 #include <util/DoublyLinkedList.h>
24 #include <util/OpenHashTable.h>
25 #include <AutoDeleter.h>
26 #include <StackOrHeapArray.h>
27
28
29 //#define TRACE_XSI_SEM
30 #ifdef TRACE_XSI_SEM
31 # define TRACE(x) dprintf x
32 # define TRACE_ERROR(x) dprintf x
33 #else
34 # define TRACE(x) /* nothing */
35 # define TRACE_ERROR(x) dprintf x
36 #endif
37
38
39 namespace {
40
41 class XsiSemaphoreSet;
42
43 struct sem_undo : DoublyLinkedListLinkImpl<sem_undo> {
sem_undo__anon183290650111::sem_undo44 sem_undo(XsiSemaphoreSet *semaphoreSet, Team *team, int16 *undoValues)
45 :
46 semaphore_set(semaphoreSet),
47 team(team),
48 undo_values(undoValues)
49 {
50 }
51
52 DoublyLinkedListLink<sem_undo> team_link;
53 XsiSemaphoreSet *semaphore_set;
54 Team *team;
55 int16 *undo_values;
56 };
57
58 typedef DoublyLinkedList<sem_undo> UndoList;
59 typedef DoublyLinkedList<sem_undo,
60 DoublyLinkedListMemberGetLink<sem_undo, &sem_undo::team_link> > TeamList;
61
62 } // namespace
63
64
65 // Forward declared in global namespace.
66 struct xsi_sem_context {
xsi_sem_contextxsi_sem_context67 xsi_sem_context()
68 {
69 mutex_init(&lock, "Private team undo_list lock");
70 }
71
~xsi_sem_contextxsi_sem_context72 ~xsi_sem_context()
73 {
74 mutex_destroy(&lock);
75 }
76
77 TeamList undo_list;
78 mutex lock;
79 };
80
81
82 namespace {
83
84 // Xsi semaphore definition
85 class XsiSemaphore {
86 public:
XsiSemaphore()87 XsiSemaphore()
88 :
89 fLastPidOperation(0),
90 fValue(0)
91 {
92 fWaitingToIncrease.Init(this, "XsiSemaphore");
93 fWaitingToBeZero.Init(this, "XsiSemaphore");
94 }
95
~XsiSemaphore()96 ~XsiSemaphore()
97 {
98 // For some reason the semaphore is getting destroyed.
99 // Wake up any remaing awaiting threads
100 fWaitingToIncrease.NotifyAll(EIDRM);
101 fWaitingToBeZero.NotifyAll(EIDRM);
102
103 // No need to remove any sem_undo request still
104 // hanging. When the process exit and doesn't found
105 // the semaphore set, it'll just ignore the sem_undo
106 // request. That's better than iterating trough the
107 // whole sUndoList. Beside we don't know our semaphore
108 // number nor our semaphore set id.
109 }
110
111 // We return true in case the operation causes the
112 // caller to wait, so it can undo all the operations
113 // previously done
Add(short value)114 bool Add(short value)
115 {
116 if ((int)(fValue + value) < 0) {
117 TRACE(("XsiSemaphore::Add: potentially going to sleep\n"));
118 return true;
119 } else {
120 fValue += value;
121 if (fValue == 0)
122 WakeUpThreads(true);
123 else if (fValue > 0)
124 WakeUpThreads(false);
125 return false;
126 }
127 }
128
Dequeue(ConditionVariableEntry * queueEntry)129 static void Dequeue(ConditionVariableEntry *queueEntry)
130 {
131 queueEntry->Wait(B_RELATIVE_TIMEOUT, 0);
132 }
133
Enqueue(ConditionVariableEntry * queueEntry,bool waitForZero)134 void Enqueue(ConditionVariableEntry *queueEntry, bool waitForZero)
135 {
136 if (waitForZero) {
137 fWaitingToBeZero.Add(queueEntry);
138 } else {
139 fWaitingToIncrease.Add(queueEntry);
140 }
141 }
142
LastPid() const143 pid_t LastPid() const
144 {
145 return fLastPidOperation;
146 }
147
Revert(short value)148 void Revert(short value)
149 {
150 fValue -= value;
151 if (fValue == 0)
152 WakeUpThreads(true);
153 else if (fValue > 0)
154 WakeUpThreads(false);
155 }
156
SetPid(pid_t pid)157 void SetPid(pid_t pid)
158 {
159 fLastPidOperation = pid;
160 }
161
SetValue(ushort value)162 void SetValue(ushort value)
163 {
164 fValue = value;
165 }
166
ThreadsWaitingToIncrease()167 ushort ThreadsWaitingToIncrease()
168 {
169 return fWaitingToIncrease.EntriesCount();
170 }
171
ThreadsWaitingToBeZero()172 ushort ThreadsWaitingToBeZero()
173 {
174 return fWaitingToBeZero.EntriesCount();
175 }
176
Value() const177 ushort Value() const
178 {
179 return fValue;
180 }
181
WakeUpThreads(bool waitingForZero)182 void WakeUpThreads(bool waitingForZero)
183 {
184 if (waitingForZero) {
185 fWaitingToBeZero.NotifyAll();
186 } else {
187 fWaitingToIncrease.NotifyAll();
188 }
189 }
190
191 private:
192 pid_t fLastPidOperation; // sempid
193 ushort fValue; // semval
194
195 ConditionVariable fWaitingToIncrease;
196 ConditionVariable fWaitingToBeZero;
197 };
198
199 #define MAX_XSI_SEMS_PER_TEAM 128
200
201 // Xsi semaphore set definition (semid_ds)
202 class XsiSemaphoreSet {
203 public:
XsiSemaphoreSet(int numberOfSemaphores,int flags)204 XsiSemaphoreSet(int numberOfSemaphores, int flags)
205 : fInitOK(false),
206 fLastSemctlTime((time_t)real_time_clock()),
207 fLastSemopTime(0),
208 fNumberOfSemaphores(numberOfSemaphores),
209 fSemaphores(0)
210 {
211 mutex_init(&fLock, "XsiSemaphoreSet private mutex");
212 SetIpcKey((key_t)-1);
213 SetPermissions(flags);
214 fSemaphores = new(std::nothrow) XsiSemaphore[numberOfSemaphores];
215 if (fSemaphores == NULL) {
216 TRACE_ERROR(("XsiSemaphoreSet::XsiSemaphore(): failed to allocate "
217 "XsiSemaphore object\n"));
218 } else
219 fInitOK = true;
220 }
221
~XsiSemaphoreSet()222 ~XsiSemaphoreSet()
223 {
224 TRACE(("XsiSemaphoreSet::~XsiSemaphoreSet(): removing semaphore "
225 "set %d\n", fID));
226 mutex_destroy(&fLock);
227 delete[] fSemaphores;
228 }
229
ClearUndo(ushort semaphoreNumber)230 void ClearUndo(ushort semaphoreNumber)
231 {
232 Team *team = thread_get_current_thread()->team;
233 UndoList::Iterator iterator = fUndoList.GetIterator();
234 while (iterator.HasNext()) {
235 struct sem_undo *current = iterator.Next();
236 if (current->team == team) {
237 TRACE(("XsiSemaphoreSet::ClearUndo: teamID = %d, "
238 "semaphoreSetID = %d, semaphoreNumber = %d\n",
239 fID, semaphoreNumber, (int)team->id));
240 MutexLocker _(team->xsi_sem_context->lock);
241 current->undo_values[semaphoreNumber] = 0;
242 return;
243 }
244 }
245 }
246
ClearUndos()247 void ClearUndos()
248 {
249 // Clear all undo_values (POSIX semadj equivalent)
250 // of the calling team. This happens only on semctl SETALL.
251 Team *team = thread_get_current_thread()->team;
252 DoublyLinkedList<sem_undo>::Iterator iterator = fUndoList.GetIterator();
253 while (iterator.HasNext()) {
254 struct sem_undo *current = iterator.Next();
255 if (current->team == team) {
256 TRACE(("XsiSemaphoreSet::ClearUndos: teamID = %d, "
257 "semaphoreSetID = %d\n", (int)team->id, fID));
258 MutexLocker _(team->xsi_sem_context->lock);
259 memset(current->undo_values, 0,
260 sizeof(int16) * fNumberOfSemaphores);
261 return;
262 }
263 }
264 }
265
DoIpcSet(struct semid_ds * result)266 void DoIpcSet(struct semid_ds *result)
267 {
268 fPermissions.uid = result->sem_perm.uid;
269 fPermissions.gid = result->sem_perm.gid;
270 fPermissions.mode = (fPermissions.mode & ~0x01ff)
271 | (result->sem_perm.mode & 0x01ff);
272 }
273
HasPermission() const274 bool HasPermission() const
275 {
276 if ((fPermissions.mode & S_IWOTH) != 0)
277 return true;
278
279 uid_t uid = geteuid();
280 if (uid == 0 || (uid == fPermissions.uid
281 && (fPermissions.mode & S_IWUSR) != 0))
282 return true;
283
284 gid_t gid = getegid();
285 if (gid == fPermissions.gid && (fPermissions.mode & S_IWGRP) != 0)
286 return true;
287
288 return false;
289 }
290
HasReadPermission() const291 bool HasReadPermission() const
292 {
293 // TODO: fix this
294 return HasPermission();
295 }
296
ID() const297 int ID() const
298 {
299 return fID;
300 }
301
InitOK()302 bool InitOK()
303 {
304 return fInitOK;
305 }
306
IpcKey() const307 key_t IpcKey() const
308 {
309 return fPermissions.key;
310 }
311
IpcPermission() const312 struct ipc_perm IpcPermission() const
313 {
314 return fPermissions;
315 }
316
LastSemctlTime() const317 time_t LastSemctlTime() const
318 {
319 return fLastSemctlTime;
320 }
321
LastSemopTime() const322 time_t LastSemopTime() const
323 {
324 return fLastSemopTime;
325 }
326
Lock()327 mutex &Lock()
328 {
329 return fLock;
330 }
331
NumberOfSemaphores() const332 ushort NumberOfSemaphores() const
333 {
334 return fNumberOfSemaphores;
335 }
336
337 // Record the sem_undo operation into our private fUndoList and
338 // the team undo_list. The only limit here is the memory needed
339 // for creating a new sem_undo structure.
RecordUndo(ushort semaphoreNumber,short value)340 int RecordUndo(ushort semaphoreNumber, short value)
341 {
342 // Look if there is already a record from the team caller
343 // for the same semaphore set
344 bool notFound = true;
345 Team *team = thread_get_current_thread()->team;
346 DoublyLinkedList<sem_undo>::Iterator iterator = fUndoList.GetIterator();
347 while (iterator.HasNext()) {
348 struct sem_undo *current = iterator.Next();
349 if (current->team == team) {
350 // Update its undo value
351 MutexLocker _(team->xsi_sem_context->lock);
352 int newValue = current->undo_values[semaphoreNumber] + value;
353 if (newValue > USHRT_MAX || newValue < -USHRT_MAX) {
354 TRACE_ERROR(("XsiSemaphoreSet::RecordUndo: newValue %d "
355 "out of range\n", newValue));
356 return ERANGE;
357 }
358 current->undo_values[semaphoreNumber] = newValue;
359 notFound = false;
360 TRACE(("XsiSemaphoreSet::RecordUndo: found record. Team = %d, "
361 "semaphoreSetID = %d, semaphoreNumber = %d, value = %d\n",
362 (int)team->id, fID, semaphoreNumber,
363 current->undo_values[semaphoreNumber]));
364 break;
365 }
366 }
367
368 if (notFound) {
369 // First sem_undo request from this team for this
370 // semaphore set
371 int16 *undoValues
372 = (int16 *)malloc(sizeof(int16) * fNumberOfSemaphores);
373 if (undoValues == NULL)
374 return B_NO_MEMORY;
375 struct sem_undo *request
376 = new(std::nothrow) sem_undo(this, team, undoValues);
377 if (request == NULL) {
378 free(undoValues);
379 return B_NO_MEMORY;
380 }
381 memset(request->undo_values, 0, sizeof(int16) * fNumberOfSemaphores);
382 request->undo_values[semaphoreNumber] = value;
383
384 // Check if it's the very first sem_undo request for this team
385 xsi_sem_context *context = atomic_pointer_get(&team->xsi_sem_context);
386 if (context == NULL) {
387 // Create the context
388 context = new(std::nothrow) xsi_sem_context;
389 if (context == NULL) {
390 free(request->undo_values);
391 delete request;
392 return B_NO_MEMORY;
393 }
394 // Since we don't hold any global lock, someone
395 // else could have been quicker than us, so we have
396 // to delete the one we just created and use the one
397 // in place.
398 if (atomic_pointer_test_and_set(&team->xsi_sem_context, context,
399 (xsi_sem_context *)NULL) != NULL)
400 delete context;
401 }
402
403 // Add the request to both XsiSemaphoreSet and team list
404 fUndoList.Add(request);
405 MutexLocker _(team->xsi_sem_context->lock);
406 team->xsi_sem_context->undo_list.Add(request);
407 TRACE(("XsiSemaphoreSet::RecordUndo: new record added. Team = %d, "
408 "semaphoreSetID = %d, semaphoreNumber = %d, value = %d\n",
409 (int)team->id, fID, semaphoreNumber, value));
410 }
411 return B_OK;
412 }
413
RevertUndo(ushort semaphoreNumber,short value)414 void RevertUndo(ushort semaphoreNumber, short value)
415 {
416 // This can be called only when RecordUndo fails.
417 Team *team = thread_get_current_thread()->team;
418 DoublyLinkedList<sem_undo>::Iterator iterator = fUndoList.GetIterator();
419 while (iterator.HasNext()) {
420 struct sem_undo *current = iterator.Next();
421 if (current->team == team) {
422 MutexLocker _(team->xsi_sem_context->lock);
423 fSemaphores[semaphoreNumber].Revert(value);
424 break;
425 }
426 }
427 }
428
Semaphore(int nth) const429 XsiSemaphore* Semaphore(int nth) const
430 {
431 return &fSemaphores[nth];
432 }
433
SequenceNumber() const434 uint32 SequenceNumber() const
435 {
436 return fSequenceNumber;
437 }
438
439 // Implemented after sGlobalSequenceNumber is declared
440 void SetID();
441
SetIpcKey(key_t key)442 void SetIpcKey(key_t key)
443 {
444 fPermissions.key = key;
445 }
446
SetLastSemctlTime()447 void SetLastSemctlTime()
448 {
449 fLastSemctlTime = real_time_clock();
450 }
451
SetLastSemopTime()452 void SetLastSemopTime()
453 {
454 fLastSemopTime = real_time_clock();
455 }
456
SetPermissions(int flags)457 void SetPermissions(int flags)
458 {
459 fPermissions.uid = fPermissions.cuid = geteuid();
460 fPermissions.gid = fPermissions.cgid = getegid();
461 fPermissions.mode = (flags & 0x01ff);
462 }
463
GetUndoList()464 UndoList &GetUndoList()
465 {
466 return fUndoList;
467 }
468
Link()469 XsiSemaphoreSet*& Link()
470 {
471 return fLink;
472 }
473
474 private:
475 int fID; // semaphore set id
476 bool fInitOK;
477 time_t fLastSemctlTime; // sem_ctime
478 time_t fLastSemopTime; // sem_otime
479 mutex fLock; // private lock
480 ushort fNumberOfSemaphores; // sem_nsems
481 struct ipc_perm fPermissions; // sem_perm
482 XsiSemaphore *fSemaphores; // array of semaphores
483 uint32 fSequenceNumber; // used as a second id
484 UndoList fUndoList; // undo list requests
485
486 XsiSemaphoreSet* fLink;
487 };
488
489 // Xsi semaphore set hash table
490 struct SemaphoreHashTableDefinition {
491 typedef int KeyType;
492 typedef XsiSemaphoreSet ValueType;
493
HashKey__anon183290650211::SemaphoreHashTableDefinition494 size_t HashKey (const int key) const
495 {
496 return (size_t)key;
497 }
498
Hash__anon183290650211::SemaphoreHashTableDefinition499 size_t Hash(XsiSemaphoreSet *variable) const
500 {
501 return (size_t)variable->ID();
502 }
503
Compare__anon183290650211::SemaphoreHashTableDefinition504 bool Compare(const int key, XsiSemaphoreSet *variable) const
505 {
506 return (int)key == (int)variable->ID();
507 }
508
GetLink__anon183290650211::SemaphoreHashTableDefinition509 XsiSemaphoreSet*& GetLink(XsiSemaphoreSet *variable) const
510 {
511 return variable->Link();
512 }
513 };
514
515
516 // IPC class
517 class Ipc {
518 public:
Ipc(key_t key)519 Ipc(key_t key)
520 : fKey(key),
521 fSemaphoreSetId(-1)
522 {
523 }
524
Key() const525 key_t Key() const
526 {
527 return fKey;
528 }
529
SemaphoreSetID() const530 int SemaphoreSetID() const
531 {
532 return fSemaphoreSetId;
533 }
534
SetSemaphoreSetID(XsiSemaphoreSet * semaphoreSet)535 void SetSemaphoreSetID(XsiSemaphoreSet *semaphoreSet)
536 {
537 fSemaphoreSetId = semaphoreSet->ID();
538 }
539
Link()540 Ipc*& Link()
541 {
542 return fLink;
543 }
544
545 private:
546 key_t fKey;
547 int fSemaphoreSetId;
548 Ipc* fLink;
549 };
550
551
552 struct IpcHashTableDefinition {
553 typedef key_t KeyType;
554 typedef Ipc ValueType;
555
HashKey__anon183290650211::IpcHashTableDefinition556 size_t HashKey (const key_t key) const
557 {
558 return (size_t)(key);
559 }
560
Hash__anon183290650211::IpcHashTableDefinition561 size_t Hash(Ipc *variable) const
562 {
563 return (size_t)HashKey(variable->Key());
564 }
565
Compare__anon183290650211::IpcHashTableDefinition566 bool Compare(const key_t key, Ipc *variable) const
567 {
568 return (key_t)key == (key_t)variable->Key();
569 }
570
GetLink__anon183290650211::IpcHashTableDefinition571 Ipc*& GetLink(Ipc *variable) const
572 {
573 return variable->Link();
574 }
575 };
576
577 } // namespace
578
579
580 // Arbitrary limit
581 #define MAX_XSI_SEMAPHORE 4096
582 #define MAX_XSI_SEMAPHORE_SET 2048
583 static BOpenHashTable<IpcHashTableDefinition> sIpcHashTable;
584 static BOpenHashTable<SemaphoreHashTableDefinition> sSemaphoreHashTable;
585
586 static mutex sIpcLock;
587 static mutex sXsiSemaphoreSetLock;
588
589 static uint32 sGlobalSequenceNumber = 1;
590 static int32 sXsiSemaphoreCount = 0;
591 static int32 sXsiSemaphoreSetCount = 0;
592
593
594 // #pragma mark -
595
596
597 void
SetID()598 XsiSemaphoreSet::SetID()
599 {
600 fID = real_time_clock();
601 // The lock is held before calling us
602 while (true) {
603 if (sSemaphoreHashTable.Lookup(fID) == NULL)
604 break;
605 fID = (fID + 1) % INT_MAX;
606 }
607 sGlobalSequenceNumber = (sGlobalSequenceNumber + 1) % UINT_MAX;
608 fSequenceNumber = sGlobalSequenceNumber;
609 }
610
611
612 // #pragma mark - Kernel exported API
613
614
615 void
xsi_sem_init()616 xsi_sem_init()
617 {
618 // Initialize hash tables
619 status_t status = sIpcHashTable.Init();
620 if (status != B_OK)
621 panic("xsi_sem_init() failed to initialize ipc hash table\n");
622 status = sSemaphoreHashTable.Init();
623 if (status != B_OK)
624 panic("xsi_sem_init() failed to initialize semaphore hash table\n");
625
626 mutex_init(&sIpcLock, "global POSIX semaphore IPC table");
627 mutex_init(&sXsiSemaphoreSetLock, "global POSIX xsi sem table");
628 }
629
630
631 /*! Function called on team exit to process any sem_undo requests */
632 void
xsi_sem_undo(Team * team)633 xsi_sem_undo(Team *team)
634 {
635 if (team->xsi_sem_context == NULL)
636 return;
637
638 // By acquiring first the semaphore hash table lock
639 // we make sure the semaphore set in our sem_undo
640 // list won't get removed by IPC_RMID call
641 MutexLocker _(sXsiSemaphoreSetLock);
642
643 // Process all sem_undo request in the team sem undo list
644 // if any
645 TeamList::Iterator iterator
646 = team->xsi_sem_context->undo_list.GetIterator();
647 while (iterator.HasNext()) {
648 struct sem_undo *current = iterator.Next();
649 XsiSemaphoreSet *semaphoreSet = current->semaphore_set;
650 // Acquire the set lock in order to prevent race
651 // condition with RecordUndo
652 MutexLocker setLocker(semaphoreSet->Lock());
653 MutexLocker _(team->xsi_sem_context->lock);
654 // Revert the changes done by this process
655 for (int i = 0; i < semaphoreSet->NumberOfSemaphores(); i++)
656 if (current->undo_values[i] != 0) {
657 TRACE(("xsi_sem_undo: TeamID = %d, SemaphoreSetID = %d, "
658 "SemaphoreNumber = %d, undo value = %d\n", (int)team->id,
659 semaphoreSet->ID(), i, (int)current->undo_values[i]));
660 semaphoreSet->Semaphore(i)->Revert(current->undo_values[i]);
661 }
662
663 // Remove and free the sem_undo structure from both lists
664 iterator.Remove();
665 semaphoreSet->GetUndoList().Remove(current);
666 delete current;
667 }
668 delete team->xsi_sem_context;
669 team->xsi_sem_context = NULL;
670 }
671
672
673 // #pragma mark - Syscalls
674
675
676 int
_user_xsi_semget(key_t key,int numberOfSemaphores,int flags)677 _user_xsi_semget(key_t key, int numberOfSemaphores, int flags)
678 {
679 TRACE(("xsi_semget: key = %d, numberOfSemaphores = %d, flags = %d\n",
680 (int)key, numberOfSemaphores, flags));
681 XsiSemaphoreSet *semaphoreSet = NULL;
682 Ipc *ipcKey = NULL;
683
684 // Default assumption
685 bool isPrivate = true;
686
687 MutexLocker ipcLocker(sIpcLock);
688 if (key != IPC_PRIVATE) {
689 isPrivate = false;
690 // Check if key already exist, if it does it already has a semaphore
691 // set associated with it
692 ipcKey = sIpcHashTable.Lookup(key);
693 if (ipcKey != NULL) {
694 // The IPC key exist and it already has a semaphore
695 if ((flags & IPC_CREAT) && (flags & IPC_EXCL)) {
696 TRACE(("xsi_semget: key %d already exist\n", (int)key));
697 return EEXIST;
698 }
699 int semaphoreSetID = ipcKey->SemaphoreSetID();
700
701 MutexLocker semaphoreSetLocker(sXsiSemaphoreSetLock);
702 semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreSetID);
703 if (semaphoreSet == NULL) {
704 TRACE(("xsi_semget: calling process has no semaphore, "
705 "key %d\n", (int)key));
706 return EINVAL;
707 }
708 if (!semaphoreSet->HasPermission()) {
709 TRACE(("xsi_semget: calling process has no permission "
710 "on semaphore %d, key %d\n", semaphoreSet->ID(),
711 (int)key));
712 return EACCES;
713 }
714 if (numberOfSemaphores > semaphoreSet->NumberOfSemaphores()
715 && numberOfSemaphores != 0) {
716 TRACE(("xsi_semget: numberOfSemaphores greater than the "
717 "one associated with semaphore %d, key %d\n",
718 semaphoreSet->ID(), (int)key));
719 return EINVAL;
720 }
721
722 return semaphoreSet->ID();
723 }
724
725 // The ipc key does not exist. Create it and add it to the system
726 if (!(flags & IPC_CREAT)) {
727 TRACE(("xsi_semget: key %d does not exist, but the "
728 "caller did not ask for creation\n",(int)key));
729 return ENOENT;
730 }
731 ipcKey = new(std::nothrow) Ipc(key);
732 if (ipcKey == NULL) {
733 TRACE_ERROR(("xsi_semget: failed to create new Ipc object "
734 "for key %d\n", (int)key));
735 return ENOMEM;
736 }
737 }
738
739 // Create a new semaphore set for this key
740 if (numberOfSemaphores <= 0
741 || numberOfSemaphores >= MAX_XSI_SEMS_PER_TEAM) {
742 TRACE_ERROR(("xsi_semget: numberOfSemaphores out of range\n"));
743 delete ipcKey;
744 return EINVAL;
745 }
746 if (sXsiSemaphoreCount >= MAX_XSI_SEMAPHORE
747 || sXsiSemaphoreSetCount >= MAX_XSI_SEMAPHORE_SET) {
748 TRACE_ERROR(("xsi_semget: reached limit of maximum number of "
749 "semaphores allowed\n"));
750 delete ipcKey;
751 return ENOSPC;
752 }
753
754 semaphoreSet = new(std::nothrow) XsiSemaphoreSet(numberOfSemaphores, flags);
755 if (semaphoreSet == NULL || !semaphoreSet->InitOK()) {
756 TRACE_ERROR(("xsi_semget: failed to allocate a new xsi "
757 "semaphore set\n"));
758 delete semaphoreSet;
759 delete ipcKey;
760 return ENOMEM;
761 }
762
763 atomic_add(&sXsiSemaphoreCount, numberOfSemaphores);
764 atomic_add(&sXsiSemaphoreSetCount, 1);
765
766 MutexLocker semaphoreSetLocker(sXsiSemaphoreSetLock);
767 semaphoreSet->SetID();
768 if (isPrivate) {
769 semaphoreSet->SetIpcKey((key_t)-1);
770 } else {
771 sIpcHashTable.Insert(ipcKey);
772 semaphoreSet->SetIpcKey(key);
773 ipcKey->SetSemaphoreSetID(semaphoreSet);
774 }
775 sSemaphoreHashTable.Insert(semaphoreSet);
776 TRACE(("semget: new set = %d created, sequence = %ld\n",
777 semaphoreSet->ID(), semaphoreSet->SequenceNumber()));
778
779 return semaphoreSet->ID();
780 }
781
782
783 int
_user_xsi_semctl(int semaphoreID,int semaphoreNumber,int command,union semun * _args)784 _user_xsi_semctl(int semaphoreID, int semaphoreNumber, int command,
785 union semun *_args)
786 {
787 TRACE(("xsi_semctl: semaphoreID = %d, semaphoreNumber = %d, command = %d\n",
788 semaphoreID, semaphoreNumber, command));
789
790 union semun args = {0};
791 if (_args != NULL) {
792 if (!IS_USER_ADDRESS(_args)
793 || user_memcpy(&args, _args, sizeof(union semun)) != B_OK)
794 return B_BAD_ADDRESS;
795 }
796
797 MutexLocker ipcHashLocker(sIpcLock);
798 MutexLocker setHashLocker(sXsiSemaphoreSetLock);
799 XsiSemaphoreSet *semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreID);
800 if (semaphoreSet == NULL) {
801 TRACE(("xsi_semctl: semaphore set id %d not valid\n",
802 semaphoreID));
803 return EINVAL;
804 }
805 if (semaphoreNumber < 0
806 || semaphoreNumber > semaphoreSet->NumberOfSemaphores()) {
807 TRACE(("xsi_semctl: semaphore number %d not valid for "
808 "semaphore %d\n", semaphoreNumber, semaphoreID));
809 return EINVAL;
810 }
811
812 // Lock the semaphore set itself and release both the semaphore
813 // set hash table lock and the ipc hash table lock _only_ if
814 // the command it's not IPC_RMID, this prevents undesidered
815 // situation from happening while (hopefully) improving the
816 // concurrency.
817 MutexLocker setLocker(semaphoreSet->Lock());
818 if (command != IPC_RMID) {
819 setHashLocker.Unlock();
820 ipcHashLocker.Unlock();
821 }
822
823 int result = 0;
824 XsiSemaphore *semaphore = semaphoreSet->Semaphore(semaphoreNumber);
825 switch (command) {
826 case GETVAL: {
827 if (!semaphoreSet->HasReadPermission()) {
828 TRACE(("xsi_semctl: calling process has not permission "
829 "on semaphore %d, key %d\n", semaphoreSet->ID(),
830 (int)semaphoreSet->IpcKey()));
831 result = EACCES;
832 } else
833 result = semaphore->Value();
834 break;
835 }
836
837 case SETVAL: {
838 if (!semaphoreSet->HasPermission()) {
839 TRACE(("xsi_semctl: calling process has not permission "
840 "on semaphore %d, key %d\n", semaphoreSet->ID(),
841 (int)semaphoreSet->IpcKey()));
842 result = EACCES;
843 } else {
844 if (args.val > USHRT_MAX) {
845 TRACE(("xsi_semctl: value %d out of range\n", args.val));
846 result = ERANGE;
847 } else {
848 semaphore->SetValue(args.val);
849 semaphoreSet->ClearUndo(semaphoreNumber);
850 }
851 }
852 break;
853 }
854
855 case GETPID: {
856 if (!semaphoreSet->HasReadPermission()) {
857 TRACE(("xsi_semctl: calling process has not permission "
858 "on semaphore %d, key %d\n", semaphoreSet->ID(),
859 (int)semaphoreSet->IpcKey()));
860 result = EACCES;
861 } else
862 result = semaphore->LastPid();
863 break;
864 }
865
866 case GETNCNT: {
867 if (!semaphoreSet->HasReadPermission()) {
868 TRACE(("xsi_semctl: calling process has not permission "
869 "on semaphore %d, key %d\n", semaphoreSet->ID(),
870 (int)semaphoreSet->IpcKey()));
871 result = EACCES;
872 } else
873 result = semaphore->ThreadsWaitingToIncrease();
874 break;
875 }
876
877 case GETZCNT: {
878 if (!semaphoreSet->HasReadPermission()) {
879 TRACE(("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->ThreadsWaitingToBeZero();
885 break;
886 }
887
888 case GETALL: {
889 if (!semaphoreSet->HasReadPermission()) {
890 TRACE(("xsi_semctl: calling process has not read "
891 "permission on semaphore %d, key %d\n", semaphoreSet->ID(),
892 (int)semaphoreSet->IpcKey()));
893 result = EACCES;
894 } else
895 for (int i = 0; i < semaphoreSet->NumberOfSemaphores(); i++) {
896 semaphore = semaphoreSet->Semaphore(i);
897 unsigned short value = semaphore->Value();
898 if (user_memcpy(args.array + i, &value,
899 sizeof(unsigned short)) != B_OK) {
900 TRACE_ERROR(("xsi_semctl: user_memcpy failed\n"));
901 result = B_BAD_ADDRESS;
902 break;
903 }
904 }
905 break;
906 }
907
908 case SETALL: {
909 if (!semaphoreSet->HasPermission()) {
910 TRACE(("xsi_semctl: calling process has not permission "
911 "on semaphore %d, key %d\n", semaphoreSet->ID(),
912 (int)semaphoreSet->IpcKey()));
913 result = EACCES;
914 } else {
915 bool doClear = true;
916 for (int i = 0; i < semaphoreSet->NumberOfSemaphores(); i++) {
917 semaphore = semaphoreSet->Semaphore(i);
918 unsigned short value;
919 if (user_memcpy(&value, args.array + i,
920 sizeof(unsigned short)) != B_OK) {
921 TRACE_ERROR(("xsi_semctl: user_memcpy failed\n"));
922 result = B_BAD_ADDRESS;
923 doClear = false;
924 break;
925 } else
926 semaphore->SetValue(value);
927 }
928 if (doClear)
929 semaphoreSet->ClearUndos();
930 }
931 break;
932 }
933
934 case IPC_STAT: {
935 if (!semaphoreSet->HasReadPermission()) {
936 TRACE(("xsi_semctl: calling process has not read "
937 "permission on semaphore %d, key %d\n", semaphoreSet->ID(),
938 (int)semaphoreSet->IpcKey()));
939 result = EACCES;
940 } else {
941 struct semid_ds sem;
942 sem.sem_perm = semaphoreSet->IpcPermission();
943 sem.sem_nsems = semaphoreSet->NumberOfSemaphores();
944 sem.sem_otime = semaphoreSet->LastSemopTime();
945 sem.sem_ctime = semaphoreSet->LastSemctlTime();
946 if (user_memcpy(args.buf, &sem, sizeof(struct semid_ds))
947 < B_OK) {
948 TRACE_ERROR(("xsi_semctl: user_memcpy failed\n"));
949 result = B_BAD_ADDRESS;
950 }
951 }
952 break;
953 }
954
955 case IPC_SET: {
956 if (!semaphoreSet->HasPermission()) {
957 TRACE(("xsi_semctl: calling process has not "
958 "permission on semaphore %d, key %d\n",
959 semaphoreSet->ID(), (int)semaphoreSet->IpcKey()));
960 result = EACCES;
961 } else {
962 struct semid_ds sem;
963 if (user_memcpy(&sem, args.buf, sizeof(struct semid_ds))
964 != B_OK) {
965 TRACE_ERROR(("xsi_semctl: user_memcpy failed\n"));
966 result = B_BAD_ADDRESS;
967 } else
968 semaphoreSet->DoIpcSet(&sem);
969 }
970 break;
971 }
972
973 case IPC_RMID: {
974 // If this was the command, we are still holding
975 // the semaphore set hash table lock along with the
976 // ipc hash table lock and the semaphore set lock
977 // itself, this way we are sure there is not
978 // one waiting in the queue of the mutex.
979 if (!semaphoreSet->HasPermission()) {
980 TRACE(("xsi_semctl: calling process has not "
981 "permission on semaphore %d, key %d\n",
982 semaphoreSet->ID(), (int)semaphoreSet->IpcKey()));
983 return EACCES;
984 }
985 key_t key = semaphoreSet->IpcKey();
986 Ipc *ipcKey = NULL;
987 if (key != -1) {
988 ipcKey = sIpcHashTable.Lookup(key);
989 sIpcHashTable.Remove(ipcKey);
990 }
991 sSemaphoreHashTable.Remove(semaphoreSet);
992 // Wake up of threads waiting on this set
993 // happens in the destructor
994 if (key != -1)
995 delete ipcKey;
996 atomic_add(&sXsiSemaphoreCount, -semaphoreSet->NumberOfSemaphores());
997 atomic_add(&sXsiSemaphoreSetCount, -1);
998 // Remove any sem_undo request
999 while (struct sem_undo *entry
1000 = semaphoreSet->GetUndoList().RemoveHead()) {
1001 MutexLocker _(entry->team->xsi_sem_context->lock);
1002 entry->team->xsi_sem_context->undo_list.Remove(entry);
1003 delete entry;
1004 }
1005
1006 setLocker.Detach();
1007 delete semaphoreSet;
1008 return 0;
1009 }
1010
1011 default:
1012 TRACE_ERROR(("xsi_semctl: command %d not valid\n", command));
1013 result = EINVAL;
1014 }
1015
1016 return result;
1017 }
1018
1019
1020 status_t
_user_xsi_semop(int semaphoreID,struct sembuf * ops,size_t numOps)1021 _user_xsi_semop(int semaphoreID, struct sembuf *ops, size_t numOps)
1022 {
1023 TRACE(("xsi_semop: semaphoreID = %d, ops = %p, numOps = %ld\n",
1024 semaphoreID, ops, numOps));
1025
1026 if (!IS_USER_ADDRESS(ops)) {
1027 TRACE(("xsi_semop: sembuf address is not valid\n"));
1028 return B_BAD_ADDRESS;
1029 }
1030 if (numOps < 0 || numOps >= MAX_XSI_SEMS_PER_TEAM) {
1031 TRACE(("xsi_semop: numOps out of range\n"));
1032 return EINVAL;
1033 }
1034
1035 MutexLocker setHashLocker(sXsiSemaphoreSetLock);
1036 XsiSemaphoreSet *semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreID);
1037 if (semaphoreSet == NULL) {
1038 TRACE(("xsi_semop: semaphore set id %d not valid\n",
1039 semaphoreID));
1040 return EINVAL;
1041 }
1042 MutexLocker setLocker(semaphoreSet->Lock());
1043 setHashLocker.Unlock();
1044
1045 BStackOrHeapArray<struct sembuf, 16> operations(numOps);
1046 if (!operations.IsValid()) {
1047 TRACE_ERROR(("xsi_semop: failed to allocate sembuf struct\n"));
1048 return B_NO_MEMORY;
1049 }
1050
1051 if (user_memcpy(operations, ops,
1052 (sizeof(struct sembuf) * numOps)) != B_OK) {
1053 TRACE_ERROR(("xsi_semop: user_memcpy failed\n"));
1054 return B_BAD_ADDRESS;
1055 }
1056
1057 // We won't do partial request, that is operations
1058 // only on some sempahores belonging to the set and then
1059 // going to sleep. If we must wait on a semaphore, we undo
1060 // all the operations already done and go to sleep, otherwise
1061 // we may caused some unwanted deadlock among threads
1062 // fighting for the same set.
1063 bool notDone = true;
1064 status_t result = 0;
1065 while (notDone) {
1066 XsiSemaphore *semaphore = NULL;
1067 const ushort numberOfSemaphores = semaphoreSet->NumberOfSemaphores();
1068 bool goToSleep = false;
1069
1070 uint32 i = 0;
1071 for (; i < numOps; i++) {
1072 ushort semaphoreNumber = operations[i].sem_num;
1073 if (semaphoreNumber >= numberOfSemaphores) {
1074 TRACE(("xsi_semop: %" B_PRIu32 " invalid semaphore number"
1075 "\n", i));
1076 result = EINVAL;
1077 break;
1078 }
1079 semaphore = semaphoreSet->Semaphore(semaphoreNumber);
1080 unsigned short value = semaphore->Value();
1081 short operation = operations[i].sem_op;
1082 TRACE(("xsi_semop: semaphoreNumber = %d, value = %d\n",
1083 semaphoreNumber, value));
1084 if (operation < 0) {
1085 if (semaphore->Add(operation)) {
1086 if (operations[i].sem_flg & IPC_NOWAIT)
1087 result = EAGAIN;
1088 else
1089 goToSleep = true;
1090 break;
1091 }
1092 } else if (operation == 0) {
1093 if (value == 0)
1094 continue;
1095 else if (operations[i].sem_flg & IPC_NOWAIT) {
1096 result = EAGAIN;
1097 break;
1098 } else {
1099 goToSleep = true;
1100 break;
1101 }
1102 } else {
1103 // Operation must be greater than zero,
1104 // just add the value and continue
1105 semaphore->Add(operation);
1106 }
1107 }
1108
1109 // Either we have to wait or an error occured
1110 if (goToSleep || result != 0) {
1111 // Undo all previously done operations
1112 for (uint32 j = 0; j < i; j++) {
1113 ushort semaphoreNumber = operations[j].sem_num;
1114 semaphore = semaphoreSet->Semaphore(semaphoreNumber);
1115 short operation = operations[j].sem_op;
1116 if (operation != 0)
1117 semaphore->Revert(operation);
1118 }
1119 if (result != 0)
1120 return result;
1121
1122 // We have to wait: first enqueue the thread
1123 // in the appropriate set waiting list, then
1124 // unlock the set itself and block the thread.
1125 bool waitOnZero = true;
1126 if (operations[i].sem_op != 0)
1127 waitOnZero = false;
1128
1129 ConditionVariableEntry queueEntry;
1130 semaphore->Enqueue(&queueEntry, waitOnZero);
1131
1132 const uint32 sequenceNumber = semaphoreSet->SequenceNumber();
1133
1134 TRACE(("xsi_semop: thread %d going to sleep\n", (int)thread->id));
1135 setLocker.Unlock();
1136 semaphoreSet = NULL;
1137 semaphore = NULL;
1138 result = queueEntry.Wait(B_CAN_INTERRUPT);
1139 TRACE(("xsi_semop: thread %d back to life\n", (int)thread->id));
1140
1141 // We are back to life. Find out why!
1142 // Make sure the set hasn't been deleted or worst yet replaced.
1143 setHashLocker.Lock();
1144 semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreID);
1145 if (result == EIDRM || semaphoreSet == NULL || (semaphoreSet != NULL
1146 && sequenceNumber != semaphoreSet->SequenceNumber())) {
1147 TRACE(("xsi_semop: semaphore set id %d (sequence = "
1148 "%" B_PRIu32 ") got destroyed\n", semaphoreID,
1149 sequenceNumber));
1150 notDone = false;
1151 result = EIDRM;
1152 } else if (result == B_INTERRUPTED) {
1153 TRACE(("xsi_semop: thread %d got interrupted while "
1154 "waiting on semaphore set id %d\n", (int)thread_get_current_thread_id(),
1155 semaphoreID));
1156 XsiSemaphore::Dequeue(&queueEntry);
1157 result = EINTR;
1158 notDone = false;
1159 } else {
1160 setLocker.Lock();
1161 setHashLocker.Unlock();
1162 }
1163 } else {
1164 // everything worked like a charm (so far)
1165 notDone = false;
1166 TRACE(("xsi_semop: semaphore acquired succesfully\n"));
1167 // We acquired the semaphore, now records the sem_undo
1168 // requests
1169 for (uint32 i = 0; i < numOps; i++) {
1170 if ((operations[i].sem_flg & SEM_UNDO) == 0)
1171 continue;
1172
1173 ushort semaphoreNumber = operations[i].sem_num;
1174 XsiSemaphore *semaphore = semaphoreSet->Semaphore(semaphoreNumber);
1175 short operation = operations[i].sem_op;
1176
1177 if (semaphoreSet->RecordUndo(semaphoreNumber, operation) != B_OK) {
1178 // Unlikely scenario, but we might get here.
1179 // Undo everything!
1180 // Start with semaphore operations
1181 for (uint32 j = 0; j < numOps; j++) {
1182 ushort semaphoreNumber = operations[j].sem_num;
1183 semaphore = semaphoreSet->Semaphore(semaphoreNumber);
1184 short operation = operations[j].sem_op;
1185 if (operation != 0)
1186 semaphore->Revert(operation);
1187 }
1188 // Remove all previously registered sem_undo request
1189 for (uint32 j = 0; j < i; j++) {
1190 if (operations[j].sem_flg & SEM_UNDO) {
1191 semaphoreSet->RevertUndo(operations[j].sem_num,
1192 operations[j].sem_op);
1193 }
1194 }
1195 result = ENOSPC;
1196 }
1197 }
1198 }
1199 }
1200
1201 // We did it. Set the pid of all semaphores used
1202 if (result == 0) {
1203 for (uint32 i = 0; i < numOps; i++) {
1204 ushort semaphoreNumber = operations[i].sem_num;
1205 XsiSemaphore *semaphore = semaphoreSet->Semaphore(semaphoreNumber);
1206 semaphore->SetPid(getpid());
1207 }
1208 semaphoreSet->SetLastSemopTime();
1209 }
1210 return result;
1211 }
1212