*
* Copyright (C) 2005 Sean Young <sean@mess.org>
*
- * $Id: rfd_ftl.c,v 1.4 2005/07/31 22:49:14 sean Exp $
- *
* This type of flash translation layer (FTL) is used by the Embedded BIOS
* by General Software. It is known as the Resident Flash Disk (RFD), see:
*
#include <linux/mtd/blktrans.h>
#include <linux/mtd/mtd.h>
#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
#include <asm/types.h>
-#define const_cpu_to_le16 __constant_cpu_to_le16
-
static int block_size = 0;
module_param(block_size, int, 0);
MODULE_PARM_DESC(block_size, "Block size to use by RFD, defaults to erase unit size");
#define PREFIX "rfd_ftl: "
-/* Major device # for FTL device */
-
-/* A request for this major has been sent to device@lanana.org */
+/* This major has been assigned by device@lanana.org */
#ifndef RFD_FTL_MAJOR
-#define RFD_FTL_MAJOR 95
+#define RFD_FTL_MAJOR 256
#endif
/* Maximum number of partitions in an FTL region */
BLOCK_OK,
BLOCK_ERASING,
BLOCK_ERASED,
+ BLOCK_UNUSED,
BLOCK_FAILED
} state;
int free_sectors;
{
struct block *block = &part->blocks[block_no];
int i;
-
+
block->offset = part->block_size * block_no;
if (le16_to_cpu(part->header_cache[0]) != RFD_MAGIC) {
- block->state = BLOCK_ERASED; /* assumption */
- block->free_sectors = part->data_sectors_per_block;
- part->reserved_block = block_no;
- return 1;
+ block->state = BLOCK_UNUSED;
+ return -ENOENT;
}
block->state = BLOCK_OK;
for (i=0; i<part->data_sectors_per_block; i++) {
u16 entry;
-
+
entry = le16_to_cpu(part->header_cache[HEADER_MAP_OFFSET + i]);
if (entry == SECTOR_DELETED)
continue;
-
+
if (entry == SECTOR_FREE) {
block->free_sectors++;
continue;
if (entry == SECTOR_ZERO)
entry = 0;
-
+
if (entry >= part->sector_count) {
- printk(KERN_NOTICE PREFIX
+ printk(KERN_WARNING PREFIX
"'%s': unit #%d: entry %d corrupt, "
"sector %d out of range\n",
part->mbd.mtd->name, block_no, i, entry);
}
if (part->sector_map[entry] != -1) {
- printk(KERN_NOTICE PREFIX
+ printk(KERN_WARNING PREFIX
"'%s': more than one entry for sector %d\n",
part->mbd.mtd->name, entry);
part->errors = 1;
continue;
}
- part->sector_map[entry] = block->offset +
+ part->sector_map[entry] = block->offset +
(i + part->header_sectors_per_block) * SECTOR_SIZE;
block->used_sectors++;
size_t retlen;
sectors_per_block = part->block_size / SECTOR_SIZE;
- part->total_blocks = part->mbd.mtd->size / part->block_size;
+ part->total_blocks = (u32)part->mbd.mtd->size / part->block_size;
if (part->total_blocks < 2)
return -ENOENT;
/* each erase block has three bytes header, followed by the map */
- part->header_sectors_per_block =
- ((HEADER_MAP_OFFSET + sectors_per_block) *
- sizeof(u16) + SECTOR_SIZE - 1) / SECTOR_SIZE;
+ part->header_sectors_per_block =
+ ((HEADER_MAP_OFFSET + sectors_per_block) *
+ sizeof(u16) + SECTOR_SIZE - 1) / SECTOR_SIZE;
- part->data_sectors_per_block = sectors_per_block -
+ part->data_sectors_per_block = sectors_per_block -
part->header_sectors_per_block;
- part->header_size = (HEADER_MAP_OFFSET +
+ part->header_size = (HEADER_MAP_OFFSET +
part->data_sectors_per_block) * sizeof(u16);
part->cylinders = (part->data_sectors_per_block *
if (!part->header_cache)
goto err;
- part->blocks = kcalloc(part->total_blocks, sizeof(struct block),
+ part->blocks = kcalloc(part->total_blocks, sizeof(struct block),
GFP_KERNEL);
if (!part->blocks)
goto err;
goto err;
}
- for (i=0; i<part->sector_count; i++)
+ for (i=0; i<part->sector_count; i++)
part->sector_map[i] = -1;
for (i=0, blocks_found=0; i<part->total_blocks; i++) {
- rc = part->mbd.mtd->read(part->mbd.mtd,
+ rc = part->mbd.mtd->read(part->mbd.mtd,
i * part->block_size, part->header_size,
&retlen, (u_char*)part->header_cache);
if (!rc && retlen != part->header_size)
rc = -EIO;
- if (rc)
+ if (rc)
goto err;
if (!build_block_map(part, i))
}
if (part->reserved_block == -1) {
- printk(KERN_NOTICE PREFIX "'%s': no empty erase unit found\n",
+ printk(KERN_WARNING PREFIX "'%s': no empty erase unit found\n",
part->mbd.mtd->name);
part->errors = 1;
u_long addr;
size_t retlen;
int rc;
-
+
if (sector >= part->sector_count)
return -EIO;
}
} else
memset(buf, 0, SECTOR_SIZE);
-
+
return 0;
-}
+}
static void erase_callback(struct erase_info *erase)
{
part = (struct partition*)erase->priv;
- i = erase->addr / part->block_size;
- if (i >= part->total_blocks || part->blocks[i].offset != erase->addr) {
- printk(KERN_ERR PREFIX "erase callback for unknown offset %x "
- "on '%s'\n", erase->addr, part->mbd.mtd->name);
+ i = (u32)erase->addr / part->block_size;
+ if (i >= part->total_blocks || part->blocks[i].offset != erase->addr ||
+ erase->addr > UINT_MAX) {
+ printk(KERN_ERR PREFIX "erase callback for unknown offset %llx "
+ "on '%s'\n", (unsigned long long)erase->addr, part->mbd.mtd->name);
return;
}
if (erase->state != MTD_ERASE_DONE) {
- printk(KERN_WARNING PREFIX "erase failed at 0x%x on '%s', "
- "state %d\n", erase->addr,
+ printk(KERN_WARNING PREFIX "erase failed at 0x%llx on '%s', "
+ "state %d\n", (unsigned long long)erase->addr,
part->mbd.mtd->name, erase->state);
part->blocks[i].state = BLOCK_FAILED;
return;
}
- magic = const_cpu_to_le16(RFD_MAGIC);
+ magic = cpu_to_le16(RFD_MAGIC);
part->blocks[i].state = BLOCK_ERASED;
part->blocks[i].free_sectors = part->data_sectors_per_block;
part->blocks[i].used_sectors = 0;
part->blocks[i].erases++;
- rc = part->mbd.mtd->write(part->mbd.mtd,
- part->blocks[i].offset, sizeof(magic), &retlen,
+ rc = part->mbd.mtd->write(part->mbd.mtd,
+ part->blocks[i].offset, sizeof(magic), &retlen,
(u_char*)&magic);
-
+
if (!rc && retlen != sizeof(magic))
rc = -EIO;
if (rc) {
- printk(KERN_NOTICE PREFIX "'%s': unable to write RFD "
+ printk(KERN_ERR PREFIX "'%s': unable to write RFD "
"header at 0x%lx\n",
- part->mbd.mtd->name,
+ part->mbd.mtd->name,
part->blocks[i].offset);
part->blocks[i].state = BLOCK_FAILED;
}
rc = part->mbd.mtd->erase(part->mbd.mtd, erase);
if (rc) {
- printk(KERN_WARNING PREFIX "erase of region %x,%x on '%s' "
- "failed\n", erase->addr, erase->len,
- part->mbd.mtd->name);
+ printk(KERN_ERR PREFIX "erase of region %llx,%llx on '%s' "
+ "failed\n", (unsigned long long)erase->addr,
+ (unsigned long long)erase->len, part->mbd.mtd->name);
kfree(erase);
}
map = kmalloc(part->header_size, GFP_KERNEL);
if (!map)
goto err2;
-
- rc = part->mbd.mtd->read(part->mbd.mtd,
- part->blocks[block_no].offset, part->header_size,
+
+ rc = part->mbd.mtd->read(part->mbd.mtd,
+ part->blocks[block_no].offset, part->header_size,
&retlen, (u_char*)map);
-
+
if (!rc && retlen != part->header_size)
rc = -EIO;
if (rc) {
- printk(KERN_NOTICE PREFIX "error reading '%s' at "
- "0x%lx\n", part->mbd.mtd->name,
+ printk(KERN_ERR PREFIX "error reading '%s' at "
+ "0x%lx\n", part->mbd.mtd->name,
part->blocks[block_no].offset);
goto err;
if (entry == SECTOR_FREE || entry == SECTOR_DELETED)
continue;
- if (entry == SECTOR_ZERO)
+ if (entry == SECTOR_ZERO)
entry = 0;
/* already warned about and ignored in build_block_map() */
- if (entry >= part->sector_count)
+ if (entry >= part->sector_count)
continue;
addr = part->blocks[block_no].offset +
}
rc = part->mbd.mtd->read(part->mbd.mtd, addr,
SECTOR_SIZE, &retlen, sector_data);
-
+
if (!rc && retlen != SECTOR_SIZE)
rc = -EIO;
if (rc) {
- printk(KERN_NOTICE PREFIX "'%s': Unable to "
+ printk(KERN_ERR PREFIX "'%s': Unable to "
"read sector for relocation\n",
part->mbd.mtd->name);
goto err;
}
-
+
rc = rfd_ftl_writesect((struct mtd_blktrans_dev*)part,
entry, sector_data);
-
- if (rc)
+
+ if (rc)
goto err;
}
return rc;
}
-static int reclaim_block(struct partition *part, u_long *old_sector)
+static int reclaim_block(struct partition *part, u_long *old_sector)
{
int block, best_block, score, old_sector_block;
int rc;
-
+
/* we have a race if sync doesn't exist */
if (part->mbd.mtd->sync)
part->mbd.mtd->sync(part->mbd.mtd);
* more removed sectors is more efficient (have to move
* less).
*/
- if (part->blocks[block].free_sectors)
+ if (part->blocks[block].free_sectors)
return 0;
this_score = part->blocks[block].used_sectors;
- if (block == old_sector_block)
+ if (block == old_sector_block)
this_score--;
else {
/* no point in moving a full block */
- if (part->blocks[block].used_sectors ==
+ if (part->blocks[block].used_sectors ==
part->data_sectors_per_block)
continue;
}
* because if we fill that one up first it'll have the most chance of having
* the least live sectors at reclaim.
*/
-static int find_free_block(const struct partition *part)
+static int find_free_block(struct partition *part)
{
int block, stop;
stop = block;
do {
- if (part->blocks[block].free_sectors &&
+ if (part->blocks[block].free_sectors &&
block != part->reserved_block)
return block;
+ if (part->blocks[block].state == BLOCK_UNUSED)
+ erase_block(part, block);
+
if (++block >= part->total_blocks)
block = 0;
return -1;
}
-static int find_writeable_block(struct partition *part, u_long *old_sector)
+static int find_writable_block(struct partition *part, u_long *old_sector)
{
int rc, block;
size_t retlen;
}
}
- rc = part->mbd.mtd->read(part->mbd.mtd, part->blocks[block].offset,
+ rc = part->mbd.mtd->read(part->mbd.mtd, part->blocks[block].offset,
part->header_size, &retlen, (u_char*)part->header_cache);
if (!rc && retlen != part->header_size)
rc = -EIO;
if (rc) {
- printk(KERN_NOTICE PREFIX "'%s': unable to read header at "
- "0x%lx\n", part->mbd.mtd->name,
+ printk(KERN_ERR PREFIX "'%s': unable to read header at "
+ "0x%lx\n", part->mbd.mtd->name,
part->blocks[block].offset);
goto err;
}
err:
return rc;
-}
+}
static int mark_sector_deleted(struct partition *part, u_long old_addr)
{
int block, offset, rc;
u_long addr;
size_t retlen;
- u16 del = const_cpu_to_le16(SECTOR_DELETED);
+ u16 del = cpu_to_le16(SECTOR_DELETED);
block = old_addr / part->block_size;
- offset = (old_addr % part->block_size) / SECTOR_SIZE -
+ offset = (old_addr % part->block_size) / SECTOR_SIZE -
part->header_sectors_per_block;
addr = part->blocks[block].offset +
rc = -EIO;
if (rc) {
- printk(KERN_WARNING PREFIX "error writing '%s' at "
+ printk(KERN_ERR PREFIX "error writing '%s' at "
"0x%lx\n", part->mbd.mtd->name, addr);
- if (rc)
+ if (rc)
goto err;
}
if (block == part->current_block)
i = stop = part->data_sectors_per_block - block->free_sectors;
do {
- if (le16_to_cpu(part->header_cache[HEADER_MAP_OFFSET + i])
+ if (le16_to_cpu(part->header_cache[HEADER_MAP_OFFSET + i])
== SECTOR_FREE)
return i;
if (part->current_block == -1 ||
!part->blocks[part->current_block].free_sectors) {
- rc = find_writeable_block(part, old_addr);
- if (rc)
+ rc = find_writable_block(part, old_addr);
+ if (rc)
goto err;
}
rc = -ENOSPC;
goto err;
}
-
- addr = (i + part->header_sectors_per_block) * SECTOR_SIZE +
+
+ addr = (i + part->header_sectors_per_block) * SECTOR_SIZE +
block->offset;
- rc = part->mbd.mtd->write(part->mbd.mtd,
+ rc = part->mbd.mtd->write(part->mbd.mtd,
addr, SECTOR_SIZE, &retlen, (u_char*)buf);
if (!rc && retlen != SECTOR_SIZE)
rc = -EIO;
if (rc) {
- printk(KERN_WARNING PREFIX "error writing '%s' at 0x%lx\n",
+ printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
part->mbd.mtd->name, addr);
- if (rc)
+ if (rc)
goto err;
}
rc = -EIO;
if (rc) {
- printk(KERN_WARNING PREFIX "error writing '%s' at 0x%lx\n",
+ printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
part->mbd.mtd->name, addr);
- if (rc)
+ if (rc)
goto err;
}
block->used_sectors++;
break;
}
- if (i == SECTOR_SIZE)
+ if (i == SECTOR_SIZE)
part->sector_map[sector] = -1;
if (old_addr != -1)
{
struct partition *part;
- if (mtd->type != MTD_NORFLASH)
+ if (mtd->type != MTD_NORFLASH || mtd->size > UINT_MAX)
return;
- part = kcalloc(1, sizeof(struct partition), GFP_KERNEL);
+ part = kzalloc(sizeof(struct partition), GFP_KERNEL);
if (!part)
return;
part->block_size = block_size;
else {
if (!mtd->erasesize) {
- printk(KERN_NOTICE PREFIX "please provide block_size");
- return;
- }
- else
+ printk(KERN_WARNING PREFIX "please provide block_size");
+ goto out;
+ } else
part->block_size = mtd->erasesize;
}
if (scan_header(part) == 0) {
part->mbd.size = part->sector_count;
- part->mbd.blksize = SECTOR_SIZE;
part->mbd.tr = tr;
part->mbd.devnum = -1;
if (!(mtd->flags & MTD_WRITEABLE))
part->mbd.readonly = 1;
else if (part->errors) {
- printk(KERN_NOTICE PREFIX "'%s': errors found, "
- "setting read-only", mtd->name);
+ printk(KERN_WARNING PREFIX "'%s': errors found, "
+ "setting read-only\n", mtd->name);
part->mbd.readonly = 1;
}
if (!add_mtd_blktrans_dev((void*)part))
return;
- }
-
+ }
+out:
kfree(part);
}
kfree(part);
}
-struct mtd_blktrans_ops rfd_ftl_tr = {
+static struct mtd_blktrans_ops rfd_ftl_tr = {
.name = "rfd",
.major = RFD_FTL_MAJOR,
.part_bits = PART_BITS,
+ .blksize = SECTOR_SIZE,
+
.readsect = rfd_ftl_readsect,
- .writesect = rfd_ftl_writesect,
+ .writesect = rfd_ftl_writesect,
.getgeo = rfd_ftl_getgeo,
.add_mtd = rfd_ftl_add_mtd,
.remove_dev = rfd_ftl_remove_dev,