View difference between Paste ID: RMAb7Ejf 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
/* Indicate that the buffer will need to be written back before it can be reclaimed */
107
int sectorcache_markdirty(struct sectorcache_entry* handle)
108
{
109
    mutex_lock(&sectorcache_mutex);
110
    handle->dirty = true;
111
    mutex_unlock(&sectorcache_mutex);
112
}
113
114
/* Indicate that the buffer may not be written back until it has been
115
   marked as consistent again. (Prevents syncing during inactivity)
116
   Release this as soon as possible. */
117
int sectorcache_markinconsistent(struct sectorcache_entry* handle)
118
{
119
    mutex_lock(&sectorcache_mutex);
120
    handle->consistent = false;
121
    mutex_unlock(&sectorcache_mutex);
122
}
123
124
/* Indicate that the buffer may not be written back again. */
125
int sectorcache_markconsistent(struct sectorcache_entry* handle)
126
{
127
    mutex_lock(&sectorcache_mutex);
128
    handle->consistent = true;
129
    mutex_unlock(&sectorcache_mutex);
130
    wakeup_signal(&sectorcache_block);
131
}
132
133
/* Write back the sector. Will mark it as consistent.
134
   Make sure it is marked as consistent before calling this internally. */
135
int sectorcache_clean(struct sectorcache_entry* handle);
136
137
/* Write back everything that's dirty,
138
   and flush the storage layer if wanted.
139
   This may block for several seconds! */
140
int sectorcache_clean_all(bool flushstorage)
141
{
142
    int i;
143
    bool retry = true;
144
    mutex_lock(&sectorcache_mutex);
145
    while (retry)
146
    {
147
        retry = false;
148
        for (i = 0; i < SECTORCACHE_SIZE; i++)
149
            if (sectorcache_entry[i].dirty)
150
            {
151
                if (!sectorcache_entry[i].consistent)
152
                {
153
                    sectorcache_wait_block(TIMEOUT_BLOCK);
154
                    retry = true;
155
                }
156
                sectorcache_clean(&sectorcache_entry[i]);
157
            }
158
    }
159
#ifdef HAVE_STORAGE_FLUSH
160
    if (flushstorage) storage_flush();
161
#endif
162
    mutex_unlock(&sectorcache_mutex);
163
}
164
165
/* Invalidate the whole cache for the drive, used by hotplug and USB.
166
   Will panic if there are still handles open for this drive. */
167
int sectorcache_invalidate(IF_MD2(int drive));