Lines Matching +full:wear +full:- +full:leveling

1 /* This version ported to the Linux-MTD system by dwmw2@infradead.org
4 * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups
12 This driver implements a disk-like block device driver with an
42 LEGAL NOTE: The FTL format is patented by M-Systems. They have
45 "M-Systems grants a royalty-free, non-exclusive license under
46 any presently existing M-Systems intellectual property rights
47 necessary for the design and development of FTL-compatible
52 Use of the FTL format for non-PCMCIA applications may be an
54 contact M-Systems directly. M-Systems since acquired by Sandisk.
105 /* Sector size -- shouldn't need to change */
157 part->header.FormattedSize = 0; in scan_header()
158 max_offset = (0x100000<part->mbd.mtd->size)?0x100000:part->mbd.mtd->size; in scan_header()
162 offset += part->mbd.mtd->erasesize ? : 0x2000) { in scan_header()
164 err = mtd_read(part->mbd.mtd, offset, sizeof(header), &ret, in scan_header()
175 return -ENOENT; in scan_header()
181 return -1; in scan_header()
183 if ((1 << header.EraseUnitSize) != part->mbd.mtd->erasesize) { in scan_header()
185 1 << header.EraseUnitSize,part->mbd.mtd->erasesize); in scan_header()
186 return -1; in scan_header()
188 part->header = header; in scan_header()
197 int hdr_ok, ret = -1; in build_maps()
202 part->DataUnits = le16_to_cpu(part->header.NumEraseUnits) - in build_maps()
203 part->header.NumTransferUnits; in build_maps()
204 part->EUNInfo = kmalloc_array(part->DataUnits, sizeof(struct eun_info_t), in build_maps()
206 if (!part->EUNInfo) in build_maps()
208 for (i = 0; i < part->DataUnits; i++) in build_maps()
209 part->EUNInfo[i].Offset = 0xffffffff; in build_maps()
210 part->XferInfo = in build_maps()
211 kmalloc_array(part->header.NumTransferUnits, in build_maps()
214 if (!part->XferInfo) in build_maps()
218 for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) { in build_maps()
219 offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN)) in build_maps()
220 << part->header.EraseUnitSize); in build_maps()
221 ret = mtd_read(part->mbd.mtd, offset, sizeof(header), &retval, in build_maps()
227 ret = -1; in build_maps()
230 if (hdr_ok && (le16_to_cpu(header.LogicalEUN) < part->DataUnits) && in build_maps()
231 (part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset == 0xffffffff)) { in build_maps()
232 part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset = offset; in build_maps()
233 part->EUNInfo[le16_to_cpu(header.LogicalEUN)].EraseCount = in build_maps()
237 if (xtrans == part->header.NumTransferUnits) { in build_maps()
243 part->XferInfo[xtrans].state = XFER_PREPARED; in build_maps()
244 part->XferInfo[xtrans].EraseCount = le32_to_cpu(header.EraseCount); in build_maps()
246 part->XferInfo[xtrans].state = XFER_UNKNOWN; in build_maps()
248 part->XferInfo[xtrans].EraseCount = in build_maps()
249 le32_to_cpu(part->header.EraseCount); in build_maps()
251 part->XferInfo[xtrans].Offset = offset; in build_maps()
256 header = part->header; in build_maps()
266 part->VirtualBlockMap = vmalloc(array_size(blocks, sizeof(uint32_t))); in build_maps()
267 if (!part->VirtualBlockMap) in build_maps()
270 memset(part->VirtualBlockMap, 0xff, blocks * sizeof(uint32_t)); in build_maps()
271 part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize; in build_maps()
273 part->bam_cache = kmalloc_array(part->BlocksPerUnit, sizeof(uint32_t), in build_maps()
275 if (!part->bam_cache) in build_maps()
278 part->bam_index = 0xffff; in build_maps()
279 part->FreeTotal = 0; in build_maps()
281 for (i = 0; i < part->DataUnits; i++) { in build_maps()
282 part->EUNInfo[i].Free = 0; in build_maps()
283 part->EUNInfo[i].Deleted = 0; in build_maps()
284 offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset); in build_maps()
286 ret = mtd_read(part->mbd.mtd, offset, in build_maps()
287 part->BlocksPerUnit * sizeof(uint32_t), &retval, in build_maps()
288 (unsigned char *)part->bam_cache); in build_maps()
293 for (j = 0; j < part->BlocksPerUnit; j++) { in build_maps()
294 if (BLOCK_FREE(le32_to_cpu(part->bam_cache[j]))) { in build_maps()
295 part->EUNInfo[i].Free++; in build_maps()
296 part->FreeTotal++; in build_maps()
297 } else if ((BLOCK_TYPE(le32_to_cpu(part->bam_cache[j])) == BLOCK_DATA) && in build_maps()
298 (BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j])) < blocks)) in build_maps()
299 part->VirtualBlockMap[BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j]))] = in build_maps()
301 else if (BLOCK_DELETED(le32_to_cpu(part->bam_cache[j]))) in build_maps()
302 part->EUNInfo[i].Deleted++; in build_maps()
310 kfree(part->bam_cache); in build_maps()
312 vfree(part->VirtualBlockMap); in build_maps()
314 kfree(part->XferInfo); in build_maps()
316 kfree(part->EUNInfo); in build_maps()
335 xfer = &part->XferInfo[xfernum]; in erase_xfer()
336 pr_debug("ftl_cs: erasing xfer unit at 0x%x\n", xfer->Offset); in erase_xfer()
337 xfer->state = XFER_ERASING; in erase_xfer()
344 return -ENOMEM; in erase_xfer()
346 erase->addr = xfer->Offset; in erase_xfer()
347 erase->len = 1 << part->header.EraseUnitSize; in erase_xfer()
349 ret = mtd_erase(part->mbd.mtd, erase); in erase_xfer()
351 xfer->state = XFER_ERASED; in erase_xfer()
352 xfer->EraseCount++; in erase_xfer()
354 xfer->state = XFER_FAILED; in erase_xfer()
379 xfer = &part->XferInfo[i]; in prepare_xfer()
380 xfer->state = XFER_FAILED; in prepare_xfer()
382 pr_debug("ftl_cs: preparing xfer unit at 0x%x\n", xfer->Offset); in prepare_xfer()
385 header = part->header; in prepare_xfer()
387 header.EraseCount = cpu_to_le32(xfer->EraseCount); in prepare_xfer()
389 ret = mtd_write(part->mbd.mtd, xfer->Offset, sizeof(header), &retlen, in prepare_xfer()
397 nbam = DIV_ROUND_UP(part->BlocksPerUnit * sizeof(uint32_t) + in prepare_xfer()
398 le32_to_cpu(part->header.BAMOffset), SECTOR_SIZE); in prepare_xfer()
400 offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset); in prepare_xfer()
405 ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen, in prepare_xfer()
411 xfer->state = XFER_PREPARED; in prepare_xfer()
441 eun = &part->EUNInfo[srcunit]; in copy_erase_unit()
442 xfer = &part->XferInfo[xferunit]; in copy_erase_unit()
444 eun->Offset, xfer->Offset); in copy_erase_unit()
448 if (part->bam_index != srcunit) { in copy_erase_unit()
450 offset = eun->Offset + le32_to_cpu(part->header.BAMOffset); in copy_erase_unit()
452 ret = mtd_read(part->mbd.mtd, offset, in copy_erase_unit()
453 part->BlocksPerUnit * sizeof(uint32_t), &retlen, in copy_erase_unit()
454 (u_char *)(part->bam_cache)); in copy_erase_unit()
457 part->bam_index = 0xffff; in copy_erase_unit()
466 xfer->state = XFER_UNKNOWN; in copy_erase_unit()
467 offset = xfer->Offset + 20; /* Bad! */ in copy_erase_unit()
470 ret = mtd_write(part->mbd.mtd, offset, sizeof(uint16_t), &retlen, in copy_erase_unit()
479 src = eun->Offset; dest = xfer->Offset; in copy_erase_unit()
483 for (i = 0; i < part->BlocksPerUnit; i++) { in copy_erase_unit()
484 switch (BLOCK_TYPE(le32_to_cpu(part->bam_cache[i]))) { in copy_erase_unit()
490 ret = mtd_read(part->mbd.mtd, src, SECTOR_SIZE, &retlen, in copy_erase_unit()
498 ret = mtd_write(part->mbd.mtd, dest, SECTOR_SIZE, &retlen, in copy_erase_unit()
508 part->bam_cache[i] = cpu_to_le32(0xffffffff); in copy_erase_unit()
517 ret = mtd_write(part->mbd.mtd, in copy_erase_unit()
518 xfer->Offset + le32_to_cpu(part->header.BAMOffset), in copy_erase_unit()
519 part->BlocksPerUnit * sizeof(int32_t), in copy_erase_unit()
521 (u_char *)part->bam_cache); in copy_erase_unit()
529 ret = mtd_write(part->mbd.mtd, xfer->Offset + 20, sizeof(uint16_t), in copy_erase_unit()
539 swap(xfer->EraseCount, eun->EraseCount); in copy_erase_unit()
540 swap(xfer->Offset, eun->Offset); in copy_erase_unit()
541 part->FreeTotal -= eun->Free; in copy_erase_unit()
542 part->FreeTotal += free; in copy_erase_unit()
543 eun->Free = free; in copy_erase_unit()
544 eun->Deleted = 0; in copy_erase_unit()
547 part->bam_index = srcunit; in copy_erase_unit()
564 stuff around a bit for wear leveling.
575 pr_debug("NumTransferUnits == %x\n", part->header.NumTransferUnits); in reclaim_block()
580 for (i = 0; i < part->header.NumTransferUnits; i++) { in reclaim_block()
582 if (part->XferInfo[i].state == XFER_UNKNOWN) { in reclaim_block()
587 if (part->XferInfo[i].state == XFER_ERASING) { in reclaim_block()
592 else if (part->XferInfo[i].state == XFER_ERASED) { in reclaim_block()
597 if (part->XferInfo[i].state == XFER_PREPARED) { in reclaim_block()
600 if (part->XferInfo[i].EraseCount <= best) { in reclaim_block()
601 best = part->XferInfo[i].EraseCount; in reclaim_block()
606 pr_debug("XferInfo[%d].state == %x\n",i, part->XferInfo[i].state); in reclaim_block()
613 mtd_sync(part->mbd.mtd); in reclaim_block()
623 return -EIO; in reclaim_block()
632 for (i = 0; i < part->DataUnits; i++) in reclaim_block()
633 if (part->EUNInfo[i].EraseCount <= best) { in reclaim_block()
634 best = part->EUNInfo[i].EraseCount; in reclaim_block()
639 for (i = 0; i < part->DataUnits; i++) in reclaim_block()
640 if (part->EUNInfo[i].Deleted >= best) { in reclaim_block()
641 best = part->EUNInfo[i].Deleted; in reclaim_block()
653 return -EIO; in reclaim_block()
668 returns the block index -- the erase unit is just the currently
669 cached unit. If there are no free blocks, it returns 0 -- this
678 printk(KERN_DEBUG "ftl_cs: Free total = %d\n", part->FreeTotal); in dump_lists()
679 for (i = 0; i < part->DataUnits; i++) in dump_lists()
682 part->EUNInfo[i].Offset >> part->header.EraseUnitSize, in dump_lists()
683 part->EUNInfo[i].Free, part->EUNInfo[i].Deleted); in dump_lists()
695 stop = (part->bam_index == 0xffff) ? 0 : part->bam_index; in find_free()
698 if (part->EUNInfo[eun].Free != 0) break; in find_free()
700 if (++eun == part->DataUnits) eun = 0; in find_free()
703 if (part->EUNInfo[eun].Free == 0) in find_free()
707 if (eun != part->bam_index) { in find_free()
709 part->bam_index = 0xffff; in find_free()
711 ret = mtd_read(part->mbd.mtd, in find_free()
712 part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset), in find_free()
713 part->BlocksPerUnit * sizeof(uint32_t), in find_free()
715 (u_char *)(part->bam_cache)); in find_free()
721 part->bam_index = eun; in find_free()
725 for (blk = 0; blk < part->BlocksPerUnit; blk++) in find_free()
726 if (BLOCK_FREE(le32_to_cpu(part->bam_cache[blk]))) break; in find_free()
727 if (blk == part->BlocksPerUnit) { in find_free()
758 if (!(part->state & FTL_FORMATTED)) { in ftl_read()
760 return -EIO; in ftl_read()
762 bsize = 1 << part->header.EraseUnitSize; in ftl_read()
765 if (((sector+i) * SECTOR_SIZE) >= le32_to_cpu(part->header.FormattedSize)) { in ftl_read()
767 return -EIO; in ftl_read()
769 log_addr = part->VirtualBlockMap[sector+i]; in ftl_read()
773 offset = (part->EUNInfo[log_addr / bsize].Offset in ftl_read()
775 ret = mtd_read(part->mbd.mtd, offset, SECTOR_SIZE, &retlen, in ftl_read()
807 bsize = 1 << part->header.EraseUnitSize; in set_bam_entry()
810 offset = (part->EUNInfo[eun].Offset + blk * sizeof(uint32_t) + in set_bam_entry()
811 le32_to_cpu(part->header.BAMOffset)); in set_bam_entry()
814 ret = mtd_read(part->mbd.mtd, offset, sizeof(uint32_t), &retlen, in set_bam_entry()
831 return -EIO; in set_bam_entry()
835 if (part->bam_index == eun) { in set_bam_entry()
837 if (le32_to_cpu(part->bam_cache[blk]) != old_addr) { in set_bam_entry()
844 le32_to_cpu(part->bam_cache[blk]), old_addr); in set_bam_entry()
846 return -EIO; in set_bam_entry()
849 part->bam_cache[blk] = le_virt_addr; in set_bam_entry()
851 ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen, in set_bam_entry()
872 if (!(part->state & FTL_FORMATTED)) { in ftl_write()
874 return -EIO; in ftl_write()
877 while (part->FreeTotal < nblocks) { in ftl_write()
883 bsize = 1 << part->header.EraseUnitSize; in ftl_write()
887 if (virt_addr >= le32_to_cpu(part->header.FormattedSize)) { in ftl_write()
889 return -EIO; in ftl_write()
899 return -ENOSPC; in ftl_write()
903 log_addr = part->bam_index * bsize + blk * SECTOR_SIZE; in ftl_write()
904 part->EUNInfo[part->bam_index].Free--; in ftl_write()
905 part->FreeTotal--; in ftl_write()
907 return -EIO; in ftl_write()
908 part->EUNInfo[part->bam_index].Deleted++; in ftl_write()
909 offset = (part->EUNInfo[part->bam_index].Offset + in ftl_write()
911 ret = mtd_write(part->mbd.mtd, offset, SECTOR_SIZE, &retlen, buffer); in ftl_write()
918 return -EIO; in ftl_write()
922 old_addr = part->VirtualBlockMap[sector+i]; in ftl_write()
924 part->VirtualBlockMap[sector+i] = 0xffffffff; in ftl_write()
925 part->EUNInfo[old_addr/bsize].Deleted++; in ftl_write()
927 return -EIO; in ftl_write()
932 return -EIO; in ftl_write()
933 part->VirtualBlockMap[sector+i] = log_addr; in ftl_write()
934 part->EUNInfo[part->bam_index].Deleted--; in ftl_write()
948 sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE; in ftl_getgeo()
950 geo->heads = 1; in ftl_getgeo()
951 geo->sectors = 8; in ftl_getgeo()
952 geo->cylinders = sect >> 3; in ftl_getgeo()
973 uint32_t bsize = 1 << part->header.EraseUnitSize; in ftl_discardsect()
979 uint32_t old_addr = part->VirtualBlockMap[sector]; in ftl_discardsect()
981 part->VirtualBlockMap[sector] = 0xffffffff; in ftl_discardsect()
982 part->EUNInfo[old_addr/bsize].Deleted++; in ftl_discardsect()
984 return -EIO; in ftl_discardsect()
986 nr_sects--; in ftl_discardsect()
996 vfree(part->VirtualBlockMap); in ftl_freepart()
997 part->VirtualBlockMap = NULL; in ftl_freepart()
998 kfree(part->EUNInfo); in ftl_freepart()
999 part->EUNInfo = NULL; in ftl_freepart()
1000 kfree(part->XferInfo); in ftl_freepart()
1001 part->XferInfo = NULL; in ftl_freepart()
1002 kfree(part->bam_cache); in ftl_freepart()
1003 part->bam_cache = NULL; in ftl_freepart()
1014 mtd->name); in ftl_add_mtd()
1018 partition->mbd.mtd = mtd; in ftl_add_mtd()
1023 partition->state = FTL_FORMATTED; in ftl_add_mtd()
1026 le32_to_cpu(partition->header.FormattedSize) >> 10); in ftl_add_mtd()
1028 partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9; in ftl_add_mtd()
1030 partition->mbd.tr = tr; in ftl_add_mtd()
1031 partition->mbd.devnum = -1; in ftl_add_mtd()
1032 if (!add_mtd_blktrans_dev(&partition->mbd)) in ftl_add_mtd()