View difference between Paste ID: 5vjMFdrf and
SHOW: | | - or go back to the newest paste.
1-
1+
/*
2
The cache needs to be able to provide the following operating modes:
3
- read sector(s) to an already allocated buffer
4
  NOTE: we can provide an hint to cache whether the sectors should be cached or not if it's not already in the cache
5
- write sectors(s) from an already filled buffer
6
  NOTE: same remark
7
- read a sector and lock a buffer to it
8
- flag a locked sector as dirty
9
- clean a locked sector (write it back if needed)
10
- flag a locked sector as inconsistent (prevent writeback)
11
- remove an inconsistency flag (should be done ASAP)
12
- unlock a sector (maybe with a flag if it should be cleaned or not)
13
- clean everything (and optionally flush the storage layer)
14
- invalidate everything? (if we respect locks, this might cause deadlocks, if not, it might cause all hell of trouble. yet, we need this, to clean things out after e.g. changing microsd cards. just fail (and block card removal) if there are still open handles?)
15
*/
16
17
struct sectorcache_entry
18
{
19
    bool valid;      /* Contains valid and up-to-date data for the specified sector */
20
    bool free;       /* Not expected to be needed again, should be reclaimed preferably */
21
    int locks;       /* Number of users of this buffer */
22
    bool exclusive;  /* Locked for exclusive access, usually for writing */
23
    bool dirty;      /* Needs to be written back before it can be reclaimed */
24
    bool consistent; /* Allowed to be written back in its current state */
25
    IF_MD2(int drive;)
26
    unsigned long sector;
27
    unsigned char* buffer;
28
};
29
30
static struct mutex sectorcache_mutex SHAREDBSS_ATTR;
31
static struct wakeup sectorcache_block SHAREDBSS_ATTR;
32
static int sectorcache_replace_idx;
33
static struct sectorcache_entry[SECTORCACHE_SIZE];
34
static unsigned char sectorcache_buffer[SECTORCACHE_SIZE][SECTOR_SIZE];
35
36
void sectorcache_init(void)
37
{
38
    int i;
39
    sectorcache_replace_idx = 0;
40
    memset(sectorcache_entry, 0, sizeof(sectorcache_entry));
41
    mutex_init(&sectorcache_mutex);
42
    wakeup_init(&sectorcache_block);
43
    for (i = 0; i < SECTORCACHE_SIZE; i++)
44
        sectorcache_entry[i].buffer = sectorcache_buffer[i];
45
}
46
47
/* Blocks the current thread until a buffer becomes free, unlocked or consistent,
48
   up to the specified number of ticks. Use TIMEOUT_BLOCK to block forever.
49
   Please note that this will release the mutex while blocking! */
50
static void sectorcache_wait_block(int timeout)
51
{
52
    mutex_unlock(&sectorcache_mutex);
53
    wakeup_wait(&sectorcache_block, timeout);
54
    mutex_lock(&sectorcache_mutex);
55
}
56
57
/* Tries to find a valid buffer for the specified sector.
58
   Returns its handle, or null if it couldn't find one.
59
   Please note that this doesn't check whether that buffer is locked.
60
   The mutex must have been locked by the caller before calling this. */
61
static struct sectorcache_entry* sectorcache_find_entry(IF_MD2(int drive,) unsigned long sector)
62
{
63
    int i;
64
    for (i = 0; i < SECTORCACHE_SIZE; i++)
65
        if (sectorcache_entry[i].valid && sectorcache_entry[i].sector == sector
66
            IF_MD2( && sectorcache_entry[i].drive == drive))
67
            return &sectorcache_entry[i];
68
    return null;
69
}
70
71
/* Tries to find a reclaimable buffer.
72
   Returns its handle, or null if it couldn't find one.
73
   Please note that this doesn't mark the buffer as used and doesn't
74
   check whether that buffer needs to be written back first.
75
   The mutex must have been locked by the caller before calling this. */
76
static struct sectorcache_entry* sectorcache_find_free(void)
77
{
78
    int i, idx;
79
    sectorcache_replace_idx++;
80
    for (int i = 0; i < SECTORCACHE_SIZE; i++)
81
    {
82
        idx = (sectorcache_replace_idx + i) % SECTORCACHE_SIZE;
83
        if (!sectorcache_entry[idx].valid)
84
            return &sectorcache_entry[idx];
85
    }
86
    for (int i = 0; i < SECTORCACHE_SIZE; i++)
87
    {
88
        idx = (sectorcache_replace_idx + i) % SECTORCACHE_SIZE;
89
        if (sectorcache_entry[idx].free)
90
            return &sectorcache_entry[idx];
91
    }
92
    for (int i = 0; i < SECTORCACHE_SIZE; i++)
93
    {
94
        idx = (sectorcache_replace_idx + i) % SECTORCACHE_SIZE;
95
        if (!sectorcache_entry[idx].locks)
96
            return &sectorcache_entry[idx];
97
    }
98
    return null;
99
}
100
101
int sectorcache_readthrough(IF_MD2(int drive,) unsigned long start, int count, void* buf, bool keepincache);
102
int sectorcache_writethrough(IF_MD2(int drive,) unsigned long start, int count, const void* buf, bool keepincache);
103
int sectorcache_lock(IF_MD2(int drive,) unsigned long sector, struct sectorcache_entry** handle, bool exclusive);
104
int sectorcache_unlock(struct sectorcache_entry** handle, bool clean, bool keepincache);
105
106
int sectorcache_markdirty(struct sectorcache_entry* handle)
107
{
108
    mutex_lock(&sectorcache_mutex);
109
    handle->dirty = true;
110
    mutex_unlock(&sectorcache_mutex);
111
}
112
113
int sectorcache_markinconsistent(struct sectorcache_entry* handle)
114
{
115
    mutex_lock(&sectorcache_mutex);
116
    handle->consistent = false;
117
    mutex_unlock(&sectorcache_mutex);
118
}
119
120
int sectorcache_markconsistent(struct sectorcache_entry* handle)
121
{
122
    mutex_lock(&sectorcache_mutex);
123
    handle->consistent = true;
124
    mutex_unlock(&sectorcache_mutex);
125
    wakeup_signal(&sectorcache_block);
126
}
127
128
int sectorcache_clean(struct sectorcache_entry* handle);
129
130
/* Write back everything that's dirty,
131
   and flush the storage layer if wanted.
132
   This may block for several seconds! */
133
int sectorcache_clean_all(bool flushstorage)
134
{
135
    int i;
136
    bool retry = true;
137
    mutex_lock(&sectorcache_mutex);
138
    while (retry)
139
    {
140
        retry = false;
141
        for (i = 0; i < SECTORCACHE_SIZE; i++)
142
            if (sectorcache_entry[i].dirty)
143
            {
144
                if (!sectorcache_entry[i].consistent)
145
                {
146
                    sectorcache_wait_block(TIMEOUT_BLOCK);
147
                    retry = true;
148
                }
149
                sectorcache_clean(&sectorcache_entry[i]);
150
            }
151
    }
152
#ifdef HAVE_STORAGE_FLUSH
153
    if (flushstorage) storage_flush();
154
#endif
155
    mutex_unlock(&sectorcache_mutex);
156
}
157
158
int sectorcache_invalidate(IF_MD2(int drive));