📄 ftl.patch
字号:
+/*======================================================================++ reclaim_block() picks a full erase unit and a transfer unit and+ then calls copy_erase_unit() to copy one to the other. Then, it+ schedules an erase on the expired block.++ What's a good way to decide which transfer unit and which erase+ unit to use? Beats me. My way is to always pick the transfer+ unit with the fewest erases, and usually pick the data unit with+ the most deleted blocks. But with a small probability, pick the+ oldest data unit instead. This means that we generally postpone+ the next reclaimation as long as possible, but shuffle static+ stuff around a bit for wear leveling.++======================================================================*/++static int reclaim_block(ftl_dev_t *dev)+{+ u_short i, eun, xfer;+ u_int best;+ int queued, ret;++ DEBUG("ftl: reclaiming space...\n");++ /* Pick the least erased transfer unit */+ best = 0xffffffff; xfer = 0xffff;+ do {+ queued = 0;+ for (i = 0; i < dev->header.NumTransferUnits; i++) {++ if (dev->XferInfo[i].state == XFER_UNKNOWN) {+ erase_xfer(dev, i);+ }+ if (dev->XferInfo[i].state == XFER_ERASING) {+ queued = 1;+ } else {+ if (dev->XferInfo[i].state == XFER_ERASED) {+ prepare_xfer(dev, i);+ }+ }+ if ((dev->XferInfo[i].state == XFER_PREPARED) &&+ (dev->XferInfo[i].EraseCount <= best)) {+ best = dev->XferInfo[i].EraseCount;+ xfer = i;+ }+ }+ if (xfer == 0xffff) {+ if (queued) {+ DEBUG("ftl: waiting for transfer unit to be prepared...\n");+ DEBUG("ftl: Hupp...\n");+ /*sleep_on(&dev->erase_pending); */+ } else {+ static int ne = 0;+ if (++ne < 5)+ printk(KERN_NOTICE "ftl: reclaim failed: no suitable transfer units!\n");+ return -EIO;+ }+ }+ } while (xfer == 0xffff);++ eun = 0;+ if ((jiffies % shuffle_freq) == 0) {+ DEBUG("ftl: recycling freshest block...\n");+ best = 0xffffffff;+ for (i = 0; i < dev->DataUnits; i++)+ if (dev->EUNInfo[i].EraseCount <= best) {+ best = dev->EUNInfo[i].EraseCount;+ eun = i;+ }+ } else {+ best = 0;+ for (i = 0; i < dev->DataUnits; i++)+ if (dev->EUNInfo[i].Deleted >= best) {+ best = dev->EUNInfo[i].Deleted;+ eun = i;+ }+ if (best == 0) {+ static int ne = 0;+ if (++ne < 5)+ printk(KERN_NOTICE "ftl: reclaim failed: no free blocks!\n");+ return -EIO;+ }+ }++ ret = copy_erase_unit(dev, eun, xfer);+ if (!ret) {+ erase_xfer(dev, xfer);+ } else {+ printk(KERN_NOTICE "ftl: copy_erase_unit failed!\n");+ }+ return ret;+} /* reclaim_block */++/*======================================================================++ Find_free() searches for a free block. If necessary, it updates+ the BAM cache for the erase unit containing the free block. It+ returns the block index -- the erase unit is just the currently+ cached unit. If there are no free blocks, it returns 0 -- this+ is never a valid data block because it contains the header.++======================================================================*/++#ifdef PSYCHO_DEBUG+static void dump_lists(ftl_dev_t *dev)+{+ int i;+ printk(KERN_DEBUG "ftl: Free total = %d\n", dev->FreeTotal);+ for (i = 0; i < dev->DataUnits; i++)+ printk(KERN_DEBUG+ "ftl: unit %d: %d phys, %d free, %d deleted\n", i,+ dev->EUNInfo[i].Offset >> dev->header.EraseUnitSize,+ dev->EUNInfo[i].Free, dev->EUNInfo[i].Deleted);+}+#endif++static u_int find_free(ftl_dev_t *dev)+{+ u_short stop, eun;+ u_int blk;+ int ret;+ off_t offset;+ size_t count;++ /* Find an erase unit with some free space */+ stop = (dev->bam_index == 0xffff) ? 0 : dev->bam_index;+ eun = stop;+ do {+ if (dev->EUNInfo[eun].Free != 0) break;+ /* Wrap around at end of table */+ if (++eun == dev->DataUnits) eun = 0;+ } while (eun != stop);++ if (dev->EUNInfo[eun].Free == 0)+ return 0;++ /* Is this unit's BAM cached? */+ if (eun != dev->bam_index) {+ /* Invalidate cache */+ dev->bam_index = 0xffff;+ count = dev->BlocksPerUnit * sizeof(u_int);+ offset = dev->EUNInfo[eun].Offset + dev->header.BAMOffset;+ ret = (*dev->flash->ops->read)(dev->flash, (char *)dev->bam_cache,+ count, offset + dev->base_offset, 0);+ if (ret) {+ printk("Error reading flash mem.\n");+ return 0;+ }+ dev->bam_index = eun;+ }++ /* Find a free block */+ for (blk = 0; blk < dev->BlocksPerUnit; blk++)+ if (BLOCK_FREE(dev->bam_cache[blk])) break;+ if (blk == dev->BlocksPerUnit) {+#ifdef PSYCHO_DEBUG+ static int ne = 0;+ if (++ne == 1)+ dump_lists(dev);+#endif+ printk(KERN_NOTICE "ftl: bad free list!\n");+ return 0;+ }+ /*DEBUG("ftl: found free block at %d in %d\n", blk, eun);*/+ return blk;++} /* find_free */++/*======================================================================++ This gets a memory handle for the region corresponding to the+ minor device number.++======================================================================*/++static int ftl_open(struct inode *inode, struct file *file)+{+ int minor = MINOR(inode->i_rdev);+ ftl_dev_t *dev;++ DEBUG("ftl_open(minor=%d, dev_nr(minor)=0x%x)\n", minor, DEVICE_NR(minor));++ dev = dev_table[DEVICE_NR(minor)];++ if ( dev == NULL )+ return -ENODEV;++ if (dev->region.RegionSize == 0)+ return -ENODEV;++ while (dev->locked) {+ DEBUG("dev->locked, sleeping on ftl_wait_open\n");+ sleep_on(&ftl_wait_open);+ }+ if ((scan_header(dev) == 0) &&+ (build_maps(dev) == 0)) {+ dev->state = FTL_FORMATTED;+ ftl_reread_partitions(minor);+#ifdef FLASH_DEBUG+ printk(KERN_INFO "ftl: opening %d kb FTL partition\n",+ dev->header.FormattedSize >> 10);+#endif+ } else {+ printk(KERN_NOTICE "ftl: FTL partition is invalid.\n");+ return -ENODEV;+ }++ dev->open++;++ /* MOD_INC_USE_COUNT; */+ return 0;+} /* ftl_open */++/*====================================================================*/++static int ftl_close(struct inode *inode, struct file *file)+{+ int minor = MINOR(inode->i_rdev);+ ftl_dev_t *dev;+ int i;+ struct super_block *sb;++ DEBUG("ftl: ftl_close(%d)\n", minor);++ /* Flush all writes */+ fsync_dev(inode->i_rdev);+ sb = get_super(inode->i_rdev);+ if (sb)+ invalidate_inodes(sb);+ invalidate_buffers(inode->i_rdev);++ dev = dev_table[DEVICE_NR(minor)];++ /* Wait for any pending erase operations to complete */+ for (i = 0; i < dev->header.NumTransferUnits; i++) {+ if (dev->XferInfo[i].state == XFER_ERASING) {+ printk("ftl: Hmmm... close before all erased...\n");+ /* sleep_on(&dev->erase_pending); */+ }+ if (dev->XferInfo[i].state == XFER_ERASED)+ prepare_xfer(dev, i);+ }++ dev->open--;+ if (dev->open == 0) {+ DEBUG("ftl: Last close. deallocating stuff\n");+ if (dev->VirtualBlockMap) {+ vfree(dev->VirtualBlockMap);+ dev->VirtualBlockMap = NULL;+ }+ if (dev->VirtualPageMap) {+ kfree(dev->VirtualPageMap);+ dev->VirtualPageMap = NULL;+ }+ if (dev->EUNInfo) {+ kfree(dev->EUNInfo);+ dev->EUNInfo = NULL;+ }+ if (dev->XferInfo) {+ kfree(dev->XferInfo);+ dev->XferInfo = NULL;+ }+ if (dev->bam_cache) {+ kfree(dev->bam_cache);+ dev->bam_cache = NULL;+ }+ }++ /* MOD_DEC_USE_COUNT; */+ return 0;+} /* ftl_close */++/*======================================================================++ Read a series of sectors from an FTL partition.++======================================================================*/++static int ftl_read(ftl_dev_t *dev, caddr_t buffer,+ u_long sector, u_long nblocks)+{+ u_int log_addr, bsize;+ u_long i;+ int ret;+ off_t offset;+ size_t count;++ if (!(dev->state & FTL_FORMATTED)) {+ printk(KERN_NOTICE "ftl: bad partition\n");+ return -EIO;+ }+ bsize = dev->region.BlockSize;++ count = SECTOR_SIZE;+ for (i = 0; i < nblocks; i++) {+ if (((sector+i) * SECTOR_SIZE) >= dev->header.FormattedSize) {+ printk(KERN_NOTICE "ftl: bad read offset\n");+ return -EIO;+ }+ log_addr = dev->VirtualBlockMap[sector+i];+ if (log_addr == 0xffffffff)+ memset(buffer, 0, SECTOR_SIZE);+ else {+ offset = (dev->EUNInfo[log_addr / bsize].Offset + (log_addr % bsize));+ ret = (*dev->flash->ops->read)(dev->flash, buffer,+ count, offset + dev->base_offset, 0);+ if (ret) {+ printk("Error reading flash mem.\n");+ return -EIO;+ }+ }+ buffer += SECTOR_SIZE;+ }+ return 0;+} /* ftl_read */++/*======================================================================++ Write a series of sectors to an FTL partition++======================================================================*/++static int set_bam_entry(ftl_dev_t *dev,+ u_int log_addr, u_int virt_addr)+{+ u_int bsize, blk;+#ifdef PSYCHO_DEBUG+ u_int old_addr;+#endif+ u_short eun;+ int ret;+ off_t offset;+ size_t count;++ /*DEBUG("ftl: set_bam_entry(0x%p, 0x%x, 0x%x)\n", dev, log_addr, virt_addr);*/++ bsize = dev->region.BlockSize;+ eun = log_addr / bsize;+ blk = (log_addr % bsize) / SECTOR_SIZE;+ count = sizeof(u_int);+ offset = (dev->EUNInfo[eun].Offset + blk * sizeof(u_int) ++ dev->header.BAMOffset);++#ifdef PSYCHO_DEBUG+ ret = (*dev->flash->ops->read)(dev->flash, (char *)&old_addr,+ count, offset + dev->base_offset, 0);+ if (((virt_addr == 0xfffffffe) && !BLOCK_FREE(old_addr)) ||+ ((virt_addr == 0) && (BLOCK_TYPE(old_addr) != BLOCK_DATA)) ||+ (!BLOCK_DELETED(virt_addr) && (old_addr != 0xfffffffe))) {+ static int ne = 0;+ if (++ne < 5) {+ printk(KERN_NOTICE "ftl: set_bam_entry() inconsistency!\n");+ printk(KERN_NOTICE "ftl: log_addr = 0x%x, old = 0x%x"+ ", new = 0x%x\n", log_addr, old_addr, virt_addr);+ }+ return -1;+ }+#endif+ if (dev->bam_index == eun) {+#ifdef PSYCHO_DEBUG+ if (dev->bam_cache[blk] != old_addr) {+ static int ne = 0;+ if (++ne < 5) {+ printk(KERN_NOTICE "ftl: set_bam_entry() inconsistency!\n");+ printk(KERN_NOTICE "ftl: log_addr = 0x%x, cache"+ " = 0x%x, card = 0x%x\n",+ dev->bam_cache[blk], old_addr, 0);+ }+ return -1;+ }+#endif+ dev->bam_cache[blk] = virt_addr;+ }++ ret = (*dev->flash->ops->write)(dev->flash, (char *)&virt_addr,+ count, offset + dev->base_offset, 0);+#ifdef PSYCHO_DEBUG+ if (ret) {+ printk(KERN_NOTICE "ftl: set_bam_entry() failed!\n");+ printk(KERN_NOTICE "ftl: log_addr = 0x%x, old = 0x%x,"+ " new = 0x%x\n", log_addr, old_addr, virt_addr);+ printk("Error writing flash mem.\n");+ }+#endif+ return ret;+} /* set_bam_entry */++static int ftl_write(ftl_dev_t *dev, caddr_t buffer,+ u_long sector, u_long nblocks)+{+ u_int bsize, log_addr, virt_addr, old_addr, blk;+ u_long i;+ int ret;+ off_t offset;+ size_t count;++ /*DEBUG("ftl: ftl_write(0x%p, %ld, %ld)\n", dev, sector, nblocks);*/++ if (!(dev->state & FTL_FORMATTED)) {+ printk(KERN_NOTICE "ftl: bad partition\n");+ return -EIO;+ }+ /* See if we need to reclaim space, before we start */+ while (dev->FreeTotal < nblocks) {+ ret = reclaim_block(dev);+ if (ret)+ return ret;+ }++ bsize = dev->region.BlockSize;+ count = SECTOR_SIZE;+ virt_addr = sector * SECTOR_SIZE | BLOCK_DATA;+ for (i = 0; i < nblocks; i++) {+ if (virt_addr >= dev->header.FormattedSize) {+ printk(KERN_NOTICE "ftl: bad write offset\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -