Kernel - MMap fixed (non-fixed mappings, bad search), SHM mmap only working
authorJohn Hodge <[email protected]>
Mon, 18 Aug 2014 23:50:06 +0000 (07:50 +0800)
committerJohn Hodge <[email protected]>
Mon, 18 Aug 2014 23:50:06 +0000 (07:50 +0800)
KernelLand/Kernel/drv/shm.c
KernelLand/Kernel/include/vfs.h
KernelLand/Kernel/vfs/io.c
KernelLand/Kernel/vfs/mmap.c

index f825a0e..da32141 100644 (file)
@@ -12,6 +12,8 @@
 #include <memfs_helpers.h> 
 #include <semaphore.h>
 
+#define PAGE_COUNT(v)  (((v)+(PAGE_SIZE-1))/PAGE_SIZE)
+
 // === TYPES ===
 #define PAGES_PER_BLOCK        1024
 typedef struct sSHM_BufferBlock
@@ -23,6 +25,7 @@ typedef struct
 {
        tMemFS_FileHdr  FileHdr;
        tVFS_Node       Node;
+       size_t  nPages;
        tSHM_BufferBlock        FirstBlock;
 } tSHM_Buffer;
 
@@ -30,6 +33,7 @@ typedef struct
  int   SHM_Install(char **Arguments);
  int   SHM_Uninstall(void);
 tSHM_Buffer    *SHM_CreateBuffer(const char *Name);
+bool   SHM_AddPages(tSHM_Buffer *Buffer, size_t num);
 void   SHM_DeleteBuffer(tSHM_Buffer *Buffer);
 // - Root directory
  int   SHM_ReadDir(tVFS_Node *Node, int Id, char Dest[FILENAME_MAX]);
@@ -39,9 +43,10 @@ tVFS_Node    *SHM_MkNod(tVFS_Node *Node, const char *Name, Uint Flags);
 // - Buffers
 void   SHM_Reference(tVFS_Node *Node);
 void   SHM_Close(tVFS_Node *Node);
+off_t  SHM_Truncate(tVFS_Node *Node, off_t NewSize);
 size_t SHM_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags);
 size_t SHM_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags);
- int   SHM_MMap(struct sVFS_Node *Node, off_t Offset, int Length, void *Dest);
+ int   SHM_MMap(struct sVFS_Node *Node, off_t Offset, size_t Length, void *Dest);
 
 // === GLOBALS ===
 MODULE_DEFINE(0, 0x0100, SHM, SHM_Install, SHM_Uninstall, NULL);
@@ -61,6 +66,7 @@ tVFS_NodeType gSHM_FileNodeType = {
        .Write = SHM_Write,
        .Close = SHM_Close,
        .MMap  = SHM_MMap,
+       .Truncate = SHM_Truncate,
        .Reference = SHM_Reference,
 };
 tDevFS_Driver  gSHM_DriverInfo = {
@@ -96,6 +102,40 @@ tSHM_Buffer *SHM_CreateBuffer(const char *Name)
        ret->Node.ReferenceCount = 1;
        return ret;
 }
+bool SHM_AddPages(tSHM_Buffer *Buffer, size_t num)
+{
+       tSHM_BufferBlock        *block = &Buffer->FirstBlock;
+       // Search for final block
+       size_t  idx = Buffer->nPages;
+       while( block->Next ) {
+               block = block->Next;
+               idx -= PAGES_PER_BLOCK;
+       }
+       ASSERTC(idx, <=, PAGES_PER_BLOCK);
+       
+       for( size_t i = 0; i < num; i ++ )
+       {
+               if( idx == PAGES_PER_BLOCK )
+               {
+                       block->Next = calloc(1, sizeof(tSHM_BufferBlock));
+                       if(!block->Next) {
+                               Log_Warning("SHM", "Out of memory, allocating new buffer block");
+                               return false;
+                       }
+                       block = block->Next;
+                       idx = 0;
+               }
+               ASSERT(block->Pages[idx] == 0);
+               block->Pages[idx] = MM_AllocPhys();
+               if( !block->Pages[idx] ) {
+                       Log_Warning("SHM", "Out of memory, allocating page");
+                       return false;
+               }
+               Buffer->nPages += 1;
+               idx ++;
+       }
+       return true;
+}
 void SHM_DeleteBuffer(tSHM_Buffer *Buffer)
 {
        ASSERTCR(Buffer->Node.ReferenceCount,==,0,);
@@ -185,6 +225,35 @@ void SHM_Close(tVFS_Node *Node)
                UNIMPLEMENTED();
        }
 }
+off_t SHM_Truncate(tVFS_Node *Node, off_t NewSize)
+{
+       ENTER("pNode XNewSize", Node, NewSize);
+       tSHM_Buffer     *buffer = Node->ImplPtr;
+       LOG("Node->Size = 0x%llx", Node->Size);
+       if( PAGE_COUNT(NewSize) != PAGE_COUNT(Node->Size) )
+       {
+                int    page_difference = PAGE_COUNT(NewSize) - PAGE_COUNT(Node->Size);
+               LOG("page_difference = %i", page_difference);
+               if( page_difference < 0 )
+               {
+                       // Truncate down
+                       // TODO: What if underlying pages are mapped?... should it matter?
+                       UNIMPLEMENTED();
+               }
+               else
+               {
+                       // Truncate up
+                       SHM_AddPages(buffer, page_difference);
+               }
+       }
+       else
+       {
+               LOG("Page count hasn't changed");
+       }
+       Node->Size = NewSize;
+       LEAVE('X', NewSize);
+       return NewSize;
+}
 size_t SHM_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags)
 {
        UNIMPLEMENTED();
@@ -196,9 +265,38 @@ size_t SHM_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffe
        UNIMPLEMENTED();
        return -1;
 }
-int SHM_MMap(struct sVFS_Node *Node, off_t Offset, int Length, void *Dest)
+int SHM_MMap(struct sVFS_Node *Node, off_t Offset, size_t Length, void *Dest)
 {
-       UNIMPLEMENTED();
-       return 1;
+       tSHM_Buffer     *buf = Node->ImplPtr;
+       if( Offset > Node->Size )       return 1;
+       if( Offset + Length > Node->Size )      return 1;
+       
+       const int pagecount = (Length + Offset % PAGE_SIZE) / PAGE_SIZE;
+       int pagenum = Offset / PAGE_SIZE;
+       
+       tSHM_BufferBlock        *block = &buf->FirstBlock;
+       while( pagenum > PAGES_PER_BLOCK ) {
+               block = block->Next;
+               ASSERT(block);
+               pagenum -= PAGES_PER_BLOCK;
+       }
+       
+       tPage *dst = Dest;
+       for( int i = 0; i < pagecount; i ++ )
+       {
+               if( pagenum == PAGES_PER_BLOCK ) {
+                       block = block->Next;
+                       ASSERT(block);
+                       pagenum = 0;
+               }
+               
+               ASSERT(block->Pages[pagenum]);
+               LOG("%p => %i:%P", dst, pagenum, block->Pages[pagenum]);
+               MM_Map(dst, block->Pages[pagenum]);
+               
+               pagenum ++;
+               dst ++;
+       }
+       return 0;
 }
 
index 72c6cbd..b030627 100644 (file)
@@ -313,7 +313,7 @@ struct sVFS_NodeType
         * \return Boolean Failure
         * \note If NULL, the VFS implements it using .Read
         */
-        int    (*MMap)(struct sVFS_Node *Node, off_t Offset, int Length, void *Dest);
+        int    (*MMap)(struct sVFS_Node *Node, off_t Offset, size_t Length, void *Dest);
        
        /**
         * \brief Resize a file
index cb0140a..35c1ebf 100644 (file)
@@ -263,6 +263,7 @@ off_t VFS_Truncate(int FD, off_t Size)
        
        if( !h->Node->Type->Truncate)
        {
+               Log_Notice("VFS", "Nodetype '%s' doesn't have a Truncate method", h->Node->Type->TypeName);
                errno = ENOTIMPL;
                return -1;      
        }
index 9a7fa44..9016d56 100644 (file)
@@ -10,6 +10,7 @@
 #include <vfs.h>
 #include <vfs_ext.h>
 #include <vfs_int.h>
+#include <mm_virt.h>   // MM_USER_MAX
 
 #define MMAP_PAGES_PER_BLOCK   16
 
@@ -27,17 +28,19 @@ struct sVFS_MMapPageBlock
 void   *VFS_MMap_Anon(void *Destination, size_t Length, Uint FlagsSet, Uint FlagsMask);
 int    VFS_MMap_MapPage(tVFS_Node *Node, unsigned int PageNum, tVFS_MMapPageBlock *pb, void *mapping_dest, unsigned int Protection);
 //int  VFS_MUnmap(void *Addr, size_t Length);
+bool   _range_free(const tPage *Base, Uint NumPages);
 
 // === CODE ===
 void *VFS_MMap(void *DestHint, size_t Length, int Protection, int Flags, int FD, Uint64 Offset)
 {
-       ENTER("pDestHint iLength xProtection xFlags xFD XOffset", DestHint, Length, Protection, Flags, FD, Offset);
+       ENTER("pDestHint xLength xProtection xFlags xFD XOffset", DestHint, Length, Protection, Flags, FD, Offset);
 
        if( Flags & MMAP_MAP_ANONYMOUS )
                Offset = (tVAddr)DestHint & 0xFFF;
        
        unsigned int npages = ((Offset & (PAGE_SIZE-1)) + Length + (PAGE_SIZE - 1)) / PAGE_SIZE;
        unsigned int pagenum = Offset / PAGE_SIZE;
+       LOG("npages=%u,pagenum=%u", npages, pagenum);
 
        tVAddr mapping_base = (tVAddr)DestHint;
 
@@ -50,16 +53,38 @@ void *VFS_MMap(void *DestHint, size_t Length, int Protection, int Flags, int FD,
        else
        {
                Log_Warning("VFS", "MMap: TODO Handle non-fixed mappings");
-               if( DestHint == NULL )
+               
+               // Locate a free location in the address space (between brk and MM_USER_MAX)
+               // TODO: Prefer first location after DestHint, but can go below
+               
+               // Search downwards from the top of user memory
+               mapping_base = 0;
+               for( tPage *dst = (tPage*)MM_USER_MAX - npages; dst > (tPage*)PAGE_SIZE; dst -- )
+               {
+                       if( _range_free(dst, npages) ) {
+                               mapping_base = (tVAddr)dst;
+                               break;
+                       }
+               }
+               if( mapping_base == 0 )
                {
-                       // TODO: Locate space for the allocation
-                       Log_Warning("VFS", "Mmap: Handle NULL destination hint");
+                       Log_Warning("VFS", "MMap: Out of address space");
+                       errno = ENOMEM;
                        LEAVE('n');
                        return NULL;
                }
        }
        tPage   *mapping_dest = (void*)(mapping_base & ~(PAGE_SIZE-1));
 
+       if( !_range_free(mapping_dest, npages) )
+       {
+               LOG("Specified range is not free");
+               //errno = EINVAL;
+               //LEAVE('n');
+               //return NULL;
+               Log_Warning("VFS", "MMap: Overwriting/replacing maps at %p+%x", mapping_base, Length);
+       }
+
        // Handle anonymous mappings
        if( Flags & MMAP_MAP_ANONYMOUS )
        {
@@ -80,7 +105,7 @@ void *VFS_MMap(void *DestHint, size_t Length, int Protection, int Flags, int FD,
        // - Sorted list of 16 page blocks
        for( pb = h->Node->MMapInfo; pb; pb_pnp = &pb->Next, pb = pb->Next )
        {
-               if( pb->BaseOffset + MMAP_PAGES_PER_BLOCK <= pagenum )
+               if( pb->BaseOffset + MMAP_PAGES_PER_BLOCK > pagenum )
                        break;
        }
 
@@ -283,5 +308,19 @@ int VFS_MMap_MapPage(tVFS_Node *Node, unsigned int pagenum, tVFS_MMapPageBlock *
 
 int VFS_MUnmap(void *Addr, size_t Length)
 {
+       UNIMPLEMENTED();
        return 0;
 }
+
+bool _range_free(const tPage *Base, Uint NumPages)
+{
+       for( int i = 0; i < NumPages; i ++ )
+       {
+               if( MM_GetPhysAddr(Base + i) )
+               {
+                       // Oh.
+                       return false;
+               }
+       }
+       return true;
+}

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