MOS Source Code
Loading...
Searching...
No Matches
page_cache.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
2
4
7#include "mos/mm/mm.hpp"
8#include "mos/mm/mmstat.hpp"
10#include "mos/syslog/printk.hpp"
11
12#include <algorithm>
13#include <mos/mos_global.h>
14#include <mos_stdlib.hpp>
15#include <mos_string.hpp>
16
23
24static bool do_flush_and_drop_cached_page(const size_t pgoff, phyframe_t *page, _flush_and_drop_data *fdd)
25{
26 // key = page number, value = phyframe_t *, data = _flush_and_drop_data *
27 inode_cache_t *const icache = fdd->icache;
28 const bool drop_page = fdd->should_drop_page;
29
30 // !! TODO
31 // !! currently we have no reliable way to track if a page is dirty
32 // !! so we always flush it
33 // !! this causes performance issues, but it's simple and works for now
34 // if (!page->pagecache.dirty)
35
36 long ret = 0;
37 if (icache->ops->flush_page)
38 ret = icache->ops->flush_page(icache, pgoff, page);
39 else
40 ret = simple_flush_page_discard_data(icache, pgoff, page);
41
42 if (!IS_ERR_VALUE(ret) && drop_page)
43 {
44 // only when the page was successfully flushed
45 icache->pages.remove(pgoff);
47 pmm_unref_one(page);
48 }
49
50 fdd->ret = ret;
51 return ret == 0;
52}
53
54long pagecache_flush_or_drop(inode_cache_t *icache, off_t pgoff, size_t npages, bool drop_page)
55{
56 struct _flush_and_drop_data data = { .icache = icache, .should_drop_page = drop_page, .ret = 0 };
57
58 long ret = 0;
59 for (size_t i = 0; i < npages; i++)
60 {
61 auto ppage = icache->pages.get(pgoff + i);
62 if (!ppage.has_value())
63 continue;
64
65 do_flush_and_drop_cached_page(pgoff + i, *ppage, &data);
66
67 if (data.ret != 0)
68 {
69 ret = data.ret;
70 break;
71 }
72 }
73 return ret;
74}
75
77{
78 struct _flush_and_drop_data data = { .icache = icache, .should_drop_page = drop_page, .ret = 0 };
79 for (const auto &[pgoff, page] : icache->pages)
80 do_flush_and_drop_cached_page(pgoff, page, &data);
81 return data.ret;
82}
83
85{
86 // fast path
87 {
88 const auto page = cache->pages.get(pgoff);
89 if (page)
90 return *page;
91 }
92
93 if (!cache->ops)
94 return -EIO;
95
96 MOS_ASSERT_X(cache->ops && cache->ops->fill_cache, "no page cache ops for inode %p", (void *) cache->owner);
97 const auto newPage = cache->ops->fill_cache(cache, pgoff);
98 if (newPage.isErr())
99 return newPage;
100
102 cache->pages.insert(pgoff, newPage.get());
103 return newPage;
104}
105
110
112{
114 size_t bytes_read = 0;
115 size_t bytes_left = size;
116 while (bytes_left > 0)
117 {
118 // bytes to copy from the current page
119 const size_t inpage_offset = offset % MOS_PAGE_SIZE;
120 const size_t inpage_size = std::min(MOS_PAGE_SIZE - inpage_offset, bytes_left); // in case we're at the end of the file,
121
122 auto page = pagecache_get_page_for_read(icache, offset / MOS_PAGE_SIZE); // the initial page
123 if (page.isErr())
124 {
126 return page.getErr();
127 }
128
129 memcpy((char *) buf + bytes_read, (void *) (phyframe_va(page.get()) + inpage_offset), inpage_size);
130
131 bytes_read += inpage_size;
132 bytes_left -= inpage_size;
133 offset += inpage_size;
134 }
135
137 return bytes_read;
138}
139
140ssize_t vfs_write_pagecache(inode_cache_t *icache, const void *buf, size_t total_size, off_t offset)
141{
143 MOS_ASSERT_X(ops, "no page cache ops for inode %p", (void *) icache->owner);
144
146
147 size_t bytes_written = 0;
148 size_t bytes_left = total_size;
149 while (bytes_left > 0)
150 {
151 // bytes to copy to the current page
152 const size_t inpage_offset = offset % MOS_PAGE_SIZE;
153 const size_t inpage_size = std::min(MOS_PAGE_SIZE - inpage_offset, bytes_left); // in case we're at the end of the file,
154
155 void *private_data;
156 phyframe_t *page;
157 const bool can_write = ops->page_write_begin(icache, offset, inpage_size, &page, &private_data);
158 if (!can_write)
159 {
160 pr_warn("page_write_begin failed");
162 return -EIO;
163 }
164
165 memcpy((char *) (phyframe_va(page) + inpage_offset), (char *) buf + bytes_written, inpage_size);
166 ops->page_write_end(icache, offset, inpage_size, page, private_data);
167
168 bytes_written += inpage_size;
169 bytes_left -= inpage_size;
170 offset += inpage_size;
171 }
172
174 return bytes_written;
175}
#define MOS_ASSERT_X(cond, msg,...)
Definition assert.hpp:15
#define MOS_PAGE_SIZE
Definition autoconf.h:6
void insert(const Key &key, const Value &value)
Definition hashmap.hpp:246
std::optional< Value > get(const Key &key)
Definition hashmap.hpp:307
std::optional< Value > remove(const Key &key)
Definition hashmap.hpp:324
#define phyframe_va(frame)
Definition mm.hpp:80
#define pmm_unref_one(thing)
Definition pmm.hpp:160
static const io_op_t ops
Definition io.cpp:29
#define mmstat_dec1(type)
Definition mmstat.hpp:35
#define mmstat_inc1(type)
Definition mmstat.hpp:26
@ MEM_PAGECACHE
Definition mmstat.hpp:10
#define IS_ERR_VALUE(x)
Definition mos_global.h:137
PtrResult< phyframe_t > pagecache_get_page_for_read(inode_cache_t *cache, off_t pgoff)
Get a page from the page cache.
long pagecache_flush_or_drop(inode_cache_t *icache, off_t pgoff, size_t npages, bool drop_page)
Flush or drop a range of pages from the page cache.
ssize_t vfs_write_pagecache(inode_cache_t *icache, const void *buf, size_t total_size, off_t offset)
long pagecache_flush_or_drop_all(inode_cache_t *icache, bool drop_page)
Flush or drop all pages in the page cache.
PtrResult< phyframe_t > pagecache_get_page_for_write(inode_cache_t *cache, off_t pgoff)
Get a page from the page cache for writing.
static bool do_flush_and_drop_cached_page(const size_t pgoff, phyframe_t *page, _flush_and_drop_data *fdd)
ssize_t vfs_read_pagecache(inode_cache_t *icache, void *buf, size_t size, off_t offset)
static void * memcpy(void *s1, const void *s2, size_t n)
Definition pb_syshdr.h:90
#define pr_warn(fmt,...)
Definition printk.hpp:38
#define mutex_acquire(mutex)
#define mutex_release(mutex)
size_t size
Definition slab.cpp:34
inode_cache_t * icache
PtrResult< phyframe_t >(* fill_cache)(inode_cache_t *cache, uint64_t pgoff)
Read a page from the underlying storage, at file offset pgoff * MOS_PAGE_SIZE.
long(* flush_page)(inode_cache_t *cache, uint64_t pgoff, phyframe_t *page)
Flush a page to the underlying storage.
inode_t * owner
mos::HashMap< size_t, phyframe_t * > pages
const inode_cache_ops_t * ops
ssize_t off_t
Definition types.h:80
signed long ssize_t
Definition types.h:79
long simple_flush_page_discard_data(inode_cache_t *icache, off_t pgoff, phyframe_t *page)
Definition vfs_utils.cpp:84