1/* @title: Block Cache */
2#include <atomic.h>
3#include <block/bio.h>
4#include <err.h>
5#include <stdbool.h>
6#include <stdint.h>
7#include <sync/mutex.h>
8#include <sync/spinlock.h>
9#include <types/refcount.h>
10#include <types/types.h>
11
12#pragma once
13#define DEFAULT_BLOCK_CACHE_SIZE 2048
14#define DEFAULT_MAX_DIRTY_ENTS 64
15
16struct block_device;
17
18/* must be allocated with malloc */
19struct bcache_entry {
20 /* allocated upon new reads */
21 uint8_t *buffer cc_counted_by(size);
22
23 uint64_t size;
24
25 /* logical block address */
26 uint64_t lba;
27
28 /* used with a counter - not a real 'timestamp' */
29 uint64_t access_time;
30 struct mutex lock;
31 bool dirty;
32 bool no_evict;
33
34 /* associated outgoing request */
35 struct bio_request *request;
36
37 refcount_t refcount;
38};
39
40struct bcache_wrapper {
41 uint64_t key; // block number
42 struct bcache_entry *value; // pointer to cache entry
43 bool occupied;
44 struct bcache_wrapper *next;
45};
46
47/* TODO: bitmap to mark present entries */
48struct bcache {
49 struct bcache_wrapper **entries;
50 atomic_uint64_t ticks;
51 uint64_t capacity;
52 uint64_t count;
53 uint64_t spb;
54 struct spinlock lock;
55};
56
57static inline uint64_t bcache_hash(uint64_t x, uint64_t capacity) {
58 x ^= x >> 30;
59 x *= 0xbf58476d1ce4e5b9ULL;
60 x ^= x >> 27;
61 x *= 0x94d049bb133111ebULL;
62 x ^= x >> 31;
63 return x % capacity;
64}
65
66void bcache_init(struct bcache *cache, uint64_t capacity);
67
68void *bcache_get(struct block_device *disk, uint64_t lba, uint64_t block_size,
69 uint64_t spb, bool no_evict, struct bcache_entry **out_entry);
70
71bool bcache_writethrough(struct block_device *disk, struct bcache_entry *ent,
72 uint64_t spb);
73
74void bcache_write_queue(struct block_device *disk, struct bcache_entry *ent,
75 uint64_t spb, enum bio_request_priority prio);
76
77void bcache_stat(struct block_device *disk, uint64_t *total_dirty_out,
78 uint64_t *total_present_out);
79
80bool bcache_insert(struct block_device *disk, uint64_t lba,
81 struct bcache_entry *ent, uint64_t spb);
82
83bool bcache_evict(struct block_device *disk, uint64_t spb);
84
85enum err bcache_prefetch_async(struct block_device *disk, uint64_t lba,
86 uint64_t block_size, uint64_t spb);
87
88void *bcache_create_ent(struct block_device *disk, uint64_t lba,
89 uint64_t block_size, uint64_t sectors_per_block,
90 bool no_evict, struct bcache_entry **out_entry);
91
92static inline void bcache_increment_ticks(struct bcache *cache) {
93 atomic_inc(&cache->ticks);
94}
95
96static inline uint64_t bcache_get_ticks(struct bcache *cache) {
97 return atomic_load(&cache->ticks);
98}
99
100static inline void bcache_ent_lock(struct bcache_entry *ent)
101 TSA_ACQUIRES(&ent->lock) {
102 mutex_lock(&ent->lock);
103}
104
105static inline void bcache_ent_unlock(struct bcache_entry *ent)
106 TSA_RELEASES(&ent->lock) {
107 mutex_unlock(&ent->lock);
108}
109
110static inline void bcache_ent_pin(struct bcache_entry *ent) {
111 refcount_inc(rc: &ent->refcount);
112}
113
114static inline void bcache_ent_unpin(struct bcache_entry *ent) {
115 refcount_dec_and_test(rc: &ent->refcount);
116}
117
118static inline void
119bcache_ent_acquire(struct bcache_entry *ent) TSA_NO_ANALYSIS {
120 bcache_ent_pin(ent);
121 bcache_ent_lock(ent);
122}
123
124static inline void
125bcache_ent_release(struct bcache_entry *ent) TSA_NO_ANALYSIS {
126 bcache_ent_unpin(ent);
127 bcache_ent_unlock(ent);
128}
129
130void bcache_destroy(struct bcache *cache);
131