3 * - By John Hodge (thePowersGang)
6 * - Shared memory "device"
12 #include <memfs_helpers.h>
13 #include <semaphore.h>
15 #define PAGE_COUNT(v) (((v)+(PAGE_SIZE-1))/PAGE_SIZE)
18 #define PAGES_PER_BLOCK 1024
19 typedef struct sSHM_BufferBlock
21 struct sSHM_BufferBlock *Next;
22 tPAddr Pages[PAGES_PER_BLOCK];
26 tMemFS_FileHdr FileHdr;
29 tSHM_BufferBlock FirstBlock;
33 int SHM_Install(char **Arguments);
34 int SHM_Uninstall(void);
35 tSHM_Buffer *SHM_CreateBuffer(const char *Name);
36 bool SHM_AddPages(tSHM_Buffer *Buffer, size_t num);
37 void SHM_DeleteBuffer(tSHM_Buffer *Buffer);
39 int SHM_ReadDir(tVFS_Node *Node, int Id, char Dest[FILENAME_MAX]);
40 tVFS_Node *SHM_FindDir(tVFS_Node *Node, const char *Filename, Uint Flags);
41 tVFS_Node *SHM_MkNod(tVFS_Node *Node, const char *Name, Uint Flags);
42 int SHM_Unlink(tVFS_Node *Node, const char *OldName);
44 void SHM_Reference(tVFS_Node *Node);
45 void SHM_Close(tVFS_Node *Node);
46 off_t SHM_Truncate(tVFS_Node *Node, off_t NewSize);
47 size_t SHM_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags);
48 size_t SHM_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags);
49 int SHM_MMap(struct sVFS_Node *Node, off_t Offset, size_t Length, void *Dest);
52 MODULE_DEFINE(0, 0x0100, SHM, SHM_Install, SHM_Uninstall, NULL);
53 tMemFS_DirHdr gSHM_RootDir = {
54 .FileHdr = {.Name = "SHMRoot"}
56 tVFS_NodeType gSHM_DirNodeType = {
57 .TypeName = "SHM Root",
58 .ReadDir = SHM_ReadDir,
59 .FindDir = SHM_FindDir,
63 tVFS_NodeType gSHM_FileNodeType = {
64 .TypeName = "SHM Buffer",
69 .Truncate = SHM_Truncate,
70 .Reference = SHM_Reference,
72 tDevFS_Driver gSHM_DriverInfo = {
77 .ACLs = &gVFS_ACL_EveryoneRW,
78 .Flags = VFS_FFLAG_DIRECTORY,
79 .Type = &gSHM_DirNodeType
84 int SHM_Install(char **Arguments)
86 MemFS_InitDir(&gSHM_RootDir);
87 DevFS_AddDevice( &gSHM_DriverInfo );
90 int SHM_Uninstall(void)
94 tSHM_Buffer *SHM_CreateBuffer(const char *Name)
96 tSHM_Buffer *ret = calloc(1, sizeof(tSHM_Buffer) + strlen(Name) + 1);
97 MemFS_InitFile(&ret->FileHdr);
98 ret->FileHdr.Name = (const char*)(ret+1);
99 strcpy((char*)ret->FileHdr.Name, Name);
100 ret->Node.ImplPtr = ret;
101 ret->Node.Type = &gSHM_FileNodeType;
102 ret->Node.ReferenceCount = 1;
105 bool SHM_AddPages(tSHM_Buffer *Buffer, size_t num)
107 tSHM_BufferBlock *block = &Buffer->FirstBlock;
108 // Search for final block
109 size_t idx = Buffer->nPages;
110 while( block->Next ) {
112 idx -= PAGES_PER_BLOCK;
114 ASSERTC(idx, <=, PAGES_PER_BLOCK);
116 for( size_t i = 0; i < num; i ++ )
118 if( idx == PAGES_PER_BLOCK )
120 block->Next = calloc(1, sizeof(tSHM_BufferBlock));
122 Log_Warning("SHM", "Out of memory, allocating new buffer block");
128 ASSERT(block->Pages[idx] == 0);
129 block->Pages[idx] = MM_AllocPhys();
130 if( !block->Pages[idx] ) {
131 Log_Warning("SHM", "Out of memory, allocating page");
139 void SHM_DeleteBuffer(tSHM_Buffer *Buffer)
141 ASSERTCR(Buffer->Node.ReferenceCount,==,0,);
143 // TODO: Destroy multi-block nodes
144 ASSERT(Buffer->FirstBlock.Next == NULL);
149 int SHM_ReadDir(tVFS_Node *Node, int Id, char Dest[FILENAME_MAX])
151 return MemFS_ReadDir(&gSHM_RootDir, Id, Dest);
155 * \brief Open a shared memory buffer
157 * \note Opening 'anon' will always succeed, and will create an anonymous mapping
159 tVFS_Node *SHM_FindDir(tVFS_Node *Node, const char *Filename, Uint Flags)
161 if( strcmp(Filename, "anon") == 0 )
163 tSHM_Buffer *ret = SHM_CreateBuffer("");
167 tMemFS_FileHdr *file = MemFS_FindDir(&gSHM_RootDir, Filename);
171 return &((tSHM_Buffer*)file)->Node;
176 * \brief Create a named shared memory file
178 tVFS_Node *SHM_MkNod(tVFS_Node *Node, const char *Name, Uint Flags)
180 if( MemFS_FindDir(&gSHM_RootDir, Name) )
183 tSHM_Buffer *ret = SHM_CreateBuffer(Name);
184 if( !MemFS_Insert(&gSHM_RootDir, &ret->FileHdr) )
186 ret->Node.ReferenceCount = 0;
187 SHM_DeleteBuffer(ret);
195 * \breif Remove a named shared memory buffer (will be deleted when all references drop)
197 int SHM_Unlink(tVFS_Node *Node, const char *OldName)
199 tMemFS_FileHdr *file = MemFS_Remove(&gSHM_RootDir, OldName);
203 tSHM_Buffer *buf = (tSHM_Buffer*)file;
204 if( buf->Node.ReferenceCount == 0 )
206 SHM_DeleteBuffer(buf);
210 // dangling references, let them clean themselves up later
215 void SHM_Reference(tVFS_Node *Node)
217 Node->ReferenceCount ++;
219 void SHM_Close(tVFS_Node *Node)
221 Node->ReferenceCount --;
222 if( Node->ReferenceCount == 0 )
224 // TODO: How to tell if a buffer should be deleted here?
228 off_t SHM_Truncate(tVFS_Node *Node, off_t NewSize)
230 ENTER("pNode XNewSize", Node, NewSize);
231 tSHM_Buffer *buffer = Node->ImplPtr;
232 LOG("Node->Size = 0x%llx", Node->Size);
233 if( PAGE_COUNT(NewSize) != PAGE_COUNT(Node->Size) )
235 int page_difference = PAGE_COUNT(NewSize) - PAGE_COUNT(Node->Size);
236 LOG("page_difference = %i", page_difference);
237 if( page_difference < 0 )
240 // TODO: What if underlying pages are mapped?... should it matter?
246 SHM_AddPages(buffer, page_difference);
251 LOG("Page count hasn't changed");
253 Node->Size = NewSize;
257 size_t SHM_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags)
262 size_t SHM_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags)
264 // TODO: Should first write determine the fixed size of the buffer?
268 int SHM_MMap(struct sVFS_Node *Node, off_t Offset, size_t Length, void *Dest)
270 tSHM_Buffer *buf = Node->ImplPtr;
271 if( Offset > Node->Size ) return 1;
272 if( Offset + Length > Node->Size ) return 1;
274 const int pagecount = (Length + Offset % PAGE_SIZE) / PAGE_SIZE;
275 int pagenum = Offset / PAGE_SIZE;
277 tSHM_BufferBlock *block = &buf->FirstBlock;
278 while( pagenum > PAGES_PER_BLOCK ) {
281 pagenum -= PAGES_PER_BLOCK;
285 for( int i = 0; i < pagecount; i ++ )
287 if( pagenum == PAGES_PER_BLOCK ) {
293 ASSERT(block->Pages[pagenum]);
294 LOG("%p => %i:%P", dst, pagenum, block->Pages[pagenum]);
295 MM_Map(dst, block->Pages[pagenum]);