comparison src/cs/drivers/drv_app/ffs/board/reclaim.c @ 0:b6a5e36de839

src/cs: initial import from Magnetite
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 15 Jul 2018 04:39:26 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:b6a5e36de839
1 /******************************************************************************
2 * Flash File System (ffs)
3 * Idea, design and coding by Mads Meisner-Jensen, mmj@ti.com
4 *
5 * FFS core reclaim functionality
6 *
7 * $Id: reclaim.c 1.4.1.28 Thu, 08 Jan 2004 15:05:23 +0100 tsj $
8 *
9 ******************************************************************************/
10
11 #ifndef TARGET
12 #include "ffs.cfg"
13 #endif
14
15 #include "ffs/ffs.h"
16 #include "ffs/board/core.h"
17 #include "ffs/board/drv.h"
18 #include "ffs/board/ffstrace.h"
19
20 #include <stdlib.h> // rand()
21
22 /******************************************************************************
23 * Inodes Reclaim
24 ******************************************************************************/
25
26 void inodes_recurse(iref_t i)
27 {
28 iref_t pi;
29 struct inode_s *ip, *newip;
30
31 tw(tr(TR_BEGIN, TrReclaimLow, "inodes_recurse(%d) {\n", i));
32
33 ip = inode_addr(i);
34 newip = (struct inode_s *) offset2addr(dev.binfo[fs.newinodes].offset) + i;
35
36 // copy inode dir to new block, except child, sibling and copied
37 ffsdrv.write((uint32*) &newip->location, (uint32*) &ip->location, sizeof(location_t));
38 ffsdrv.write_halfword((uint16*) &newip->size, ip->size);
39 ffsdrv_write_byte (&newip->flags, ip->flags);
40 ffsdrv.write_halfword((uint16*) &newip->sequence, ip->sequence);
41 ffsdrv.write_halfword((uint16*) &newip->updates, ip->updates);
42 bstat[fs.newinodes].used++;
43
44 // if no children of this dir, we have no more work to do
45 if (ip->child == (iref_t) IREF_NULL) {
46 tw(tr(TR_END, TrReclaimLow, "}\n"));
47 return;
48 }
49
50 pi = -i;
51 i = ip->child;
52 ip = inode_addr(i);
53
54 do {
55 tw(tr(TR_FUNC, TrReclaimLow, "pi = %d, i = %d", pi, i));
56
57 tw(tr(TR_NULL, TrReclaimLow, ", size = %d, location = 0x%x", ip->size,
58 ip->location));
59
60 tw(tr(TR_NULL, TrReclaimLow, ", name_addr = 0x%x",
61 addr2name(offset2addr(location2offset(ip->location)))));
62
63 if (is_object(ip, OT_SEGMENT))
64 tw(tr(TR_NULL, TrReclaimLow, ", (segment)\n"));
65
66 else
67 tw(tr(TR_NULL, TrReclaimLow, ", '%s'\n",
68 (ip->size ? addr2name(offset2addr(location2offset(ip->location)))
69 : "(cleaned)")));
70
71 if (is_object_valid(ip))
72 {
73 if (is_object(ip, OT_DIR)) {
74 tw(tr(TR_NULL, TrReclaimLow, "recursing...\n", i));
75 inodes_recurse(i);
76 }
77 else {
78 tw(tr(TR_NULL, TrReclaimLow, "copying...\n"));
79 // copy inode to new block, except child, sibling and copied
80 newip = (struct inode_s *)
81 offset2addr(dev.binfo[fs.newinodes].offset) + i;
82 ffsdrv.write((uint32*) &newip->location, (uint32*) &ip->location, sizeof(location_t));
83 ffsdrv.write_halfword((uint16*) &newip->size, ip->size);
84 ffsdrv_write_byte (&newip->flags, ip->flags);
85 ffsdrv.write_halfword((uint16*) &newip->sequence, ip->sequence);
86 ffsdrv.write_halfword((uint16*) &newip->updates, ip->updates);
87 bstat[fs.newinodes].used++;
88 }
89
90 tw(tr(TR_FUNC, TrReclaimLow, "Linking: %d->%d\n",pi, i));
91 // now write the child or sibling link of previous inode
92 newip = (struct inode_s *)
93 offset2addr(dev.binfo[fs.newinodes].offset);
94 if (pi > 0)
95 ffsdrv.write_halfword((uint16*) &(newip + pi)->sibling, i);
96 else
97 ffsdrv.write_halfword((uint16*) &(newip + (-pi))->child, i);
98
99 pi = i; // save index of previous inode
100
101 if (ip->child != (iref_t) IREF_NULL && is_object(ip, OT_FILE)) {
102 iref_t pis, is;
103 struct inode_s *ips;
104 pis = i;
105 ips = ip;
106
107 tw(tr(TR_FUNC, TrReclaimLow, "Follow segment head\n"));
108 // While child is valid
109 while ((is = ips->child) != (iref_t) IREF_NULL) {
110
111 // Get child
112 is = ips->child;
113 ips = inode_addr(is);
114 tw(tr(TR_FUNC, TrReclaimLow, "Child ok, got new child i = %d\n", is));
115 // While object not is valid
116 while (!is_object_valid(ips)) {
117 tw(tr(TR_FUNC, TrReclaimLow, "pi = %d, i = %d c(cleaned)\n", pis, is));
118 // If sibling are valid
119 if (ips->sibling != (iref_t) IREF_NULL) {
120 // Get sibling
121 is = ips->sibling;
122 ips = inode_addr(is);
123 tw(tr(TR_FUNC, TrReclaimLow, "Sibling ok, got new sibling i = %d\n", is));
124 }
125 else {
126 tw(tr(TR_FUNC, TrReclaimLow, "Sibling = FF (%d)\n", ips->sibling));
127 break; // Nothing more todo, child and sibling = FF
128 }
129 }
130 // If object is valid
131 if (is_object_valid(ips)) {
132 tw(tr(TR_NULL, TrReclaimLow, "copying...\n"));
133 // copy inode to new block, except child, sibling and copied
134 newip = (struct inode_s *)
135 offset2addr(dev.binfo[fs.newinodes].offset) + is;
136 ffsdrv.write((uint32*) &newip->location, (uint32*) &ips->location, sizeof(location_t));
137 ffsdrv.write_halfword((uint16*) &newip->size, ips->size);
138 ffsdrv_write_byte (&newip->flags, ips->flags);
139 ffsdrv.write_halfword((uint16*) &newip->sequence, ips->sequence);
140 ffsdrv.write_halfword((uint16*) &newip->updates, ips->updates);
141 bstat[fs.newinodes].used++;
142
143 tw(tr(TR_FUNC, TrReclaimLow, "Linking child: %d->%d\n",pis, is));
144 // now write the child link of previous inode
145 newip = (struct inode_s *)
146 offset2addr(dev.binfo[fs.newinodes].offset);
147 ffsdrv.write_halfword((uint16*) &(newip + (pis))->child, is);
148
149 pis = is; // save index of previous inode
150
151 }
152 else {
153 tw(tr(TR_FUNC, TrReclaimLow, "Sibling = FF (%d, %d)\n",
154 ips->sibling, ips->child));
155 }
156
157 }
158 }
159 }
160 else {
161 tw(tr(TR_NULL, TrReclaimLow, "(ignoring)\n"));
162 }
163 i = ip->sibling;
164 ip = inode_addr(i);
165
166 } while (i != (iref_t) IREF_NULL);
167
168 tw(tr(TR_END, TrReclaimLow, "}\n"));
169 }
170
171 // Reclaim inodes, eg. move inodes to another block and erase old one.
172 effs_t inodes_reclaim(void)
173 {
174 tw(tr(TR_BEGIN, TrIReclaim, "inodes_reclaim() {\n"));
175 ttw(str(TTrRec, "irec{"));
176
177 if (fs.initerror != EFFS_OK) {
178 tw(tr(TR_END, TrIReclaim, "} %d\n", fs.initerror));
179 ttw(ttr(TTrRec, "} %d" NL, fs.initerror));
180 return fs.initerror;
181 }
182
183 if ((fs.newinodes = block_alloc(1, BF_COPYING)) < 0) {
184 tw(tr(TR_END, TrIReclaim, "} %d\n", EFFS_NOBLOCKS));
185 ttw(ttr(TTrRec, "} %d" NL, EFFS_NOBLOCKS));
186 return EFFS_NOBLOCKS;
187 }
188
189 statistics_update_irec(bstat[fs.inodes].used - bstat[fs.inodes].lost,
190 bstat[fs.inodes].lost);
191
192 // copy all inodes...
193 bstat[fs.newinodes].used = 0;
194 inodes_recurse(fs.root);
195
196 block_commit();
197
198 tw(tr(TR_END, TrIReclaim, "} 0\n"));
199 ttw(str(TTrRec, "} 0" NL));
200
201 return EFFS_OK;
202 }
203
204 #if (FFS_TEST == 0)
205 #define BLOCK_COMMIT_TEST(testcase, text)
206 #else
207 #if (TARGET == 0)
208 // NOTEME: We have compressed the macro code because it will NOT compile on
209 // Unix otherwise. So until we find out why, we use this as a work-around.
210 #define BLOCK_COMMIT_TEST(testcase, text) if (fs.testflags == testcase) { tw(tr(TR_FUNC, TrData, "} (" text ")\n")); return; }
211 #else
212 #define BLOCK_COMMIT_TEST(testcase, text) if (fs.testflags == testcase) { ttw(ttr(TTrData, "} (" text ")\n")); return; }
213 #endif
214 #endif
215
216 // Inode -> Lost, Copying -> Inode, Lost -> Free
217 void block_commit(void)
218 {
219 int oldinodes = fs.inodes;
220
221 tw(tr(TR_BEGIN, TrIReclaim, "block_commit(%d -> %d) {\n",
222 oldinodes, fs.newinodes));
223 ttw(ttr(TTrRec, "block_commit(%d -> %d) {\n" NL,
224 oldinodes, fs.newinodes));
225
226 BLOCK_COMMIT_TEST(BLOCK_COMMIT_BEFORE, "Oops before commit");
227
228 block_flags_write(oldinodes, BF_LOST);
229
230 BLOCK_COMMIT_TEST(BLOCK_COMMIT_NO_VALID, "Oops no valid inode block");
231
232 // Validate new block as an inodes block
233 block_flags_write(fs.newinodes, BF_INODES);
234
235 bstat[fs.newinodes].lost = 0;
236 bstat[fs.newinodes].objects = 1;
237 inodes_set(fs.newinodes);
238
239 // Free old inodes block
240 block_free(oldinodes);
241
242 BLOCK_COMMIT_TEST(BLOCK_COMMIT_OLD_FREE, "Oops after freeing old block");
243
244 BLOCK_COMMIT_TEST(BLOCK_COMMIT_AFTER, "Oops after commit");
245
246 ttw(str(TTrRec, "} 0" NL));
247 tw(tr(TR_END, TrIReclaim, "}\n"));
248 }
249
250
251 /******************************************************************************
252 * Data Reclaim
253 ******************************************************************************/
254
255 // Important note: We must NOT perform a data reclaim when we are in the
256 // process of creating the journal file!
257
258 // Reclaim a data block, eg. move files to other blocks and erase old one.
259 // When the reclaim is done, we must completely delete the old inodes which
260 // are pointing into the old data sector which is going to be erased now.
261 iref_t data_reclaim(int space)
262 {
263 iref_t error;
264
265 tw(tr(TR_BEGIN, TrDReclaim, "data_reclaim(%d) {\n", space));
266
267 if (fs.initerror != EFFS_OK) {
268 tw(tr(TR_END, TrDReclaim, "} %d\n", fs.initerror));
269 return fs.initerror;
270 }
271
272 error = data_reclaim_try(space);
273
274 tw(tr(TR_END, TrDReclaim, "} (data_reclaim) %d\n", error));
275
276 return error;
277 }
278
279 int dage_max_reached(int dage_blk, int agegain)
280 {
281 int reclaim, early, log2, mask;
282
283 tw(tr(TR_BEGIN, TrDReclaim, "young(%d, %d) {\n", dage_blk, agegain));
284
285 // Simple algorithm
286 reclaim = (dage_blk + agegain - 2 * FFS_DAGE_MAX >= 0);
287
288 // Early exponential probability based reclaim
289 early = FFS_DAGE_MAX - dage_blk;
290 if (agegain > dage_blk - 4 && 0 < early && early <= FFS_DAGE_EARLY_WIDTH) {
291 if (early < 4)
292 early = 2;
293 if (early < FFS_DAGE_EARLY_WIDTH) {
294 // Now make an exponential probability distributon by
295 // generating a bitmask of a size relative to (dage_blk
296 // - DAGE_EARLY_WIDTH)
297 log2 = -1;
298 while (early > 0) {
299 early >>= 1;
300 log2++;
301 }
302 reclaim = log2;
303
304 mask = (1 << (log2 + 1)) - 1;
305 reclaim = ((rand() & mask) == 0);
306 }
307 }
308
309 // Do not perform a reclaim unless we gain a certain minimum
310 if (agegain < FFS_DAGE_GAIN_MIN)
311 reclaim = 0;
312
313 tw(tr(TR_END, TrDReclaim, "} (%d)\n", reclaim));
314 return reclaim;
315 }
316
317
318 // Try to reclaim at least <space> bytes of data space. On success, return
319 // the number of bytes actually reclaimed. Otherwise, on failure, return a
320 // (negative) error.
321 int data_reclaim_try(int space)
322 {
323 // 1. Find a suitable block to reclaim.
324 //
325 // 2. Relocate each valid object from old block (to another block). An
326 // object relocation is similar to a normal file update, e.g. similar to
327 // fupdate().
328 //
329 // 3. If there is not enough space to relocate a file, we must alloc a
330 // new block then data_format() it.
331 //
332 // 4. set BF_CLEANING flag of old block.
333 //
334 // 5. ALL inodes (also invalid an erased ones) referring into reclaimed
335 // block must now be totally wiped out.
336 //
337 // 6. Free (invalidate) old block.
338
339 int result = 0, reserved_ok = 0;
340 bref_t b, blocks_free;
341 bref_t brc_young_b, brc_lost_b, brc_unused_b;
342
343 blocksize_t brc_lost_lost, brc_lost_unused;
344 blocksize_t brc_unused_unused;
345 blocksize_t unused, unused_total, lost, lost_total, free;
346
347 age_t brc_young_dage, free_dage, dage;
348 struct block_header_s *bhp;
349 // Note gain can be negative if the free block is younger than the youngest data block
350 int age_gain;
351
352 tw(tr(TR_BEGIN, TrDReclaim, "data_reclaim_try(%d) {\n", space));
353 ttw(str(TTrRec, "drec{" NL));
354
355 // While searching for a block to reclaim, we maintain three block
356 // reclaim candidates (brc): One with the maximum number of lost bytes,
357 // one with the maximum number of unused bytes and another for the
358 // youngest block, e.g. the one with the largest age distance to
359 // fs.age_max. The candidates are tried in the order mentioned.
360
361 // This counts free blocks, so we initialize to number of blocks minus
362 // one for inodes.
363 blocks_free = dev.numblocks - 1;
364
365 // Initialize Block Reclaim Candidate (brc) variables
366 brc_lost_b = -1; brc_lost_unused = 0; brc_lost_lost = 0;
367 brc_unused_b = -1; brc_unused_unused = 0;
368
369 brc_young_b = -1; brc_young_dage = 0; free_dage = 0;
370
371 lost_total = 0;
372 unused_total = 0;
373
374 tw(tr(TR_FUNC, TrDReclaim,
375 "blk unused lost w/age age dist objs\n"));
376 for (b = 0; b < dev.numblocks; b++)
377 {
378 bhp = (struct block_header_s *) offset2addr(dev.binfo[b].offset);
379
380 if (is_block(b, BF_IS_DATA))
381 {
382 // Record number of lost bytes and number of unused bytes,
383 // eg. total space that would be freed if this block was
384 // reclaimed
385 lost = bstat[b].lost;
386 unused = dev.blocksize - (bstat[b].used - bstat[b].lost);
387 free = dev.blocksize - bstat[b].used;
388
389 lost_total += lost;
390 unused_total += unused;
391
392 if (free >= RESERVED_LOW)
393 reserved_ok = 1;
394 if (lost > brc_lost_lost) {
395 brc_lost_b = b;
396 brc_lost_lost = lost;
397 brc_lost_unused = unused;
398 }
399 if (unused > brc_unused_unused) {
400 brc_unused_b = b;
401 brc_unused_unused = unused;
402 }
403
404 tw(tr(TR_FUNC, TrDReclaim, "%3d %7d %7d ", b, unused, lost));
405
406 dage = saturate_dage(fs.age_max - bhp->age);
407
408 tw(tr(TR_NULL, TrDReclaim, "%6d %5d %4d %3d\n",
409 lost, bhp->age, dage, bstat[b].objects));
410
411 if (dage >= brc_young_dage) {
412 brc_young_b = b;
413 brc_young_dage = dage;
414 }
415 blocks_free--;
416 }
417 else if (is_block(b, BF_IS_FREE)) {
418 unused_total += dev.blocksize;
419
420 // Find youngest free block (in must cases we will only have one free b)
421 dage = saturate_dage(fs.age_max - bhp->age);
422
423 if (dage >= free_dage)
424 free_dage = dage; // Delta age of youngest free block
425 }
426 }
427 tw(tr(TR_FUNC, TrDReclaim, "sum %7d %7d\n", unused_total, lost_total));
428 tw(tr(TR_FUNC, TrDReclaim, "blocks_free = %d, fs.age_max = %d\n", blocks_free, fs.age_max));
429
430 age_gain = brc_young_dage - free_dage; // Same as free - block age
431
432 if (space > unused_total) {
433 // We will never be able to reclaim this amount...
434 result = 0;
435 }
436 else {
437 // No additional blocks (apart from spare block) are free...
438 tw(tr(TR_FUNC, TrDReclaim,
439 "brc_young_dage = %d, brc_lost_unused = %d, brc_unused_unused = %d\n",
440 brc_young_dage, brc_lost_unused, brc_unused_unused));
441
442 if (reserved_ok == 0) {
443 tw(tr(TR_FUNC, TrDReclaim,
444 "No reserved, reclaim most-lost block (%d)\n", brc_unused_b));
445 result = data_block_reclaim(brc_lost_b, MOST_LOST);
446 }
447 else if (dage_max_reached(brc_young_dage, age_gain) > 0 ) {
448 tw(tr(TR_FUNC, TrDReclaim, "Reclaiming youngest block (%d)\n",
449 brc_young_b));
450 result = data_block_reclaim(brc_young_b, YOUNGEST);
451 }
452 else if (brc_lost_unused >= space) {
453 tw(tr(TR_FUNC, TrDReclaim, "Reclaiming most-lost block (%d)\n",
454 brc_lost_b));
455 result = data_block_reclaim(brc_lost_b, MOST_LOST);
456 }
457 else if (brc_unused_unused >= space) {
458 tw(tr(TR_FUNC, TrDReclaim, "Reclaiming most-unused block (%d)\n",
459 brc_unused_b));
460 result = data_block_reclaim(brc_unused_b, MOST_UNUSED);
461 }
462 else {
463 tw(tr(TR_FUNC, TrDReclaim, "Reclaiming most-lost blockx (%d)\n",
464 brc_lost_b));
465 result = data_block_reclaim(brc_lost_b, MOST_LOST);
466 if (result >= 0)
467 result = 0; // We reclaimed a block but we still need more space
468 }
469
470 }
471 tw(tr(TR_END, TrDReclaim, "} (data_reclaim_try) %d\n", result));
472
473 return result;
474 }
475
476
477 #if (FFS_TEST == 0)
478 #define BLOCK_RECLAIM_TEST(testcase, text)
479 #else
480 #if (TARGET == 0)
481 // NOTEME: We have compressed the macro code because it will NOT compile on
482 // Unix otherwise. So until we find out why, we use this as a work-around.
483 #define BLOCK_RECLAIM_TEST(testcase, text) if (fs.testflags == testcase) { tw(tr(TR_FUNC, TrTestHigh, "(" text ")\n")); tw(tr(TR_END, TrDReclaim, "} (Test) -100\n", result));return -100; }
484 #else
485 #define BLOCK_RECLAIM_TEST(testcase, text) if (fs.testflags == testcase) { ttw(ttr(TTrData, "} (" text ")"NL)); ttw(ttr(TTrRec, "} (Test) -100" NL));return -100; }
486 #endif
487 #endif
488
489 #if (FFS_TEST == 0)
490 #define BLOCK_RECOVER_TEST_INIT(testcase, text)
491 #define BLOCK_RECOVER_TEST(testcase, text)
492 #else
493 #if (TARGET == 0)
494 #define BLOCK_RECOVER_TEST_INIT(testcase, text) int rand_object; if (fs.testflags == testcase) { rand_object = rand() % bstat[b].objects; tw(tr(TR_FUNC, TrTestHigh, "Fail when object nr %d is relocated\n", rand_object)); }
495
496 #define BLOCK_RECOVER_TEST(testcase, text) if (fs.testflags == testcase) {if (rand_object == n) { tw(tr(TR_FUNC, TrTestHigh, "(" text ")\n")); tw(tr(TR_END, TrDReclaim, "} (Test) -101\n", result)); return -101; } }
497
498 #else
499 #define BLOCK_RECOVER_TEST_INIT(testcase, text) int rand_object; if (fs.testflags == testcase) { rand_object = rand() % bstat[b].objects; ttw(ttr(TTrData, "Fail when object nr %d is relocated" NL, rand_object)); }
500 #define BLOCK_RECOVER_TEST(testcase, text) if (fs.testflags == testcase) {if (rand_object == n) { ttw(ttr(TTrData, "(" text ")" NL)); ttw(ttr(TTrRec, "} (Test) -101" NL, result)); return -101; } }
501 #endif
502 #endif
503
504 iref_t data_block_reclaim(bref_t b, int candidate)
505 {
506 iref_t i, n, j;
507 blocksize_t used_old, lost_old;
508 int org_res_space, result = 0;
509 iref_t org_block_files_reserved;
510 offset_t lower, upper;
511 struct inode_s *ip;
512 static int is_reclaim_running = 0;
513
514 tw(tr(TR_BEGIN, TrDReclaim, "data_block_reclaim(%d) {\n", b));
515
516 // In case of no free blocks (after sudden power off) or if the file
517 // system is near full we risk to be reentered (infinity recursively
518 // loop) and we can not allow that, so just return.
519 if (is_reclaim_running == 1) {
520 tw(tr(TR_END, TrDReclaim, "} (reenteret skip reclaim) 0\n"));
521 return EFFS_RECLAIMLOOP;
522 }
523
524 is_reclaim_running = 1;
525
526 // If there are more objects in this block than there are remaining
527 // free inodes, we have to make an inodes_reclaim() first.
528 tw(tr(TR_FUNC, TrDReclaim,
529 "block_objects, fs.inodes_max, inodes: used, free\n"));
530 tw(tr(TR_FUNC, TrDReclaim,
531 "%10d, %13d, %15d, %4d\n",
532 bstat[b].objects,
533 fs.inodes_max, bstat[fs.inodes].used,
534 fs.inodes_max - (bstat[fs.inodes].used + bstat[fs.inodes].lost)));
535
536 if (bstat[b].objects >= (fs.inodes_max - (bstat[fs.inodes].used +
537 bstat[fs.inodes].lost +
538 FFS_INODES_MARGIN))) {
539 tw(tr(TR_FUNC, TrInode, "NOTE: Will run out of free inodes...\n"));
540 inodes_reclaim();
541 }
542
543 // Allocate a new block. NOTE: we don't return an error because if we
544 // get in the situation where we don't have any free blocks this is the
545 // only way to recover.
546 if ((result = block_alloc(1, BF_DATA)) < 0) {
547 tw(tr(TR_FUNC, TrAll, "WARNING: block_alloc failed\n"));
548 }
549
550 BLOCK_RECLAIM_TEST(BLOCK_RECLAIM_ALLOC, "Oops after ffs_block_alloc()");
551
552 // If there are any objects at all to reclaim...
553 if (bstat[b].objects > 0)
554 {
555 BLOCK_RECOVER_TEST_INIT(BLOCK_RECOVER_OBJECTS, "Dummy")
556 // Save the current journal state
557 if (journal_push() != EFFS_OK) {
558 is_reclaim_running = 0; // NOTEME: change to goto?
559 return EFFS_CORRUPTED;
560 }
561
562 // We simulate that this block is completely full, such that we
563 // don't relocate files to the end of the block
564 used_old = bstat[b].used;
565 lost_old = bstat[b].lost; // For statistics
566 bstat[b].used = dev.blocksize - 1;
567
568
569 // Compute lower (inclusive) and upper (exclusive) bounds of the
570 // location of files in this block
571 lower = offset2location(dev.binfo[b].offset);
572 upper = offset2location(dev.binfo[b].offset + dev.blocksize);
573
574 tw(tr(TR_FUNC, TrDReclaim, "Block addr range = 0x%X..0x%X\n",
575 location2offset(lower), location2offset(upper)));
576
577 // This is the only time we are allowed to use the reserved
578 org_block_files_reserved= fs.block_files_reserved;
579 fs.block_files_reserved = 0;
580
581 org_res_space = fs.reserved_space;
582 fs.reserved_space = RESERVED_NONE;
583
584 ip = inode_addr(1);
585 for (i = 1, n = 0; i < fs.inodes_max; i++, ip++)
586 {
587 BLOCK_RECOVER_TEST(BLOCK_RECOVER_OBJECTS, "Oops before relocate all objects");
588 // Ensure object is valid and within the block to be reclaimed
589 if (is_object_valid(ip) &&
590 lower <= ip->location && ip->location < upper)
591 {
592 if ((result = object_relocate(i)) < 0) {
593 tw(tr(TR_FUNC, TrAll, "FATAL object_relocate failed\n"));
594 break;
595 }
596
597 // If we reclaim a segment head or wch that is in use we must
598 // update the file descriptor as well
599 for (j = 0; j < fs.fd_max; j++) {
600 if (i == fs.fd[j].seghead) {
601 tw(tr(TR_FUNC, TrDReclaim,
602 "Updated seghead %d -> %d \n",
603 fs.fd[j].seghead, result));
604 fs.fd[j].seghead = result;
605 }
606 if (i == fs.fd[j].wch) {
607 tw(tr(TR_FUNC, TrDReclaim,
608 "Updated wch %d -> %d \n",
609 fs.fd[j].wch, result));
610 fs.fd[j].wch = result;
611 }
612 }
613
614 // If we have just reclaimed an object which we started on
615 // updating we must also update ojournal
616 if (i == fs.ojournal.oldi) {
617 struct inode_s *ip = inode_addr(result);
618 tw(tr(TR_FUNC, TrDReclaim,
619 "Updated ojournal oldi %d -> %d \n",
620 fs.ojournal.oldi, result));
621 fs.ojournal.oldi = result;
622 fs.ojournal.location = ip->location;
623 }
624
625 if (i == fs.ojournal.diri || i == -fs.ojournal.diri) {
626 fs.ojournal.diri = (fs.ojournal.diri < 0 ? -result : result);
627 tw(tr(TR_FUNC, TrDReclaim,
628 "Updated ojournal: diri %d -> %d \n",
629 i, fs.ojournal.diri));
630 }
631
632 if (i == fs.ojournal.repli || i == -fs.ojournal.repli) {
633 fs.ojournal.repli = (fs.ojournal.repli < 0 ? -result : result);
634 tw(tr(TR_FUNC, TrDReclaim,
635 "Updated ojournal: repli %d -> %d \n",
636 i, fs.ojournal.repli));
637 }
638
639 if (i == fs.i_backup || i == -fs.i_backup) {
640 fs.i_backup = (fs.i_backup < 0 ? -result : result);
641 tw(tr(TR_FUNC, TrDReclaim,
642 "Updated i_backup: %d -> %d \n", i, fs.i_backup));
643 }
644
645 n++;
646 }
647 }
648
649 fs.block_files_reserved = org_block_files_reserved; // Restore
650 fs.reserved_space = org_res_space;
651
652 tw(tr(TR_FUNC, TrDReclaim, "Reclaimed %d objects\n", n));
653 if (result >= 0)
654 result = n; // We return number of objects relocated
655
656 if (i < fs.inodes_max) {
657 // We did not finish, so restore the old bstat[].used of the block.
658 bstat[b].used = used_old;
659 tw(tr(TR_FUNC, TrAll,
660 "WARNING: data_block_reclaim() not completed\n"));
661 result = EFFS_DBR;
662 }
663
664 // Restore the saved journal state
665 if (journal_pop() != EFFS_OK) {
666 is_reclaim_running = 0; // NOTEME: change to goto?
667 return EFFS_CORRUPTED;
668 }
669 }
670 BLOCK_RECLAIM_TEST(BLOCK_RECLAIM_NO_CLEAN, "Oops before clean old data block");
671
672 if (result >= 0) {
673 // Clean the block (remove all inodes that refer to this block)
674 block_flags_write(b, BF_CLEANING);
675 block_clean(b);
676
677 statistics_update_drec(used_old - lost_old, lost_old, candidate);
678
679 BLOCK_RECLAIM_TEST(BLOCK_RECLAIM_CLEANING, "Oops before free old data block");
680
681 // Free the old block
682 block_free(b);
683 }
684
685 is_reclaim_running = 0;
686
687 tw(tr(TR_END, TrDReclaim, "} (data_block_reclaim) %d\n", result));
688 ttw(ttr(TTrRec, "} %d" NL, result));
689
690 return result;
691 }
692
693 // Relocate object represented by inode reference <i>.
694 iref_t object_relocate(iref_t oldi)
695 {
696 iref_t newi;
697 struct inode_s *oldip;
698 char *olddata, *oldname;
699 int oldsize;
700
701 tw(tr(TR_BEGIN, TrReclaimLow, "object_relocate(%d) {\n", oldi));
702
703 journal_begin(oldi);
704
705 oldip = inode_addr(oldi);
706
707 oldsize = segment_datasize(oldip);
708 olddata = offset2addr(location2offset(oldip->location));
709 oldname = addr2name(olddata);
710 olddata = addr2data(olddata, oldip);
711
712 if (is_object(oldip, OT_SEGMENT))
713 newi = segment_create(olddata, oldsize, -oldi);
714 else {
715 // root inode is a special case
716 if (*oldname == '/')
717 newi = object_create(oldname, olddata, oldsize, 0);
718 else
719 newi = object_create(oldname, olddata, oldsize, oldi);
720 }
721
722 if (newi < 0) {
723 tw(tr(TR_END, TrReclaimLow, "} %d\n", newi));
724 return newi;
725 }
726
727 // root inode is a special case
728 if ((*oldname == '/') && !is_object(oldip, OT_SEGMENT)) {
729 tw(tr(TR_FUNC, TrDReclaim, "Relocating fs.root: %d->%d\n", oldi, newi));
730 fs.root = newi;
731 }
732
733 journal_end(0);
734
735 tw(tr(TR_END, TrReclaimLow, "} %d\n", newi));
736
737 return newi;
738 }
739
740 // Clean a block, eg. erase all inodes that refer to this block.
741 iref_t block_clean(bref_t b)
742 {
743 iref_t i, n;
744 struct inode_s *ip;
745 offset_t lower, upper;
746
747 tw(tr(TR_FUNC, TrDReclaim, "block_clean(%d) { ", b));
748
749 // Compute lower (inclusive) and upper (exclusive) bounds of the
750 // location of files in this block
751 lower = offset2location(dev.binfo[b].offset);
752 upper = offset2location(dev.binfo[b].offset + dev.blocksize);
753
754 tw(tr(TR_FUNC, TrDReclaim, "offset range = 0x%X..0x%X: ", lower, upper));
755
756 ip = inode_addr(1);
757 for (i = 1, n = 0; i < fs.inodes_max; i++, ip++)
758 {
759 // Ensure object is within the block to be reclaimed. Note: if ffs
760 // is conf. with 1MB or above will all not used inodes default have
761 // the location to FFFF which will trigger a clean and make a error!
762 if (lower <= ip->location && upper > ip->location)
763 {
764 tw(tr(TR_NULL, TrReclaimLow, "%d ", i));
765 // Set the size to zero so it won't be counted in ffs_initialize()
766 ffsdrv.write_halfword((uint16 *) &ip->size, 0);
767 n++;
768 }
769 }
770 tw(tr(TR_NULL, TrDReclaim, "} %d\n", n));
771
772 return n;
773 }
774
775
776 /******************************************************************************
777 * Main and block reclaim
778 ******************************************************************************/
779
780 // Reclaim (erase) all blocks that are marked as invalid/reclaimable. Each
781 // time a block is erased, its age is incremented so as to support wear
782 // levelling. Also, the global age limits are updated. FIXME: Should we
783 // avoid having ffs_initialize() do a block_reclaim() because it delays reboot?.
784 int blocks_reclaim(void)
785 {
786 bref_t b, n, b_lost_space;
787 int blocks_free = 0, lost_space;
788
789 int free_space, b_free_space;
790
791 tw(tr(TR_BEGIN, TrBlock, "blocks_reclaim() {\n"));
792 ttw(str(TTrRec, "blocks_reclaim() {" NL));
793
794 // Testing of fs.testflags is for the sake of testing block_commit()
795 if ((fs.testflags & BLOCK_COMMIT_BASE) != 0) {
796 tw(tr(TR_FUNC, TrBlock, "Bailing out because fs.testflags = 0x%X\n",
797 fs.testflags));
798 }
799 else {
800 for (b = 0, n = 0; b < dev.numblocks; b++) {
801 if (is_block_flag(b, BF_LOST)) {
802 block_reclaim(b);
803 n++;
804 }
805 if (is_block(b, BF_IS_FREE)) {
806 blocks_free++;
807 }
808 }
809 }
810
811 // If the number of free blocks is less than fs.blocks_free_min we
812 // call data_block_reclaim(). We will reclaim the block with most lost
813 // space. This should only happend if we got a sudden power off/reset
814 // while we reclaimed a block.
815 if (blocks_free < fs.blocks_free_min) {
816 lost_space = 0;
817 free_space = 0;
818
819 // We most never reclaim the block with most free space because this
820 // is the only block we can relocate the objects to.
821 for (b = 0; b < dev.numblocks; b++) {
822 if (is_block_flag(b, BF_DATA)) {
823 if ((dev.blocksize - bstat[b].used) > free_space) {
824 free_space = dev.blocksize - bstat[b].used;
825 b_free_space = b;
826 }
827 }
828 }
829 tw(tr(TR_FUNC, TrBlock, "most free space: %d in block: %d \n",
830 free_space, b_free_space));
831
832 for (b = 0; b < dev.numblocks; b++) {
833 if (is_block_flag(b, BF_DATA) && b != b_free_space) {
834 if (bstat[b].lost > lost_space) {
835 lost_space = bstat[b].lost;
836 b_lost_space = b;
837 }
838 }
839 }
840 tw(tr(TR_FUNC, TrBlock, "most lost space: %d in block: %d \n",
841 lost_space, b_lost_space));
842
843 data_block_reclaim(b_lost_space, MOST_LOST);
844 }
845 tw(tr(TR_END, TrBlock, "} %d\n", n));
846 ttw(ttr(TTrRec, "} %d" NL, n));
847
848 return n;
849 }
850
851 int block_reclaim(bref_t b)
852 {
853 age_t age;
854 struct block_header_s *bhp;
855
856 tw(tr(TR_BEGIN, TrBlock, "block_reclaim(%d) {\n", b));
857
858 // In ffs_initialize() we set fs.initerror = EFFS_INVALID while we call
859 // blocks_fsck(). We test for that condition now, in order to avoid
860 // doing sector erases that will delay the whole target boot process.
861 if (fs.initerror == EFFS_INVALID) {
862 tw(tr(TR_END, TrBlock, "} %d\n", fs.initerror));
863 return fs.initerror;
864 }
865
866 // Testing of fs.testflags is for the sake of testing block_commit()
867 if ((fs.testflags & BLOCK_COMMIT_BASE) != 0 &&
868 fs.testflags != BLOCK_COMMIT_OLD_FREE) {
869 tw(tr(TR_FUNC, TrBlock, "Bailing out because fs.testflags = 0x%X\n",
870 fs.testflags));
871 }
872 else {
873 // We must read block's age before we erase it.
874 bhp = (struct block_header_s *) offset2addr(dev.binfo[b].offset);
875 age = bhp->age;
876 ffsdrv.erase(b);
877 block_preformat(b, age);
878 }
879
880 tw(tr(TR_END, TrBlock, "} %d\n", 0));
881
882 return 0;
883 }