Root/drivers/mtd/inftlcore.c

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

Archive Download this file



interactive