int INFTL_mount(struct INFTLrecord *s)
{
	unsigned int block, first_block, prev_block, last_block;
	unsigned int first_logical_block, logical_block, erase_mark;
	int chain_length, do_format_chain;
	struct inftl_unithead1 h0;
	struct inftl_unittail h1;
	int i, retlen;
	u8 *ANACtable, ANAC;

	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_mount(inftl=0x%x)\n", (int)s);

	/* Search for INFTL MediaHeader and Spare INFTL Media Header */
	if (find_boot_record(s) < 0) {
		printk(KERN_WARNING "INFTL: could not find valid boot record?\n");
		return -1;
	}

	/* Init the logical to physical table */
	for (i = 0; i < s->nb_blocks; i++)
		s->VUtable[i] = BLOCK_NIL;

	logical_block = block = BLOCK_NIL;

	/* Temporary buffer to store ANAC numbers. */
	ANACtable = kmalloc(s->nb_blocks * sizeof(u8), GFP_KERNEL);
	memset(ANACtable, 0, s->nb_blocks);

	/*
	 * First pass is to explore each physical unit, and construct the
	 * virtual chains that exist (newest physical unit goes into VUtable).
	 * Any block that is in any way invalid will be left in the
	 * NOTEXPLORED state. Then at the end we will try to format it and
	 * mark it as free.
	 */
	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 1, explore each unit\n");
	for (first_block = s->firstEUN; first_block <= s->lastEUN; first_block++) {
		if (s->PUtable[first_block] != BLOCK_NOTEXPLORED)
			continue;

		do_format_chain = 0;
		first_logical_block = BLOCK_NIL;
		last_block = BLOCK_NIL;
		block = first_block;

		for (chain_length = 0; ; chain_length++) {

			if ((chain_length == 0) && 
			    (s->PUtable[block] != BLOCK_NOTEXPLORED)) {
				/* Nothing to do here, onto next block */
				break;
			}

			if (MTD_READOOB(s->mtd, block * s->EraseSize + 8,
			    8, &retlen, (char *)&h0) < 0 ||
			    MTD_READOOB(s->mtd, block * s->EraseSize +
			    2 * SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0) {
				/* Should never happen? */
				do_format_chain++;
				break;
			}

			logical_block = le16_to_cpu(h0.virtualUnitNo);
			prev_block = le16_to_cpu(h0.prevUnitNo);
			erase_mark = le16_to_cpu((h1.EraseMark | h1.EraseMark1));
			ANACtable[block] = h0.ANAC;

			/* Previous block is relative to start of Partition */
			if (prev_block < s->nb_blocks)
				prev_block += s->firstEUN;

			/* Already explored partial chain? */
			if (s->PUtable[block] != BLOCK_NOTEXPLORED) {
				/* Check if chain for this logical */
				if (logical_block == first_logical_block) {
					if (last_block != BLOCK_NIL)
						s->PUtable[last_block] = block;
				}
				break;
			}

			/* Check for invalid block */
			if (erase_mark != ERASE_MARK) {
				printk(KERN_WARNING "INFTL: corrupt block %d "
					"in chain %d, chain length %d, erase "
					"mark 0x%x?\n", block, first_block,
					chain_length, erase_mark);
				/*
				 * Assume end of chain, probably incomplete
				 * fold/erase...
				 */
				if (chain_length == 0)
					do_format_chain++;
				break;
			}

			/* Check for it being free already then... */
			if ((logical_block == BLOCK_FREE) ||
			    (logical_block == BLOCK_NIL)) {
				s->PUtable[block] = BLOCK_FREE;
				break;
			}

			/* Sanity checks on block numbers */
			if ((logical_block >= s->nb_blocks) ||
			    ((prev_block >= s->nb_blocks) &&
			     (prev_block != BLOCK_NIL))) {
				if (chain_length > 0) {
					printk(KERN_WARNING "INFTL: corrupt "
						"block %d in chain %d?\n",
						block, first_block);
					do_format_chain++;
				}
				break;
			}

			if (first_logical_block == BLOCK_NIL) {
				first_logical_block = logical_block;
			} else {
				if (first_logical_block != logical_block) {
					/* Normal for folded chain... */
					break;
				}
			}

			/*
			 * Current block is valid, so if we followed a virtual
			 * chain to get here then we can set the previous
			 * block pointer in our PUtable now. Then move onto
			 * the previous block in the chain.
			 */
			s->PUtable[block] = BLOCK_NIL;
			if (last_block != BLOCK_NIL)
				s->PUtable[last_block] = block;
			last_block = block;
			block = prev_block;

			/* Check for end of chain */
			if (block == BLOCK_NIL)
				break;

			/* Validate next block before following it... */
			if (block > s->lastEUN) {
				printk(KERN_WARNING "INFTL: invalid previous "
					"block %d in chain %d?\n", block,
					first_block);
				do_format_chain++;
				break;
			}
		}

		if (do_format_chain) {
			format_chain(s, first_block);
			continue;
		}

		/*
		 * Looks like a valid chain then. It may not really be the
		 * newest block in the chain, but it is the newest we have
		 * found so far. We might update it in later iterations of
		 * this loop if we find something newer.
		 */
		s->VUtable[first_logical_block] = first_block;
		logical_block = BLOCK_NIL;
	}

#ifdef CONFIG_MTD_DEBUG_VERBOSE
	if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
		INFTL_dumptables(s);
#endif

	/*
	 * Second pass, check for infinite loops in chains. These are
	 * possible because we don't update the previous pointers when
	 * we fold chains. No big deal, just fix them up in PUtable.
	 */
	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 2, validate virtual chains\n");
	for (logical_block = 0; logical_block < s->numvunits; logical_block++) {
		block = s->VUtable[logical_block];
		last_block = BLOCK_NIL;

		/* Check for free/reserved/nil */
		if (block >= BLOCK_RESERVED)
			continue;

		ANAC = ANACtable[block];
		for (i = 0; i < s->numvunits; i++) {
			if (s->PUtable[block] == BLOCK_NIL)
				break;
			if (s->PUtable[block] > s->lastEUN) {
				printk(KERN_WARNING "INFTL: invalid prev %d, "
					"in virtual chain %d\n",
					s->PUtable[block], logical_block);
				s->PUtable[block] = BLOCK_NIL;
					
			}
			if (ANACtable[block] != ANAC) {
				/*
				 * Chain must point back to itself. This is ok,
				 * but we will need adjust the tables with this
				 * newest block and oldest block.
				 */
				s->VUtable[logical_block] = block;
				s->PUtable[last_block] = BLOCK_NIL;
				break;
			}

			ANAC--;
			last_block = block;
			block = s->PUtable[block];
		}

		if (i >= s->nb_blocks) {
			/*
			 * Uhoo, infinite chain with valid ANACS!
			 * Format whole chain...
			 */
			format_chain(s, first_block);
		}
	}

#ifdef CONFIG_MTD_DEBUG_VERBOSE
	if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
		INFTL_dumptables(s);
	if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
		INFTL_dumpVUchains(s);
#endif

	/*
	 * Third pass, format unreferenced blocks and init free block count.
	 */
	s->numfreeEUNs = 0;
	s->LastFreeEUN = BLOCK_NIL;

	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 3, format unused blocks\n");
	for (block = s->firstEUN; block <= s->lastEUN; block++) {
		if (s->PUtable[block] == BLOCK_NOTEXPLORED) {
			printk("INFTL: unreferenced block %d, formatting it\n",
				block);
			if (INFTL_formatblock(s, block) < 0)
				s->PUtable[block] = BLOCK_RESERVED;
			else
				s->PUtable[block] = BLOCK_FREE;
		}
		if (s->PUtable[block] == BLOCK_FREE) {
			s->numfreeEUNs++;
			if (s->LastFreeEUN == BLOCK_NIL)
				s->LastFreeEUN = block;
		}
	}

	kfree(ANACtable);
	return 0;
}
Пример #2
0
/*
 * INFTL_findwriteunit: Return the unit number into which we can write 
 *                      for this block. Make it available if it isn't already.
 */
static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block)
{
	unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE);
	unsigned int thisEUN, writeEUN, prev_block, status;
	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1);
	struct inftl_oob oob;
	struct inftl_bci bci;
	unsigned char anac, nacs, parity;
	size_t retlen;
	int silly, silly2 = 3;

	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findwriteunit(inftl=%p,"
		"block=%d)\n", inftl, block);

	do {
		/*
		 * Scan the media to find a unit in the VUC which has
		 * a free space for the block in question.
		 */
		writeEUN = BLOCK_NIL;
		thisEUN = inftl->VUtable[thisVUC];
		silly = MAX_LOOPS;

		while (thisEUN <= inftl->lastEUN) {
			MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +
				blockofs, 8, &retlen, (char *)&bci);

                        status = bci.Status | bci.Status1;
			DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in "
				"EUN %d is %x\n", block , writeEUN, status);

			switch(status) {
			case SECTOR_FREE:
				writeEUN = thisEUN;
				break;
			case SECTOR_DELETED:
			case SECTOR_USED:
				/* Can't go any further */
				goto hitused;
			case SECTOR_IGNORE:
				break;
			default:
				/*
				 * Invalid block. Don't use it any more.
				 * Must implement.
				 */
				break;			
			}
			
			if (!silly--) { 
				printk(KERN_WARNING "INFTL: infinite loop in "
					"Virtual Unit Chain 0x%x\n", thisVUC);
				return 0xffff;
			}

			/* Skip to next block in chain */
			thisEUN = inftl->PUtable[thisEUN];
		}

hitused:
		if (writeEUN != BLOCK_NIL)
			return writeEUN;


		/*
		 * OK. We didn't find one in the existing chain, or there 
		 * is no existing chain. Allocate a new one.
		 */
		writeEUN = INFTL_findfreeblock(inftl, 0);

		if (writeEUN == BLOCK_NIL) {
			/*
			 * That didn't work - there were no free blocks just
			 * waiting to be picked up. We're going to have to fold
			 * a chain to make room.
			 */
			thisEUN = INFTL_makefreeblock(inftl, 0xffff);

			/*
			 * Hopefully we free something, lets try again.
			 * This time we are desperate...
			 */
			DEBUG(MTD_DEBUG_LEVEL1, "INFTL: using desperate==1 "
				"to find free EUN to accommodate write to "
				"VUC %d\n", thisVUC);
			writeEUN = INFTL_findfreeblock(inftl, 1);
			if (writeEUN == BLOCK_NIL) {
				/*
				 * Ouch. This should never happen - we should
				 * always be able to make some room somehow. 
				 * If we get here, we've allocated more storage 
				 * space than actual media, or our makefreeblock
				 * routine is missing something.
				 */
				printk(KERN_WARNING "INFTL: cannot make free "
					"space.\n");
#ifdef DEBUG
				INFTL_dumptables(inftl);
				INFTL_dumpVUchains(inftl);
#endif
				return BLOCK_NIL;
			}			
		}

		/*
		 * Insert new block into virtual chain. Firstly update the
		 * block headers in flash...
		 */
		anac = 0;
		nacs = 0;
		thisEUN = inftl->VUtable[thisVUC];
		if (thisEUN != BLOCK_NIL) {
			MTD_READOOB(inftl->mbd.mtd, thisEUN * inftl->EraseSize
				+ 8, 8, &retlen, (char *)&oob.u);
			anac = oob.u.a.ANAC + 1;
			nacs = oob.u.a.NACs + 1;
		}

		prev_block = inftl->VUtable[thisVUC];
		if (prev_block < inftl->nb_blocks)
			prev_block -= inftl->firstEUN;

		parity = (nrbits(thisVUC, 16) & 0x1) ? 0x1 : 0;
		parity |= (nrbits(prev_block, 16) & 0x1) ? 0x2 : 0;
		parity |= (nrbits(anac, 8) & 0x1) ? 0x4 : 0;
		parity |= (nrbits(nacs, 8) & 0x1) ? 0x8 : 0;
 
		oob.u.a.virtualUnitNo = cpu_to_le16(thisVUC);
		oob.u.a.prevUnitNo = cpu_to_le16(prev_block);
		oob.u.a.ANAC = anac;
		oob.u.a.NACs = nacs;
		oob.u.a.parityPerField = parity;
		oob.u.a.discarded = 0xaa;

		MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize + 8, 8,
			&retlen, (char *)&oob.u);

		/* Also back up header... */
		oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC);
		oob.u.b.prevUnitNo = cpu_to_le16(prev_block);
		oob.u.b.ANAC = anac;
		oob.u.b.NACs = nacs;
		oob.u.b.parityPerField = parity;
		oob.u.b.discarded = 0xaa;

		MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize + 
			SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u);

		inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC];
		inftl->VUtable[thisVUC] = writeEUN;

		inftl->numfreeEUNs--;
		return writeEUN;

	} while (silly2--);

	printk(KERN_WARNING "INFTL: error folding to make room for Virtual "
		"Unit Chain 0x%x\n", thisVUC);
	return 0xffff;
}
static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block)
{
	unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE);
	unsigned int thisEUN, writeEUN, prev_block, status;
	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1);
	struct mtd_info *mtd = inftl->mbd.mtd;
	struct inftl_oob oob;
	struct inftl_bci bci;
	unsigned char anac, nacs, parity;
	size_t retlen;
	int silly, silly2 = 3;

	pr_debug("INFTL: INFTL_findwriteunit(inftl=%p,block=%d)\n",
			inftl, block);

	do {
		writeEUN = BLOCK_NIL;
		thisEUN = inftl->VUtable[thisVUC];
		silly = MAX_LOOPS;

		while (thisEUN <= inftl->lastEUN) {
			inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
				       blockofs, 8, &retlen, (char *)&bci);

			status = bci.Status | bci.Status1;
			pr_debug("INFTL: status of block %d in EUN %d is %x\n",
					block , writeEUN, status);

			switch(status) {
			case SECTOR_FREE:
				writeEUN = thisEUN;
				break;
			case SECTOR_DELETED:
			case SECTOR_USED:
				
				goto hitused;
			case SECTOR_IGNORE:
				break;
			default:
				break;
			}

			if (!silly--) {
				printk(KERN_WARNING "INFTL: infinite loop in "
					"Virtual Unit Chain 0x%x\n", thisVUC);
				return BLOCK_NIL;
			}

			
			thisEUN = inftl->PUtable[thisEUN];
		}

