19
19
20
20
mod gpt;
21
21
22
- use alloc:: boxed:: Box ;
23
22
use gpt:: Gpt ;
24
23
25
24
use core:: mem:: MaybeUninit ;
@@ -32,34 +31,92 @@ use crate::fs::devfs::install_device;
32
31
use crate :: fs:: { FileSystem , Result } ;
33
32
34
33
use crate :: fs:: ext2:: Ext2 ;
34
+ use crate :: mem:: paging:: * ;
35
35
use crate :: utils:: sync:: Mutex ;
36
36
37
- use super :: cache:: { Cache , Cacheable } ;
37
+ use super :: cache:: { Cache , CacheArc , CacheItem , Cacheable } ;
38
38
use super :: devfs:: { alloc_device_marker, Device } ;
39
39
use super :: inode:: INodeInterface ;
40
40
41
- type CachedBlockKey = ( usize , usize ) ; // (block device pointer, block)
41
+ type PageCacheKey = ( usize , usize ) ; // (block device pointer, offset)
42
+ type PageCacheItem = CacheArc < CacheItem < PageCacheKey , CachedPage > > ;
42
43
43
- struct CachedBlock {
44
+ struct CachedPage {
44
45
device : Weak < dyn CachedAccess > ,
45
- block : usize ,
46
- buffer : Box < [ u8 ] > ,
46
+ offset : usize ,
47
+ page : PhysFrame ,
47
48
}
48
49
49
- impl CachedBlock {
50
- fn make_key ( device : Weak < dyn CachedAccess > , block : usize ) -> CachedBlockKey {
51
- ( device. as_ptr ( ) as * const u8 as usize , block)
50
+ impl CachedPage {
51
+ fn new ( device : Weak < dyn CachedAccess > , offset : usize ) -> Self {
52
+ Self {
53
+ device,
54
+ offset,
55
+ page : FRAME_ALLOCATOR
56
+ . allocate_frame ( )
57
+ . expect ( "page_cache: out of memory" ) ,
58
+ }
59
+ }
60
+
61
+ fn data_mut ( & self ) -> & mut [ MaybeUninit < u8 > ] {
62
+ let data_ptr = self
63
+ . page
64
+ . start_address ( )
65
+ . as_hhdm_virt ( )
66
+ . as_mut_ptr :: < MaybeUninit < u8 > > ( ) ;
67
+
68
+ // SAFETY: It is safe to create a slice of MaybeUninit<T> because it has the same
69
+ // size and alignment as T.
70
+ unsafe { core:: slice:: from_raw_parts_mut ( data_ptr, Size4KiB :: SIZE as usize ) }
71
+ }
72
+
73
+ fn make_key ( device : Weak < dyn CachedAccess > , offset : usize ) -> PageCacheKey {
74
+ ( device. as_ptr ( ) as * const u8 as usize , offset)
52
75
}
53
76
}
54
77
55
- impl Cacheable < CachedBlockKey > for CachedBlock {
56
- fn cache_key ( & self ) -> CachedBlockKey {
57
- Self :: make_key ( self . device . clone ( ) , self . block )
78
+ impl Cacheable < PageCacheKey > for CachedPage {
79
+ fn cache_key ( & self ) -> PageCacheKey {
80
+ Self :: make_key ( self . device . clone ( ) , self . offset )
58
81
}
59
82
}
60
83
61
84
lazy_static:: lazy_static! {
62
- static ref BLOCK_CACHE : Arc <Cache <CachedBlockKey , CachedBlock >> = Cache :: new( ) ;
85
+ static ref PAGE_CACHE : Arc <Cache <PageCacheKey , CachedPage >> = Cache :: new( ) ;
86
+ }
87
+
88
+ impl Cache < PageCacheKey , CachedPage > {
89
+ /// Returns the cached page at the given offset, if not present, it will be allocated,
90
+ /// initialized with the data on the disk and placed in the page cache.
91
+ ///
92
+ /// ## Arguments
93
+ ///
94
+ /// * `device` - The device to get the page from.
95
+ ///
96
+ /// * `offset` - The offset in bytes to the data. This will be rounded down to
97
+ /// the nearest page boundary.
98
+ pub fn get_page ( & self , device : Weak < dyn CachedAccess > , offset : usize ) -> PageCacheItem {
99
+ let cache_offset = offset / Size4KiB :: SIZE as usize ;
100
+ let cache_key = CachedPage :: make_key ( device. clone ( ) , cache_offset) ;
101
+
102
+ if let Some ( page) = PAGE_CACHE . get ( cache_key) {
103
+ return page;
104
+ }
105
+
106
+ let page = CachedPage :: new ( device. clone ( ) , offset) ;
107
+ let device = device. upgrade ( ) . expect ( "page_cache: device dropped" ) ;
108
+
109
+ let aligned_offset = align_down ( offset as u64 , Size4KiB :: SIZE ) as usize ;
110
+
111
+ device
112
+ // FIXME(perf,mem): internally read_block makes use of the DMA API (cc drivers::nvme::dma), which in turn
113
+ // allocates another frame in order to make sure the destination buffer is DMA capable. In this
114
+ // case, this is not required since we have already allocated a DMA capable frame.
115
+ . read_block ( aligned_offset / device. block_size ( ) , page. data_mut ( ) )
116
+ . expect ( "page_cache: failed to read block" ) ;
117
+
118
+ PAGE_CACHE . make_item_cached ( page)
119
+ }
63
120
}
64
121
65
122
pub trait BlockDeviceInterface : Send + Sync {
@@ -72,44 +129,23 @@ pub trait BlockDeviceInterface: Send + Sync {
72
129
pub trait CachedAccess : BlockDeviceInterface {
73
130
fn sref ( & self ) -> Weak < dyn CachedAccess > ;
74
131
75
- fn read ( & self , offset : usize , dest : & mut [ MaybeUninit < u8 > ] ) -> Option < usize > {
76
- let mut progress = 0 ;
77
- let block_size = self . block_size ( ) ;
78
-
79
- while progress < dest. len ( ) {
80
- let block = ( offset + progress) / block_size;
81
- let loc = ( offset + progress) % block_size;
132
+ fn read ( & self , mut offset : usize , dest : & mut [ MaybeUninit < u8 > ] ) -> Option < usize > {
133
+ let mut loc = 0 ;
82
134
83
- let mut chunk = dest. len ( ) - progress;
135
+ while loc < dest. len ( ) {
136
+ let page = PAGE_CACHE . get_page ( self . sref ( ) , offset) ;
84
137
85
- if chunk > ( block_size - loc) {
86
- chunk = block_size - loc;
87
- }
138
+ let page_offset = offset % Size4KiB :: SIZE as usize ;
139
+ let size = core:: cmp:: min ( Size4KiB :: SIZE as usize - page_offset, dest. len ( ) - loc) ;
88
140
89
- let key = CachedBlock :: make_key ( self . sref ( ) , block) ;
90
-
91
- if let Some ( cached) = BLOCK_CACHE . get ( key) {
92
- MaybeUninit :: write_slice (
93
- & mut dest[ progress..( progress + chunk) ] ,
94
- & cached. buffer [ loc..loc + chunk] ,
95
- ) ;
96
- } else {
97
- let mut buffer = Box :: < [ u8 ] > :: new_uninit_slice ( block_size) ;
98
-
99
- self . read_block ( block, MaybeUninit :: slice_as_bytes_mut ( & mut buffer) ) ?;
100
- dest[ progress..( progress + chunk) ] . copy_from_slice ( & buffer[ loc..loc + chunk] ) ;
101
-
102
- BLOCK_CACHE . make_item_cached ( CachedBlock {
103
- device : self . sref ( ) ,
104
- block,
105
- buffer : unsafe { buffer. assume_init ( ) } ,
106
- } ) ;
107
- }
141
+ let data = & page. data_mut ( ) [ page_offset..page_offset + size] ;
142
+ dest[ loc..loc + size] . copy_from_slice ( data) ;
108
143
109
- progress += chunk;
144
+ loc += size;
145
+ offset += align_down ( offset as u64 + Size4KiB :: SIZE , Size4KiB :: SIZE ) as usize ;
110
146
}
111
147
112
- Some ( progress )
148
+ Some ( loc )
113
149
}
114
150
}
115
151
0 commit comments