1/* @title: Block Cache */
2#include <block/bio.h>
3#include <errno.h>
4#include <stdatomic.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;
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 errno 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_fetch_add(&cache->ticks, 1);
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 mutex_lock(mutex: &ent->lock);
102}
103
104static inline void bcache_ent_unlock(struct bcache_entry *ent) {
105 mutex_unlock(mutex: &ent->lock);
106}
107
108static inline void bcache_ent_pin(struct bcache_entry *ent) {
109 refcount_inc(rc: &ent->refcount);
110}
111
112static inline void bcache_ent_unpin(struct bcache_entry *ent) {
113 refcount_dec_and_test(rc: &ent->refcount);
114}
115
116static inline void bcache_ent_acquire(struct bcache_entry *ent) {
117 bcache_ent_pin(ent);
118 bcache_ent_lock(ent);
119}
120
121static inline void bcache_ent_release(struct bcache_entry *ent) {
122 bcache_ent_unpin(ent);
123 bcache_ent_unlock(ent);
124}
125