MOS Source Code
Loading...
Searching...
No Matches
page_cache.c
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
2
4
7#include "mos/mm/mm.h"
8#include "mos/mm/mmstat.h"
10#include "mos/syslog/printk.h"
11
12#include <mos/mos_global.h>
13#include <mos_stdlib.h>
14#include <mos_string.h>
15
22
23static bool do_flush_and_drop_cached_page(const uintn key, void *value, void *data)
24{
25 // key = page number, value = phyframe_t *, data = _flush_and_drop_data *
26 struct _flush_and_drop_data *const fdd = data;
27 inode_cache_t *const icache = fdd->icache;
28 phyframe_t *const page = value;
29 off_t const pgoff = key;
30 const bool drop_page = fdd->should_drop_page;
31
32 // !! TODO
33 // !! currently we have no reliable way to track if a page is dirty
34 // !! so we always flush it
35 // !! this causes performance issues, but it's simple and works for now
36 // if (!page->pagecache.dirty)
37
38 long ret = 0;
39 if (icache->ops->flush_page)
40 ret = icache->ops->flush_page(icache, pgoff, page);
41 else
43
44 if (!IS_ERR_VALUE(ret) && drop_page)
45 {
46 // only when the page was successfully flushed
47 hashmap_remove(&icache->pages, pgoff);
49 pmm_unref_one(page);
50 }
51
52 fdd->ret = ret;
53 return ret == 0;
54}
55
56long pagecache_flush_or_drop(inode_cache_t *icache, off_t pgoff, size_t npages, bool drop_page)
57{
58 struct _flush_and_drop_data data = { .icache = icache, .should_drop_page = drop_page };
59
60 long ret = 0;
61 for (size_t i = 0; i < npages; i++)
62 {
63 phyframe_t *page = hashmap_get(&icache->pages, pgoff + i);
64 if (!page)
65 continue;
66
67 do_flush_and_drop_cached_page(pgoff + i, page, &data);
68
69 if (data.ret != 0)
70 {
71 ret = data.ret;
72 break;
73 }
74 }
75 return ret;
76}
77
79{
80 struct _flush_and_drop_data data = { .icache = icache, .should_drop_page = drop_page };
82 return data.ret;
83}
84
86{
87 phyframe_t *page = hashmap_get(&cache->pages, pgoff);
88 if (page)
89 return page; // fast path
90
91 if (!cache->ops)
92 return ERR_PTR(-EIO);
93
94 MOS_ASSERT_X(cache->ops && cache->ops->fill_cache, "no page cache ops for inode %p", (void *) cache->owner);
95 page = cache->ops->fill_cache(cache, pgoff);
96 if (IS_ERR(page))
97 return page;
99 MOS_ASSERT(hashmap_put(&cache->pages, pgoff, page) == NULL);
100 return page;
101}
102
107
109{
111 size_t bytes_read = 0;
112 size_t bytes_left = size;
113 while (bytes_left > 0)
114 {
115 // bytes to copy from the current page
116 const size_t inpage_offset = offset % MOS_PAGE_SIZE;
117 const size_t inpage_size = MIN(MOS_PAGE_SIZE - inpage_offset, bytes_left); // in case we're at the end of the file,
118
119 phyframe_t *page = pagecache_get_page_for_read(icache, offset / MOS_PAGE_SIZE); // the initial page
120 if (IS_ERR(page))
121 {
123 return PTR_ERR(page);
124 }
125
126 memcpy((char *) buf + bytes_read, (void *) (phyframe_va(page) + inpage_offset), inpage_size);
127
128 bytes_read += inpage_size;
129 bytes_left -= inpage_size;
130 offset += inpage_size;
131 }
132
134 return bytes_read;
135}
136
137ssize_t vfs_write_pagecache(inode_cache_t *icache, const void *buf, size_t total_size, off_t offset)
138{
139 const inode_cache_ops_t *ops = icache->ops;
140 MOS_ASSERT_X(ops, "no page cache ops for inode %p", (void *) icache->owner);
141
143
144 size_t bytes_written = 0;
145 size_t bytes_left = total_size;
146 while (bytes_left > 0)
147 {
148 // bytes to copy to the current page
149 const size_t inpage_offset = offset % MOS_PAGE_SIZE;
150 const size_t inpage_size = MIN(MOS_PAGE_SIZE - inpage_offset, bytes_left); // in case we're at the end of the file,
151
152 void *private;
153 phyframe_t *page;
154 const bool can_write = ops->page_write_begin(icache, offset, inpage_size, &page, &private);
155 if (!can_write)
156 {
157 pr_warn("page_write_begin failed");
159 return -EIO;
160 }
161
162 memcpy((char *) (phyframe_va(page) + inpage_offset), (char *) buf + bytes_written, inpage_size);
163 ops->page_write_end(icache, offset, inpage_size, page, private);
164
165 bytes_written += inpage_size;
166 bytes_left -= inpage_size;
167 offset += inpage_size;
168 }
169
171 return bytes_written;
172}
#define MOS_ASSERT_X(cond, msg,...)
Definition assert.h:15
#define MOS_ASSERT(cond)
Definition assert.h:14
#define MOS_PAGE_SIZE
Definition autoconf.h:6
MOSAPI void * hashmap_get(hashmap_t *map, uintn key)
Definition hashmap.c:96
MOSAPI void * hashmap_put(hashmap_t *map, uintn key, void *value)
Definition hashmap.c:68
MOSAPI void * hashmap_remove(hashmap_t *map, uintn key)
Definition hashmap.c:117
MOSAPI void hashmap_foreach(hashmap_t *map, hashmap_foreach_func_t func, void *data)
Definition hashmap.c:146
#define MIN(a, b)
Definition mos_stdlib.h:30
#define phyframe_va(frame)
Definition mm.h:79
#define pmm_unref_one(thing)
Definition pmm.h:147
#define mmstat_dec1(type)
Definition mmstat.h:35
#define mmstat_inc1(type)
Definition mmstat.h:26
@ MEM_PAGECACHE
Definition mmstat.h:10
#define IS_ERR_VALUE(x)
Definition mos_global.h:126
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.
Definition page_cache.c:56
ssize_t vfs_write_pagecache(inode_cache_t *icache, const void *buf, size_t total_size, off_t offset)
Definition page_cache.c:137
long pagecache_flush_or_drop_all(inode_cache_t *icache, bool drop_page)
Flush or drop all pages in the page cache.
Definition page_cache.c:78
phyframe_t * pagecache_get_page_for_read(inode_cache_t *cache, off_t pgoff)
Get a page from the page cache.
Definition page_cache.c:85
phyframe_t * pagecache_get_page_for_write(inode_cache_t *cache, off_t pgoff)
Get a page from the page cache for writing.
Definition page_cache.c:103
static bool do_flush_and_drop_cached_page(const uintn key, void *value, void *data)
Definition page_cache.c:23
ssize_t vfs_read_pagecache(inode_cache_t *icache, void *buf, size_t size, off_t offset)
Definition page_cache.c:108
static void * memcpy(void *s1, const void *s2, size_t n)
Definition pb_syshdr.h:90
#define NULL
Definition pb_syshdr.h:46
#define pr_warn(fmt,...)
Definition printk.h:38
#define mutex_acquire(mutex)
Definition rpc_client.c:41
#define mutex_release(mutex)
Definition rpc_client.c:42
size_t size
Definition slab.c:30
inode_cache_t * icache
Definition page_cache.c:18
long(* flush_page)(inode_cache_t *cache, off_t pgoff, phyframe_t *page)
Flush a page to the underlying storage.
Definition vfs_types.h:144
phyframe_t *(* fill_cache)(inode_cache_t *cache, off_t pgoff)
Read a page from the underlying storage, at file offset pgoff * MOS_PAGE_SIZE.
Definition vfs_types.h:136
bool(* page_write_begin)(inode_cache_t *cache, off_t file_offset, size_t inpage_size, phyframe_t **page_out, void **data)
Definition vfs_types.h:138
void(* page_write_end)(inode_cache_t *cache, off_t file_offset, size_t inpage_size, phyframe_t *page, void *data)
Definition vfs_types.h:139
inode_t * owner
Definition vfs_types.h:150
const inode_cache_ops_t * ops
Definition vfs_types.h:152
hashmap_t pages
Definition vfs_types.h:151
mutex_t lock
Definition vfs_types.h:149
ssize_t off_t
Definition types.h:84
unsigned long uintn
Definition types.h:30
signed long ssize_t
Definition types.h:83
long simple_flush_page_discard_data(inode_cache_t *icache, off_t pgoff, phyframe_t *page)
Definition vfs_utils.c:86