1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23#include <linux/kernel.h>
24#include <asm/errno.h>
25#include <linux/delay.h>
26#include <linux/slab.h>
27#include <linux/mtd/mtd.h>
28#include <linux/mtd/nand.h>
29#include <linux/mtd/nftl.h>
30
31#define SECTORSIZE 512
32
33
34
35
36
37
38static int find_boot_record(struct NFTLrecord *nftl)
39{
40 struct nftl_uci1 h1;
41 unsigned int block, boot_record_count = 0;
42 size_t retlen;
43 u8 buf[SECTORSIZE];
44 struct NFTLMediaHeader *mh = &nftl->MediaHdr;
45 struct mtd_info *mtd = nftl->mbd.mtd;
46 unsigned int i;
47
48
49
50
51
52
53
54 nftl->EraseSize = nftl->mbd.mtd->erasesize;
55 nftl->nb_blocks = (u32)nftl->mbd.mtd->size / nftl->EraseSize;
56
57 nftl->MediaUnit = BLOCK_NIL;
58 nftl->SpareMediaUnit = BLOCK_NIL;
59
60
61 for (block = 0; block < nftl->nb_blocks; block++) {
62 int ret;
63
64
65
66 ret = mtd_read(mtd, block * nftl->EraseSize, SECTORSIZE,
67 &retlen, buf);
68
69
70 if (retlen != SECTORSIZE) {
71 static int warncount = 5;
72
73 if (warncount) {
74 printk(KERN_WARNING "Block read at 0x%x of mtd%d failed: %d\n",
75 block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
76 if (!--warncount)
77 printk(KERN_WARNING "Further failures for this block will not be printed\n");
78 }
79 continue;
80 }
81
82 if (retlen < 6 || memcmp(buf, "ANAND", 6)) {
83
84#if 0
85 printk(KERN_DEBUG "ANAND header not found at 0x%x in mtd%d\n",
86 block * nftl->EraseSize, nftl->mbd.mtd->index);
87#endif
88 continue;
89 }
90
91
92 if ((ret = nftl_read_oob(mtd, block * nftl->EraseSize +
93 SECTORSIZE + 8, 8, &retlen,
94 (char *)&h1) < 0)) {
95 printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n",
96 block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
97 continue;
98 }
99
100#if 0
101
102
103
104 if (le16_to_cpu(h1.EraseMark | h1.EraseMark1) != ERASE_MARK) {
105 printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but erase mark not present (0x%04x,0x%04x instead)\n",
106 block * nftl->EraseSize, nftl->mbd.mtd->index,
107 le16_to_cpu(h1.EraseMark), le16_to_cpu(h1.EraseMark1));
108 continue;
109 }
110
111
112 if ((ret = mtd->read(mtd, block * nftl->EraseSize, SECTORSIZE,
113 &retlen, buf) < 0)) {
114 printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n",
115 block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
116 continue;
117 }
118
119
120 if (memcmp(buf, "ANAND", 6)) {
121 printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but went away on reread!\n",
122 block * nftl->EraseSize, nftl->mbd.mtd->index);
123 printk(KERN_NOTICE "New data are: %02x %02x %02x %02x %02x %02x\n",
124 buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
125 continue;
126 }
127#endif
128
129
130 if (boot_record_count) {
131
132
133 if (memcmp(mh, buf, sizeof(struct NFTLMediaHeader))) {
134 printk(KERN_NOTICE "NFTL Media Headers at 0x%x and 0x%x disagree.\n",
135 nftl->MediaUnit * nftl->EraseSize, block * nftl->EraseSize);
136
137 if (boot_record_count < 2) {
138
139 return -1;
140 }
141 continue;
142 }
143 if (boot_record_count == 1)
144 nftl->SpareMediaUnit = block;
145
146
147 nftl->ReplUnitTable[block] = BLOCK_RESERVED;
148
149
150 boot_record_count++;
151 continue;
152 }
153
154
155 memcpy(mh, buf, sizeof(struct NFTLMediaHeader));
156
157
158#if 0
159The new DiskOnChip driver scans the MediaHeader itself, and presents a virtual
160erasesize based on UnitSizeFactor. So the erasesize we read from the mtd
161device is already correct.
162 if (mh->UnitSizeFactor == 0) {
163 printk(KERN_NOTICE "NFTL: UnitSizeFactor 0x00 detected. This violates the spec but we think we know what it means...\n");
164 } else if (mh->UnitSizeFactor < 0xfc) {
165 printk(KERN_NOTICE "Sorry, we don't support UnitSizeFactor 0x%02x\n",
166 mh->UnitSizeFactor);
167 return -1;
168 } else if (mh->UnitSizeFactor != 0xff) {
169 printk(KERN_NOTICE "WARNING: Support for NFTL with UnitSizeFactor 0x%02x is experimental\n",
170 mh->UnitSizeFactor);
171 nftl->EraseSize = nftl->mbd.mtd->erasesize << (0xff - mh->UnitSizeFactor);
172 nftl->nb_blocks = (u32)nftl->mbd.mtd->size / nftl->EraseSize;
173 }
174#endif
175 nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN);
176 if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) {
177 printk(KERN_NOTICE "NFTL Media Header sanity check failed:\n");
178 printk(KERN_NOTICE "nb_boot_blocks (%d) + 2 > nb_blocks (%d)\n",
179 nftl->nb_boot_blocks, nftl->nb_blocks);
180 return -1;
181 }
182
183 nftl->numvunits = le32_to_cpu(mh->FormattedSize) / nftl->EraseSize;
184 if (nftl->numvunits > (nftl->nb_blocks - nftl->nb_boot_blocks - 2)) {
185 printk(KERN_NOTICE "NFTL Media Header sanity check failed:\n");
186 printk(KERN_NOTICE "numvunits (%d) > nb_blocks (%d) - nb_boot_blocks(%d) - 2\n",
187 nftl->numvunits, nftl->nb_blocks, nftl->nb_boot_blocks);
188 return -1;
189 }
190
191 nftl->mbd.size = nftl->numvunits * (nftl->EraseSize / SECTORSIZE);
192
193
194
195 nftl->nb_blocks = le16_to_cpu(mh->NumEraseUnits) + le16_to_cpu(mh->FirstPhysicalEUN);
196
197
198 nftl->lastEUN = nftl->nb_blocks - 1;
199
200
201 nftl->EUNtable = kmalloc(nftl->nb_blocks * sizeof(u16), GFP_KERNEL);
202 if (!nftl->EUNtable) {
203 printk(KERN_NOTICE "NFTL: allocation of EUNtable failed\n");
204 return -ENOMEM;
205 }
206
207 nftl->ReplUnitTable = kmalloc(nftl->nb_blocks * sizeof(u16), GFP_KERNEL);
208 if (!nftl->ReplUnitTable) {
209 kfree(nftl->EUNtable);
210 printk(KERN_NOTICE "NFTL: allocation of ReplUnitTable failed\n");
211 return -ENOMEM;
212 }
213
214
215 for (i = 0; i < nftl->nb_boot_blocks; i++)
216 nftl->ReplUnitTable[i] = BLOCK_RESERVED;
217
218 for (; i < nftl->nb_blocks; i++) {
219 nftl->ReplUnitTable[i] = BLOCK_NOTEXPLORED;
220 }
221
222
223 nftl->ReplUnitTable[block] = BLOCK_RESERVED;
224
225
226 for (i = 0; i < nftl->nb_blocks; i++) {
227#if 0
228The new DiskOnChip driver already scanned the bad block table. Just query it.
229 if ((i & (SECTORSIZE - 1)) == 0) {
230
231 if ((ret = mtd->read(nftl->mbd.mtd, block * nftl->EraseSize +
232 i + SECTORSIZE, SECTORSIZE, &retlen,
233 buf)) < 0) {
234 printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n",
235 ret);
236 kfree(nftl->ReplUnitTable);
237 kfree(nftl->EUNtable);
238 return -1;
239 }
240 }
241
242 if (buf[i & (SECTORSIZE - 1)] != 0xff)
243 nftl->ReplUnitTable[i] = BLOCK_RESERVED;
244#endif
245 if (mtd_block_isbad(nftl->mbd.mtd,
246 i * nftl->EraseSize))
247 nftl->ReplUnitTable[i] = BLOCK_RESERVED;
248 }
249
250 nftl->MediaUnit = block;
251 boot_record_count++;
252
253 }
254
255 return boot_record_count?0:-1;
256}
257
258static int memcmpb(void *a, int c, int n)
259{
260 int i;
261 for (i = 0; i < n; i++) {
262 if (c != ((unsigned char *)a)[i])
263 return 1;
264 }
265 return 0;
266}
267
268
269static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int len,
270 int check_oob)
271{
272 u8 buf[SECTORSIZE + nftl->mbd.mtd->oobsize];
273 struct mtd_info *mtd = nftl->mbd.mtd;
274 size_t retlen;
275 int i;
276
277 for (i = 0; i < len; i += SECTORSIZE) {
278 if (mtd_read(mtd, address, SECTORSIZE, &retlen, buf))
279 return -1;
280 if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
281 return -1;
282
283 if (check_oob) {
284 if(nftl_read_oob(mtd, address, mtd->oobsize,
285 &retlen, &buf[SECTORSIZE]) < 0)
286 return -1;
287 if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0)
288 return -1;
289 }
290 address += SECTORSIZE;
291 }
292
293 return 0;
294}
295
296
297
298
299
300
301
302
303int NFTL_formatblock(struct NFTLrecord *nftl, int block)
304{
305 size_t retlen;
306 unsigned int nb_erases, erase_mark;
307 struct nftl_uci1 uci;
308 struct erase_info *instr = &nftl->instr;
309 struct mtd_info *mtd = nftl->mbd.mtd;
310
311
312 if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8,
313 8, &retlen, (char *)&uci) < 0)
314 goto default_uci1;
315
316 erase_mark = le16_to_cpu ((uci.EraseMark | uci.EraseMark1));
317 if (erase_mark != ERASE_MARK) {
318 default_uci1:
319 uci.EraseMark = cpu_to_le16(ERASE_MARK);
320 uci.EraseMark1 = cpu_to_le16(ERASE_MARK);
321 uci.WearInfo = cpu_to_le32(0);
322 }
323
324 memset(instr, 0, sizeof(struct erase_info));
325
326
327 instr->mtd = nftl->mbd.mtd;
328 instr->addr = block * nftl->EraseSize;
329 instr->len = nftl->EraseSize;
330 mtd_erase(mtd, instr);
331
332 if (instr->state == MTD_ERASE_FAILED) {
333 printk("Error while formatting block %d\n", block);
334 goto fail;
335 }
336
337
338 nb_erases = le32_to_cpu(uci.WearInfo);
339 nb_erases++;
340
341
342 if (nb_erases == 0)
343 nb_erases = 1;
344
345
346
347
348 if (check_free_sectors(nftl, instr->addr, nftl->EraseSize, 1) != 0)
349 goto fail;
350
351 uci.WearInfo = le32_to_cpu(nb_erases);
352 if (nftl_write_oob(mtd, block * nftl->EraseSize + SECTORSIZE +
353 8, 8, &retlen, (char *)&uci) < 0)
354 goto fail;
355 return 0;
356fail:
357
358
359 mtd_block_markbad(nftl->mbd.mtd, instr->addr);
360 return -1;
361}
362
363
364
365
366
367
368
369
370
371
372static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_block)
373{
374 struct mtd_info *mtd = nftl->mbd.mtd;
375 unsigned int block, i, status;
376 struct nftl_bci bci;
377 int sectors_per_block;
378 size_t retlen;
379
380 sectors_per_block = nftl->EraseSize / SECTORSIZE;
381 block = first_block;
382 for (;;) {
383 for (i = 0; i < sectors_per_block; i++) {
384 if (nftl_read_oob(mtd,
385 block * nftl->EraseSize + i * SECTORSIZE,
386 8, &retlen, (char *)&bci) < 0)
387 status = SECTOR_IGNORE;
388 else
389 status = bci.Status | bci.Status1;
390
391 switch(status) {
392 case SECTOR_FREE:
393
394
395 if (memcmpb(&bci, 0xff, 8) != 0 ||
396 check_free_sectors(nftl, block * nftl->EraseSize + i * SECTORSIZE,
397 SECTORSIZE, 0) != 0) {
398 printk("Incorrect free sector %d in block %d: "
399 "marking it as ignored\n",
400 i, block);
401
402
403 bci.Status = SECTOR_IGNORE;
404 bci.Status1 = SECTOR_IGNORE;
405 nftl_write_oob(mtd, block *
406 nftl->EraseSize +
407 i * SECTORSIZE, 8,
408 &retlen, (char *)&bci);
409 }
410 break;
411 default:
412 break;
413 }
414 }
415
416
417 block = nftl->ReplUnitTable[block];
418 if (!(block == BLOCK_NIL || block < nftl->nb_blocks))
419 printk("incorrect ReplUnitTable[] : %d\n", block);
420 if (block == BLOCK_NIL || block >= nftl->nb_blocks)
421 break;
422 }
423}
424
425
426static int calc_chain_length(struct NFTLrecord *nftl, unsigned int first_block)
427{
428 unsigned int length = 0, block = first_block;
429
430 for (;;) {
431 length++;
432
433
434 if (length >= nftl->nb_blocks) {
435 printk("nftl: length too long %d !\n", length);
436 break;
437 }
438
439 block = nftl->ReplUnitTable[block];
440 if (!(block == BLOCK_NIL || block < nftl->nb_blocks))
441 printk("incorrect ReplUnitTable[] : %d\n", block);
442 if (block == BLOCK_NIL || block >= nftl->nb_blocks)
443 break;
444 }
445 return length;
446}
447
448
449
450
451
452
453
454
455
456
457
458static void format_chain(struct NFTLrecord *nftl, unsigned int first_block)
459{
460 unsigned int block = first_block, block1;
461
462 printk("Formatting chain at block %d\n", first_block);
463
464 for (;;) {
465 block1 = nftl->ReplUnitTable[block];
466
467 printk("Formatting block %d\n", block);
468 if (NFTL_formatblock(nftl, block) < 0) {
469
470 nftl->ReplUnitTable[block] = BLOCK_RESERVED;
471 } else {
472 nftl->ReplUnitTable[block] = BLOCK_FREE;
473 }
474
475
476 block = block1;
477
478 if (!(block == BLOCK_NIL || block < nftl->nb_blocks))
479 printk("incorrect ReplUnitTable[] : %d\n", block);
480 if (block == BLOCK_NIL || block >= nftl->nb_blocks)
481 break;
482 }
483}
484
485
486
487
488
489
490
491static int check_and_mark_free_block(struct NFTLrecord *nftl, int block)
492{
493 struct mtd_info *mtd = nftl->mbd.mtd;
494 struct nftl_uci1 h1;
495 unsigned int erase_mark;
496 size_t retlen;
497
498
499 if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
500 &retlen, (char *)&h1) < 0)
501 return -1;
502
503 erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1));
504 if (erase_mark != ERASE_MARK) {
505
506
507 if (check_free_sectors (nftl, block * nftl->EraseSize, nftl->EraseSize, 1) != 0)
508 return -1;
509
510
511 h1.EraseMark = cpu_to_le16(ERASE_MARK);
512 h1.EraseMark1 = cpu_to_le16(ERASE_MARK);
513 h1.WearInfo = cpu_to_le32(0);
514 if (nftl_write_oob(mtd,
515 block * nftl->EraseSize + SECTORSIZE + 8, 8,
516 &retlen, (char *)&h1) < 0)
517 return -1;
518 } else {
519#if 0
520
521 for (i = 0; i < nftl->EraseSize; i += SECTORSIZE) {
522
523 if (check_free_sectors (nftl, block * nftl->EraseSize + i,
524 SECTORSIZE, 0) != 0)
525 return -1;
526
527 if (nftl_read_oob(mtd, block * nftl->EraseSize + i,
528 16, &retlen, buf) < 0)
529 return -1;
530 if (i == SECTORSIZE) {
531
532 if (memcmpb(buf, 0xff, 8))
533 return -1;
534 } else {
535 if (memcmpb(buf, 0xff, 16))
536 return -1;
537 }
538 }
539#endif
540 }
541
542 return 0;
543}
544
545
546
547
548
549
550
551
552static int get_fold_mark(struct NFTLrecord *nftl, unsigned int block)
553{
554 struct mtd_info *mtd = nftl->mbd.mtd;
555 struct nftl_uci2 uci;
556 size_t retlen;
557
558 if (nftl_read_oob(mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8,
559 8, &retlen, (char *)&uci) < 0)
560 return 0;
561
562 return le16_to_cpu((uci.FoldMark | uci.FoldMark1));
563}
564
565int NFTL_mount(struct NFTLrecord *s)
566{
567 int i;
568 unsigned int first_logical_block, logical_block, rep_block, nb_erases, erase_mark;
569 unsigned int block, first_block, is_first_block;
570 int chain_length, do_format_chain;
571 struct nftl_uci0 h0;
572 struct nftl_uci1 h1;
573 struct mtd_info *mtd = s->mbd.mtd;
574 size_t retlen;
575
576
577 if (find_boot_record(s) < 0) {
578 printk("Could not find valid boot record\n");
579 return -1;
580 }
581
582
583 for (i = 0; i < s->nb_blocks; i++) {
584 s->EUNtable[i] = BLOCK_NIL;
585 }
586
587
588 first_logical_block = 0;
589 for (first_block = 0; first_block < s->nb_blocks; first_block++) {
590
591 if (s->ReplUnitTable[first_block] == BLOCK_NOTEXPLORED) {
592 block = first_block;
593 chain_length = 0;
594 do_format_chain = 0;
595
596 for (;;) {
597
598 if (nftl_read_oob(mtd,
599 block * s->EraseSize + 8, 8,
600 &retlen, (char *)&h0) < 0 ||
601 nftl_read_oob(mtd,
602 block * s->EraseSize +
603 SECTORSIZE + 8, 8,
604 &retlen, (char *)&h1) < 0) {
605 s->ReplUnitTable[block] = BLOCK_NIL;
606 do_format_chain = 1;
607 break;
608 }
609
610 logical_block = le16_to_cpu ((h0.VirtUnitNum | h0.SpareVirtUnitNum));
611 rep_block = le16_to_cpu ((h0.ReplUnitNum | h0.SpareReplUnitNum));
612 nb_erases = le32_to_cpu (h1.WearInfo);
613 erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1));
614
615 is_first_block = !(logical_block >> 15);
616 logical_block = logical_block & 0x7fff;
617
618
619 if (erase_mark != ERASE_MARK || logical_block >= s->nb_blocks) {
620 if (chain_length == 0) {
621
622 if (check_and_mark_free_block(s, block) < 0) {
623
624 printk("Formatting block %d\n", block);
625 if (NFTL_formatblock(s, block) < 0) {
626
627 s->ReplUnitTable[block] = BLOCK_RESERVED;
628 } else {
629 s->ReplUnitTable[block] = BLOCK_FREE;
630 }
631 } else {
632
633 s->ReplUnitTable[block] = BLOCK_FREE;
634 }
635
636 goto examine_ReplUnitTable;
637 } else {
638
639
640 printk("Block %d: free but referenced in chain %d\n",
641 block, first_block);
642 s->ReplUnitTable[block] = BLOCK_NIL;
643 do_format_chain = 1;
644 break;
645 }
646 }
647
648
649 if (chain_length == 0) {
650
651
652
653 if (!is_first_block)
654 goto examine_ReplUnitTable;
655 first_logical_block = logical_block;
656 } else {
657 if (logical_block != first_logical_block) {
658 printk("Block %d: incorrect logical block: %d expected: %d\n",
659 block, logical_block, first_logical_block);
660
661
662 do_format_chain = 1;
663 }
664 if (is_first_block) {
665
666
667
668 if (get_fold_mark(s, block) != FOLD_MARK_IN_PROGRESS ||
669 rep_block != 0xffff) {
670 printk("Block %d: incorrectly marked as first block in chain\n",
671 block);
672
673
674 do_format_chain = 1;
675 } else {
676 printk("Block %d: folding in progress - ignoring first block flag\n",
677 block);
678 }
679 }
680 }
681 chain_length++;
682 if (rep_block == 0xffff) {
683
684 s->ReplUnitTable[block] = BLOCK_NIL;
685 break;
686 } else if (rep_block >= s->nb_blocks) {
687 printk("Block %d: referencing invalid block %d\n",
688 block, rep_block);
689 do_format_chain = 1;
690 s->ReplUnitTable[block] = BLOCK_NIL;
691 break;
692 } else if (s->ReplUnitTable[rep_block] != BLOCK_NOTEXPLORED) {
693
694
695
696
697
698 if (s->ReplUnitTable[rep_block] == BLOCK_NIL &&
699 s->EUNtable[first_logical_block] == rep_block &&
700 get_fold_mark(s, first_block) == FOLD_MARK_IN_PROGRESS) {
701
702 printk("Block %d: folding in progress - ignoring first block flag\n",
703 rep_block);
704 s->ReplUnitTable[block] = rep_block;
705 s->EUNtable[first_logical_block] = BLOCK_NIL;
706 } else {
707 printk("Block %d: referencing block %d already in another chain\n",
708 block, rep_block);
709
710 do_format_chain = 1;
711 s->ReplUnitTable[block] = BLOCK_NIL;
712 }
713 break;
714 } else {
715
716 s->ReplUnitTable[block] = rep_block;
717 block = rep_block;
718 }
719 }
720
721
722
723 if (do_format_chain) {
724
725 format_chain(s, first_block);
726 } else {
727 unsigned int first_block1, chain_to_format, chain_length1;
728 int fold_mark;
729
730
731 fold_mark = get_fold_mark(s, first_block);
732 if (fold_mark == 0) {
733
734 printk("Could read foldmark at block %d\n", first_block);
735 format_chain(s, first_block);
736 } else {
737 if (fold_mark == FOLD_MARK_IN_PROGRESS)
738 check_sectors_in_chain(s, first_block);
739
740
741
742
743
744 first_block1 = s->EUNtable[first_logical_block];
745 if (first_block1 != BLOCK_NIL) {
746
747 chain_length1 = calc_chain_length(s, first_block1);
748 printk("Two chains at blocks %d (len=%d) and %d (len=%d)\n",
749 first_block1, chain_length1, first_block, chain_length);
750
751 if (chain_length >= chain_length1) {
752 chain_to_format = first_block1;
753 s->EUNtable[first_logical_block] = first_block;
754 } else {
755 chain_to_format = first_block;
756 }
757 format_chain(s, chain_to_format);
758 } else {
759 s->EUNtable[first_logical_block] = first_block;
760 }
761 }
762 }
763 }
764 examine_ReplUnitTable:;
765 }
766
767
768 s->numfreeEUNs = 0;
769 s->LastFreeEUN = le16_to_cpu(s->MediaHdr.FirstPhysicalEUN);
770
771 for (block = 0; block < s->nb_blocks; block++) {
772 if (s->ReplUnitTable[block] == BLOCK_NOTEXPLORED) {
773 printk("Unreferenced block %d, formatting it\n", block);
774 if (NFTL_formatblock(s, block) < 0)
775 s->ReplUnitTable[block] = BLOCK_RESERVED;
776 else
777 s->ReplUnitTable[block] = BLOCK_FREE;
778 }
779 if (s->ReplUnitTable[block] == BLOCK_FREE) {
780 s->numfreeEUNs++;
781 s->LastFreeEUN = block;
782 }
783 }
784
785 return 0;
786}
787