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