ringfs
 All Data Structures Files Functions Variables Enumerations Enumerator Macros Groups Pages
ringfs.c
Go to the documentation of this file.
1 /*
2  * Copyright © 2014 Kosma Moczek <kosma@cloudyourcar.com>
3  * This program is free software. It comes without any warranty, to the extent
4  * permitted by applicable law. You can redistribute it and/or modify it under
5  * the terms of the Do What The Fuck You Want To Public License, Version 2, as
6  * published by Sam Hocevar. See the COPYING file for more details.
7  */
8 
16 #include <ringfs.h>
17 
18 #include <inttypes.h>
19 #include <stdbool.h>
20 #include <stddef.h>
21 #include <stdio.h>
22 
23 
30  SECTOR_ERASED = 0xFFFFFFFF,
31  SECTOR_FREE = 0xFFFFFF00,
32  SECTOR_IN_USE = 0xFFFF0000,
33  SECTOR_ERASING = 0xFF000000,
34  SECTOR_FORMATTING = 0x00000000,
35 };
36 
37 struct sector_header {
38  uint32_t status;
39  uint32_t version;
40 };
41 
42 static int _sector_address(struct ringfs *fs, int sector_offset)
43 {
44  return (fs->flash->sector_offset + sector_offset) * fs->flash->sector_size;
45 }
46 
47 static int _sector_get_status(struct ringfs *fs, int sector, uint32_t *status)
48 {
49  return fs->flash->read(_sector_address(fs, sector) + offsetof(struct sector_header, status),
50  status, sizeof(*status));
51 }
52 
53 static int _sector_set_status(struct ringfs *fs, int sector, uint32_t status)
54 {
55  return fs->flash->program(_sector_address(fs, sector) + offsetof(struct sector_header, status),
56  &status, sizeof(status));
57 }
58 
59 static int _sector_free(struct ringfs *fs, int sector)
60 {
61  int sector_addr = _sector_address(fs, sector);
63  fs->flash->sector_erase(sector_addr);
64  fs->flash->program(sector_addr + offsetof(struct sector_header, version), &fs->version, sizeof(fs->version));
65  _sector_set_status(fs, sector, SECTOR_FREE);
66  return 0;
67 }
68 
76  SLOT_ERASED = 0xFFFFFFFF,
77  SLOT_RESERVED = 0xFFFFFF00,
78  SLOT_VALID = 0xFFFF0000,
79  SLOT_GARBAGE = 0xFF000000,
80 };
81 
82 struct slot_header {
83  uint32_t status;
84 };
85 
86 static int _slot_address(struct ringfs *fs, struct ringfs_loc *loc)
87 {
88  return _sector_address(fs, loc->sector) +
89  sizeof(struct sector_header) +
90  (sizeof(struct slot_header) + fs->object_size) * loc->slot;
91 }
92 
93 static int _slot_get_status(struct ringfs *fs, struct ringfs_loc *loc, uint32_t *status)
94 {
95  return fs->flash->read(_slot_address(fs, loc) + offsetof(struct slot_header, status),
96  status, sizeof(*status));
97 }
98 
99 static int _slot_set_status(struct ringfs *fs, struct ringfs_loc *loc, uint32_t status)
100 {
101  return fs->flash->program(_slot_address(fs, loc) + offsetof(struct slot_header, status),
102  &status, sizeof(status));
103 }
104 
111 static bool _loc_equal(struct ringfs_loc *a, struct ringfs_loc *b)
112 {
113  return (a->sector == b->sector) && (a->slot == b->slot);
114 }
115 
117 static void _loc_advance_sector(struct ringfs *fs, struct ringfs_loc *loc)
118 {
119  loc->slot = 0;
120  loc->sector++;
121  if (loc->sector >= fs->flash->sector_count)
122  loc->sector = 0;
123 }
124 
126 static void _loc_advance_slot(struct ringfs *fs, struct ringfs_loc *loc)
127 {
128  loc->slot++;
129  if (loc->slot >= fs->slots_per_sector)
130  _loc_advance_sector(fs, loc);
131 }
132 
137 /* And here we go. */
138 
139 int ringfs_init(struct ringfs *fs, const struct ringfs_flash_partition *flash, uint32_t version, int object_size)
140 {
141  /* Copy arguments to instance. */
142  fs->flash = flash;
143  fs->version = version;
144  fs->object_size = object_size;
145 
146  /* Precalculate commonly used values. */
147  fs->slots_per_sector = (fs->flash->sector_size - sizeof(struct sector_header)) /
148  (sizeof(struct slot_header) + fs->object_size);
149 
150  return 0;
151 }
152 
153 int ringfs_format(struct ringfs *fs)
154 {
155  /* Mark all sectors to prevent half-erased filesystems. */
156  for (int sector=0; sector<fs->flash->sector_count; sector++)
158 
159  /* Erase, update version, mark as free. */
160  for (int sector=0; sector<fs->flash->sector_count; sector++)
161  _sector_free(fs, sector);
162 
163  /* Start reading & writing at the first sector. */
164  fs->read.sector = 0;
165  fs->read.slot = 0;
166  fs->write.sector = 0;
167  fs->write.slot = 0;
168  fs->cursor.sector = 0;
169  fs->cursor.slot = 0;
170 
171  return 0;
172 }
173 
174 int ringfs_scan(struct ringfs *fs)
175 {
176  uint32_t previous_sector_status = SECTOR_FREE;
177  /* The read sector is the first IN_USE sector *after* a FREE sector
178  * (or the first one). */
179  int read_sector = 0;
180  /* The write sector is the last IN_USE sector *before* a FREE sector
181  * (or the last one). */
182  int write_sector = fs->flash->sector_count - 1;
183  /* There must be at least one FREE sector available at all times. */
184  bool free_seen = false;
185  /* If there's no IN_USE sector, we start at the first one. */
186  bool used_seen = false;
187 
188  /* Iterate over sectors. */
189  for (int sector=0; sector<fs->flash->sector_count; sector++) {
190  int addr = _sector_address(fs, sector);
191 
192  /* Read sector header. */
193  struct sector_header header;
194  fs->flash->read(addr, &header, sizeof(header));
195 
196  /* Detect partially-formatted partitions. */
197  if (header.status == SECTOR_FORMATTING) {
198  printf("ringfs_scan: partially formatted partition\r\n");
199  return -1;
200  }
201 
202  /* Detect and fix partially erased sectors. */
203  if (header.status == SECTOR_ERASING || header.status == SECTOR_ERASED) {
204  _sector_free(fs, addr);
205  header.status = SECTOR_FREE;
206  }
207 
208  /* Detect corrupted sectors. */
209  if (header.status != SECTOR_FREE && header.status != SECTOR_IN_USE) {
210  printf("ringfs_scan: corrupted sector %d\r\n", sector);
211  return -1;
212  }
213 
214  /* Detect obsolete versions. We can't do this earlier because the version
215  * could have been invalid due to a partial erase. */
216  if (header.version != fs->version) {
217  printf("ringfs_scan: incompatible version 0x%08"PRIx32"\r\n", header.version);
218  return -1;
219  }
220 
221  /* Record the presence of a FREE sector. */
222  if (header.status == SECTOR_FREE)
223  free_seen = true;
224 
225  /* Update read & write sectors according to the above rules. */
226  if (header.status == SECTOR_IN_USE && previous_sector_status == SECTOR_FREE)
227  read_sector = sector;
228  if (header.status == SECTOR_FREE && previous_sector_status == SECTOR_IN_USE)
229  write_sector = sector-1;
230 
231  previous_sector_status = header.status;
232  }
233 
234  /* Detect the lack of a FREE sector. */
235  if (!free_seen) {
236  printf("ringfs_scan: invariant violated: no FREE sector found\r\n");
237  return -1;
238  }
239 
240  /* Start writing at the first sector if the filesystem is empty. */
241  if (!used_seen) {
242  write_sector = 0;
243  }
244 
245  /* Scan the write sector and skip all occupied slots at the beginning. */
246  fs->write.sector = write_sector;
247  fs->write.slot = 0;
248  while (fs->write.sector == write_sector) {
249  uint32_t status;
250  _slot_get_status(fs, &fs->write, &status);
251  if (status == SLOT_ERASED)
252  break;
253 
254  _loc_advance_slot(fs, &fs->write);
255  }
256  /* If the sector was full, we're at the beginning of a FREE sector now. */
257 
258  /* Position the read head at the start of the first IN_USE sector, then skip
259  * over garbage/invalid slots until something of value is found or we reach
260  * the write head which means there's no data. */
261  fs->read.sector = read_sector;
262  fs->read.slot = 0;
263  while (!_loc_equal(&fs->read, &fs->write)) {
264  uint32_t status;
265  _slot_get_status(fs, &fs->read, &status);
266  if (status == SLOT_VALID)
267  break;
268 
269  _loc_advance_slot(fs, &fs->read);
270  }
271 
272  /* Move the read cursor to the read head position. */
273  fs->cursor = fs->read;
274 
275  return 0;
276 }
277 
278 int ringfs_capacity(struct ringfs *fs)
279 {
280  return fs->slots_per_sector * (fs->flash->sector_count - 1);
281 }
282 
284 {
285  int sector_diff = (fs->write.sector - fs->read.sector + fs->flash->sector_count) %
286  fs->flash->sector_count;
287 
288  return sector_diff * fs->slots_per_sector + fs->write.slot - fs->read.slot;
289 }
290 
291 int ringfs_count_exact(struct ringfs *fs)
292 {
293  int count = 0;
294 
295  /* Use a temporary loc for iteration. */
296  struct ringfs_loc loc = fs->read;
297  while (!_loc_equal(&loc, &fs->write)) {
298  uint32_t status;
299  _slot_get_status(fs, &loc, &status);
300 
301  if (status == SLOT_VALID)
302  count++;
303 
304  _loc_advance_slot(fs, &loc);
305  }
306 
307  return count;
308 }
309 
310 int ringfs_append(struct ringfs *fs, const void *object)
311 {
312  uint32_t status;
313 
314  /*
315  * There are three sectors involved in appending a value:
316  * - the sector where the append happens: it has to be writable
317  * - the next sector: it must be free (invariant)
318  * - the next-next sector: read & cursor heads are moved there if needed
319  */
320 
321  /* Make sure the next sector is free. */
322  int next_sector = (fs->write.sector+1) % fs->flash->sector_count;
323  _sector_get_status(fs, next_sector, &status);
324  if (status != SECTOR_FREE) {
325  /* Next sector must be freed. But first... */
326 
327  /* Move the read & cursor heads out of the way. */
328  if (fs->read.sector == next_sector)
329  _loc_advance_sector(fs, &fs->read);
330  if (fs->cursor.sector == next_sector)
331  _loc_advance_sector(fs, &fs->cursor);
332 
333  /* Free the next sector. */
334  _sector_free(fs, next_sector);
335  }
336 
337  /* Now we can make sure the current write sector is writable. */
338  _sector_get_status(fs, fs->write.sector, &status);
339  if (status == SECTOR_FREE) {
340  /* Free sector. Mark as used. */
341  _sector_set_status(fs, fs->write.sector, SECTOR_IN_USE);
342  } else if (status != SECTOR_IN_USE) {
343  printf("ringfs_append: corrupted filesystem\r\n");
344  return -1;
345  }
346 
347  /* Preallocate slot. */
349 
350  /* Write object. */
351  fs->flash->program(_slot_address(fs, &fs->write) + sizeof(struct slot_header),
352  object, fs->object_size);
353 
354  /* Commit write. */
355  _slot_set_status(fs, &fs->write, SLOT_VALID);
356 
357  /* Advance the write head. */
358  _loc_advance_slot(fs, &fs->write);
359 
360  return 0;
361 }
362 
363 int ringfs_fetch(struct ringfs *fs, void *object)
364 {
365  /* Advance forward in search of a valid slot. */
366  while (!_loc_equal(&fs->cursor, &fs->write)) {
367  uint32_t status;
368 
369  _slot_get_status(fs, &fs->cursor, &status);
370 
371  if (status == SLOT_VALID) {
372  fs->flash->read(_slot_address(fs, &fs->cursor) + sizeof(struct slot_header),
373  object, fs->object_size);
374  _loc_advance_slot(fs, &fs->cursor);
375  return 0;
376  }
377 
378  _loc_advance_slot(fs, &fs->cursor);
379  }
380 
381  return -1;
382 }
383 
384 int ringfs_discard(struct ringfs *fs)
385 {
386  while (!_loc_equal(&fs->read, &fs->cursor)) {
388  _loc_advance_slot(fs, &fs->read);
389  }
390 
391  return 0;
392 }
393 
394 int ringfs_rewind(struct ringfs *fs)
395 {
396  fs->cursor = fs->read;
397  return 0;
398 }
399 
404 /* vim: set ts=4 sw=4 et: */
int sector_size
Sector size, in bytes.
Definition: ringfs.h:25
int ringfs_format(struct ringfs *fs)
Format the flash memory.
Definition: ringfs.c:153
static int _sector_get_status(struct ringfs *fs, int sector, uint32_t *status)
Definition: ringfs.c:47
struct ringfs_loc write
Definition: ringfs.h:73
The entire partition is being formatted.
Definition: ringfs.c:34
Sector erased.
Definition: ringfs.c:31
uint32_t status
Definition: ringfs.c:83
static void _loc_advance_slot(struct ringfs *fs, struct ringfs_loc *loc)
Advance a location to the next slot, advancing the sector too if needed.
Definition: ringfs.c:126
int sector_count
Partition size, in sectors.
Definition: ringfs.h:27
int ringfs_capacity(struct ringfs *fs)
Calculate maximum RingFS capacity.
Definition: ringfs.c:278
Default state after NOR flash erase.
Definition: ringfs.c:76
int ringfs_init(struct ringfs *fs, const struct ringfs_flash_partition *flash, uint32_t version, int object_size)
Initialize a RingFS instance.
Definition: ringfs.c:139
Sector should be erased.
Definition: ringfs.c:33
Flash memory+parition descriptor.
Definition: ringfs.h:23
int object_size
Definition: ringfs.h:67
static int _slot_set_status(struct ringfs *fs, struct ringfs_loc *loc, uint32_t status)
Definition: ringfs.c:99
sector_status
Definition: ringfs.c:29
Write started but not yet committed.
Definition: ringfs.c:77
static int _sector_address(struct ringfs *fs, int sector_offset)
Definition: ringfs.c:42
ssize_t(* program)(int address, const void *data, size_t size)
Program flash memory bits by toggling them from 1 to 0.
Definition: ringfs.h:42
struct ringfs_loc cursor
Definition: ringfs.h:74
int ringfs_count_exact(struct ringfs *fs)
Calculate exact object count.
Definition: ringfs.c:291
int slots_per_sector
Definition: ringfs.h:69
Default state after NOR flash erase.
Definition: ringfs.c:30
ssize_t(* read)(int address, void *data, size_t size)
Read flash memory.
Definition: ringfs.h:50
int ringfs_count_estimate(struct ringfs *fs)
Calculate approximate object count.
Definition: ringfs.c:283
static int _sector_free(struct ringfs *fs, int sector)
Definition: ringfs.c:59
static bool _loc_equal(struct ringfs_loc *a, struct ringfs_loc *b)
Definition: ringfs.c:111
uint32_t status
Definition: ringfs.c:38
slot_status
Definition: ringfs.c:75
int ringfs_discard(struct ringfs *fs)
Discard all fetched objects up to the read cursor.
Definition: ringfs.c:384
RingFS instance.
Definition: ringfs.h:63
int ringfs_scan(struct ringfs *fs)
Scan the flash memory for a valid filesystem.
Definition: ringfs.c:174
int ringfs_fetch(struct ringfs *fs, void *object)
Fetch next object from the ring, oldest-first.
Definition: ringfs.c:363
static void _loc_advance_sector(struct ringfs *fs, struct ringfs_loc *loc)
Advance a location to the beginning of the next sector.
Definition: ringfs.c:117
uint32_t version
Definition: ringfs.h:66
struct ringfs_loc read
Definition: ringfs.h:72
int sector_offset
Partition offset, in sectors.
Definition: ringfs.h:26
uint32_t version
Definition: ringfs.c:39
static int _sector_set_status(struct ringfs *fs, int sector, uint32_t status)
Definition: ringfs.c:53
static const struct ringfs_flash_partition flash
Definition: tests.c:81
static int _slot_get_status(struct ringfs *fs, struct ringfs_loc *loc, uint32_t *status)
Definition: ringfs.c:93
int ringfs_rewind(struct ringfs *fs)
Rewind the read cursor back to the oldest object.
Definition: ringfs.c:394
static int _slot_address(struct ringfs *fs, struct ringfs_loc *loc)
Definition: ringfs.c:86
Write committed, slot contains valid data.
Definition: ringfs.c:78
Slot contents discarded and no longer valid.
Definition: ringfs.c:79
int(* sector_erase)(int address)
Erase a sector.
Definition: ringfs.h:34
const struct ringfs_flash_partition * flash
Definition: ringfs.h:65
Sector contains valid data.
Definition: ringfs.c:32
int ringfs_append(struct ringfs *fs, const void *object)
Append an object at the end of the ring.
Definition: ringfs.c:310