summaryrefslogtreecommitdiff
path: root/Kernel/DiskBackedFileSystem.cpp
blob: f6214bb8b33b1c4618c2faf7007e7fe4cf468c58 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include "DiskBackedFileSystem.h"
#include "i386.h"
#include <AK/InlineLRUCache.h>
#include <Kernel/Process.h>

//#define DBFS_DEBUG

struct BlockIdentifier {
    unsigned fsid { 0 };
    unsigned index { 0 };

    bool operator==(const BlockIdentifier& other) const { return fsid == other.fsid && index == other.index; }
};

namespace AK {

template<>
struct Traits<BlockIdentifier> {
    static unsigned hash(const BlockIdentifier& block_id) { return pair_int_hash(block_id.fsid, block_id.index); }
    static void dump(const BlockIdentifier& block_id) { kprintf("[block %02u:%08u]", block_id.fsid, block_id.index); }
};

}

class CachedBlock : public InlineLinkedListNode<CachedBlock> {
public:
    CachedBlock(const BlockIdentifier& block_id, const ByteBuffer& buffer)
        : m_key(block_id)
        , m_buffer(buffer)
    {
    }

    BlockIdentifier m_key;
    CachedBlock* m_next { nullptr };
    CachedBlock* m_prev { nullptr };

    ByteBuffer m_buffer;
};

Lockable<InlineLRUCache<BlockIdentifier, CachedBlock>>& block_cache()
{
    static Lockable<InlineLRUCache<BlockIdentifier, CachedBlock>>* s_cache;
    if (!s_cache)
        s_cache = new Lockable<InlineLRUCache<BlockIdentifier, CachedBlock>>;
    return *s_cache;
}

DiskBackedFS::DiskBackedFS(Retained<DiskDevice>&& device)
    : m_device(move(device))
{
}

DiskBackedFS::~DiskBackedFS()
{
}

bool DiskBackedFS::write_block(unsigned index, const ByteBuffer& data)
{
#ifdef DBFS_DEBUG
    kprintf("DiskBackedFileSystem::write_block %u, size=%u\n", index, data.size());
#endif
    ASSERT(data.size() == block_size());

    {
        LOCKER(block_cache().lock());
        if (auto* cached_block = block_cache().resource().get({ fsid(), index }))
            cached_block->m_buffer = data;
    }
    DiskOffset base_offset = static_cast<DiskOffset>(index) * static_cast<DiskOffset>(block_size());
    return device().write(base_offset, block_size(), data.pointer());
}

bool DiskBackedFS::write_blocks(unsigned index, unsigned count, const ByteBuffer& data)
{
#ifdef DBFS_DEBUG
    kprintf("DiskBackedFileSystem::write_blocks %u x%u\n", index, count);
#endif
    // FIXME: Maybe reorder this so we send out the write commands before updating cache?
    {
        LOCKER(block_cache().lock());
        for (unsigned i = 0; i < count; ++i) {
            if (auto* cached_block = block_cache().resource().get({ fsid(), index + i }))
                cached_block->m_buffer = data.slice(i * block_size(), block_size());
        }
    }
    DiskOffset base_offset = static_cast<DiskOffset>(index) * static_cast<DiskOffset>(block_size());
    return device().write(base_offset, count * block_size(), data.pointer());
}

ByteBuffer DiskBackedFS::read_block(unsigned index) const
{
#ifdef DBFS_DEBUG
    kprintf("DiskBackedFileSystem::read_block %u\n", index);
#endif
    {
        LOCKER(block_cache().lock());
        if (auto* cached_block = block_cache().resource().get({ fsid(), index }))
            return cached_block->m_buffer;
    }

    auto buffer = ByteBuffer::create_uninitialized(block_size());
    //kprintf("created block buffer with size %u\n", block_size());
    DiskOffset base_offset = static_cast<DiskOffset>(index) * static_cast<DiskOffset>(block_size());
    auto* buffer_pointer = buffer.pointer();
    bool success = device().read(base_offset, block_size(), buffer_pointer);
    ASSERT(success);
    ASSERT(buffer.size() == block_size());
    {
        LOCKER(block_cache().lock());
        block_cache().resource().put({ fsid(), index }, CachedBlock({ fsid(), index }, buffer));
    }
    return buffer;
}

ByteBuffer DiskBackedFS::read_blocks(unsigned index, unsigned count) const
{
    if (!count)
        return nullptr;
    if (count == 1)
        return read_block(index);
    auto blocks = ByteBuffer::create_uninitialized(count * block_size());
    byte* out = blocks.pointer();

    for (unsigned i = 0; i < count; ++i) {
        auto block = read_block(index + i);
        if (!block)
            return nullptr;
        memcpy(out, block.pointer(), block.size());
        out += block_size();
    }

    return blocks;
}

void DiskBackedFS::set_block_size(unsigned block_size)
{
    if (block_size == m_block_size)
        return;
    m_block_size = block_size;
}