root/drivers/mtd/nftlcore.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. nftl_add_mtd
  2. nftl_remove_dev
  3. nftl_read_oob
  4. nftl_write_oob
  5. nftl_write
  6. NFTL_findfreeblock
  7. NFTL_foldchain
  8. NFTL_makefreeblock
  9. NFTL_findwriteunit
  10. nftl_writeblock
  11. nftl_readblock
  12. nftl_getgeo
  13. init_nftl
  14. cleanup_nftl

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Linux driver for NAND Flash Translation Layer
   4  *
   5  * Copyright © 1999 Machine Vision Holdings, Inc.
   6  * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org>
   7  */
   8 
   9 #define PRERELEASE
  10 
  11 #include <linux/kernel.h>
  12 #include <linux/module.h>
  13 #include <asm/errno.h>
  14 #include <asm/io.h>
  15 #include <linux/uaccess.h>
  16 #include <linux/delay.h>
  17 #include <linux/slab.h>
  18 #include <linux/init.h>
  19 #include <linux/hdreg.h>
  20 #include <linux/blkdev.h>
  21 
  22 #include <linux/kmod.h>
  23 #include <linux/mtd/mtd.h>
  24 #include <linux/mtd/rawnand.h>
  25 #include <linux/mtd/nftl.h>
  26 #include <linux/mtd/blktrans.h>
  27 
  28 /* maximum number of loops while examining next block, to have a
  29    chance to detect consistency problems (they should never happen
  30    because of the checks done in the mounting */
  31 
  32 #define MAX_LOOPS 10000
  33 
  34 
  35 static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
  36 {
  37         struct NFTLrecord *nftl;
  38         unsigned long temp;
  39 
  40         if (!mtd_type_is_nand(mtd) || mtd->size > UINT_MAX)
  41                 return;
  42         /* OK, this is moderately ugly.  But probably safe.  Alternatives? */
  43         if (memcmp(mtd->name, "DiskOnChip", 10))
  44                 return;
  45 
  46         pr_debug("NFTL: add_mtd for %s\n", mtd->name);
  47 
  48         nftl = kzalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
  49 
  50         if (!nftl)
  51                 return;
  52 
  53         nftl->mbd.mtd = mtd;
  54         nftl->mbd.devnum = -1;
  55 
  56         nftl->mbd.tr = tr;
  57 
  58         if (NFTL_mount(nftl) < 0) {
  59                 printk(KERN_WARNING "NFTL: could not mount device\n");
  60                 kfree(nftl);
  61                 return;
  62         }
  63 
  64         /* OK, it's a new one. Set up all the data structures. */
  65 
  66         /* Calculate geometry */
  67         nftl->cylinders = 1024;
  68         nftl->heads = 16;
  69 
  70         temp = nftl->cylinders * nftl->heads;
  71         nftl->sectors = nftl->mbd.size / temp;
  72         if (nftl->mbd.size % temp) {
  73                 nftl->sectors++;
  74                 temp = nftl->cylinders * nftl->sectors;
  75                 nftl->heads = nftl->mbd.size / temp;
  76 
  77                 if (nftl->mbd.size % temp) {
  78                         nftl->heads++;
  79                         temp = nftl->heads * nftl->sectors;
  80                         nftl->cylinders = nftl->mbd.size / temp;
  81                 }
  82         }
  83 
  84         if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) {
  85                 /*
  86                   Oh no we don't have
  87                    mbd.size == heads * cylinders * sectors
  88                 */
  89                 printk(KERN_WARNING "NFTL: cannot calculate a geometry to "
  90                        "match size of 0x%lx.\n", nftl->mbd.size);
  91                 printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d "
  92                         "(== 0x%lx sects)\n",
  93                         nftl->cylinders, nftl->heads , nftl->sectors,
  94                         (long)nftl->cylinders * (long)nftl->heads *
  95                         (long)nftl->sectors );
  96         }
  97 
  98         if (add_mtd_blktrans_dev(&nftl->mbd)) {
  99                 kfree(nftl->ReplUnitTable);
 100                 kfree(nftl->EUNtable);
 101                 kfree(nftl);
 102                 return;
 103         }
 104 #ifdef PSYCHO_DEBUG
 105         printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');
 106 #endif
 107 }
 108 
 109 static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
 110 {
 111         struct NFTLrecord *nftl = (void *)dev;
 112 
 113         pr_debug("NFTL: remove_dev (i=%d)\n", dev->devnum);
 114 
 115         del_mtd_blktrans_dev(dev);
 116         kfree(nftl->ReplUnitTable);
 117         kfree(nftl->EUNtable);
 118 }
 119 
 120 /*
 121  * Read oob data from flash
 122  */
 123 int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
 124                   size_t *retlen, uint8_t *buf)
 125 {
 126         loff_t mask = mtd->writesize - 1;
 127         struct mtd_oob_ops ops;
 128         int res;
 129 
 130         ops.mode = MTD_OPS_PLACE_OOB;
 131         ops.ooboffs = offs & mask;
 132         ops.ooblen = len;
 133         ops.oobbuf = buf;
 134         ops.datbuf = NULL;
 135 
 136         res = mtd_read_oob(mtd, offs & ~mask, &ops);
 137         *retlen = ops.oobretlen;
 138         return res;
 139 }
 140 
 141 /*
 142  * Write oob data to flash
 143  */
 144 int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
 145                    size_t *retlen, uint8_t *buf)
 146 {
 147         loff_t mask = mtd->writesize - 1;
 148         struct mtd_oob_ops ops;
 149         int res;
 150 
 151         ops.mode = MTD_OPS_PLACE_OOB;
 152         ops.ooboffs = offs & mask;
 153         ops.ooblen = len;
 154         ops.oobbuf = buf;
 155         ops.datbuf = NULL;
 156 
 157         res = mtd_write_oob(mtd, offs & ~mask, &ops);
 158         *retlen = ops.oobretlen;
 159         return res;
 160 }
 161 
 162 #ifdef CONFIG_NFTL_RW
 163 
 164 /*
 165  * Write data and oob to flash
 166  */
 167 static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
 168                       size_t *retlen, uint8_t *buf, uint8_t *oob)
 169 {
 170         loff_t mask = mtd->writesize - 1;
 171         struct mtd_oob_ops ops;
 172         int res;
 173 
 174         ops.mode = MTD_OPS_PLACE_OOB;
 175         ops.ooboffs = offs & mask;
 176         ops.ooblen = mtd->oobsize;
 177         ops.oobbuf = oob;
 178         ops.datbuf = buf;
 179         ops.len = len;
 180 
 181         res = mtd_write_oob(mtd, offs & ~mask, &ops);
 182         *retlen = ops.retlen;
 183         return res;
 184 }
 185 
 186 /* Actual NFTL access routines */
 187 /* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used
 188  *      when the give Virtual Unit Chain
 189  */
 190 static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
 191 {
 192         /* For a given Virtual Unit Chain: find or create a free block and
 193            add it to the chain */
 194         /* We're passed the number of the last EUN in the chain, to save us from
 195            having to look it up again */
 196         u16 pot = nftl->LastFreeEUN;
 197         int silly = nftl->nb_blocks;
 198 
 199         /* Normally, we force a fold to happen before we run out of free blocks completely */
 200         if (!desperate && nftl->numfreeEUNs < 2) {
 201                 pr_debug("NFTL_findfreeblock: there are too few free EUNs\n");
 202                 return BLOCK_NIL;
 203         }
 204 
 205         /* Scan for a free block */
 206         do {
 207                 if (nftl->ReplUnitTable[pot] == BLOCK_FREE) {
 208                         nftl->LastFreeEUN = pot;
 209                         nftl->numfreeEUNs--;
 210                         return pot;
 211                 }
 212 
 213                 /* This will probably point to the MediaHdr unit itself,
 214                    right at the beginning of the partition. But that unit
 215                    (and the backup unit too) should have the UCI set
 216                    up so that it's not selected for overwriting */
 217                 if (++pot > nftl->lastEUN)
 218                         pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN);
 219 
 220                 if (!silly--) {
 221                         printk("Argh! No free blocks found! LastFreeEUN = %d, "
 222                                "FirstEUN = %d\n", nftl->LastFreeEUN,
 223                                le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN));
 224                         return BLOCK_NIL;
 225                 }
 226         } while (pot != nftl->LastFreeEUN);
 227 
 228         return BLOCK_NIL;
 229 }
 230 
 231 static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
 232 {
 233         struct mtd_info *mtd = nftl->mbd.mtd;
 234         u16 BlockMap[MAX_SECTORS_PER_UNIT];
 235         unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
 236         unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
 237         unsigned int thisEUN;
 238         int block;
 239         int silly;
 240         unsigned int targetEUN;
 241         struct nftl_oob oob;
 242         int inplace = 1;
 243         size_t retlen;
 244 
 245         memset(BlockMap, 0xff, sizeof(BlockMap));
 246         memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
 247 
 248         thisEUN = nftl->EUNtable[thisVUC];
 249 
 250         if (thisEUN == BLOCK_NIL) {
 251                 printk(KERN_WARNING "Trying to fold non-existent "
 252                        "Virtual Unit Chain %d!\n", thisVUC);
 253                 return BLOCK_NIL;
 254         }
 255 
 256         /* Scan to find the Erase Unit which holds the actual data for each
 257            512-byte block within the Chain.
 258         */
 259         silly = MAX_LOOPS;
 260         targetEUN = BLOCK_NIL;
 261         while (thisEUN <= nftl->lastEUN ) {
 262                 unsigned int status, foldmark;
 263 
 264                 targetEUN = thisEUN;
 265                 for (block = 0; block < nftl->EraseSize / 512; block ++) {
 266                         nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
 267                                       (block * 512), 16 , &retlen,
 268                                       (char *)&oob);
 269                         if (block == 2) {
 270                                 foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
 271                                 if (foldmark == FOLD_MARK_IN_PROGRESS) {
 272                                         pr_debug("Write Inhibited on EUN %d\n", thisEUN);
 273                                         inplace = 0;
 274                                 } else {
 275                                         /* There's no other reason not to do inplace,
 276                                            except ones that come later. So we don't need
 277                                            to preserve inplace */
 278                                         inplace = 1;
 279                                 }
 280                         }
 281                         status = oob.b.Status | oob.b.Status1;
 282                         BlockLastState[block] = status;
 283 
 284                         switch(status) {
 285                         case SECTOR_FREE:
 286                                 BlockFreeFound[block] = 1;
 287                                 break;
 288 
 289                         case SECTOR_USED:
 290                                 if (!BlockFreeFound[block])
 291                                         BlockMap[block] = thisEUN;
 292                                 else
 293                                         printk(KERN_WARNING
 294                                                "SECTOR_USED found after SECTOR_FREE "
 295                                                "in Virtual Unit Chain %d for block %d\n",
 296                                                thisVUC, block);
 297                                 break;
 298                         case SECTOR_DELETED:
 299                                 if (!BlockFreeFound[block])
 300                                         BlockMap[block] = BLOCK_NIL;
 301                                 else
 302                                         printk(KERN_WARNING
 303                                                "SECTOR_DELETED found after SECTOR_FREE "
 304                                                "in Virtual Unit Chain %d for block %d\n",
 305                                                thisVUC, block);
 306                                 break;
 307 
 308                         case SECTOR_IGNORE:
 309                                 break;
 310                         default:
 311                                 printk("Unknown status for block %d in EUN %d: %x\n",
 312                                        block, thisEUN, status);
 313                         }
 314                 }
 315 
 316                 if (!silly--) {
 317                         printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
 318                                thisVUC);
 319                         return BLOCK_NIL;
 320                 }
 321 
 322                 thisEUN = nftl->ReplUnitTable[thisEUN];
 323         }
 324 
 325         if (inplace) {
 326                 /* We're being asked to be a fold-in-place. Check
 327                    that all blocks which actually have data associated
 328                    with them (i.e. BlockMap[block] != BLOCK_NIL) are
 329                    either already present or SECTOR_FREE in the target
 330                    block. If not, we're going to have to fold out-of-place
 331                    anyway.
 332                 */
 333                 for (block = 0; block < nftl->EraseSize / 512 ; block++) {
 334                         if (BlockLastState[block] != SECTOR_FREE &&
 335                             BlockMap[block] != BLOCK_NIL &&
 336                             BlockMap[block] != targetEUN) {
 337                                 pr_debug("Setting inplace to 0. VUC %d, "
 338                                       "block %d was %x lastEUN, "
 339                                       "and is in EUN %d (%s) %d\n",
 340                                       thisVUC, block, BlockLastState[block],
 341                                       BlockMap[block],
 342                                       BlockMap[block]== targetEUN ? "==" : "!=",
 343                                       targetEUN);
 344                                 inplace = 0;
 345                                 break;
 346                         }
 347                 }
 348 
 349                 if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
 350                     pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
 351                     BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
 352                     SECTOR_FREE) {
 353                         pr_debug("Pending write not free in EUN %d. "
 354                               "Folding out of place.\n", targetEUN);
 355                         inplace = 0;
 356                 }
 357         }
 358 
 359         if (!inplace) {
 360                 pr_debug("Cannot fold Virtual Unit Chain %d in place. "
 361                       "Trying out-of-place\n", thisVUC);
 362                 /* We need to find a targetEUN to fold into. */
 363                 targetEUN = NFTL_findfreeblock(nftl, 1);
 364                 if (targetEUN == BLOCK_NIL) {
 365                         /* Ouch. Now we're screwed. We need to do a
 366                            fold-in-place of another chain to make room
 367                            for this one. We need a better way of selecting
 368                            which chain to fold, because makefreeblock will
 369                            only ask us to fold the same one again.
 370                         */
 371                         printk(KERN_WARNING
 372                                "NFTL_findfreeblock(desperate) returns 0xffff.\n");
 373                         return BLOCK_NIL;
 374                 }
 375         } else {
 376                 /* We put a fold mark in the chain we are folding only if we
 377                fold in place to help the mount check code. If we do not fold in
 378                place, it is possible to find the valid chain by selecting the
 379                longer one */
 380                 oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
 381                 oob.u.c.unused = 0xffffffff;
 382                 nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
 383                                8, &retlen, (char *)&oob.u);
 384         }
 385 
 386         /* OK. We now know the location of every block in the Virtual Unit Chain,
 387            and the Erase Unit into which we are supposed to be copying.
 388            Go for it.
 389         */
 390         pr_debug("Folding chain %d into unit %d\n", thisVUC, targetEUN);
 391         for (block = 0; block < nftl->EraseSize / 512 ; block++) {
 392                 unsigned char movebuf[512];
 393                 int ret;
 394 
 395                 /* If it's in the target EUN already, or if it's pending write, do nothing */
 396                 if (BlockMap[block] == targetEUN ||
 397                     (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
 398                         continue;
 399                 }
 400 
 401                 /* copy only in non free block (free blocks can only
 402                    happen in case of media errors or deleted blocks) */
 403                 if (BlockMap[block] == BLOCK_NIL)
 404                         continue;
 405 
 406                 ret = mtd_read(mtd,
 407                                (nftl->EraseSize * BlockMap[block]) + (block * 512),
 408                                512,
 409                                &retlen,
 410                                movebuf);
 411                 if (ret < 0 && !mtd_is_bitflip(ret)) {
 412                         ret = mtd_read(mtd,
 413                                        (nftl->EraseSize * BlockMap[block]) + (block * 512),
 414                                        512,
 415                                        &retlen,
 416                                        movebuf);
 417                         if (ret != -EIO)
 418                                 printk("Error went away on retry.\n");
 419                 }
 420                 memset(&oob, 0xff, sizeof(struct nftl_oob));
 421                 oob.b.Status = oob.b.Status1 = SECTOR_USED;
 422 
 423                 nftl_write(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) +
 424                            (block * 512), 512, &retlen, movebuf, (char *)&oob);
 425         }
 426 
 427         /* add the header so that it is now a valid chain */
 428         oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
 429         oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = BLOCK_NIL;
 430 
 431         nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8,
 432                        8, &retlen, (char *)&oob.u);
 433 
 434         /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
 435 
 436         /* At this point, we have two different chains for this Virtual Unit, and no way to tell
 437            them apart. If we crash now, we get confused. However, both contain the same data, so we
 438            shouldn't actually lose data in this case. It's just that when we load up on a medium which
 439            has duplicate chains, we need to free one of the chains because it's not necessary any more.
 440         */
 441         thisEUN = nftl->EUNtable[thisVUC];
 442         pr_debug("Want to erase\n");
 443 
 444         /* For each block in the old chain (except the targetEUN of course),
 445            free it and make it available for future use */
 446         while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
 447                 unsigned int EUNtmp;
 448 
 449                 EUNtmp = nftl->ReplUnitTable[thisEUN];
 450 
 451                 if (NFTL_formatblock(nftl, thisEUN) < 0) {
 452                         /* could not erase : mark block as reserved
 453                          */
 454                         nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
 455                 } else {
 456                         /* correctly erased : mark it as free */
 457                         nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
 458                         nftl->numfreeEUNs++;
 459                 }
 460                 thisEUN = EUNtmp;
 461         }
 462 
 463         /* Make this the new start of chain for thisVUC */
 464         nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
 465         nftl->EUNtable[thisVUC] = targetEUN;
 466 
 467         return targetEUN;
 468 }
 469 
 470 static u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock)
 471 {
 472         /* This is the part that needs some cleverness applied.
 473            For now, I'm doing the minimum applicable to actually
 474            get the thing to work.
 475            Wear-levelling and other clever stuff needs to be implemented
 476            and we also need to do some assessment of the results when
 477            the system loses power half-way through the routine.
 478         */
 479         u16 LongestChain = 0;
 480         u16 ChainLength = 0, thislen;
 481         u16 chain, EUN;
 482 
 483         for (chain = 0; chain < le32_to_cpu(nftl->MediaHdr.FormattedSize) / nftl->EraseSize; chain++) {
 484                 EUN = nftl->EUNtable[chain];
 485                 thislen = 0;
 486 
 487                 while (EUN <= nftl->lastEUN) {
 488                         thislen++;
 489                         //printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN);
 490                         EUN = nftl->ReplUnitTable[EUN] & 0x7fff;
 491                         if (thislen > 0xff00) {
 492                                 printk("Endless loop in Virtual Chain %d: Unit %x\n",
 493                                        chain, EUN);
 494                         }
 495                         if (thislen > 0xff10) {
 496                                 /* Actually, don't return failure. Just ignore this chain and
 497                                    get on with it. */
 498                                 thislen = 0;
 499                                 break;
 500                         }
 501                 }
 502 
 503                 if (thislen > ChainLength) {
 504                         //printk("New longest chain is %d with length %d\n", chain, thislen);
 505                         ChainLength = thislen;
 506                         LongestChain = chain;
 507                 }
 508         }
 509 
 510         if (ChainLength < 2) {
 511                 printk(KERN_WARNING "No Virtual Unit Chains available for folding. "
 512                        "Failing request\n");
 513                 return BLOCK_NIL;
 514         }
 515 
 516         return NFTL_foldchain (nftl, LongestChain, pendingblock);
 517 }
 518 
 519 /* NFTL_findwriteunit: Return the unit number into which we can write
 520                        for this block. Make it available if it isn't already
 521 */
 522 static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
 523 {
 524         u16 lastEUN;
 525         u16 thisVUC = block / (nftl->EraseSize / 512);
 526         struct mtd_info *mtd = nftl->mbd.mtd;
 527         unsigned int writeEUN;
 528         unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
 529         size_t retlen;
 530         int silly, silly2 = 3;
 531         struct nftl_oob oob;
 532 
 533         do {
 534                 /* Scan the media to find a unit in the VUC which has
 535                    a free space for the block in question.
 536                 */
 537 
 538                 /* This condition catches the 0x[7f]fff cases, as well as
 539                    being a sanity check for past-end-of-media access
 540                 */
 541                 lastEUN = BLOCK_NIL;
 542                 writeEUN = nftl->EUNtable[thisVUC];
 543                 silly = MAX_LOOPS;
 544                 while (writeEUN <= nftl->lastEUN) {
 545                         struct nftl_bci bci;
 546                         size_t retlen;
 547                         unsigned int status;
 548 
 549                         lastEUN = writeEUN;
 550 
 551                         nftl_read_oob(mtd,
 552                                       (writeEUN * nftl->EraseSize) + blockofs,
 553                                       8, &retlen, (char *)&bci);
 554 
 555                         pr_debug("Status of block %d in EUN %d is %x\n",
 556                               block , writeEUN, le16_to_cpu(bci.Status));
 557 
 558                         status = bci.Status | bci.Status1;
 559                         switch(status) {
 560                         case SECTOR_FREE:
 561                                 return writeEUN;
 562 
 563                         case SECTOR_DELETED:
 564                         case SECTOR_USED:
 565                         case SECTOR_IGNORE:
 566                                 break;
 567                         default:
 568                                 // Invalid block. Don't use it any more. Must implement.
 569                                 break;
 570                         }
 571 
 572                         if (!silly--) {
 573                                 printk(KERN_WARNING
 574                                        "Infinite loop in Virtual Unit Chain 0x%x\n",
 575                                        thisVUC);
 576                                 return BLOCK_NIL;
 577                         }
 578 
 579                         /* Skip to next block in chain */
 580                         writeEUN = nftl->ReplUnitTable[writeEUN];
 581                 }
 582 
 583                 /* OK. We didn't find one in the existing chain, or there
 584                    is no existing chain. */
 585 
 586                 /* Try to find an already-free block */
 587                 writeEUN = NFTL_findfreeblock(nftl, 0);
 588 
 589                 if (writeEUN == BLOCK_NIL) {
 590                         /* That didn't work - there were no free blocks just
 591                            waiting to be picked up. We're going to have to fold
 592                            a chain to make room.
 593                         */
 594 
 595                         /* First remember the start of this chain */
 596                         //u16 startEUN = nftl->EUNtable[thisVUC];
 597 
 598                         //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC);
 599                         writeEUN = NFTL_makefreeblock(nftl, BLOCK_NIL);
 600 
 601                         if (writeEUN == BLOCK_NIL) {
 602                                 /* OK, we accept that the above comment is
 603                                    lying - there may have been free blocks
 604                                    last time we called NFTL_findfreeblock(),
 605                                    but they are reserved for when we're
 606                                    desperate. Well, now we're desperate.
 607                                 */
 608                                 pr_debug("Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC);
 609                                 writeEUN = NFTL_findfreeblock(nftl, 1);
 610                         }
 611                         if (writeEUN == BLOCK_NIL) {
 612                                 /* Ouch. This should never happen - we should
 613                                    always be able to make some room somehow.
 614                                    If we get here, we've allocated more storage
 615                                    space than actual media, or our makefreeblock
 616                                    routine is missing something.
 617                                 */
 618                                 printk(KERN_WARNING "Cannot make free space.\n");
 619                                 return BLOCK_NIL;
 620                         }
 621                         //printk("Restarting scan\n");
 622                         lastEUN = BLOCK_NIL;
 623                         continue;
 624                 }
 625 
 626                 /* We've found a free block. Insert it into the chain. */
 627 
 628                 if (lastEUN != BLOCK_NIL) {
 629                         thisVUC |= 0x8000; /* It's a replacement block */
 630                 } else {
 631                         /* The first block in a new chain */
 632                         nftl->EUNtable[thisVUC] = writeEUN;
 633                 }
 634 
 635                 /* set up the actual EUN we're writing into */
 636                 /* Both in our cache... */
 637                 nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
 638 
 639                 /* ... and on the flash itself */
 640                 nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
 641                               &retlen, (char *)&oob.u);
 642 
 643                 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
 644 
 645                 nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
 646                                &retlen, (char *)&oob.u);
 647 
 648                 /* we link the new block to the chain only after the
 649                    block is ready. It avoids the case where the chain
 650                    could point to a free block */
 651                 if (lastEUN != BLOCK_NIL) {
 652                         /* Both in our cache... */
 653                         nftl->ReplUnitTable[lastEUN] = writeEUN;
 654                         /* ... and on the flash itself */
 655                         nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
 656                                       8, &retlen, (char *)&oob.u);
 657 
 658                         oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
 659                                 = cpu_to_le16(writeEUN);
 660 
 661                         nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
 662                                        8, &retlen, (char *)&oob.u);
 663                 }
 664 
 665                 return writeEUN;
 666 
 667         } while (silly2--);
 668 
 669         printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
 670                thisVUC);
 671         return BLOCK_NIL;
 672 }
 673 
 674 static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
 675                            char *buffer)
 676 {
 677         struct NFTLrecord *nftl = (void *)mbd;
 678         u16 writeEUN;
 679         unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
 680         size_t retlen;
 681         struct nftl_oob oob;
 682 
 683         writeEUN = NFTL_findwriteunit(nftl, block);
 684 
 685         if (writeEUN == BLOCK_NIL) {
 686                 printk(KERN_WARNING
 687                        "NFTL_writeblock(): Cannot find block to write to\n");
 688                 /* If we _still_ haven't got a block to use, we're screwed */
 689                 return 1;
 690         }
 691 
 692         memset(&oob, 0xff, sizeof(struct nftl_oob));
 693         oob.b.Status = oob.b.Status1 = SECTOR_USED;
 694 
 695         nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
 696                    512, &retlen, (char *)buffer, (char *)&oob);
 697         return 0;
 698 }
 699 #endif /* CONFIG_NFTL_RW */
 700 
 701 static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
 702                           char *buffer)
 703 {
 704         struct NFTLrecord *nftl = (void *)mbd;
 705         struct mtd_info *mtd = nftl->mbd.mtd;
 706         u16 lastgoodEUN;
 707         u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
 708         unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
 709         unsigned int status;
 710         int silly = MAX_LOOPS;
 711         size_t retlen;
 712         struct nftl_bci bci;
 713 
 714         lastgoodEUN = BLOCK_NIL;
 715 
 716         if (thisEUN != BLOCK_NIL) {
 717                 while (thisEUN < nftl->nb_blocks) {
 718                         if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
 719                                           blockofs, 8, &retlen,
 720                                           (char *)&bci) < 0)
 721                                 status = SECTOR_IGNORE;
 722                         else
 723                                 status = bci.Status | bci.Status1;
 724 
 725                         switch (status) {
 726                         case SECTOR_FREE:
 727                                 /* no modification of a sector should follow a free sector */
 728                                 goto the_end;
 729                         case SECTOR_DELETED:
 730                                 lastgoodEUN = BLOCK_NIL;
 731                                 break;
 732                         case SECTOR_USED:
 733                                 lastgoodEUN = thisEUN;
 734                                 break;
 735                         case SECTOR_IGNORE:
 736                                 break;
 737                         default:
 738                                 printk("Unknown status for block %ld in EUN %d: %x\n",
 739                                        block, thisEUN, status);
 740                                 break;
 741                         }
 742 
 743                         if (!silly--) {
 744                                 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n",
 745                                        block / (nftl->EraseSize / 512));
 746                                 return 1;
 747                         }
 748                         thisEUN = nftl->ReplUnitTable[thisEUN];
 749                 }
 750         }
 751 
 752  the_end:
 753         if (lastgoodEUN == BLOCK_NIL) {
 754                 /* the requested block is not on the media, return all 0x00 */
 755                 memset(buffer, 0, 512);
 756         } else {
 757                 loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
 758                 size_t retlen;
 759                 int res = mtd_read(mtd, ptr, 512, &retlen, buffer);
 760 
 761                 if (res < 0 && !mtd_is_bitflip(res))
 762                         return -EIO;
 763         }
 764         return 0;
 765 }
 766 
 767 static int nftl_getgeo(struct mtd_blktrans_dev *dev,  struct hd_geometry *geo)
 768 {
 769         struct NFTLrecord *nftl = (void *)dev;
 770 
 771         geo->heads = nftl->heads;
 772         geo->sectors = nftl->sectors;
 773         geo->cylinders = nftl->cylinders;
 774 
 775         return 0;
 776 }
 777 
 778 /****************************************************************************
 779  *
 780  * Module stuff
 781  *
 782  ****************************************************************************/
 783 
 784 
 785 static struct mtd_blktrans_ops nftl_tr = {
 786         .name           = "nftl",
 787         .major          = NFTL_MAJOR,
 788         .part_bits      = NFTL_PARTN_BITS,
 789         .blksize        = 512,
 790         .getgeo         = nftl_getgeo,
 791         .readsect       = nftl_readblock,
 792 #ifdef CONFIG_NFTL_RW
 793         .writesect      = nftl_writeblock,
 794 #endif
 795         .add_mtd        = nftl_add_mtd,
 796         .remove_dev     = nftl_remove_dev,
 797         .owner          = THIS_MODULE,
 798 };
 799 
 800 static int __init init_nftl(void)
 801 {
 802         return register_mtd_blktrans(&nftl_tr);
 803 }
 804 
 805 static void __exit cleanup_nftl(void)
 806 {
 807         deregister_mtd_blktrans(&nftl_tr);
 808 }
 809 
 810 module_init(init_nftl);
 811 module_exit(cleanup_nftl);
 812 
 813 MODULE_LICENSE("GPL");
 814 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
 815 MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");
 816 MODULE_ALIAS_BLOCKDEV_MAJOR(NFTL_MAJOR);

/* [<][>][^][v][top][bottom][index][help] */