Kernel/VFS - Better error reporting in open
[tpg/acess2.git] / KernelLand / Kernel / vfs / select.c
1 /*
2  * Acess2 VFS
3  * - By thePowersGang (John Hodge)
4  * 
5  * select.c
6  * - Implements the select() system call (and supporting code)
7  *
8  * TODO: Remove malloc for read/write queues
9  */
10 #define DEBUG   0
11 #include <acess.h>
12 #include "vfs.h"
13 #include "vfs_int.h"
14 #include "vfs_ext.h"
15 #include <semaphore.h>
16 #include <threads.h>
17 #include <events.h>
18 #include <timers.h>
19
20 // === CONSTANTS ===
21 #define NUM_THREADS_PER_ALLOC   4
22
23 // === TYPES ===
24 typedef struct sVFS_SelectListEnt       tVFS_SelectListEnt;
25
26 // === STRUCTURES ===
27 struct sVFS_SelectListEnt
28 {
29         tVFS_SelectListEnt      *Next;
30         tThread *Threads[NUM_THREADS_PER_ALLOC];
31 };
32
33 // NOTE: Typedef is in vfs.h
34 struct sVFS_SelectList
35 {
36         tMutex  Lock;
37         tVFS_SelectListEnt      FirstEnt;
38 };
39
40 // === PROTOTYPES ===
41 // int  VFS_SelectNode(tVFS_Node *Node, enum eVFS_SelectTypes Type, tTime *Timeout);
42 // int  VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, Uint32 ExtraEvents, BOOL IsKernel);
43 // int  VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull);
44 // int  VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable);
45 // int  VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState);
46  int    VFS_int_Select_GetType(int Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed);
47  int    VFS_int_Select_Register(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel);
48  int    VFS_int_Select_Deregister(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel);
49  int    VFS_int_Select_AddThread(tVFS_SelectList *List, tThread *Thread, int MaxAllowed);
50 void    VFS_int_Select_RemThread(tVFS_SelectList *List, tThread *Thread);
51 void    VFS_int_Select_SignalAll(tVFS_SelectList *List);
52
53 // === GLOBALS ===
54
55 // === FUNCTIONS ===
56 int VFS_SelectNode(tVFS_Node *Node, int TypeFlags, tTime *Timeout, const char *Name)
57 {
58         tThread * const thisthread = Proc_GetCurThread();
59          int    ret;
60         
61         ENTER("pNode iTypeFlags pTimeout sName", Node, TypeFlags, Timeout, Name);
62         
63         // Initialise
64         ret = 0;
65         for( int type = 0; type < 3; type ++ )
66         {
67                 tVFS_SelectList **list;
68                  int    *flag, wanted, maxAllowed;
69                 if( !(TypeFlags & (1 << type)) )        continue;
70                 if( VFS_int_Select_GetType(type, Node, &list, &flag, &wanted, &maxAllowed) ) {
71                         LEAVE('i', -1);
72                         return -1;
73                 }
74         
75                 // Alloc if needed
76                 if( !*list )    *list = calloc(1, sizeof(tVFS_SelectList));
77         
78                 VFS_int_Select_AddThread(*list, thisthread, maxAllowed);
79                 if( *flag == wanted )
80                 {
81                         ret |= (1 << type);
82                 }
83         }
84
85         // Wait for things      
86         if( ret )
87         {
88                 // Skip wait, conditions already met
89                 LOG("ret = %i, skipping wait", ret);
90         }
91         else if( !Timeout )
92         {
93                 LOG("Semaphore_Wait()");
94                 Threads_WaitEvents( THREAD_EVENT_VFS|THREAD_EVENT_SIGNAL );
95         }
96         else if( *Timeout > 0 )
97         {
98                 tTimer *t = Time_AllocateTimer(NULL, NULL);
99                 // Clear timer event
100                 Threads_ClearEvent( THREAD_EVENT_TIMER );
101                 LOG("Timeout %lli ms", *Timeout);
102                 Time_ScheduleTimer( t, *Timeout );
103                 // Wait for the timer or a VFS event
104                 Threads_WaitEvents( THREAD_EVENT_VFS|THREAD_EVENT_TIMER|THREAD_EVENT_SIGNAL );
105                 Time_FreeTimer(t);
106         }
107         
108         // Get return value
109         ret = 0;
110         for( int type = 0; type < 3; type ++ )
111         {
112                 tVFS_SelectList **list;
113                  int    *flag, wanted, maxAllowed;
114                 if( !(TypeFlags & (1 << type)) )        continue;
115                 VFS_int_Select_GetType(type, Node, &list, &flag, &wanted, &maxAllowed);
116                 LOG("VFS_int_Select_RemThread() for %i", type);
117                 ASSERT(*list);
118                 VFS_int_Select_RemThread(*list, thisthread);
119                 ret = ret || *flag == wanted;
120         }
121
122         Threads_ClearEvent( THREAD_EVENT_VFS );
123         Threads_ClearEvent( THREAD_EVENT_TIMER );
124         
125         LEAVE('i', ret);
126         return ret;
127 }
128
129 int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, Uint32 ExtraEvents, BOOL IsKernel)
130 {
131         tThread *thisthread = Proc_GetCurThread();
132          int    ret;
133         
134         ENTER("iMaxHandle pReadHandles pWriteHandles pErrHandles pTimeout xExtraEvents bIsKernel",
135                 MaxHandle, ReadHandles, WriteHandles, ErrHandles, Timeout, ExtraEvents, IsKernel);
136         
137         // Notes: The idea is to make sure we only enter wait (Threads_WaitEvents)
138         // if we are going to be woken up (either by an event at a later time,
139         // or by an event that happened while or before we were registering).
140         // Hence, register must happen _before_ we check the state flag
141         // (See VFS_int_Select_Register), that way either we pick up the flag,
142         // or the semaphore is incremeneted (or both, but never none)
143         
144         // Register with nodes
145         if( ReadHandles )
146                 LOG(" - ReadHandles[0] = %x", ReadHandles->flags[0]);
147         ret  = VFS_int_Select_Register(thisthread, MaxHandle, ReadHandles, 0, IsKernel);
148         ret += VFS_int_Select_Register(thisthread, MaxHandle, WriteHandles, 1, IsKernel);
149         ret += VFS_int_Select_Register(thisthread, MaxHandle, ErrHandles, 2, IsKernel);
150         
151         LOG("Register ret = %i", ret);
152         
153         // If there were events waiting, de-register and return
154         if( ret > 0 )
155         {
156                 LOG(" - ReadHandles[0] = %x", ReadHandles->flags[0]);
157                 ret  = VFS_int_Select_Deregister(thisthread, MaxHandle, ReadHandles, 0, IsKernel);
158                 ret += VFS_int_Select_Deregister(thisthread, MaxHandle, WriteHandles, 1, IsKernel);
159                 ret += VFS_int_Select_Deregister(thisthread, MaxHandle, ErrHandles, 2, IsKernel);
160                 LOG(" - ReadHandles[0] = %x", ReadHandles->flags[0]);
161                 LEAVE('i', ret);
162                 return ret;
163         }
164
165         // Wait for things      
166         if( !Timeout )
167         {
168                 LOG("Waiting for VFS/SIGNAL events (Plus 0x%x)", ExtraEvents);
169                 // TODO: Actual timeout
170                 Threads_WaitEvents( THREAD_EVENT_VFS|THREAD_EVENT_SIGNAL|ExtraEvents );
171         }
172         else if( *Timeout > 0 )
173         {
174                 tTimer *t = Time_AllocateTimer(NULL, NULL);
175                 // Clear timer event
176                 Threads_ClearEvent( THREAD_EVENT_TIMER );
177                 // TODO: Convert *Timeout?
178                 LOG("Timeout %lli ms", *Timeout);
179                 Time_ScheduleTimer( t, *Timeout );
180                 // Wait for the timer or a VFS event
181                 Threads_WaitEvents( THREAD_EVENT_VFS|THREAD_EVENT_TIMER|ExtraEvents );
182                 Time_FreeTimer(t);
183         }
184         // Fill output (modify *Handles)
185         LOG("De-registering");
186         // - Also, de-register
187         ret  = VFS_int_Select_Deregister(thisthread, MaxHandle, ReadHandles, 0, IsKernel);
188         ret += VFS_int_Select_Deregister(thisthread, MaxHandle, WriteHandles, 1, IsKernel);
189         ret += VFS_int_Select_Deregister(thisthread, MaxHandle, ErrHandles, 2, IsKernel);
190         
191         Threads_ClearEvent( THREAD_EVENT_VFS );
192         Threads_ClearEvent( THREAD_EVENT_TIMER );
193         
194         LEAVE('i', ret);
195         return ret;
196 }
197
198 // Mark a node as having data ready for reading
199 int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable)
200 {
201         ASSERTR(Node, 1);
202         ENTER("pNode bIsDataAvaliable", Node, IsDataAvaliable);
203         Node->DataAvaliable = !!IsDataAvaliable;
204         if( Node->DataAvaliable )
205                 VFS_int_Select_SignalAll(Node->ReadThreads);
206         LEAVE('i', 0);
207         return 0;
208 }
209
210 // Mark a node as having a full buffer
211 int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull)
212 {
213         ASSERTR(Node, 1);
214         ENTER("pNode bIsBufferFull", Node, IsBufferFull);
215         Node->BufferFull = !!IsBufferFull;
216         if( !Node->BufferFull )
217                 VFS_int_Select_SignalAll(Node->WriteThreads);
218         LEAVE('i', 0);
219         return 0;
220 }
221
222 // Mark a node as errored
223 int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState)
224 {
225         ASSERTR(Node, 1);
226         ENTER("pNode bIsErrorState", Node, IsErrorState);
227         Node->ErrorOccurred = !!IsErrorState;
228         if( Node->ErrorOccurred )
229                 VFS_int_Select_SignalAll(Node->ErrorThreads);
230         LEAVE('i', 0);
231         return 0;
232 }
233
234 // --- Internal ---
235 int VFS_int_Select_GetType(int Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed)
236 {
237         // Get the type of the listen
238         switch(Type)
239         {
240         case 0: // Read
241                 if(List)        *List = &Node->ReadThreads;
242                 if(Flag)        *Flag = &Node->DataAvaliable;
243                 if(WantedFlag)  *WantedFlag = 1;
244                 if(MaxAllowed)  *MaxAllowed = 1;        // Max of 1 for read
245                 break;
246         case 1: // Write
247                 if(List)        *List = &Node->WriteThreads;
248                 if(Flag)        *Flag = &Node->BufferFull;
249                 if(WantedFlag)  *WantedFlag = 0;
250                 if(MaxAllowed)  *MaxAllowed = 1;        // Max of 1 for write
251                 break;
252         case 2: // Error
253                 if(List)        *List = &Node->ErrorThreads;
254                 if(Flag)        *Flag = &Node->ErrorOccurred;
255                 if(WantedFlag)  *WantedFlag = 1;
256                 if(MaxAllowed)  *MaxAllowed = -1;       // No max for error listeners
257                 break;
258         default:
259                 Log_Error("VFS", "VFS_int_Select_GetType: BUG CHECK, Unknown Type %i", Type);
260                 return 1;
261         }
262         return 0;
263 }
264
265 /**
266  * \return Number of files with an action
267  */
268 int VFS_int_Select_Register(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
269 {
270          int    i, numFlagged = 0;
271         tVFS_SelectList **list;
272          int    *flag, wantedFlagValue;
273          int    maxAllowed;
274         
275         if( !Handles )  return 0;
276         
277         ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
278         
279         for( i = 0; i < MaxHandle; i ++ )
280         {
281                 tVFS_Handle     *handle;
282                 
283                 // Is the descriptor set
284                 if( !FD_ISSET(i, Handles) )     continue;
285                 LOG("FD #%i", i);
286                 
287                 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
288                 // Is the handle valid?
289                 if( !handle || !handle->Node )
290                 {
291                         if( Type == 2 ) {       // Bad FD counts as an error
292                                 numFlagged ++;
293                         }
294                         else {
295                                 FD_CLR(i, Handles);
296                         }
297                         continue;
298                 }
299         
300                 // Get the type of the listen
301                 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, &maxAllowed) ) {
302                         LEAVE('i', 0);
303                         return 0;
304                 }
305                 
306                 // Alloc if needed
307                 if( !*list ) {
308                         *list = calloc(1, sizeof(tVFS_SelectList));
309                 }
310                 
311                 // Register
312                 if( VFS_int_Select_AddThread(*list, Thread, maxAllowed ) )
313                 {
314                         // Oops, error (or just no space)
315                         FD_CLR(i, Handles);
316                 }
317                 
318                 // Check for the flag
319                 if( !!*flag == !!wantedFlagValue ) {
320                         LOG(" %i == want %i", !!*flag, !!wantedFlagValue);
321                         numFlagged ++;
322                 }
323         }
324         
325         LEAVE('i', numFlagged);
326         
327         return numFlagged;
328 }
329 /**
330  * \return Number of files with an action
331  */
332 int VFS_int_Select_Deregister(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
333 {
334          int    i, numFlagged = 0;
335         tVFS_SelectList **list;
336          int    *flag, wantedFlagValue;
337         
338         if( !Handles )  return 0;
339         
340         ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
341         
342         for( i = 0; i < MaxHandle; i ++ )
343         {
344                 tVFS_Handle     *handle;
345                 
346                 // Is the descriptor set
347                 if( !FD_ISSET(i, Handles) )     continue;
348                 LOG("FD #%i", i);
349                 
350                 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
351                 // Is the handle valid?
352                 if( !handle || !handle->Node )
353                 {
354                         if( Type == 2 ) {       // Bad FD counts as an error
355                                 numFlagged ++;
356                         }
357                         else {
358                                 FD_CLR(i, Handles);
359                         }
360                         continue;
361                 }
362         
363                 // Get the type of the listen
364                 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, NULL) ) {
365                         LEAVE('i', 0);
366                         return 0;
367                 }
368                 
369                 // Remove
370                 VFS_int_Select_RemThread(*list, Thread );
371                 
372                 // Check for the flag
373                 if( !!*flag == !!wantedFlagValue ) {
374                         numFlagged ++;
375                         LOG(" %i == want %i", !!*flag, !!wantedFlagValue);
376                         FD_SET(i, Handles);
377                 }
378                 else {
379                         FD_CLR(i, Handles);
380                 }
381         }
382         
383         LEAVE('i', numFlagged);
384         
385         return numFlagged;
386 }
387
388 /**
389  * \return Boolean failure
390  */
391 int VFS_int_Select_AddThread(tVFS_SelectList *List, tThread *Thread, int MaxAllowed)
392 {
393          int    i, count = 0;
394         tVFS_SelectListEnt      *block, *prev;
395         
396         ENTER("pList pThread iMaxAllowed", List, Thread, MaxAllowed);
397         
398         // Lock to avoid concurrency issues
399         Mutex_Acquire(&List->Lock);
400         
401         block = &List->FirstEnt;
402         
403         // Look for free space
404         do
405         {
406                 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
407                 {
408                         if( block->Threads[i] == NULL )
409                         {
410                                 block->Threads[i] = Thread;
411                                 Mutex_Release(&List->Lock);
412                                 LEAVE('i', 0);
413                                 return 0;
414                         }
415                         count ++;
416                         if( MaxAllowed && count >= MaxAllowed ) {
417                                 Mutex_Release(&List->Lock);
418                                 LEAVE('i', 1);
419                                 return 1;
420                         }
421                 }
422                 
423                 prev = block;
424                 block = block->Next;
425         } while(block);
426         
427         LOG("New block");
428         
429         // Create new block
430         block = malloc( sizeof(tVFS_SelectListEnt) );
431         if( !block ) {
432                 Log_Warning("VFS", "VFS_int_Select_AddThread: malloc() failed");
433                 Mutex_Release(&List->Lock);
434                 return -1;
435         }
436         block->Next = NULL;
437         block->Threads[0] = Thread;
438         for( i = 1; i < NUM_THREADS_PER_ALLOC; i ++ )
439         {
440                 block->Threads[i] = NULL;
441         }
442         
443         // Add to list
444         prev->Next = block;
445         
446         // Release
447         Mutex_Release(&List->Lock);
448         
449         LEAVE('i', 0);
450         return 0;
451 }
452
453 void VFS_int_Select_RemThread(tVFS_SelectList *List, tThread *Thread)
454 {
455         tVFS_SelectListEnt      *block, *prev = NULL;
456         
457         ENTER("pList pThread", List, Thread);
458         
459         // Lock to avoid concurrency issues
460         Mutex_Acquire(&List->Lock);
461         
462         block = &List->FirstEnt;
463         
464         // Look for the thread
465         do
466         {
467                 for( int i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
468                 {
469                         if( block->Threads[i] == Thread )
470                         {
471                                 block->Threads[i] = NULL;
472                                 
473                                 // Check if this block is empty
474                                 if( block != &List->FirstEnt )
475                                 {
476                                         for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
477                                                 if( block->Threads[i] )
478                                                         break;
479                                         // If empty, free it
480                                         if( i == NUM_THREADS_PER_ALLOC ) {
481                                                 LOG("Deleting block");
482                                                 prev->Next = block->Next;
483                                                 free(block);
484                                         }
485                                         //TODO: If not empty, check if it can be merged downwards
486                                 }
487                                 
488                                 Mutex_Release(&List->Lock);
489                                 LEAVE('-');
490                                 return ;
491                         }
492                 }
493                 
494                 prev = block;
495                 block = block->Next;
496         } while(block);
497         
498         // Not on list, is this an error?
499         
500         Mutex_Release(&List->Lock);
501         
502         LOG("Not on list");
503         LEAVE('-');
504 }
505
506 /**
507  * \brief Signal all threads on a list
508  */
509 void VFS_int_Select_SignalAll(tVFS_SelectList *List)
510 {
511          int    i;
512         tVFS_SelectListEnt      *block;
513         
514         if( !List )     return ;
515         
516         ENTER("pList", List);
517         
518         // Lock to avoid concurrency issues
519         Mutex_Acquire(&List->Lock);
520         
521         block = &List->FirstEnt;
522         
523         // Look for the thread
524         do
525         {
526                 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
527                 {
528                         if( block->Threads[i]  )
529                         {
530                                 LOG("block(%p)->Threads[%i] = %p", block, i, block->Threads[i]);
531                                 Threads_PostEvent( block->Threads[i], THREAD_EVENT_VFS );
532                         }
533                 }
534                 
535                 block = block->Next;
536         } while(block);
537         
538         Mutex_Release(&List->Lock);
539         
540         LEAVE('-');
541 }

UCC git Repository :: git.ucc.asn.au