ia64/linux-2.6.18-xen.hg

view drivers/mtd/inftlcore.c @ 897:329ea0ccb344

balloon: try harder to balloon up under memory pressure.

Currently if the balloon driver is unable to increase the guest's
reservation it assumes the failure was due to reaching its full
allocation, gives up on the ballooning operation and records the limit
it reached as the "hard limit". The driver will not try again until
the target is set again (even to the same value).

However it is possible that ballooning has in fact failed due to
memory pressure in the host and therefore it is desirable to keep
attempting to reach the target in case memory becomes available. The
most likely scenario is that some guests are ballooning down while
others are ballooning up and therefore there is temporary memory
pressure while things stabilise. You would not expect a well behaved
toolstack to ask a domain to balloon to more than its allocation nor
would you expect it to deliberately over-commit memory by setting
balloon targets which exceed the total host memory.

This patch drops the concept of a hard limit and causes the balloon
driver to retry increasing the reservation on a timer in the same
manner as when decreasing the reservation.

Also if we partially succeed in increasing the reservation
(i.e. receive less pages than we asked for) then we may as well keep
those pages rather than returning them to Xen.

Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Jun 05 14:01:20 2009 +0100 (2009-06-05)
parents 831230e53067
children
line source
1 /*
2 * inftlcore.c -- Linux driver for Inverse Flash Translation Layer (INFTL)
3 *
4 * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
5 *
6 * Based heavily on the nftlcore.c code which is:
7 * (c) 1999 Machine Vision Holdings, Inc.
8 * Author: David Woodhouse <dwmw2@infradead.org>
9 *
10 * $Id: inftlcore.c,v 1.19 2005/11/07 11:14:20 gleixner Exp $
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
27 #include <linux/kernel.h>
28 #include <linux/module.h>
29 #include <linux/delay.h>
30 #include <linux/slab.h>
31 #include <linux/sched.h>
32 #include <linux/init.h>
33 #include <linux/kmod.h>
34 #include <linux/hdreg.h>
35 #include <linux/mtd/mtd.h>
36 #include <linux/mtd/nftl.h>
37 #include <linux/mtd/inftl.h>
38 #include <linux/mtd/nand.h>
39 #include <asm/uaccess.h>
40 #include <asm/errno.h>
41 #include <asm/io.h>
43 /*
44 * Maximum number of loops while examining next block, to have a
45 * chance to detect consistency problems (they should never happen
46 * because of the checks done in the mounting.
47 */
48 #define MAX_LOOPS 10000
50 static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
51 {
52 struct INFTLrecord *inftl;
53 unsigned long temp;
55 if (mtd->type != MTD_NANDFLASH)
56 return;
57 /* OK, this is moderately ugly. But probably safe. Alternatives? */
58 if (memcmp(mtd->name, "DiskOnChip", 10))
59 return;
61 if (!mtd->block_isbad) {
62 printk(KERN_ERR
63 "INFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n"
64 "Please use the new diskonchip driver under the NAND subsystem.\n");
65 return;
66 }
68 DEBUG(MTD_DEBUG_LEVEL3, "INFTL: add_mtd for %s\n", mtd->name);
70 inftl = kmalloc(sizeof(*inftl), GFP_KERNEL);
72 if (!inftl) {
73 printk(KERN_WARNING "INFTL: Out of memory for data structures\n");
74 return;
75 }
76 memset(inftl, 0, sizeof(*inftl));
78 inftl->mbd.mtd = mtd;
79 inftl->mbd.devnum = -1;
80 inftl->mbd.blksize = 512;
81 inftl->mbd.tr = tr;
83 if (INFTL_mount(inftl) < 0) {
84 printk(KERN_WARNING "INFTL: could not mount device\n");
85 kfree(inftl);
86 return;
87 }
89 /* OK, it's a new one. Set up all the data structures. */
91 /* Calculate geometry */
92 inftl->cylinders = 1024;
93 inftl->heads = 16;
95 temp = inftl->cylinders * inftl->heads;
96 inftl->sectors = inftl->mbd.size / temp;
97 if (inftl->mbd.size % temp) {
98 inftl->sectors++;
99 temp = inftl->cylinders * inftl->sectors;
100 inftl->heads = inftl->mbd.size / temp;
102 if (inftl->mbd.size % temp) {
103 inftl->heads++;
104 temp = inftl->heads * inftl->sectors;
105 inftl->cylinders = inftl->mbd.size / temp;
106 }
107 }
109 if (inftl->mbd.size != inftl->heads * inftl->cylinders * inftl->sectors) {
110 /*
111 Oh no we don't have
112 mbd.size == heads * cylinders * sectors
113 */
114 printk(KERN_WARNING "INFTL: cannot calculate a geometry to "
115 "match size of 0x%lx.\n", inftl->mbd.size);
116 printk(KERN_WARNING "INFTL: using C:%d H:%d S:%d "
117 "(== 0x%lx sects)\n",
118 inftl->cylinders, inftl->heads , inftl->sectors,
119 (long)inftl->cylinders * (long)inftl->heads *
120 (long)inftl->sectors );
121 }
123 if (add_mtd_blktrans_dev(&inftl->mbd)) {
124 kfree(inftl->PUtable);
125 kfree(inftl->VUtable);
126 kfree(inftl);
127 return;
128 }
129 #ifdef PSYCHO_DEBUG
130 printk(KERN_INFO "INFTL: Found new inftl%c\n", inftl->mbd.devnum + 'a');
131 #endif
132 return;
133 }
135 static void inftl_remove_dev(struct mtd_blktrans_dev *dev)
136 {
137 struct INFTLrecord *inftl = (void *)dev;
139 DEBUG(MTD_DEBUG_LEVEL3, "INFTL: remove_dev (i=%d)\n", dev->devnum);
141 del_mtd_blktrans_dev(dev);
143 kfree(inftl->PUtable);
144 kfree(inftl->VUtable);
145 kfree(inftl);
146 }
148 /*
149 * Actual INFTL access routines.
150 */
152 /*
153 * Read oob data from flash
154 */
155 int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
156 size_t *retlen, uint8_t *buf)
157 {
158 struct mtd_oob_ops ops;
159 int res;
161 ops.mode = MTD_OOB_PLACE;
162 ops.ooboffs = offs & (mtd->writesize - 1);
163 ops.ooblen = len;
164 ops.oobbuf = buf;
165 ops.datbuf = NULL;
166 ops.len = len;
168 res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
169 *retlen = ops.retlen;
170 return res;
171 }
173 /*
174 * Write oob data to flash
175 */
176 int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
177 size_t *retlen, uint8_t *buf)
178 {
179 struct mtd_oob_ops ops;
180 int res;
182 ops.mode = MTD_OOB_PLACE;
183 ops.ooboffs = offs & (mtd->writesize - 1);
184 ops.ooblen = len;
185 ops.oobbuf = buf;
186 ops.datbuf = NULL;
187 ops.len = len;
189 res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
190 *retlen = ops.retlen;
191 return res;
192 }
194 /*
195 * Write data and oob to flash
196 */
197 static int inftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
198 size_t *retlen, uint8_t *buf, uint8_t *oob)
199 {
200 struct mtd_oob_ops ops;
201 int res;
203 ops.mode = MTD_OOB_PLACE;
204 ops.ooboffs = offs;
205 ops.ooblen = mtd->oobsize;
206 ops.oobbuf = oob;
207 ops.datbuf = buf;
208 ops.len = len;
210 res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
211 *retlen = ops.retlen;
212 return res;
213 }
215 /*
216 * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition.
217 * This function is used when the give Virtual Unit Chain.
218 */
219 static u16 INFTL_findfreeblock(struct INFTLrecord *inftl, int desperate)
220 {
221 u16 pot = inftl->LastFreeEUN;
222 int silly = inftl->nb_blocks;
224 DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findfreeblock(inftl=%p,"
225 "desperate=%d)\n", inftl, desperate);
227 /*
228 * Normally, we force a fold to happen before we run out of free
229 * blocks completely.
230 */
231 if (!desperate && inftl->numfreeEUNs < 2) {
232 DEBUG(MTD_DEBUG_LEVEL1, "INFTL: there are too few free "
233 "EUNs (%d)\n", inftl->numfreeEUNs);
234 return 0xffff;
235 }
237 /* Scan for a free block */
238 do {
239 if (inftl->PUtable[pot] == BLOCK_FREE) {
240 inftl->LastFreeEUN = pot;
241 return pot;
242 }
244 if (++pot > inftl->lastEUN)
245 pot = 0;
247 if (!silly--) {
248 printk(KERN_WARNING "INFTL: no free blocks found! "
249 "EUN range = %d - %d\n", 0, inftl->LastFreeEUN);
250 return BLOCK_NIL;
251 }
252 } while (pot != inftl->LastFreeEUN);
254 return BLOCK_NIL;
255 }
257 static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned pendingblock)
258 {
259 u16 BlockMap[MAX_SECTORS_PER_UNIT];
260 unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
261 unsigned int thisEUN, prevEUN, status;
262 struct mtd_info *mtd = inftl->mbd.mtd;
263 int block, silly;
264 unsigned int targetEUN;
265 struct inftl_oob oob;
266 size_t retlen;
268 DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d,"
269 "pending=%d)\n", inftl, thisVUC, pendingblock);
271 memset(BlockMap, 0xff, sizeof(BlockMap));
272 memset(BlockDeleted, 0, sizeof(BlockDeleted));
274 thisEUN = targetEUN = inftl->VUtable[thisVUC];
276 if (thisEUN == BLOCK_NIL) {
277 printk(KERN_WARNING "INFTL: trying to fold non-existent "
278 "Virtual Unit Chain %d!\n", thisVUC);
279 return BLOCK_NIL;
280 }
282 /*
283 * Scan to find the Erase Unit which holds the actual data for each
284 * 512-byte block within the Chain.
285 */
286 silly = MAX_LOOPS;
287 while (thisEUN < inftl->nb_blocks) {
288 for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) {
289 if ((BlockMap[block] != 0xffff) || BlockDeleted[block])
290 continue;
292 if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
293 + (block * SECTORSIZE), 16, &retlen,
294 (char *)&oob) < 0)
295 status = SECTOR_IGNORE;
296 else
297 status = oob.b.Status | oob.b.Status1;
299 switch(status) {
300 case SECTOR_FREE:
301 case SECTOR_IGNORE:
302 break;
303 case SECTOR_USED:
304 BlockMap[block] = thisEUN;
305 continue;
306 case SECTOR_DELETED:
307 BlockDeleted[block] = 1;
308 continue;
309 default:
310 printk(KERN_WARNING "INFTL: unknown status "
311 "for block %d in EUN %d: %x\n",
312 block, thisEUN, status);
313 break;
314 }
315 }
317 if (!silly--) {
318 printk(KERN_WARNING "INFTL: infinite loop in Virtual "
319 "Unit Chain 0x%x\n", thisVUC);
320 return BLOCK_NIL;
321 }
323 thisEUN = inftl->PUtable[thisEUN];
324 }
326 /*
327 * OK. We now know the location of every block in the Virtual Unit
328 * Chain, and the Erase Unit into which we are supposed to be copying.
329 * Go for it.
330 */
331 DEBUG(MTD_DEBUG_LEVEL1, "INFTL: folding chain %d into unit %d\n",
332 thisVUC, targetEUN);
334 for (block = 0; block < inftl->EraseSize/SECTORSIZE ; block++) {
335 unsigned char movebuf[SECTORSIZE];
336 int ret;
338 /*
339 * If it's in the target EUN already, or if it's pending write,
340 * do nothing.
341 */
342 if (BlockMap[block] == targetEUN || (pendingblock ==
343 (thisVUC * (inftl->EraseSize / SECTORSIZE) + block))) {
344 continue;
345 }
347 /*
348 * Copy only in non free block (free blocks can only
349 * happen in case of media errors or deleted blocks).
350 */
351 if (BlockMap[block] == BLOCK_NIL)
352 continue;
354 ret = mtd->read(mtd, (inftl->EraseSize * BlockMap[block]) +
355 (block * SECTORSIZE), SECTORSIZE, &retlen,
356 movebuf);
357 if (ret < 0 && ret != -EUCLEAN) {
358 ret = mtd->read(mtd,
359 (inftl->EraseSize * BlockMap[block]) +
360 (block * SECTORSIZE), SECTORSIZE,
361 &retlen, movebuf);
362 if (ret != -EIO)
363 DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went "
364 "away on retry?\n");
365 }
366 memset(&oob, 0xff, sizeof(struct inftl_oob));
367 oob.b.Status = oob.b.Status1 = SECTOR_USED;
369 inftl_write(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +
370 (block * SECTORSIZE), SECTORSIZE, &retlen,
371 movebuf, (char *)&oob);
372 }
374 /*
375 * Newest unit in chain now contains data from _all_ older units.
376 * So go through and erase each unit in chain, oldest first. (This
377 * is important, by doing oldest first if we crash/reboot then it
378 * it is relatively simple to clean up the mess).
379 */
380 DEBUG(MTD_DEBUG_LEVEL1, "INFTL: want to erase virtual chain %d\n",
381 thisVUC);
383 for (;;) {
384 /* Find oldest unit in chain. */
385 thisEUN = inftl->VUtable[thisVUC];
386 prevEUN = BLOCK_NIL;
387 while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
388 prevEUN = thisEUN;
389 thisEUN = inftl->PUtable[thisEUN];
390 }
392 /* Check if we are all done */
393 if (thisEUN == targetEUN)
394 break;
396 if (INFTL_formatblock(inftl, thisEUN) < 0) {
397 /*
398 * Could not erase : mark block as reserved.
399 */
400 inftl->PUtable[thisEUN] = BLOCK_RESERVED;
401 } else {
402 /* Correctly erased : mark it as free */
403 inftl->PUtable[thisEUN] = BLOCK_FREE;
404 inftl->PUtable[prevEUN] = BLOCK_NIL;
405 inftl->numfreeEUNs++;
406 }
407 }
409 return targetEUN;
410 }
412 static u16 INFTL_makefreeblock(struct INFTLrecord *inftl, unsigned pendingblock)
413 {
414 /*
415 * This is the part that needs some cleverness applied.
416 * For now, I'm doing the minimum applicable to actually
417 * get the thing to work.
418 * Wear-levelling and other clever stuff needs to be implemented
419 * and we also need to do some assessment of the results when
420 * the system loses power half-way through the routine.
421 */
422 u16 LongestChain = 0;
423 u16 ChainLength = 0, thislen;
424 u16 chain, EUN;
426 DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_makefreeblock(inftl=%p,"
427 "pending=%d)\n", inftl, pendingblock);
429 for (chain = 0; chain < inftl->nb_blocks; chain++) {
430 EUN = inftl->VUtable[chain];
431 thislen = 0;
433 while (EUN <= inftl->lastEUN) {
434 thislen++;
435 EUN = inftl->PUtable[EUN];
436 if (thislen > 0xff00) {
437 printk(KERN_WARNING "INFTL: endless loop in "
438 "Virtual Chain %d: Unit %x\n",
439 chain, EUN);
440 /*
441 * Actually, don't return failure.
442 * Just ignore this chain and get on with it.
443 */
444 thislen = 0;
445 break;
446 }
447 }
449 if (thislen > ChainLength) {
450 ChainLength = thislen;
451 LongestChain = chain;
452 }
453 }
455 if (ChainLength < 2) {
456 printk(KERN_WARNING "INFTL: no Virtual Unit Chains available "
457 "for folding. Failing request\n");
458 return BLOCK_NIL;
459 }
461 return INFTL_foldchain(inftl, LongestChain, pendingblock);
462 }
464 static int nrbits(unsigned int val, int bitcount)
465 {
466 int i, total = 0;
468 for (i = 0; (i < bitcount); i++)
469 total += (((0x1 << i) & val) ? 1 : 0);
470 return total;
471 }
473 /*
474 * INFTL_findwriteunit: Return the unit number into which we can write
475 * for this block. Make it available if it isn't already.
476 */
477 static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block)
478 {
479 unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE);
480 unsigned int thisEUN, writeEUN, prev_block, status;
481 unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1);
482 struct mtd_info *mtd = inftl->mbd.mtd;
483 struct inftl_oob oob;
484 struct inftl_bci bci;
485 unsigned char anac, nacs, parity;
486 size_t retlen;
487 int silly, silly2 = 3;
489 DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findwriteunit(inftl=%p,"
490 "block=%d)\n", inftl, block);
492 do {
493 /*
494 * Scan the media to find a unit in the VUC which has
495 * a free space for the block in question.
496 */
497 writeEUN = BLOCK_NIL;
498 thisEUN = inftl->VUtable[thisVUC];
499 silly = MAX_LOOPS;
501 while (thisEUN <= inftl->lastEUN) {
502 inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
503 blockofs, 8, &retlen, (char *)&bci);
505 status = bci.Status | bci.Status1;
506 DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in "
507 "EUN %d is %x\n", block , writeEUN, status);
509 switch(status) {
510 case SECTOR_FREE:
511 writeEUN = thisEUN;
512 break;
513 case SECTOR_DELETED:
514 case SECTOR_USED:
515 /* Can't go any further */
516 goto hitused;
517 case SECTOR_IGNORE:
518 break;
519 default:
520 /*
521 * Invalid block. Don't use it any more.
522 * Must implement.
523 */
524 break;
525 }
527 if (!silly--) {
528 printk(KERN_WARNING "INFTL: infinite loop in "
529 "Virtual Unit Chain 0x%x\n", thisVUC);
530 return 0xffff;
531 }
533 /* Skip to next block in chain */
534 thisEUN = inftl->PUtable[thisEUN];
535 }
537 hitused:
538 if (writeEUN != BLOCK_NIL)
539 return writeEUN;
542 /*
543 * OK. We didn't find one in the existing chain, or there
544 * is no existing chain. Allocate a new one.
545 */
546 writeEUN = INFTL_findfreeblock(inftl, 0);
548 if (writeEUN == BLOCK_NIL) {
549 /*
550 * That didn't work - there were no free blocks just
551 * waiting to be picked up. We're going to have to fold
552 * a chain to make room.
553 */
554 thisEUN = INFTL_makefreeblock(inftl, 0xffff);
556 /*
557 * Hopefully we free something, lets try again.
558 * This time we are desperate...
559 */
560 DEBUG(MTD_DEBUG_LEVEL1, "INFTL: using desperate==1 "
561 "to find free EUN to accommodate write to "
562 "VUC %d\n", thisVUC);
563 writeEUN = INFTL_findfreeblock(inftl, 1);
564 if (writeEUN == BLOCK_NIL) {
565 /*
566 * Ouch. This should never happen - we should
567 * always be able to make some room somehow.
568 * If we get here, we've allocated more storage
569 * space than actual media, or our makefreeblock
570 * routine is missing something.
571 */
572 printk(KERN_WARNING "INFTL: cannot make free "
573 "space.\n");
574 #ifdef DEBUG
575 INFTL_dumptables(inftl);
576 INFTL_dumpVUchains(inftl);
577 #endif
578 return BLOCK_NIL;
579 }
580 }
582 /*
583 * Insert new block into virtual chain. Firstly update the
584 * block headers in flash...
585 */
586 anac = 0;
587 nacs = 0;
588 thisEUN = inftl->VUtable[thisVUC];
589 if (thisEUN != BLOCK_NIL) {
590 inftl_read_oob(mtd, thisEUN * inftl->EraseSize
591 + 8, 8, &retlen, (char *)&oob.u);
592 anac = oob.u.a.ANAC + 1;
593 nacs = oob.u.a.NACs + 1;
594 }
596 prev_block = inftl->VUtable[thisVUC];
597 if (prev_block < inftl->nb_blocks)
598 prev_block -= inftl->firstEUN;
600 parity = (nrbits(thisVUC, 16) & 0x1) ? 0x1 : 0;
601 parity |= (nrbits(prev_block, 16) & 0x1) ? 0x2 : 0;
602 parity |= (nrbits(anac, 8) & 0x1) ? 0x4 : 0;
603 parity |= (nrbits(nacs, 8) & 0x1) ? 0x8 : 0;
605 oob.u.a.virtualUnitNo = cpu_to_le16(thisVUC);
606 oob.u.a.prevUnitNo = cpu_to_le16(prev_block);
607 oob.u.a.ANAC = anac;
608 oob.u.a.NACs = nacs;
609 oob.u.a.parityPerField = parity;
610 oob.u.a.discarded = 0xaa;
612 inftl_write_oob(mtd, writeEUN * inftl->EraseSize + 8, 8,
613 &retlen, (char *)&oob.u);
615 /* Also back up header... */
616 oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC);
617 oob.u.b.prevUnitNo = cpu_to_le16(prev_block);
618 oob.u.b.ANAC = anac;
619 oob.u.b.NACs = nacs;
620 oob.u.b.parityPerField = parity;
621 oob.u.b.discarded = 0xaa;
623 inftl_write_oob(mtd, writeEUN * inftl->EraseSize +
624 SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u);
626 inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC];
627 inftl->VUtable[thisVUC] = writeEUN;
629 inftl->numfreeEUNs--;
630 return writeEUN;
632 } while (silly2--);
634 printk(KERN_WARNING "INFTL: error folding to make room for Virtual "
635 "Unit Chain 0x%x\n", thisVUC);
636 return 0xffff;
637 }
639 /*
640 * Given a Virtual Unit Chain, see if it can be deleted, and if so do it.
641 */
642 static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
643 {
644 struct mtd_info *mtd = inftl->mbd.mtd;
645 unsigned char BlockUsed[MAX_SECTORS_PER_UNIT];
646 unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
647 unsigned int thisEUN, status;
648 int block, silly;
649 struct inftl_bci bci;
650 size_t retlen;
652 DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_trydeletechain(inftl=%p,"
653 "thisVUC=%d)\n", inftl, thisVUC);
655 memset(BlockUsed, 0, sizeof(BlockUsed));
656 memset(BlockDeleted, 0, sizeof(BlockDeleted));
658 thisEUN = inftl->VUtable[thisVUC];
659 if (thisEUN == BLOCK_NIL) {
660 printk(KERN_WARNING "INFTL: trying to delete non-existent "
661 "Virtual Unit Chain %d!\n", thisVUC);
662 return;
663 }
665 /*
666 * Scan through the Erase Units to determine whether any data is in
667 * each of the 512-byte blocks within the Chain.
668 */
669 silly = MAX_LOOPS;
670 while (thisEUN < inftl->nb_blocks) {
671 for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) {
672 if (BlockUsed[block] || BlockDeleted[block])
673 continue;
675 if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
676 + (block * SECTORSIZE), 8 , &retlen,
677 (char *)&bci) < 0)
678 status = SECTOR_IGNORE;
679 else
680 status = bci.Status | bci.Status1;
682 switch(status) {
683 case SECTOR_FREE:
684 case SECTOR_IGNORE:
685 break;
686 case SECTOR_USED:
687 BlockUsed[block] = 1;
688 continue;
689 case SECTOR_DELETED:
690 BlockDeleted[block] = 1;
691 continue;
692 default:
693 printk(KERN_WARNING "INFTL: unknown status "
694 "for block %d in EUN %d: 0x%x\n",
695 block, thisEUN, status);
696 }
697 }
699 if (!silly--) {
700 printk(KERN_WARNING "INFTL: infinite loop in Virtual "
701 "Unit Chain 0x%x\n", thisVUC);
702 return;
703 }
705 thisEUN = inftl->PUtable[thisEUN];
706 }
708 for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++)
709 if (BlockUsed[block])
710 return;
712 /*
713 * For each block in the chain free it and make it available
714 * for future use. Erase from the oldest unit first.
715 */
716 DEBUG(MTD_DEBUG_LEVEL1, "INFTL: deleting empty VUC %d\n", thisVUC);
718 for (;;) {
719 u16 *prevEUN = &inftl->VUtable[thisVUC];
720 thisEUN = *prevEUN;
722 /* If the chain is all gone already, we're done */
723 if (thisEUN == BLOCK_NIL) {
724 DEBUG(MTD_DEBUG_LEVEL2, "INFTL: Empty VUC %d for deletion was already absent\n", thisEUN);
725 return;
726 }
728 /* Find oldest unit in chain. */
729 while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
730 BUG_ON(thisEUN >= inftl->nb_blocks);
732 prevEUN = &inftl->PUtable[thisEUN];
733 thisEUN = *prevEUN;
734 }
736 DEBUG(MTD_DEBUG_LEVEL3, "Deleting EUN %d from VUC %d\n",
737 thisEUN, thisVUC);
739 if (INFTL_formatblock(inftl, thisEUN) < 0) {
740 /*
741 * Could not erase : mark block as reserved.
742 */
743 inftl->PUtable[thisEUN] = BLOCK_RESERVED;
744 } else {
745 /* Correctly erased : mark it as free */
746 inftl->PUtable[thisEUN] = BLOCK_FREE;
747 inftl->numfreeEUNs++;
748 }
750 /* Now sort out whatever was pointing to it... */
751 *prevEUN = BLOCK_NIL;
753 /* Ideally we'd actually be responsive to new
754 requests while we're doing this -- if there's
755 free space why should others be made to wait? */
756 cond_resched();
757 }
759 inftl->VUtable[thisVUC] = BLOCK_NIL;
760 }
762 static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block)
763 {
764 unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
765 unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
766 struct mtd_info *mtd = inftl->mbd.mtd;
767 unsigned int status;
768 int silly = MAX_LOOPS;
769 size_t retlen;
770 struct inftl_bci bci;
772 DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_deleteblock(inftl=%p,"
773 "block=%d)\n", inftl, block);
775 while (thisEUN < inftl->nb_blocks) {
776 if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
777 blockofs, 8, &retlen, (char *)&bci) < 0)
778 status = SECTOR_IGNORE;
779 else
780 status = bci.Status | bci.Status1;
782 switch (status) {
783 case SECTOR_FREE:
784 case SECTOR_IGNORE:
785 break;
786 case SECTOR_DELETED:
787 thisEUN = BLOCK_NIL;
788 goto foundit;
789 case SECTOR_USED:
790 goto foundit;
791 default:
792 printk(KERN_WARNING "INFTL: unknown status for "
793 "block %d in EUN %d: 0x%x\n",
794 block, thisEUN, status);
795 break;
796 }
798 if (!silly--) {
799 printk(KERN_WARNING "INFTL: infinite loop in Virtual "
800 "Unit Chain 0x%x\n",
801 block / (inftl->EraseSize / SECTORSIZE));
802 return 1;
803 }
804 thisEUN = inftl->PUtable[thisEUN];
805 }
807 foundit:
808 if (thisEUN != BLOCK_NIL) {
809 loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
811 if (inftl_read_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
812 return -EIO;
813 bci.Status = bci.Status1 = SECTOR_DELETED;
814 if (inftl_write_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
815 return -EIO;
816 INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE));
817 }
818 return 0;
819 }
821 static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
822 char *buffer)
823 {
824 struct INFTLrecord *inftl = (void *)mbd;
825 unsigned int writeEUN;
826 unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
827 size_t retlen;
828 struct inftl_oob oob;
829 char *p, *pend;
831 DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_writeblock(inftl=%p,block=%ld,"
832 "buffer=%p)\n", inftl, block, buffer);
834 /* Is block all zero? */
835 pend = buffer + SECTORSIZE;
836 for (p = buffer; p < pend && !*p; p++)
837 ;
839 if (p < pend) {
840 writeEUN = INFTL_findwriteunit(inftl, block);
842 if (writeEUN == BLOCK_NIL) {
843 printk(KERN_WARNING "inftl_writeblock(): cannot find "
844 "block to write to\n");
845 /*
846 * If we _still_ haven't got a block to use,
847 * we're screwed.
848 */
849 return 1;
850 }
852 memset(&oob, 0xff, sizeof(struct inftl_oob));
853 oob.b.Status = oob.b.Status1 = SECTOR_USED;
855 inftl_write(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
856 blockofs, SECTORSIZE, &retlen, (char *)buffer,
857 (char *)&oob);
858 /*
859 * need to write SECTOR_USED flags since they are not written
860 * in mtd_writeecc
861 */
862 } else {
863 INFTL_deleteblock(inftl, block);
864 }
866 return 0;
867 }
869 static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
870 char *buffer)
871 {
872 struct INFTLrecord *inftl = (void *)mbd;
873 unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
874 unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
875 struct mtd_info *mtd = inftl->mbd.mtd;
876 unsigned int status;
877 int silly = MAX_LOOPS;
878 struct inftl_bci bci;
879 size_t retlen;
881 DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_readblock(inftl=%p,block=%ld,"
882 "buffer=%p)\n", inftl, block, buffer);
884 while (thisEUN < inftl->nb_blocks) {
885 if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
886 blockofs, 8, &retlen, (char *)&bci) < 0)
887 status = SECTOR_IGNORE;
888 else
889 status = bci.Status | bci.Status1;
891 switch (status) {
892 case SECTOR_DELETED:
893 thisEUN = BLOCK_NIL;
894 goto foundit;
895 case SECTOR_USED:
896 goto foundit;
897 case SECTOR_FREE:
898 case SECTOR_IGNORE:
899 break;
900 default:
901 printk(KERN_WARNING "INFTL: unknown status for "
902 "block %ld in EUN %d: 0x%04x\n",
903 block, thisEUN, status);
904 break;
905 }
907 if (!silly--) {
908 printk(KERN_WARNING "INFTL: infinite loop in "
909 "Virtual Unit Chain 0x%lx\n",
910 block / (inftl->EraseSize / SECTORSIZE));
911 return 1;
912 }
914 thisEUN = inftl->PUtable[thisEUN];
915 }
917 foundit:
918 if (thisEUN == BLOCK_NIL) {
919 /* The requested block is not on the media, return all 0x00 */
920 memset(buffer, 0, SECTORSIZE);
921 } else {
922 size_t retlen;
923 loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
924 int ret = mtd->read(mtd, ptr, SECTORSIZE, &retlen, buffer);
926 /* Handle corrected bit flips gracefully */
927 if (ret < 0 && ret != -EUCLEAN)
928 return -EIO;
929 }
930 return 0;
931 }
933 static int inftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
934 {
935 struct INFTLrecord *inftl = (void *)dev;
937 geo->heads = inftl->heads;
938 geo->sectors = inftl->sectors;
939 geo->cylinders = inftl->cylinders;
941 return 0;
942 }
944 static struct mtd_blktrans_ops inftl_tr = {
945 .name = "inftl",
946 .major = INFTL_MAJOR,
947 .part_bits = INFTL_PARTN_BITS,
948 .getgeo = inftl_getgeo,
949 .readsect = inftl_readblock,
950 .writesect = inftl_writeblock,
951 .add_mtd = inftl_add_mtd,
952 .remove_dev = inftl_remove_dev,
953 .owner = THIS_MODULE,
954 };
956 static int __init init_inftl(void)
957 {
958 printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.19 $, "
959 "inftlmount.c %s\n", inftlmountrev);
961 return register_mtd_blktrans(&inftl_tr);
962 }
964 static void __exit cleanup_inftl(void)
965 {
966 deregister_mtd_blktrans(&inftl_tr);
967 }
969 module_init(init_inftl);
970 module_exit(cleanup_inftl);
972 MODULE_LICENSE("GPL");
973 MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>, David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
974 MODULE_DESCRIPTION("Support code for Inverse Flash Translation Layer, used on M-Systems DiskOnChip 2000, Millennium and Millennium Plus");