Skip to content

Commit 08115e5

Browse files
committed
Initial work on disk caching
1 parent 7c189d3 commit 08115e5

File tree

9 files changed

+245
-26
lines changed

9 files changed

+245
-26
lines changed

include/Cache.h

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#pragma once
2+
3+
#include <functional>
4+
#include <list>
5+
#include <map>
6+
7+
#include "Assert.h"
8+
9+
namespace Thorn {
10+
template <typename K, typename V, template <typename...> typename M, typename E>
11+
class Cache {
12+
private:
13+
size_t maxSize{};
14+
15+
// The void pointer here is actually a list iterator...
16+
// I'm sorry.
17+
M<K, std::pair<void *, V>, E> map;
18+
using MapIterator = decltype(map)::iterator;
19+
20+
std::list<MapIterator> list;
21+
using ListIterator = decltype(list)::iterator;
22+
23+
static_assert(sizeof(void *) == sizeof(ListIterator));
24+
25+
void ensureSpace(size_t count) {
26+
while (map.size() + count > maxSize) {
27+
map.erase(list.back());
28+
list.pop_back();
29+
}
30+
}
31+
32+
void moveFront(auto map_iter) {
33+
auto &list_iter = *reinterpret_cast<ListIterator *>(&map_iter->second.first);
34+
list.erase(list_iter);
35+
list.push_front(map_iter);
36+
list_iter = list.begin();
37+
}
38+
39+
public:
40+
using Key = K;
41+
using Value = V;
42+
43+
Cache(size_t max_size): maxSize(max_size) {
44+
assert(max_size != 0);
45+
}
46+
47+
std::pair<MapIterator, bool> emplace(const K &key, V value) {
48+
if (auto iter = map.find(key); iter != map.end()) {
49+
moveFront(iter);
50+
iter->second.second = std::move(value);
51+
return {iter, false};
52+
}
53+
54+
ensureSpace(1);
55+
auto [iter, inserted] = map.try_emplace(key, std::bit_cast<void *>(list.end()), std::move(value));
56+
assert(inserted);
57+
list.push_front(iter);
58+
iter->second.first = std::bit_cast<void *>(list.begin());
59+
return {iter, true};
60+
}
61+
62+
V & at(const K &key) {
63+
if (auto iter = map.find(key); iter != map.end()) {
64+
moveFront(iter);
65+
return iter->second.second;
66+
}
67+
68+
assert(!"Key is present in Cache");
69+
}
70+
71+
const V & at(const K &key) const {
72+
return map.at(key);
73+
}
74+
75+
/** The bool in the returned pair indicates whether a new entry was created. */
76+
std::pair<std::reference_wrapper<V>, bool> operator[](const K &key) {
77+
if (auto iter = map.find(key); iter != map.end()) {
78+
moveFront(iter);
79+
return {std::ref(iter->second.second), false};
80+
}
81+
82+
auto [iter, inserted] = emplace(key, V());
83+
return {std::ref(iter->second.second), true};
84+
}
85+
86+
void erase(const K &key) {
87+
auto iter = map.find(key);
88+
if (iter == map.end())
89+
return;
90+
91+
list.erase(iter->second.first);
92+
map.erase(iter);
93+
}
94+
95+
auto begin() { return map.begin(); }
96+
auto end() { return map.end(); }
97+
auto begin() const { return map.begin(); }
98+
auto end() const { return map.end(); }
99+
};
100+
101+
template <typename K, typename V, typename C = std::less<K>>
102+
using TreeCache = Cache<K, V, std::map, C>;
103+
104+
template <typename K, typename V, typename H = std::hash<K>>
105+
using HashCache = Cache<K, V, std::unordered_map, H>;
106+
}

include/Kernel.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace Thorn {
1515
using PageFrameNumber = uint64_t;
1616

1717
struct StorageController;
18-
struct StorageDevice;
18+
struct StorageDeviceBase;
1919

2020
class Kernel {
2121
private:
@@ -26,7 +26,7 @@ namespace Thorn {
2626
std::unique_ptr<Locked<ProcessMap, RecursiveMutex>> processes;
2727
std::unique_ptr<Locked<std::map<PageFrameNumber, PID>>> pageFrameProcesses;
2828
std::unique_ptr<Locked<std::vector<std::unique_ptr<StorageController>>>> storageControllers;
29-
std::unique_ptr<Locked<std::vector<std::unique_ptr<StorageDevice>>>> storageDevices;
29+
std::unique_ptr<Locked<std::vector<std::unique_ptr<StorageDeviceBase>>>> storageDevices;
3030

3131
PID lastPID = 1;
3232

include/Test.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ namespace Thorn {
3737
AHCI::Port *port = nullptr;
3838
bool ahci = true;
3939
int idePort = -1;
40-
StorageDevice *device = nullptr;
40+
StorageDeviceBase *device = nullptr;
4141
FS::Partition *partition = nullptr;
4242
FS::ThornFAT::ThornFATDriver *driver = nullptr;
4343
std::string path = "/";

include/device/AHCIDevice.h

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,22 @@
44
#include "hardware/AHCI.h"
55

66
namespace Thorn {
7-
struct AHCIDevice: public StorageDevice {
8-
AHCI::Port *port;
9-
AHCIDevice() = delete;
10-
AHCIDevice(AHCI::Port *port_): port(port_) {}
11-
virtual ~AHCIDevice() {}
7+
class AHCIDevice: public StorageDevice<AHCI::Port::BLOCKSIZE> {
8+
public:
9+
AHCI::Port *port;
1210

13-
virtual int read(void *buffer, size_t size, size_t offset) override;
14-
virtual int write(const void *buffer, size_t size, size_t offset) override;
15-
virtual int clear(size_t offset, size_t size) override;
16-
virtual std::string getName() const override;
11+
AHCIDevice(AHCI::Port *port_):
12+
port(port_) {}
13+
14+
int read(void *buffer, size_t size, size_t offset) final;
15+
int write(const void *buffer, size_t size, size_t offset) final;
16+
int clear(size_t offset, size_t size) final;
17+
void flush() final;
18+
void flush(StorageCacheEntry<BlockSize> &, uint64_t block) final;
19+
std::string getName() const final;
20+
21+
private:
22+
int readCache(void *buffer, size_t size, size_t offset);
23+
int writeCache(const void *buffer, size_t size, size_t offset);
1724
};
1825
}

include/device/IDEDevice.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
#pragma once
22

33
#include "device/Storage.h"
4+
#include "hardware/IDE.h"
45

56
namespace Thorn {
6-
struct IDEDevice: public StorageDevice {
7+
struct IDEDevice: StorageDevice<IDE::SECTOR_SIZE> {
78
uint8_t ideID;
89
IDEDevice() = delete;
910
IDEDevice(uint8_t ide_id): ideID(ide_id) {}
1011

11-
virtual int read(void *buffer, size_t size, size_t offset) override;
12-
virtual int write(const void *buffer, size_t size, size_t offset) override;
13-
virtual int clear(size_t offset, size_t size) override;
14-
virtual std::string getName() const override;
12+
int read(void *buffer, size_t size, size_t offset) final;
13+
int write(const void *buffer, size_t size, size_t offset) final;
14+
int clear(size_t offset, size_t size) final;
15+
void flush() final {}
16+
void flush(StorageCacheEntry<BlockSize> &, uint64_t) final {}
17+
std::string getName() const final;
1518
};
1619
}

include/device/Storage.h

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,46 @@
11
#pragma once
22

3+
#include <cstdint>
34
#include <string>
45

6+
#include "Cache.h"
57
#include "Defs.h"
68

79
namespace Thorn {
8-
struct StorageDevice {
10+
template <size_t Size>
11+
struct StorageCacheEntry {
12+
uint8_t data[Size];
13+
bool flushed = false;
14+
15+
StorageCacheEntry() = default;
16+
};
17+
18+
template <size_t CacheLength, size_t BlockSize>
19+
struct StorageCache: TreeCache<uint64_t, StorageCacheEntry<BlockSize>> {
20+
StorageCache():
21+
TreeCache<uint64_t, StorageCacheEntry<BlockSize>>(CacheLength) {}
22+
};
23+
24+
struct StorageDeviceBase {
25+
virtual ~StorageDeviceBase() = default;
26+
927
virtual int read(void *buffer, size_t size, size_t offset) = 0;
1028
virtual int write(const void *buffer, size_t size, size_t offset) = 0;
1129
virtual int clear(size_t offset, size_t size) = 0;
30+
virtual void flush() = 0;
1231
virtual std::string getName() const = 0;
13-
virtual ~StorageDevice() = default;
32+
};
33+
34+
template <size_t BS>
35+
struct StorageDevice: StorageDeviceBase {
36+
constexpr static size_t BlockSize = BS;
37+
38+
StorageCache<(1 << 20 >> 3), BlockSize> cache;
39+
40+
StorageDevice() = default;
41+
42+
using StorageDeviceBase::flush;
43+
virtual void flush(StorageCacheEntry<BlockSize> &, uint64_t block) = 0;
1444
};
1545

1646
struct StorageController {

include/fs/Partition.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#include <vector>
55

66
namespace Thorn {
7-
struct StorageDevice;
7+
struct StorageDeviceBase;
88
}
99

1010
namespace Thorn::FS {
@@ -18,13 +18,13 @@ namespace Thorn::FS {
1818

1919
struct Partition {
2020
std::vector<Record> writeRecords, readRecords;
21-
StorageDevice *parent;
21+
StorageDeviceBase *parent;
2222
/** Number of bytes after the start of the disk. */
2323
size_t offset;
2424
/** Length of the partition in bytes. */
2525
size_t length;
2626

27-
Partition(StorageDevice *parent_, size_t offset_, size_t length_):
27+
Partition(StorageDeviceBase *parent_, size_t offset_, size_t length_):
2828
parent(parent_), offset(offset_), length(length_) {}
2929

3030
int read(void *buffer, size_t size, size_t byte_offset);

src/device/AHCIDevice.cpp

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "device/AHCIDevice.h"
22
#include "memory/Memory.h"
3+
#include "Kernel.h"
34

45
namespace Thorn {
56
int AHCIDevice::read(void *buffer, size_t size, size_t offset) {
@@ -9,9 +10,10 @@ namespace Thorn {
910
}
1011

1112
int AHCIDevice::write(const void *buffer, size_t size, size_t offset) {
12-
if (offset % AHCI::Port::BLOCKSIZE == 0 && size % AHCI::Port::BLOCKSIZE == 0)
13-
return static_cast<int>(port->write(offset / AHCI::Port::BLOCKSIZE, size, buffer));
14-
return static_cast<int>(port->writeBytes(size, offset, buffer));
13+
// if (offset % AHCI::Port::BLOCKSIZE == 0 && size % AHCI::Port::BLOCKSIZE == 0)
14+
// return static_cast<int>(port->write(offset / AHCI::Port::BLOCKSIZE, size, buffer));
15+
// return static_cast<int>(port->writeBytes(size, offset, buffer));
16+
return writeCache(buffer, size, offset);
1517
}
1618

1719
int AHCIDevice::clear(size_t offset, size_t size) {
@@ -61,4 +63,76 @@ namespace Thorn {
6163
port->getInfo().copyModel(model);
6264
return model;
6365
}
66+
67+
void AHCIDevice::flush() {
68+
for (auto &[block, pair]: cache) {
69+
flush(pair.second, block);
70+
}
71+
}
72+
73+
void AHCIDevice::flush(StorageCacheEntry<BlockSize> &entry, uint64_t block) {
74+
if (entry.flushed)
75+
return;
76+
77+
const auto status = static_cast<int>(port->write(block, BlockSize, entry.data));
78+
79+
if (!status) {
80+
printf("Failed to flush AHCI block %ld\n", block);
81+
Kernel::perish();
82+
}
83+
84+
entry.flushed = true;
85+
}
86+
87+
int AHCIDevice::writeCache(const void *buffer, size_t size, size_t offset) {
88+
int status{};
89+
90+
auto advance = [&](size_t amount) {
91+
offset += amount;
92+
buffer = reinterpret_cast<const char *>(buffer) + amount;
93+
size -= amount;
94+
return size == 0;
95+
};
96+
97+
if (size_t rem = offset % BlockSize; rem != 0) {
98+
auto [ref, created] = cache[offset / BlockSize];
99+
StorageCacheEntry<BlockSize> &entry = ref.get();
100+
101+
if (created) {
102+
if (0 != (status = read(entry.data, BlockSize, offset - rem)))
103+
return status;
104+
} else {
105+
entry.flushed = false;
106+
}
107+
108+
size_t affected = std::min(size, BlockSize - rem);
109+
std::memmove(&entry.data[rem], buffer, affected);
110+
if (advance(affected))
111+
return 0;
112+
}
113+
114+
while (size >= BlockSize) {
115+
auto [ref, created] = cache[offset / BlockSize];
116+
StorageCacheEntry<BlockSize> &entry = ref.get();
117+
entry.flushed = false;
118+
std::memmove(entry.data, buffer, BlockSize);
119+
if (advance(BlockSize))
120+
return 0;
121+
}
122+
123+
assert(size > 0);
124+
125+
auto [ref, created] = cache[offset / BlockSize];
126+
StorageCacheEntry<BlockSize> &entry = ref.get();
127+
128+
if (created) {
129+
if (0 != (status = read(entry.data, size, offset)))
130+
return status;
131+
} else {
132+
entry.flushed = false;
133+
}
134+
135+
std::memmove(entry.data, buffer, size);
136+
return 0;
137+
}
64138
}

src/device/IDEDevice.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include "device/IDEDevice.h"
22
#include "memory/Memory.h"
3-
#include "hardware/IDE.h"
43

54
namespace Thorn {
65
int IDEDevice::read(void *buffer, size_t size, size_t offset) {

0 commit comments

Comments
 (0)