sky2: Refactor sky2_up into two functions
[safe/jmp/linux-2.6] / drivers / mtd / rfd_ftl.c
index 7b9f359..d2aa9c4 100644 (file)
@@ -3,8 +3,6 @@
  *
  * 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 */
@@ -61,6 +57,7 @@ struct block {
                BLOCK_OK,
                BLOCK_ERASING,
                BLOCK_ERASED,
+               BLOCK_UNUSED,
                BLOCK_FAILED
        } state;
        int free_sectors;
@@ -95,26 +92,24 @@ static int build_block_map(struct partition *part, int block_no)
 {
        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;
@@ -122,9 +117,9 @@ static int build_block_map(struct partition *part, int block_no)
 
                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);
@@ -132,14 +127,14 @@ static int build_block_map(struct partition *part, int block_no)
                }
 
                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++;
@@ -159,20 +154,20 @@ static int scan_header(struct partition *part)
        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 *
@@ -188,7 +183,7 @@ static int scan_header(struct partition *part)
        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;
@@ -200,18 +195,18 @@ static int scan_header(struct partition *part)
                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))
@@ -226,7 +221,7 @@ static int scan_header(struct partition *part)
        }
 
        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;
@@ -248,7 +243,7 @@ static int rfd_ftl_readsect(struct mtd_blktrans_dev *dev, u_long sector, char *b
        u_long addr;
        size_t retlen;
        int rc;
-       
+
        if (sector >= part->sector_count)
                return -EIO;
 
@@ -266,9 +261,9 @@ static int rfd_ftl_readsect(struct mtd_blktrans_dev *dev, u_long sector, char *b
                }
        } else
                memset(buf, 0, SECTOR_SIZE);
-       
+
        return 0;
-} 
+}
 
 static void erase_callback(struct erase_info *erase)
 {
@@ -279,16 +274,17 @@ 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;
@@ -300,24 +296,24 @@ static void erase_callback(struct erase_info *erase)
                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;
        }
@@ -348,9 +344,9 @@ static int erase_block(struct partition *part, int block)
        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);
        }
 
@@ -374,17 +370,17 @@ static int move_block_contents(struct partition *part, int block_no, u_long *old
        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;
@@ -398,11 +394,11 @@ static int move_block_contents(struct partition *part, int block_no, u_long *old
                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 +
@@ -418,22 +414,22 @@ static int move_block_contents(struct partition *part, int block_no, u_long *old
                }
                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;
        }
 
@@ -447,11 +443,11 @@ err3:
        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);
@@ -474,16 +470,16 @@ static int reclaim_block(struct partition *part, u_long *old_sector)
                 * 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;
                }
@@ -520,7 +516,7 @@ static int reclaim_block(struct partition *part, u_long *old_sector)
  * 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;
 
@@ -529,10 +525,13 @@ static int find_free_block(const struct partition *part)
        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;
 
@@ -541,7 +540,7 @@ static int find_free_block(const struct partition *part)
        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;
@@ -563,15 +562,15 @@ static int find_writeable_block(struct partition *part, u_long *old_sector)
                }
        }
 
-       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;
        }
@@ -580,17 +579,17 @@ static int find_writeable_block(struct partition *part, u_long *old_sector)
 
 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 +
@@ -602,9 +601,9 @@ static int mark_sector_deleted(struct partition *part, u_long old_addr)
                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)
@@ -627,7 +626,7 @@ static int find_free_sector(const struct partition *part, const struct block *bl
        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;
 
@@ -652,8 +651,8 @@ static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf,
        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;
        }
 
@@ -665,19 +664,19 @@ static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf,
                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;
        }
 
@@ -695,9 +694,9 @@ static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf,
                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++;
@@ -738,7 +737,7 @@ static int rfd_ftl_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *
                break;
        }
 
-       if (i == SECTOR_SIZE) 
+       if (i == SECTOR_SIZE)
                part->sector_map[sector] = -1;
 
        if (old_addr != -1)
@@ -763,10 +762,10 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 {
        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;
 
@@ -776,23 +775,21 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
                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;
                }
 
@@ -801,8 +798,8 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 
                if (!add_mtd_blktrans_dev((void*)part))
                        return;
-       } 
-
+       }
+out:
        kfree(part);
 }
 
@@ -823,12 +820,14 @@ static void rfd_ftl_remove_dev(struct mtd_blktrans_dev *dev)
        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,