hitused:
		if (writeEUN != BLOCK_NIL)
			return writeEUN;


		writeEUN = INFTL_findfreeblock(inftl, 0);

		if (writeEUN == BLOCK_NIL) {
			thisEUN = INFTL_makefreeblock(inftl, block);

			pr_debug("INFTL: using desperate==1 to find free EUN "
					"to accommodate write to VUC %d\n",
					thisVUC);
			writeEUN = INFTL_findfreeblock(inftl, 1);
			if (writeEUN == BLOCK_NIL) {
				printk(KERN_WARNING "INFTL: cannot make free "
					"space.\n");
#ifdef DEBUG
				INFTL_dumptables(inftl);
				INFTL_dumpVUchains(inftl);
#endif
				return BLOCK_NIL;
			}
		}

		anac = 0;
		nacs = 0;
		thisEUN = inftl->VUtable[thisVUC];
		if (thisEUN != BLOCK_NIL) {
			inftl_read_oob(mtd, thisEUN * inftl->EraseSize
				       + 8, 8, &retlen, (char *)&oob.u);
			anac = oob.u.a.ANAC + 1;
			nacs = oob.u.a.NACs + 1;
		}

		prev_block = inftl->VUtable[thisVUC];
		if (prev_block < inftl->nb_blocks)
			prev_block -= inftl->firstEUN;

		parity = (nrbits(thisVUC, 16) & 0x1) ? 0x1 : 0;
		parity |= (nrbits(prev_block, 16) & 0x1) ? 0x2 : 0;
		parity |= (nrbits(anac, 8) & 0x1) ? 0x4 : 0;
		parity |= (nrbits(nacs, 8) & 0x1) ? 0x8 : 0;

		oob.u.a.virtualUnitNo = cpu_to_le16(thisVUC);
		oob.u.a.prevUnitNo = cpu_to_le16(prev_block);
		oob.u.a.ANAC = anac;
		oob.u.a.NACs = nacs;
		oob.u.a.parityPerField = parity;
		oob.u.a.discarded = 0xaa;

		inftl_write_oob(mtd, writeEUN * inftl->EraseSize + 8, 8,
				&retlen, (char *)&oob.u);

		
		oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC);
		oob.u.b.prevUnitNo = cpu_to_le16(prev_block);
		oob.u.b.ANAC = anac;
		oob.u.b.NACs = nacs;
		oob.u.b.parityPerField = parity;
		oob.u.b.discarded = 0xaa;

		inftl_write_oob(mtd, writeEUN * inftl->EraseSize +
				SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u);

		inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC];
		inftl->VUtable[thisVUC] = writeEUN;

		inftl->numfreeEUNs--;
		return writeEUN;

	} while (silly2--);

	printk(KERN_WARNING "INFTL: error folding to make room for Virtual "
		"Unit Chain 0x%x\n", thisVUC);
	return BLOCK_NIL;
}