📄 ftl.c
字号:
if (((sector+i) * SECTOR_SIZE) >= le32_to_cpu(part->header.FormattedSize)) { printk(KERN_NOTICE "ftl_cs: bad read offset\n"); return -EIO; } log_addr = part->VirtualBlockMap[sector+i]; if (log_addr == 0xffffffff) memset(buffer, 0, SECTOR_SIZE); else { offset = (part->EUNInfo[log_addr / bsize].Offset + (log_addr % bsize)); ret = part->mtd->read(part->mtd, offset, SECTOR_SIZE, &retlen, (u_char *) buffer); if (ret) { printk(KERN_WARNING "Error reading MTD device in ftl_read()\n"); return ret; } } buffer += SECTOR_SIZE; } return 0;} /* ftl_read *//*====================================================================== Write a series of sectors to an FTL partition ======================================================================*/static int set_bam_entry(partition_t *part, u_int32_t log_addr, u_int32_t virt_addr){ u_int32_t bsize, blk, le_virt_addr;#ifdef PSYCHO_DEBUG u_int32_t old_addr;#endif u_int16_t eun; int ret; size_t retlen, offset; DEBUG(2, "ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n", part, log_addr, virt_addr); bsize = 1 << part->header.EraseUnitSize; eun = log_addr / bsize; blk = (log_addr % bsize) / SECTOR_SIZE; offset = (part->EUNInfo[eun].Offset + blk * sizeof(u_int32_t) + le32_to_cpu(part->header.BAMOffset)); #ifdef PSYCHO_DEBUG ret = part->mtd->read(part->mtd, offset, sizeof(u_int32_t), &retlen, (u_char *)&old_addr); if (ret) { printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret); return ret; } old_addr = le32_to_cpu(old_addr); 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_cs: set_bam_entry() inconsistency!\n"); printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, old = 0x%x" ", new = 0x%x\n", log_addr, old_addr, virt_addr); } return -EIO; }#endif le_virt_addr = cpu_to_le32(virt_addr); if (part->bam_index == eun) {#ifdef PSYCHO_DEBUG if (le32_to_cpu(part->bam_cache[blk]) != old_addr) { static int ne = 0; if (++ne < 5) { printk(KERN_NOTICE "ftl_cs: set_bam_entry() " "inconsistency!\n"); printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, cache" " = 0x%x\n", le32_to_cpu(part->bam_cache[blk]), old_addr); } return -EIO; }#endif part->bam_cache[blk] = le_virt_addr; } ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t), &retlen, (u_char *)&le_virt_addr); if (ret) { printk(KERN_NOTICE "ftl_cs: set_bam_entry() failed!\n"); printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, new = 0x%x\n", log_addr, virt_addr); } return ret;} /* set_bam_entry */static int ftl_write(partition_t *part, caddr_t buffer, u_long sector, u_long nblocks){ u_int32_t bsize, log_addr, virt_addr, old_addr, blk; u_long i; int ret; size_t retlen, offset; DEBUG(2, "ftl_cs: ftl_write(0x%p, %ld, %ld)\n", part, sector, nblocks); if (!(part->state & FTL_FORMATTED)) { printk(KERN_NOTICE "ftl_cs: bad partition\n"); return -EIO; } /* See if we need to reclaim space, before we start */ while (part->FreeTotal < nblocks) { ret = reclaim_block(part); if (ret) return ret; } bsize = 1 << part->header.EraseUnitSize; virt_addr = sector * SECTOR_SIZE | BLOCK_DATA; for (i = 0; i < nblocks; i++) { if (virt_addr >= le32_to_cpu(part->header.FormattedSize)) { printk(KERN_NOTICE "ftl_cs: bad write offset\n"); return -EIO; } /* Grab a free block */ blk = find_free(part); if (blk == 0) { static int ne = 0; if (++ne < 5) printk(KERN_NOTICE "ftl_cs: internal error: " "no free blocks!\n"); return -ENOSPC; } /* Tag the BAM entry, and write the new block */ log_addr = part->bam_index * bsize + blk * SECTOR_SIZE; part->EUNInfo[part->bam_index].Free--; part->FreeTotal--; if (set_bam_entry(part, log_addr, 0xfffffffe)) return -EIO; part->EUNInfo[part->bam_index].Deleted++; offset = (part->EUNInfo[part->bam_index].Offset + blk * SECTOR_SIZE); ret = part->mtd->write(part->mtd, offset, SECTOR_SIZE, &retlen, buffer); if (ret) { printk(KERN_NOTICE "ftl_cs: block write failed!\n"); printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, virt_addr" " = 0x%x, Offset = 0x%x\n", log_addr, virt_addr, offset); return -EIO; } /* Only delete the old entry when the new entry is ready */ old_addr = part->VirtualBlockMap[sector+i]; if (old_addr != 0xffffffff) { part->VirtualBlockMap[sector+i] = 0xffffffff; part->EUNInfo[old_addr/bsize].Deleted++; if (set_bam_entry(part, old_addr, 0)) return -EIO; } /* Finally, set up the new pointers */ if (set_bam_entry(part, log_addr, virt_addr)) return -EIO; part->VirtualBlockMap[sector+i] = log_addr; part->EUNInfo[part->bam_index].Deleted--; buffer += SECTOR_SIZE; virt_addr += SECTOR_SIZE; } return 0;} /* ftl_write *//*====================================================================== IOCTL calls for getting device parameters.======================================================================*/static int ftl_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg){ struct hd_geometry *geo = (struct hd_geometry *)arg; int ret = 0, minor = MINOR(inode->i_rdev); partition_t *part= myparts[minor >> 4]; u_long sect; if (!part) return -ENODEV; /* How? */ switch (cmd) { case HDIO_GETGEO: ret = verify_area(VERIFY_WRITE, (long *)arg, sizeof(*geo)); if (ret) return ret; /* Sort of arbitrary: round size down to 4K boundary */ sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE; put_user(1, (char *)&geo->heads); put_user(8, (char *)&geo->sectors); put_user((sect>>3), (short *)&geo->cylinders); put_user(ftl_hd[minor].start_sect, (u_long *)&geo->start); break; case BLKGETSIZE: ret = put_user(ftl_hd[minor].nr_sects, (unsigned long *)arg); break; case BLKGETSIZE64: ret = put_user((u64)ftl_hd[minor].nr_sects << 9, (u64 *)arg); break; case BLKRRPART: ret = ftl_reread_partitions(minor); break;#if (LINUX_VERSION_CODE < 0x20303) case BLKFLSBUF:#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) if (!capable(CAP_SYS_ADMIN)) return -EACCES;#endif fsync_dev(inode->i_rdev); invalidate_buffers(inode->i_rdev); break; RO_IOCTLS(inode->i_rdev, arg);#else case BLKROSET: case BLKROGET: case BLKFLSBUF: ret = blk_ioctl(inode->i_rdev, cmd, arg); break;#endif default: ret = -EINVAL; } return ret;} /* ftl_ioctl *//*====================================================================== Handler for block device requests======================================================================*/static int ftl_reread_partitions(int minor){ partition_t *part = myparts[minor >> 4]; int i, whole; DEBUG(0, "ftl_cs: ftl_reread_partition(%d)\n", minor); if ((atomic_read(&part->open) > 1)) { return -EBUSY; } whole = minor & ~(MAX_PART-1); i = MAX_PART - 1; while (i-- > 0) { if (ftl_hd[whole+i].nr_sects > 0) { kdev_t rdev = MKDEV(FTL_MAJOR, whole+i); invalidate_device(rdev, 1); } ftl_hd[whole+i].start_sect = 0; ftl_hd[whole+i].nr_sects = 0; } scan_header(part); register_disk(&ftl_gendisk, whole >> PART_BITS, MAX_PART, &ftl_blk_fops, le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE);#ifdef PCMCIA_DEBUG for (i = 0; i < MAX_PART; i++) { if (ftl_hd[whole+i].nr_sects > 0) printk(KERN_INFO " %d: start %ld size %ld\n", i, ftl_hd[whole+i].start_sect, ftl_hd[whole+i].nr_sects); }#endif return 0;}/*====================================================================== Handler for block device requests======================================================================*/static void do_ftl_request(request_arg_t){ int ret, minor; partition_t *part; do { // sti(); INIT_REQUEST; minor = MINOR(CURRENT->rq_dev); part = myparts[minor >> 4]; if (part) { ret = 0; switch (CURRENT->cmd) { case READ: ret = ftl_read(part, CURRENT->buffer, CURRENT->sector+ftl_hd[minor].start_sect, CURRENT->current_nr_sectors); if (ret) printk("ftl_read returned %d\n", ret); break; case WRITE: ret = ftl_write(part, CURRENT->buffer, CURRENT->sector+ftl_hd[minor].start_sect, CURRENT->current_nr_sectors); if (ret) printk("ftl_write returned %d\n", ret); break; default: panic("ftl_cs: unknown block command!\n"); } } else { ret = 1; printk("NULL part in ftl_request\n"); } if (!ret) { CURRENT->sector += CURRENT->current_nr_sectors; } end_request((ret == 0) ? 1 : 0); } while (1);} /* do_ftl_request *//*====================================================================*/void ftl_freepart(partition_t *part){ if (part->VirtualBlockMap) { vfree(part->VirtualBlockMap); part->VirtualBlockMap = NULL; } if (part->VirtualPageMap) { kfree(part->VirtualPageMap); part->VirtualPageMap = NULL; } if (part->EUNInfo) { kfree(part->EUNInfo); part->EUNInfo = NULL; } if (part->XferInfo) { kfree(part->XferInfo); part->XferInfo = NULL; } if (part->bam_cache) { kfree(part->bam_cache); part->bam_cache = NULL; } } /* ftl_freepart */static void ftl_notify_add(struct mtd_info *mtd){ partition_t *partition; int device; for (device=0; device < MAX_MTD_DEVICES && myparts[device]; device++) ; if (device == MAX_MTD_DEVICES) { printk(KERN_NOTICE "Maximum number of FTL partitions reached\n" "Not scanning <%s>\n", mtd->name); return; } partition = kmalloc(sizeof(partition_t), GFP_KERNEL); if (!partition) { printk(KERN_WARNING "No memory to scan for FTL on %s\n", mtd->name); return; } memset(partition, 0, sizeof(partition_t)); partition->mtd = mtd; if ((scan_header(partition) == 0) && (build_maps(partition) == 0)) { partition->state = FTL_FORMATTED; atomic_set(&partition->open, 0); myparts[device] = partition; ftl_reread_partitions(device << 4);#ifdef PCMCIA_DEBUG printk(KERN_INFO "ftl_cs: opening %d kb FTL partition\n", le32_to_cpu(partition->header.FormattedSize) >> 10);#endif } else kfree(partition);}static void ftl_notify_remove(struct mtd_info *mtd){ int i,j; /* Q: What happens if you try to remove a device which has * a currently-open FTL partition on it? * * A: You don't. The ftl_open routine is responsible for * increasing the use count of the driver module which * it uses. */ /* That's the theory, anyway :) */ for (i=0; i< MAX_MTD_DEVICES; i++) if (myparts[i] && myparts[i]->mtd == mtd) { if (myparts[i]->state == FTL_FORMATTED) ftl_freepart(myparts[i]); myparts[i]->state = 0; for (j=0; j<16; j++) { ftl_gendisk.part[j].nr_sects=0; ftl_gendisk.part[j].start_sect=0; } kfree(myparts[i]); myparts[i] = NULL; }}int init_ftl(void){ int i; memset(myparts, 0, sizeof(myparts)); DEBUG(0, "$Id: ftl.c,v 1.39 2001/10/02 15:05:11 dwmw2 Exp $\n"); if (register_blkdev(FTL_MAJOR, "ftl", &ftl_blk_fops)) { printk(KERN_NOTICE "ftl_cs: unable to grab major " "device number!\n"); return -EAGAIN; } for (i = 0; i < MINOR_NR(MAX_DEV, 0, 0); i++) ftl_blocksizes[i] = 1024; for (i = 0; i < MAX_DEV*MAX_PART; i++) { ftl_hd[i].nr_sects = 0; ftl_hd[i].start_sect = 0; } blksize_size[FTL_MAJOR] = ftl_blocksizes; ftl_gendisk.major = FTL_MAJOR; blk_init_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR), &do_ftl_request); add_gendisk(&ftl_gendisk); register_mtd_user(&ftl_notifier); return 0;}static void __exit cleanup_ftl(void){ unregister_mtd_user(&ftl_notifier); unregister_blkdev(FTL_MAJOR, "ftl"); blk_cleanup_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR)); blksize_size[FTL_MAJOR] = NULL; del_gendisk(&ftl_gendisk);}module_init(init_ftl);module_exit(cleanup_ftl);MODULE_LICENSE("Dual MPL/GPL");MODULE_AUTHOR("David Hinds <dhinds@sonic.net>");MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices and M-Systems DiskOnChip 1000");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -