duskos

dusk os fork
git clone git://git.alexwennerberg.com/duskos
Log | Files | Refs | README | LICENSE

commit c19708929d22fbedb8cf4147d1a087cf7cd10e1d
parent 077c0f357402032abc4978e7f5e20ca310a3d628
Author: Virgil Dupras <hsoft@hardcoded.net>
Date:   Tue,  5 Jul 2022 14:04:31 -0400

fs/fat: use smaller buffers

My first implementation has the whole FAT in memory at once and a buffer large
enough to contain all of the root dirs in memory at once. This commit reduces
this to a single sector buffer.

The primary goal is not to save memory (although it does save some), but to
simplify the caching logic when writes are going to be involved. With the whole
FAT in memory, every change in cluster would involve either detecting dirty
areas or always writing the whole FAT. Those options are either too complicated
or too wasteful.

It's simpler to always access the FAT from disk and to always write the FAT
directly, at sector precision, when changes are made to file allocation.

Diffstat:
Mfs/fs/fatlo.fs | 94++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mfs/tests/fs/fat.fs | 1-
Mfs/xcomp/glue2.fs | 1-
3 files changed, 55 insertions(+), 41 deletions(-)

diff --git a/fs/fs/fatlo.fs b/fs/fs/fatlo.fs @@ -23,7 +23,9 @@ create bpb 0 here (drv@) $18 allot : RootDirSectors BPB_RootEntCnt 32 * BPB_BytsPerSec /mod ( r q ) swap if 1+ then ; : FirstDataSector BPB_RsvdSecCnt BPB_NumFATs BPB_FATSz16 * + RootDirSectors + ; -: FirstSectorOfCluster ( n -- sec ) 1- 1- BPB_SecPerClus * FirstDataSector + ; +: FirstSectorOfCluster ( n -- sec ) + dup << BPB_BytsPerSec BPB_FATSz16 * >= if abort" cluster out of range" then + 1- 1- BPB_SecPerClus * FirstDataSector + ; : FirstRootDirSecNum BPB_RsvdSecCnt BPB_NumFATs BPB_FATSz16 * + ; : ClusterSize BPB_SecPerClus BPB_BytsPerSec * ; : DataSec @@ -31,13 +33,40 @@ create bpb 0 here (drv@) $18 allot : CountOfClusters DataSec BPB_SecPerClus / ; : FAT12? CountOfClusters 4085 < ; -\ read multiple sectors in buf -: readsectors ( sec u buf -- ) - A>r swap >r swap >A begin ( buf ) - A> over (drv@) A+ drvblksz + next ( buf ) drop r>A ; +\ Buffer for either reading FAT sectors or Directory contents. It is one sector +\ in size and knows the number of sequential sector read it has in front of it. +create buf( BPB_BytsPerSec allot +here const )buf +0 value bufsec \ sector number of current buf +0 value bufseccnt \ number of sectors ahead for sequential read +0 value bufcluster \ cluster number of current buf + +\ "cnt" is the number of sectors ahead of "sec" that are available for a +\ seqential read. +: readsector ( sec cnt -- ) to bufseccnt dup to bufsec buf( (drv@) ; + +: FAT12@ ( cluster -- entry ) + dup dup >> + ( cl offset ) BPB_BytsPerSec /mod ( cl secoff sec ) + BPB_RsvdSecCnt + 0 readsector ( cl secoff ) + buf( + w@ ( cl entry ) + swap 1 and if 4 rshift else $fff and then ; +: FAT16@ ( cluster -- entry ) + << ( offset ) BPB_BytsPerSec /mod ( secoff sec ) + BPB_RsvdSecCnt + 0 readsector ( secoff ) + buf( + w@ ; +: FAT@ ( cluster -- entry ) FAT12? if FAT12@ else FAT16@ then ; + +: EOC? ( cluster -- f ) + FAT12? if $ff8 else $fff8 then tuck and = ; -create FAT( BPB_BytsPerSec BPB_FATSz16 * allot -: readFAT BPB_RsvdSecCnt BPB_FATSz16 FAT( readsectors ; +\ Read next sector if a sequential read is available, else return false. +: nextsector? ( -- f ) + bufseccnt if \ still on a sector streak + bufseccnt 1+ bufseccnt 1- readsector 1 + else \ out of sector, try next cluster + bufcluster FAT@ dup EOC? if drop 0 else \ we have another cluster + dup to bufcluster FirstSectorOfCluster BPB_SecPerClus readsector 1 + then then ; 32 const DIRENTRYSZ 11 const FNAMESZ @@ -45,11 +74,6 @@ create FAT( BPB_BytsPerSec BPB_FATSz16 * allot : DIR_Cluster ( direntry -- cluster ) 26 + w@ ; : DIR_FileSize ( direntry -- sz ) 28 + @ ; -\ A buffer where dir entries are copied before we search in them. It's big -\ enough to hold the root dir entries. This means that no directory in the FS -\ can have more than BPB_RootEntCnt entries. -create dirbuf( RootDirSectors BPB_BytsPerSec * allot -here const )dirbuf create fnbuf( FNAMESZ allot here const )fnbuf @@ -62,43 +86,27 @@ here const )fnbuf Ac@+ dup '.' = if ( a c ) 2drop fnbuf( 8 + else upcase swap c!+ then next drop r>A ; -: _findindir ( -- direntry ) - dirbuf( begin ( a ) - dup )dirbuf < while ( a ) +: _ ( -- direntry-or-0 ) + buf( begin ( a ) + dup )buf < while ( a ) fnbuf( over DIR_Name []= not while ( a ) DIRENTRYSZ + repeat - ( success ) else ( not found ) abort" file not found" then ( a ) ; + ( success ) else ( not found ) drop 0 then ( a ) ; +: _findindir ( -- direntry ) + begin + _ ?dup not while nextsector? not if abort" file not found" then + repeat ; \ Search in directory that is currently loaded in dirbuf. \ Return address of directory entry, abort if not found. : findindir ( fname -- direntry ) _tofnbuf _findindir ; \ Make the current dir the root -: readroot FirstRootDirSecNum RootDirSectors dirbuf( readsectors ; - -: EOC? ( cluster -- f ) - FAT12? if $ff8 else $fff8 then tuck and = ; - -\ get cluster following this one in the FAT -: nextcluster ( cluster -- nextcluster ) - FAT12? if - dup dup >> + ( cl off ) - FAT( + w@ ( cl ncl ) swap 1 and if 4 rshift else $fff and then - else - << FAT( + w@ then ; - -: readcluster ( cluster dst -- ) - over << BPB_BytsPerSec BPB_FATSz16 * >= if abort" cluster out of range" then - swap FirstSectorOfCluster ( dst sec ) swap BPB_SecPerClus swap readsectors ; +: readroot 1 to bufcluster FirstRootDirSecNum RootDirSectors readsector ; \ Read specified "direntry" in dirbuf( \ Errors out if it has more entries that BPB_RootEntCnt : readdir ( direntry -- ) - DIR_Cluster ( cluster ) dirbuf( begin ( cluster buf ) - over EOC? not while - 2dup readcluster - ClusterSize + swap nextcluster swap repeat ( cluster buf ) - 2drop ; - + DIR_Cluster dup to bufcluster FirstSectorOfCluster BPB_SecPerClus readsector ; : findpath ( path -- direntry ) A>r fnbufclr fnbuf( >A c@+ >r readroot begin ( a ) @@ -135,6 +143,14 @@ create fcursors( FCursorSize FCURSORCNT * allot0 dup FCUR_cluster0 not if ( found! ) r~ exit then FCursorSize + next abort" out of file cursors!" ; +\ read multiple sectors in buf +: readsectors ( sec u buf -- ) + A>r swap >r swap >A begin ( buf ) + A> over (drv@) A+ drvblksz + next ( buf ) drop r>A ; + +: readcluster ( cluster dst -- ) + swap FirstSectorOfCluster ( dst sec ) swap BPB_SecPerClus swap readsectors ; + \ Open the specified "direntry" into one of the free cursors and return that \ cursor. : openfile ( direntry -- fcursor ) @@ -149,7 +165,7 @@ create fcursors( FCursorSize FCURSORCNT * allot0 dup FCUR_pos over FCUR_size = if drop -1 exit then dup FCUR_pos+ ClusterSize mod over FCUR_buf( + c@ ( fc c ) over FCUR_pos ClusterSize mod not if ( fc c ) \ end of cluster, read next - over FCUR_cluster nextcluster ( fc c cluster ) + over FCUR_cluster FAT@ ( fc c cluster ) dup EOC? if drop else dup 2 < if abort" cluster out of range" then rot 2dup FCUR_cluster! ( c cluster fc ) diff --git a/fs/tests/fs/fat.fs b/fs/tests/fs/fat.fs @@ -8,7 +8,6 @@ : readN ( fcursor n -- ) >r begin dup fat16getc drop next drop ; testbegin \ Tests for fs/boot -readFAT readroot S" tests/fattest" findpath ( dirent ) openfile ( fcursor ) dup fat16getc 'T' #eq diff --git a/fs/xcomp/glue2.fs b/fs/xcomp/glue2.fs @@ -1,5 +1,4 @@ \ Glue code that goes between the filesystem part and boothi -readFAT alias fat16open fopen alias fat16getc fgetc alias fat16close fclose