Example #1
0
static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
                           char *buffer)
{
    struct NFTLrecord *nftl = (void *)mbd;
    u16 writeEUN;
    unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
    size_t retlen;
    struct nftl_oob oob;

    writeEUN = NFTL_findwriteunit(nftl, block);

    if (writeEUN == BLOCK_NIL) {
        printk(KERN_WARNING
               "NFTL_writeblock(): Cannot find block to write to\n");
        /* If we _still_ haven't got a block to use, we're screwed */
        return 1;
    }

    memset(&oob, 0xff, sizeof(struct nftl_oob));
    oob.b.Status = oob.b.Status1 = SECTOR_USED;
    MTD_WRITEECC(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
                 512, &retlen, (char *)buffer, (char *)&oob, &nftl->oobinfo);
    /* need to write SECTOR_USED flags since they are not written in mtd_writeecc */

    return 0;
}
Example #2
0
static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
			    char *buffer)
{
	struct INFTLrecord *inftl = (void *)mbd;
	unsigned int writeEUN;
	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
	size_t retlen;
	struct inftl_oob oob;
	char *p, *pend;

	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_writeblock(inftl=%p,block=%ld,"
		"buffer=%p)\n", inftl, block, buffer);

	/* Is block all zero? */
	pend = buffer + SECTORSIZE;
	for (p = buffer; p < pend && !*p; p++)
		;

	if (p < pend) {
		writeEUN = INFTL_findwriteunit(inftl, block);

		if (writeEUN == BLOCK_NIL) {
			printk(KERN_WARNING "inftl_writeblock(): cannot find "
				"block to write to\n");
			/*
			 * If we _still_ haven't got a block to use,
			 * we're screwed.
			 */
			return 1;
		}

		memset(&oob, 0xff, sizeof(struct inftl_oob));
		oob.b.Status = oob.b.Status1 = SECTOR_USED;
		MTD_WRITEECC(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
			blockofs, SECTORSIZE, &retlen, (char *)buffer,
			(char *)&oob, &inftl->oobinfo);
		/*
		 * need to write SECTOR_USED flags since they are not written
		 * in mtd_writeecc
		 */
	} else {
		INFTL_deleteblock(inftl, block);
	}

	return 0;
}
Example #3
0
static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, 
			    char *buffer)
{
	struct INFTLrecord *inftl = (void *)mbd;
	unsigned int writeEUN;
	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
	size_t retlen;
	u8 eccbuf[6];
	char *p, *pend;

	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_writeblock(inftl=0x%x,block=%d,"
		"buffer=0x%x)\n", (int)inftl, block, (int)buffer);

	/* Is block all zero? */
	pend = buffer + SECTORSIZE;
	for (p = buffer; p < pend && !*p; p++)
		;

	if (p < pend) {
		writeEUN = INFTL_findwriteunit(inftl, block);

		if (writeEUN == BLOCK_NIL) {
			printk(KERN_WARNING "inftl_writeblock(): cannot find "
				"block to write to\n");
			/*
			 * If we _still_ haven't got a block to use,
			 * we're screwed.
			 */
			return 1;
		}

		MTD_WRITEECC(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
			blockofs, SECTORSIZE, &retlen, (char *)buffer,
			(char *)eccbuf, NULL);
		/*
		 * No need to write SECTOR_USED flags since they are written
		 * in mtd_writeecc
		 */
	} else {
		INFTL_deleteblock(inftl, block);
	}

	return 0;
}
Example #4
0
static int NFTL_writeblock(struct NFTLrecord *nftl, unsigned block, char *buffer)
{
	u16 writeEUN;
	unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
	size_t retlen;
	u8 eccbuf[6];

	writeEUN = NFTL_findwriteunit(nftl, block);

	if (writeEUN == BLOCK_NIL) {
		printk(KERN_WARNING
		       "NFTL_writeblock(): Cannot find block to write to\n");
		/* If we _still_ haven't got a block to use, we're screwed */
		return 1;
	}

	MTD_WRITEECC(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs,
		     512, &retlen, (char *)buffer, (char *)eccbuf, NAND_ECC_DISKONCHIP);
        /* no need to write SECTOR_USED flags since they are written in mtd_writeecc */

	return 0;
}
Example #5
0
static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
{
    u16 BlockMap[MAX_SECTORS_PER_UNIT];
    unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
    unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
    unsigned int thisEUN;
    int block;
    int silly;
    unsigned int targetEUN;
    struct nftl_oob oob;
    int inplace = 1;
    size_t retlen;

    memset(BlockMap, 0xff, sizeof(BlockMap));
    memset(BlockFreeFound, 0, sizeof(BlockFreeFound));

    thisEUN = nftl->EUNtable[thisVUC];

    if (thisEUN == BLOCK_NIL) {
        printk(KERN_WARNING "Trying to fold non-existent "
               "Virtual Unit Chain %d!\n", thisVUC);
        return BLOCK_NIL;
    }

    /* Scan to find the Erase Unit which holds the actual data for each
       512-byte block within the Chain.
    */
    silly = MAX_LOOPS;
    targetEUN = BLOCK_NIL;
    while (thisEUN <= nftl->lastEUN ) {
        unsigned int status, foldmark;

        targetEUN = thisEUN;
        for (block = 0; block < nftl->EraseSize / 512; block ++) {
            MTD_READOOB(nftl->mbd.mtd,
                        (thisEUN * nftl->EraseSize) + (block * 512),
                        16 , &retlen, (char *)&oob);
            if (block == 2) {
                foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
                if (foldmark == FOLD_MARK_IN_PROGRESS) {
                    DEBUG(MTD_DEBUG_LEVEL1,
                          "Write Inhibited on EUN %d\n", thisEUN);
                    inplace = 0;
                } else {
                    /* There's no other reason not to do inplace,
                       except ones that come later. So we don't need
                       to preserve inplace */
                    inplace = 1;
                }
            }
            status = oob.b.Status | oob.b.Status1;
            BlockLastState[block] = status;

            switch(status) {
            case SECTOR_FREE:
                BlockFreeFound[block] = 1;
                break;

            case SECTOR_USED:
                if (!BlockFreeFound[block])
                    BlockMap[block] = thisEUN;
                else
                    printk(KERN_WARNING
                           "SECTOR_USED found after SECTOR_FREE "
                           "in Virtual Unit Chain %d for block %d\n",
                           thisVUC, block);
                break;
            case SECTOR_DELETED:
                if (!BlockFreeFound[block])
                    BlockMap[block] = BLOCK_NIL;
                else
                    printk(KERN_WARNING
                           "SECTOR_DELETED found after SECTOR_FREE "
                           "in Virtual Unit Chain %d for block %d\n",
                           thisVUC, block);
                break;

            case SECTOR_IGNORE:
                break;
            default:
                printk("Unknown status for block %d in EUN %d: %x\n",
                       block, thisEUN, status);
            }
        }

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

        thisEUN = nftl->ReplUnitTable[thisEUN];
    }

    if (inplace) {
        /* We're being asked to be a fold-in-place. Check
           that all blocks which actually have data associated
           with them (i.e. BlockMap[block] != BLOCK_NIL) are
           either already present or SECTOR_FREE in the target
           block. If not, we're going to have to fold out-of-place
           anyway.
        */
        for (block = 0; block < nftl->EraseSize / 512 ; block++) {
            if (BlockLastState[block] != SECTOR_FREE &&
                    BlockMap[block] != BLOCK_NIL &&
                    BlockMap[block] != targetEUN) {
                DEBUG(MTD_DEBUG_LEVEL1, "Setting inplace to 0. VUC %d, "
                      "block %d was %x lastEUN, "
                      "and is in EUN %d (%s) %d\n",
                      thisVUC, block, BlockLastState[block],
                      BlockMap[block],
                      BlockMap[block]== targetEUN ? "==" : "!=",
                      targetEUN);
                inplace = 0;
                break;
            }
        }

        if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
                pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
                BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
                SECTOR_FREE) {
            DEBUG(MTD_DEBUG_LEVEL1, "Pending write not free in EUN %d. "
                  "Folding out of place.\n", targetEUN);
            inplace = 0;
        }
    }

    if (!inplace) {
        DEBUG(MTD_DEBUG_LEVEL1, "Cannot fold Virtual Unit Chain %d in place. "
              "Trying out-of-place\n", thisVUC);
        /* We need to find a targetEUN to fold into. */
        targetEUN = NFTL_findfreeblock(nftl, 1);
        if (targetEUN == BLOCK_NIL) {
            /* Ouch. Now we're screwed. We need to do a
               fold-in-place of another chain to make room
               for this one. We need a better way of selecting
               which chain to fold, because makefreeblock will
               only ask us to fold the same one again.
            */
            printk(KERN_WARNING
                   "NFTL_findfreeblock(desperate) returns 0xffff.\n");
            return BLOCK_NIL;
        }
    } else {
        /* We put a fold mark in the chain we are folding only if
           we fold in place to help the mount check code. If we do
           not fold in place, it is possible to find the valid
           chain by selecting the longer one */
        oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
        oob.u.c.unused = 0xffffffff;
        MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
                     8, &retlen, (char *)&oob.u);
    }

    /* OK. We now know the location of every block in the Virtual Unit Chain,
       and the Erase Unit into which we are supposed to be copying.
       Go for it.
    */
    DEBUG(MTD_DEBUG_LEVEL1,"Folding chain %d into unit %d\n", thisVUC, targetEUN);
    for (block = 0; block < nftl->EraseSize / 512 ; block++) {
        unsigned char movebuf[512];
        int ret;

        /* If it's in the target EUN already, or if it's pending write, do nothing */
        if (BlockMap[block] == targetEUN ||
                (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
            continue;
        }

        /* copy only in non free block (free blocks can only
           happen in case of media errors or deleted blocks) */
        if (BlockMap[block] == BLOCK_NIL)
            continue;

        ret = MTD_READ(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
                       512, &retlen, movebuf);
        if (ret < 0) {
            ret = MTD_READ(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block])
                           + (block * 512), 512, &retlen,
                           movebuf);
            if (ret != -EIO)
                printk("Error went away on retry.\n");
        }
        memset(&oob, 0xff, sizeof(struct nftl_oob));
        oob.b.Status = oob.b.Status1 = SECTOR_USED;
        MTD_WRITEECC(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + (block * 512),
                     512, &retlen, movebuf, (char *)&oob, &nftl->oobinfo);
    }

    /* add the header so that it is now a valid chain */
    oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum
                          = cpu_to_le16(thisVUC);
    oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;

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

    /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */

    /* At this point, we have two different chains for this Virtual Unit, and no way to tell
       them apart. If we crash now, we get confused. However, both contain the same data, so we
       shouldn't actually lose data in this case. It's just that when we load up on a medium which
       has duplicate chains, we need to free one of the chains because it's not necessary any more.
    */
    thisEUN = nftl->EUNtable[thisVUC];
    DEBUG(MTD_DEBUG_LEVEL1,"Want to erase\n");

    /* For each block in the old chain (except the targetEUN of course),
       free it and make it available for future use */
    while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
        unsigned int EUNtmp;

        EUNtmp = nftl->ReplUnitTable[thisEUN];

        if (NFTL_formatblock(nftl, thisEUN) < 0) {
            /* could not erase : mark block as reserved
             */
            nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
        } else {
            /* correctly erased : mark it as free */
            nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
            nftl->numfreeEUNs++;
        }
        thisEUN = EUNtmp;
    }

    /* Make this the new start of chain for thisVUC */
    nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
    nftl->EUNtable[thisVUC] = targetEUN;

    return targetEUN;
}
Example #6
0
static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned pendingblock)
{
	u16 BlockMap[MAX_SECTORS_PER_UNIT];
	unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
	unsigned int thisEUN, prevEUN, status;
	int block, silly;
	unsigned int targetEUN;
	struct inftl_oob oob;
        size_t retlen;

	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d,"
		"pending=%d)\n", inftl, thisVUC, pendingblock);

	memset(BlockMap, 0xff, sizeof(BlockMap));
	memset(BlockDeleted, 0, sizeof(BlockDeleted));

	thisEUN = targetEUN = inftl->VUtable[thisVUC];

	if (thisEUN == BLOCK_NIL) {
		printk(KERN_WARNING "INFTL: trying to fold non-existent "
		       "Virtual Unit Chain %d!\n", thisVUC);
		return BLOCK_NIL;
	}

	/*
	 * Scan to find the Erase Unit which holds the actual data for each
	 * 512-byte block within the Chain.
	 */
        silly = MAX_LOOPS;
	while (thisEUN < inftl->nb_blocks) {
		for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) {
			if ((BlockMap[block] != 0xffff) || BlockDeleted[block])
				continue;

			if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize)
			     + (block * SECTORSIZE), 16 , &retlen,
			     (char *)&oob) < 0)
				status = SECTOR_IGNORE;
			else
                        	status = oob.b.Status | oob.b.Status1;

			switch(status) {
			case SECTOR_FREE:
			case SECTOR_IGNORE:
				break;
			case SECTOR_USED:
				BlockMap[block] = thisEUN;
				continue;
			case SECTOR_DELETED:
				BlockDeleted[block] = 1;
				continue;
			default:
				printk(KERN_WARNING "INFTL: unknown status "
					"for block %d in EUN %d: %x\n",
					block, thisEUN, status);
				break;
			}
		}

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

		thisEUN = inftl->PUtable[thisEUN];
	}

	/*
	 * OK. We now know the location of every block in the Virtual Unit
	 * Chain, and the Erase Unit into which we are supposed to be copying.
	 * Go for it.
	 */
	DEBUG(MTD_DEBUG_LEVEL1, "INFTL: folding chain %d into unit %d\n",
		thisVUC, targetEUN);

	for (block = 0; block < inftl->EraseSize/SECTORSIZE ; block++) {
		unsigned char movebuf[SECTORSIZE];
		int ret;

		/*
		 * If it's in the target EUN already, or if it's pending write,
		 * do nothing.
		 */
		if (BlockMap[block] == targetEUN || (pendingblock ==
		    (thisVUC * (inftl->EraseSize / SECTORSIZE) + block))) {
			continue;
		}

                /*
		 * Copy only in non free block (free blocks can only
                 * happen in case of media errors or deleted blocks).
		 */
                if (BlockMap[block] == BLOCK_NIL)
                        continue;

                ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize *
			BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE,
			&retlen, movebuf);
                if (ret < 0) {
			ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize *
				BlockMap[block]) + (block * SECTORSIZE),
				SECTORSIZE, &retlen, movebuf);
			if (ret != -EIO)
                        	DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went "
					"away on retry?\n");
                }
                memset(&oob, 0xff, sizeof(struct inftl_oob));
                oob.b.Status = oob.b.Status1 = SECTOR_USED;
                MTD_WRITEECC(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +
			(block * SECTORSIZE), SECTORSIZE, &retlen,
			movebuf, (char *)&oob, &inftl->oobinfo);
	}

	/*
	 * Newest unit in chain now contains data from _all_ older units.
	 * So go through and erase each unit in chain, oldest first. (This
	 * is important, by doing oldest first if we crash/reboot then it
	 * it is relatively simple to clean up the mess).
	 */
	DEBUG(MTD_DEBUG_LEVEL1, "INFTL: want to erase virtual chain %d\n",
		thisVUC);

	for (;;) {
		/* Find oldest unit in chain. */
		thisEUN = inftl->VUtable[thisVUC];
		prevEUN = BLOCK_NIL;
		while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
			prevEUN = thisEUN;
			thisEUN = inftl->PUtable[thisEUN];
		}

		/* Check if we are all done */
		if (thisEUN == targetEUN)
			break;

                if (INFTL_formatblock(inftl, thisEUN) < 0) {
			/*
			 * Could not erase : mark block as reserved.
			 */
			inftl->PUtable[thisEUN] = BLOCK_RESERVED;
                } else {
			/* Correctly erased : mark it as free */
			inftl->PUtable[thisEUN] = BLOCK_FREE;
			inftl->PUtable[prevEUN] = BLOCK_NIL;
			inftl->numfreeEUNs++;
                }
	}

	return targetEUN;
}