📄 ftl_cs.c
字号:
(header.NumTransferUnits >= header.NumEraseUnits)) { printk(KERN_NOTICE "ftl_cs: FTL header corrupt!\n"); return -1; } part->header = header; return 0;}static int build_maps(partition_t *part){ erase_unit_header_t header; mem_op_t req; u_short xvalid, xtrans, i; u_int blocks, j; int hdr_ok, ret; /* Set up erase unit maps */ part->DataUnits = part->header.NumEraseUnits - part->header.NumTransferUnits; part->EUNInfo = kmalloc(part->DataUnits * sizeof(struct eun_info_t), GFP_KERNEL); if (!part->EUNInfo) return -1; for (i = 0; i < part->DataUnits; i++) part->EUNInfo[i].Offset = 0xffffffff; part->XferInfo = kmalloc(part->header.NumTransferUnits * sizeof(struct xfer_info_t), GFP_KERNEL); if (!part->XferInfo) return -1; req.Attributes = MEM_OP_BUFFER_KERNEL; req.Count = sizeof(header); xvalid = xtrans = 0; for (i = 0; i < part->header.NumEraseUnits; i++) { req.Offset = ((i + part->header.FirstPhysicalEUN) << part->header.EraseUnitSize); ret = CardServices(ReadMemory, part->handle, &req, &header); if (ret != CS_SUCCESS) { cs_error(ReadMemory, ret); return -1; } /* Is this a transfer partition? */ hdr_ok = (strcmp(header.DataOrgTuple+3, "FTL100") == 0); if (hdr_ok && (header.LogicalEUN < part->DataUnits) && (part->EUNInfo[header.LogicalEUN].Offset == 0xffffffff)) { part->EUNInfo[header.LogicalEUN].Offset = req.Offset; part->EUNInfo[header.LogicalEUN].EraseCount = header.EraseCount; xvalid++; } else { if (xtrans == part->header.NumTransferUnits) { printk(KERN_NOTICE "ftl_cs: format error: too many " "transfer units!\n"); return -1; } if (hdr_ok && (header.LogicalEUN == 0xffff)) { part->XferInfo[xtrans].state = XFER_PREPARED; part->XferInfo[xtrans].EraseCount = header.EraseCount; } else { part->XferInfo[xtrans].state = XFER_UNKNOWN; /* Pick anything reasonable for the erase count */ part->XferInfo[xtrans].EraseCount = part->header.EraseCount; } part->XferInfo[xtrans].Offset = req.Offset; xtrans++; } } /* Check for format trouble */ header = part->header; if ((xtrans != header.NumTransferUnits) || (xvalid+xtrans != header.NumEraseUnits)) { printk(KERN_NOTICE "ftl_cs: format error: erase units " "don't add up!\n"); return -1; } /* Set up virtual page map */ blocks = header.FormattedSize >> header.BlockSize; part->VirtualBlockMap = vmalloc(blocks * sizeof(u_int)); memset(part->VirtualBlockMap, 0xff, blocks * sizeof(u_int)); part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize; req.Count = part->BlocksPerUnit * sizeof(u_int); part->bam_cache = kmalloc(part->BlocksPerUnit * sizeof(u_int), GFP_KERNEL); if (!part->bam_cache) return -1; part->bam_index = 0xffff; part->FreeTotal = 0; for (i = 0; i < part->DataUnits; i++) { part->EUNInfo[i].Free = 0; part->EUNInfo[i].Deleted = 0; req.Offset = part->EUNInfo[i].Offset + header.BAMOffset; ret = CardServices(ReadMemory, part->handle, &req, part->bam_cache); if (ret != CS_SUCCESS) { cs_error(ReadMemory, ret); return -1; } for (j = 0; j < part->BlocksPerUnit; j++) { if (BLOCK_FREE(part->bam_cache[j])) { part->EUNInfo[i].Free++; part->FreeTotal++; } else if ((BLOCK_TYPE(part->bam_cache[j]) == BLOCK_DATA) && (BLOCK_NUMBER(part->bam_cache[j]) < blocks)) part->VirtualBlockMap[BLOCK_NUMBER(part->bam_cache[j])] = (i << header.EraseUnitSize) + (j << header.BlockSize); else if (BLOCK_DELETED(part->bam_cache[j])) part->EUNInfo[i].Deleted++; } } return 0; } /* build_maps *//*====================================================================== Erase_xfer() schedules an asynchronous erase operation for a transfer unit. ======================================================================*/static int erase_xfer(ftl_dev_t *dev, partition_t *part, u_short xfernum){ int i, ret; struct xfer_info_t *xfer; xfer = &part->XferInfo[xfernum]; DEBUG(1, "ftl_cs: erasing xfer unit at 0x%x\n", xfer->Offset); xfer->state = XFER_ERASING; /* Is there a free erase slot? */ for (;;) { for (i = 0; i < MAX_ERASE; i++) if (!ERASE_IN_PROGRESS(dev->eraseq[i].State)) break; if (i < MAX_ERASE) break; DEBUG(0, "ftl_cs: erase queue is full\n"); sleep_on(&dev->erase_pending); } /* Queue the request */ dev->eraseq[i].State = ERASE_QUEUED; dev->eraseq[i].Handle = part->handle; dev->eraseq[i].Offset = xfer->Offset; dev->eraseq[i].Size = part->region.BlockSize; dev->eraseq[i].Optional = part; ret = CardServices(CheckEraseQueue, dev->eraseq_handle); if (ret != CS_SUCCESS) { cs_error(CheckEraseQueue, ret); return -EIO; } xfer->EraseCount++; return ret;} /* erase_xfer *//*====================================================================== Prepare_xfer() takes a freshly erased transfer unit and gives it an appropriate header. ======================================================================*/static void save_status(eraseq_entry_t *erase){ partition_t *part; struct xfer_info_t *xfer; int i; /* Look up the transfer unit */ part = (partition_t *)(erase->Optional); for (i = 0; i < part->header.NumTransferUnits; i++) if (part->XferInfo[i].Offset == erase->Offset) break; if (i == part->header.NumTransferUnits) { printk(KERN_NOTICE "ftl_cs: internal error: " "erase lookup failed!\n"); return; } xfer = &part->XferInfo[i]; if (erase->State == ERASE_PASSED) xfer->state = XFER_ERASED; else { xfer->state = XFER_FAILED; printk(KERN_NOTICE "ftl_cs: erase failed: state = %d\n", erase->State); }}static void prepare_xfer(partition_t *part, int i){ erase_unit_header_t header; mem_op_t req; struct xfer_info_t *xfer; int nbam, ret; u_int ctl; xfer = &part->XferInfo[i]; xfer->state = XFER_FAILED; DEBUG(1, "ftl_cs: preparing xfer unit at 0x%x\n", xfer->Offset); /* Write the transfer unit header */ header = part->header; header.LogicalEUN = 0xffff; header.EraseCount = xfer->EraseCount; req.Attributes = MEM_OP_BUFFER_KERNEL; req.Count = sizeof(header); req.Offset = xfer->Offset; ret = CardServices(WriteMemory, part->handle, &req, &header); if (ret != CS_SUCCESS) { cs_error(WriteMemory, ret); return; } /* Write the BAM stub */ nbam = (part->BlocksPerUnit * sizeof(u_int) + part->header.BAMOffset + SECTOR_SIZE - 1) / SECTOR_SIZE; req.Offset = xfer->Offset + part->header.BAMOffset; req.Count = sizeof(u_int); ctl = BLOCK_CONTROL; for (i = 0; i < nbam; i++, req.Offset += sizeof(u_int)) { ret = CardServices(WriteMemory, part->handle, &req, &ctl); if (ret != CS_SUCCESS) { cs_error(WriteMemory, ret); return; } } xfer->state = XFER_PREPARED; } /* prepare_xfer *//*====================================================================== Copy_erase_unit() takes a full erase block and a transfer unit, copies everything to the transfer unit, then swaps the block pointers. All data blocks are copied to the corresponding blocks in the target unit, so the virtual block map does not need to be updated. ======================================================================*/static int copy_erase_unit(partition_t *part, u_short srcunit, u_short xferunit){ u_char buf[SECTOR_SIZE]; struct eun_info_t *eun; struct xfer_info_t *xfer; mem_op_t req; u_int src, dest, free, i; u_short unit; int ret; eun = &part->EUNInfo[srcunit]; xfer = &part->XferInfo[xferunit]; DEBUG(2, "ftl_cs: copying block 0x%x to 0x%x\n", eun->Offset, xfer->Offset); req.Attributes = MEM_OP_BUFFER_KERNEL; /* Read current BAM */ if (part->bam_index != srcunit) { req.Offset = eun->Offset + part->header.BAMOffset; req.Count = part->BlocksPerUnit * sizeof(u_int); ret = CardServices(ReadMemory, part->handle, &req, part->bam_cache); /* mark the cache bad, in case we get an error later */ part->bam_index = 0xffff; if (ret != CS_SUCCESS) goto read_error; } /* Write the LogicalEUN for the transfer unit */ xfer->state = XFER_UNKNOWN; req.Count = sizeof(u_short); req.Offset = xfer->Offset + 20; /* Bad! */ unit = 0x7fff; ret = CardServices(WriteMemory, part->handle, &req, &unit); if (ret != CS_SUCCESS) goto write_error; /* Copy all data blocks from source unit to transfer unit */ src = eun->Offset; dest = xfer->Offset; req.Count = SECTOR_SIZE; free = 0; ret = 0; for (i = 0; i < part->BlocksPerUnit; i++) { switch (BLOCK_TYPE(part->bam_cache[i])) { case BLOCK_CONTROL: /* This gets updated later */ break; case BLOCK_DATA: case BLOCK_REPLACEMENT: req.Offset = src; ret = CardServices(ReadMemory, part->handle, &req, buf); if (ret != CS_SUCCESS) goto read_error; req.Offset = dest; ret = CardServices(WriteMemory, part->handle, &req, buf); if (ret != CS_SUCCESS) goto write_error; break; default: /* All other blocks must be free */ part->bam_cache[i] = 0xffffffff; free++; break; } src += SECTOR_SIZE; dest += SECTOR_SIZE; } /* Write the BAM to the transfer unit */ req.Offset = xfer->Offset + part->header.BAMOffset; req.Count = part->BlocksPerUnit * sizeof(int); ret = CardServices(WriteMemory, part->handle, &req, part->bam_cache); if (ret != CS_SUCCESS) goto write_error; /* All clear? Then update the LogicalEUN again */ req.Offset = xfer->Offset + 20; /* Bad! */ req.Count = sizeof(u_short); ret = CardServices(WriteMemory, part->handle, &req, &srcunit); if (ret != CS_SUCCESS) goto write_error; /* Update the maps and usage stats*/ i = xfer->EraseCount; xfer->EraseCount = eun->EraseCount; eun->EraseCount = i; i = xfer->Offset; xfer->Offset = eun->Offset; eun->Offset = i; part->FreeTotal -= eun->Free; part->FreeTotal += free; eun->Free = free; eun->Deleted = 0; /* Now, the cache should be valid for the new block */ part->bam_index = srcunit; return CS_SUCCESS; read_error: cs_error(ReadMemory, ret); return ret; write_error: cs_error(WriteMemory, ret); return ret;} /* copy_erase_unit *//*====================================================================== 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, partition_t *part){ u_short i, eun, xfer; u_int best; int queued, ret; DEBUG(0, "ftl_cs: reclaiming space...\n"); /* Pick the least erased transfer unit */ best = 0xffffffff; xfer = 0xffff; do { queued = 0; for (i = 0; i < part->header.NumTransferUnits; i++) { if (part->XferInfo[i].state == XFER_UNKNOWN) erase_xfer(dev, part, i); if (part->XferInfo[i].state == XFER_ERASING) queued = 1; else if (part->XferInfo[i].state == XFER_ERASED) prepare_xfer(part, i); if ((part->XferInfo[i].state == XFER_PREPARED) && (part->XferInfo[i].EraseCount <= best)) { best = part->XferInfo[i].EraseCount; xfer = i; } } if (xfer == 0xffff) { if (queued) { DEBUG(1, "ftl_cs: waiting for transfer " "unit to be prepared...\n"); sleep_on(&dev->erase_pending); } else { static int ne = 0; if (++ne < 5) printk(KERN_NOTICE "ftl_cs: reclaim failed: no " "suitable transfer units!\n"); return CS_GENERAL_FAILURE; } } } while (xfer == 0xffff); eun = 0; if ((jiffies % shuffle_freq) == 0) { DEBUG(1, "ftl_cs: recycling freshest block...\n"); best = 0xffffffff; for (i = 0; i < part->DataUnits; i++) if (part->EUNInfo[i].EraseCount <= best) { best = part->EUNInfo[i].EraseCount; eun = i; } } else { best = 0; for (i = 0; i < part->DataUnits; i++) if (part->EUNInfo[i].Deleted >= best) { best = part->EUNInfo[i].Deleted; eun = i; } if (best == 0) { static int ne = 0; if (++ne < 5) printk(KERN_NOTICE "ftl_cs: reclaim failed: " "no free blocks!\n"); return CS_GENERAL_FAILURE; } } ret = copy_erase_unit(part, eun, xfer); if (ret == CS_SUCCESS) erase_xfer(dev, part, xfer); else printk(KERN_NOTICE "ftl_cs: 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_DEBUGstatic void dump_lists(partition_t *part){ int i; printk(KERN_DEBUG "ftl_cs: Free total = %d\n", part->FreeTotal); for (i = 0; i < part->DataUnits; i++) printk(KERN_DEBUG "ftl_cs: unit %d: %d phys, %d free, " "%d deleted\n", i, part->EUNInfo[i].Offset >> part->header.EraseUnitSize, part->EUNInfo[i].Free, part->EUNInfo[i].Deleted);}#endifstatic u_int find_free(partition_t *part){ u_short stop, eun; u_int blk; mem_op_t req; int ret; /* Find an erase unit with some free space */ stop = (part->bam_index == 0xffff) ? 0 : part->bam_index; eun = stop; do { if (part->EUNInfo[eun].Free != 0) break; /* Wrap around at end of table */ if (++eun == part->DataUnits) eun = 0; } while (eun != stop); if (part->EUNInfo[eun].Free == 0) return 0; /* Is this unit's BAM cached? */ if (eun != part->bam_index) { /* Invalidate cache */ part->bam_index = 0xffff; req.Attributes = MEM_OP_BUFFER_KERNEL; req.Count = part->BlocksPerUnit * sizeof(u_int); req.Offset = part->EUNInfo[eun].Offset + part->header.BAMOffset; ret = CardServices(ReadMemory, part->handle, &req, part->bam_cache); if (ret != CS_SUCCESS) { cs_error(ReadMemory, ret); return 0; } part->bam_index = eun; } /* Find a free block */ for (blk = 0; blk < part->BlocksPerUnit; blk++) if (BLOCK_FREE(part->bam_cache[blk])) break; if (blk == part->BlocksPerUnit) {#ifdef PSYCHO_DEBUG static int ne = 0; if (++ne == 1) dump_lists(part);#endif printk(KERN_NOTICE "ftl_cs: bad free list!\n"); return 0; } DEBUG(2, "ftl_cs: 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); dev_link_t *link; ftl_dev_t *dev; partition_t *partition; open_mem_t open; int ret;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -