root/drivers/mtd/ftl.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. scan_header
  2. build_maps
  3. erase_xfer
  4. prepare_xfer
  5. copy_erase_unit
  6. reclaim_block
  7. dump_lists
  8. find_free
  9. ftl_read
  10. set_bam_entry
  11. ftl_write
  12. ftl_getgeo
  13. ftl_readsect
  14. ftl_writesect
  15. ftl_discardsect
  16. ftl_freepart
  17. ftl_add_mtd
  18. ftl_remove_dev
  19. init_ftl
  20. cleanup_ftl

   1 /* This version ported to the Linux-MTD system by dwmw2@infradead.org
   2  *
   3  * Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
   4  * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups
   5  *
   6  * Based on:
   7  */
   8 /*======================================================================
   9 
  10     A Flash Translation Layer memory card driver
  11 
  12     This driver implements a disk-like block device driver with an
  13     apparent block size of 512 bytes for flash memory cards.
  14 
  15     ftl_cs.c 1.62 2000/02/01 00:59:04
  16 
  17     The contents of this file are subject to the Mozilla Public
  18     License Version 1.1 (the "License"); you may not use this file
  19     except in compliance with the License. You may obtain a copy of
  20     the License at http://www.mozilla.org/MPL/
  21 
  22     Software distributed under the License is distributed on an "AS
  23     IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  24     implied. See the License for the specific language governing
  25     rights and limitations under the License.
  26 
  27     The initial developer of the original code is David A. Hinds
  28     <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
  29     are Copyright © 1999 David A. Hinds.  All Rights Reserved.
  30 
  31     Alternatively, the contents of this file may be used under the
  32     terms of the GNU General Public License version 2 (the "GPL"), in
  33     which case the provisions of the GPL are applicable instead of the
  34     above.  If you wish to allow the use of your version of this file
  35     only under the terms of the GPL and not to allow others to use
  36     your version of this file under the MPL, indicate your decision
  37     by deleting the provisions above and replace them with the notice
  38     and other provisions required by the GPL.  If you do not delete
  39     the provisions above, a recipient may use your version of this
  40     file under either the MPL or the GPL.
  41 
  42     LEGAL NOTE: The FTL format is patented by M-Systems.  They have
  43     granted a license for its use with PCMCIA devices:
  44 
  45      "M-Systems grants a royalty-free, non-exclusive license under
  46       any presently existing M-Systems intellectual property rights
  47       necessary for the design and development of FTL-compatible
  48       drivers, file systems and utilities using the data formats with
  49       PCMCIA PC Cards as described in the PCMCIA Flash Translation
  50       Layer (FTL) Specification."
  51 
  52     Use of the FTL format for non-PCMCIA applications may be an
  53     infringement of these patents.  For additional information,
  54     contact M-Systems directly. M-Systems since acquired by Sandisk. 
  55 
  56 ======================================================================*/
  57 #include <linux/mtd/blktrans.h>
  58 #include <linux/module.h>
  59 #include <linux/mtd/mtd.h>
  60 /*#define PSYCHO_DEBUG */
  61 
  62 #include <linux/kernel.h>
  63 #include <linux/ptrace.h>
  64 #include <linux/slab.h>
  65 #include <linux/string.h>
  66 #include <linux/timer.h>
  67 #include <linux/major.h>
  68 #include <linux/fs.h>
  69 #include <linux/init.h>
  70 #include <linux/hdreg.h>
  71 #include <linux/vmalloc.h>
  72 #include <linux/blkpg.h>
  73 #include <linux/uaccess.h>
  74 
  75 #include <linux/mtd/ftl.h>
  76 
  77 /*====================================================================*/
  78 
  79 /* Parameters that can be set with 'insmod' */
  80 static int shuffle_freq = 50;
  81 module_param(shuffle_freq, int, 0);
  82 
  83 /*====================================================================*/
  84 
  85 /* Major device # for FTL device */
  86 #ifndef FTL_MAJOR
  87 #define FTL_MAJOR       44
  88 #endif
  89 
  90 
  91 /*====================================================================*/
  92 
  93 /* Maximum number of separate memory devices we'll allow */
  94 #define MAX_DEV         4
  95 
  96 /* Maximum number of regions per device */
  97 #define MAX_REGION      4
  98 
  99 /* Maximum number of partitions in an FTL region */
 100 #define PART_BITS       4
 101 
 102 /* Maximum number of outstanding erase requests per socket */
 103 #define MAX_ERASE       8
 104 
 105 /* Sector size -- shouldn't need to change */
 106 #define SECTOR_SIZE     512
 107 
 108 
 109 /* Each memory region corresponds to a minor device */
 110 typedef struct partition_t {
 111     struct mtd_blktrans_dev mbd;
 112     uint32_t            state;
 113     uint32_t            *VirtualBlockMap;
 114     uint32_t            FreeTotal;
 115     struct eun_info_t {
 116         uint32_t                Offset;
 117         uint32_t                EraseCount;
 118         uint32_t                Free;
 119         uint32_t                Deleted;
 120     } *EUNInfo;
 121     struct xfer_info_t {
 122         uint32_t                Offset;
 123         uint32_t                EraseCount;
 124         uint16_t                state;
 125     } *XferInfo;
 126     uint16_t            bam_index;
 127     uint32_t            *bam_cache;
 128     uint16_t            DataUnits;
 129     uint32_t            BlocksPerUnit;
 130     erase_unit_header_t header;
 131 } partition_t;
 132 
 133 /* Partition state flags */
 134 #define FTL_FORMATTED   0x01
 135 
 136 /* Transfer unit states */
 137 #define XFER_UNKNOWN    0x00
 138 #define XFER_ERASING    0x01
 139 #define XFER_ERASED     0x02
 140 #define XFER_PREPARED   0x03
 141 #define XFER_FAILED     0x04
 142 
 143 /*======================================================================
 144 
 145     Scan_header() checks to see if a memory region contains an FTL
 146     partition.  build_maps() reads all the erase unit headers, builds
 147     the erase unit map, and then builds the virtual page map.
 148 
 149 ======================================================================*/
 150 
 151 static int scan_header(partition_t *part)
 152 {
 153     erase_unit_header_t header;
 154     loff_t offset, max_offset;
 155     size_t ret;
 156     int err;
 157     part->header.FormattedSize = 0;
 158     max_offset = (0x100000<part->mbd.mtd->size)?0x100000:part->mbd.mtd->size;
 159     /* Search first megabyte for a valid FTL header */
 160     for (offset = 0;
 161          (offset + sizeof(header)) < max_offset;
 162          offset += part->mbd.mtd->erasesize ? : 0x2000) {
 163 
 164         err = mtd_read(part->mbd.mtd, offset, sizeof(header), &ret,
 165                        (unsigned char *)&header);
 166 
 167         if (err)
 168             return err;
 169 
 170         if (strcmp(header.DataOrgTuple+3, "FTL100") == 0) break;
 171     }
 172 
 173     if (offset == max_offset) {
 174         printk(KERN_NOTICE "ftl_cs: FTL header not found.\n");
 175         return -ENOENT;
 176     }
 177     if (header.BlockSize != 9 ||
 178         (header.EraseUnitSize < 10) || (header.EraseUnitSize > 31) ||
 179         (header.NumTransferUnits >= le16_to_cpu(header.NumEraseUnits))) {
 180         printk(KERN_NOTICE "ftl_cs: FTL header corrupt!\n");
 181         return -1;
 182     }
 183     if ((1 << header.EraseUnitSize) != part->mbd.mtd->erasesize) {
 184         printk(KERN_NOTICE "ftl: FTL EraseUnitSize %x != MTD erasesize %x\n",
 185                1 << header.EraseUnitSize,part->mbd.mtd->erasesize);
 186         return -1;
 187     }
 188     part->header = header;
 189     return 0;
 190 }
 191 
 192 static int build_maps(partition_t *part)
 193 {
 194     erase_unit_header_t header;
 195     uint16_t xvalid, xtrans, i;
 196     unsigned blocks, j;
 197     int hdr_ok, ret = -1;
 198     ssize_t retval;
 199     loff_t offset;
 200 
 201     /* Set up erase unit maps */
 202     part->DataUnits = le16_to_cpu(part->header.NumEraseUnits) -
 203         part->header.NumTransferUnits;
 204     part->EUNInfo = kmalloc_array(part->DataUnits, sizeof(struct eun_info_t),
 205                                   GFP_KERNEL);
 206     if (!part->EUNInfo)
 207             goto out;
 208     for (i = 0; i < part->DataUnits; i++)
 209         part->EUNInfo[i].Offset = 0xffffffff;
 210     part->XferInfo =
 211         kmalloc_array(part->header.NumTransferUnits,
 212                       sizeof(struct xfer_info_t),
 213                       GFP_KERNEL);
 214     if (!part->XferInfo)
 215             goto out_EUNInfo;
 216 
 217     xvalid = xtrans = 0;
 218     for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) {
 219         offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN))
 220                       << part->header.EraseUnitSize);
 221         ret = mtd_read(part->mbd.mtd, offset, sizeof(header), &retval,
 222                        (unsigned char *)&header);
 223 
 224         if (ret)
 225             goto out_XferInfo;
 226 
 227         ret = -1;
 228         /* Is this a transfer partition? */
 229         hdr_ok = (strcmp(header.DataOrgTuple+3, "FTL100") == 0);
 230         if (hdr_ok && (le16_to_cpu(header.LogicalEUN) < part->DataUnits) &&
 231             (part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset == 0xffffffff)) {
 232             part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset = offset;
 233             part->EUNInfo[le16_to_cpu(header.LogicalEUN)].EraseCount =
 234                 le32_to_cpu(header.EraseCount);
 235             xvalid++;
 236         } else {
 237             if (xtrans == part->header.NumTransferUnits) {
 238                 printk(KERN_NOTICE "ftl_cs: format error: too many "
 239                        "transfer units!\n");
 240                 goto out_XferInfo;
 241             }
 242             if (hdr_ok && (le16_to_cpu(header.LogicalEUN) == 0xffff)) {
 243                 part->XferInfo[xtrans].state = XFER_PREPARED;
 244                 part->XferInfo[xtrans].EraseCount = le32_to_cpu(header.EraseCount);
 245             } else {
 246                 part->XferInfo[xtrans].state = XFER_UNKNOWN;
 247                 /* Pick anything reasonable for the erase count */
 248                 part->XferInfo[xtrans].EraseCount =
 249                     le32_to_cpu(part->header.EraseCount);
 250             }
 251             part->XferInfo[xtrans].Offset = offset;
 252             xtrans++;
 253         }
 254     }
 255     /* Check for format trouble */
 256     header = part->header;
 257     if ((xtrans != header.NumTransferUnits) ||
 258         (xvalid+xtrans != le16_to_cpu(header.NumEraseUnits))) {
 259         printk(KERN_NOTICE "ftl_cs: format error: erase units "
 260                "don't add up!\n");
 261         goto out_XferInfo;
 262     }
 263 
 264     /* Set up virtual page map */
 265     blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize;
 266     part->VirtualBlockMap = vmalloc(array_size(blocks, sizeof(uint32_t)));
 267     if (!part->VirtualBlockMap)
 268             goto out_XferInfo;
 269 
 270     memset(part->VirtualBlockMap, 0xff, blocks * sizeof(uint32_t));
 271     part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize;
 272 
 273     part->bam_cache = kmalloc_array(part->BlocksPerUnit, sizeof(uint32_t),
 274                                     GFP_KERNEL);
 275     if (!part->bam_cache)
 276             goto out_VirtualBlockMap;
 277 
 278     part->bam_index = 0xffff;
 279     part->FreeTotal = 0;
 280 
 281     for (i = 0; i < part->DataUnits; i++) {
 282         part->EUNInfo[i].Free = 0;
 283         part->EUNInfo[i].Deleted = 0;
 284         offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset);
 285 
 286         ret = mtd_read(part->mbd.mtd, offset,
 287                        part->BlocksPerUnit * sizeof(uint32_t), &retval,
 288                        (unsigned char *)part->bam_cache);
 289 
 290         if (ret)
 291                 goto out_bam_cache;
 292 
 293         for (j = 0; j < part->BlocksPerUnit; j++) {
 294             if (BLOCK_FREE(le32_to_cpu(part->bam_cache[j]))) {
 295                 part->EUNInfo[i].Free++;
 296                 part->FreeTotal++;
 297             } else if ((BLOCK_TYPE(le32_to_cpu(part->bam_cache[j])) == BLOCK_DATA) &&
 298                      (BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j])) < blocks))
 299                 part->VirtualBlockMap[BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j]))] =
 300                     (i << header.EraseUnitSize) + (j << header.BlockSize);
 301             else if (BLOCK_DELETED(le32_to_cpu(part->bam_cache[j])))
 302                 part->EUNInfo[i].Deleted++;
 303         }
 304     }
 305 
 306     ret = 0;
 307     goto out;
 308 
 309 out_bam_cache:
 310     kfree(part->bam_cache);
 311 out_VirtualBlockMap:
 312     vfree(part->VirtualBlockMap);
 313 out_XferInfo:
 314     kfree(part->XferInfo);
 315 out_EUNInfo:
 316     kfree(part->EUNInfo);
 317 out:
 318     return ret;
 319 } /* build_maps */
 320 
 321 /*======================================================================
 322 
 323     Erase_xfer() schedules an asynchronous erase operation for a
 324     transfer unit.
 325 
 326 ======================================================================*/
 327 
 328 static int erase_xfer(partition_t *part,
 329                       uint16_t xfernum)
 330 {
 331     int ret;
 332     struct xfer_info_t *xfer;
 333     struct erase_info *erase;
 334 
 335     xfer = &part->XferInfo[xfernum];
 336     pr_debug("ftl_cs: erasing xfer unit at 0x%x\n", xfer->Offset);
 337     xfer->state = XFER_ERASING;
 338 
 339     /* Is there a free erase slot? Always in MTD. */
 340 
 341 
 342     erase=kmalloc(sizeof(struct erase_info), GFP_KERNEL);
 343     if (!erase)
 344             return -ENOMEM;
 345 
 346     erase->addr = xfer->Offset;
 347     erase->len = 1 << part->header.EraseUnitSize;
 348 
 349     ret = mtd_erase(part->mbd.mtd, erase);
 350     if (!ret) {
 351         xfer->state = XFER_ERASED;
 352         xfer->EraseCount++;
 353     } else {
 354         xfer->state = XFER_FAILED;
 355         pr_notice("ftl_cs: erase failed: err = %d\n", ret);
 356     }
 357 
 358     kfree(erase);
 359 
 360     return ret;
 361 } /* erase_xfer */
 362 
 363 /*======================================================================
 364 
 365     Prepare_xfer() takes a freshly erased transfer unit and gives
 366     it an appropriate header.
 367 
 368 ======================================================================*/
 369 
 370 static int prepare_xfer(partition_t *part, int i)
 371 {
 372     erase_unit_header_t header;
 373     struct xfer_info_t *xfer;
 374     int nbam, ret;
 375     uint32_t ctl;
 376     ssize_t retlen;
 377     loff_t offset;
 378 
 379     xfer = &part->XferInfo[i];
 380     xfer->state = XFER_FAILED;
 381 
 382     pr_debug("ftl_cs: preparing xfer unit at 0x%x\n", xfer->Offset);
 383 
 384     /* Write the transfer unit header */
 385     header = part->header;
 386     header.LogicalEUN = cpu_to_le16(0xffff);
 387     header.EraseCount = cpu_to_le32(xfer->EraseCount);
 388 
 389     ret = mtd_write(part->mbd.mtd, xfer->Offset, sizeof(header), &retlen,
 390                     (u_char *)&header);
 391 
 392     if (ret) {
 393         return ret;
 394     }
 395 
 396     /* Write the BAM stub */
 397     nbam = DIV_ROUND_UP(part->BlocksPerUnit * sizeof(uint32_t) +
 398                         le32_to_cpu(part->header.BAMOffset), SECTOR_SIZE);
 399 
 400     offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset);
 401     ctl = cpu_to_le32(BLOCK_CONTROL);
 402 
 403     for (i = 0; i < nbam; i++, offset += sizeof(uint32_t)) {
 404 
 405         ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
 406                         (u_char *)&ctl);
 407 
 408         if (ret)
 409             return ret;
 410     }
 411     xfer->state = XFER_PREPARED;
 412     return 0;
 413 
 414 } /* prepare_xfer */
 415 
 416 /*======================================================================
 417 
 418     Copy_erase_unit() takes a full erase block and a transfer unit,
 419     copies everything to the transfer unit, then swaps the block
 420     pointers.
 421 
 422     All data blocks are copied to the corresponding blocks in the
 423     target unit, so the virtual block map does not need to be
 424     updated.
 425 
 426 ======================================================================*/
 427 
 428 static int copy_erase_unit(partition_t *part, uint16_t srcunit,
 429                            uint16_t xferunit)
 430 {
 431     u_char buf[SECTOR_SIZE];
 432     struct eun_info_t *eun;
 433     struct xfer_info_t *xfer;
 434     uint32_t src, dest, free, i;
 435     uint16_t unit;
 436     int ret;
 437     ssize_t retlen;
 438     loff_t offset;
 439     uint16_t srcunitswap = cpu_to_le16(srcunit);
 440 
 441     eun = &part->EUNInfo[srcunit];
 442     xfer = &part->XferInfo[xferunit];
 443     pr_debug("ftl_cs: copying block 0x%x to 0x%x\n",
 444           eun->Offset, xfer->Offset);
 445 
 446 
 447     /* Read current BAM */
 448     if (part->bam_index != srcunit) {
 449 
 450         offset = eun->Offset + le32_to_cpu(part->header.BAMOffset);
 451 
 452         ret = mtd_read(part->mbd.mtd, offset,
 453                        part->BlocksPerUnit * sizeof(uint32_t), &retlen,
 454                        (u_char *)(part->bam_cache));
 455 
 456         /* mark the cache bad, in case we get an error later */
 457         part->bam_index = 0xffff;
 458 
 459         if (ret) {
 460             printk( KERN_WARNING "ftl: Failed to read BAM cache in copy_erase_unit()!\n");
 461             return ret;
 462         }
 463     }
 464 
 465     /* Write the LogicalEUN for the transfer unit */
 466     xfer->state = XFER_UNKNOWN;
 467     offset = xfer->Offset + 20; /* Bad! */
 468     unit = cpu_to_le16(0x7fff);
 469 
 470     ret = mtd_write(part->mbd.mtd, offset, sizeof(uint16_t), &retlen,
 471                     (u_char *)&unit);
 472 
 473     if (ret) {
 474         printk( KERN_WARNING "ftl: Failed to write back to BAM cache in copy_erase_unit()!\n");
 475         return ret;
 476     }
 477 
 478     /* Copy all data blocks from source unit to transfer unit */
 479     src = eun->Offset; dest = xfer->Offset;
 480 
 481     free = 0;
 482     ret = 0;
 483     for (i = 0; i < part->BlocksPerUnit; i++) {
 484         switch (BLOCK_TYPE(le32_to_cpu(part->bam_cache[i]))) {
 485         case BLOCK_CONTROL:
 486             /* This gets updated later */
 487             break;
 488         case BLOCK_DATA:
 489         case BLOCK_REPLACEMENT:
 490             ret = mtd_read(part->mbd.mtd, src, SECTOR_SIZE, &retlen,
 491                            (u_char *)buf);
 492             if (ret) {
 493                 printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n");
 494                 return ret;
 495             }
 496 
 497 
 498             ret = mtd_write(part->mbd.mtd, dest, SECTOR_SIZE, &retlen,
 499                             (u_char *)buf);
 500             if (ret)  {
 501                 printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n");
 502                 return ret;
 503             }
 504 
 505             break;
 506         default:
 507             /* All other blocks must be free */
 508             part->bam_cache[i] = cpu_to_le32(0xffffffff);
 509             free++;
 510             break;
 511         }
 512         src += SECTOR_SIZE;
 513         dest += SECTOR_SIZE;
 514     }
 515 
 516     /* Write the BAM to the transfer unit */
 517     ret = mtd_write(part->mbd.mtd,
 518                     xfer->Offset + le32_to_cpu(part->header.BAMOffset),
 519                     part->BlocksPerUnit * sizeof(int32_t),
 520                     &retlen,
 521                     (u_char *)part->bam_cache);
 522     if (ret) {
 523         printk( KERN_WARNING "ftl: Error writing BAM in copy_erase_unit\n");
 524         return ret;
 525     }
 526 
 527 
 528     /* All clear? Then update the LogicalEUN again */
 529     ret = mtd_write(part->mbd.mtd, xfer->Offset + 20, sizeof(uint16_t),
 530                     &retlen, (u_char *)&srcunitswap);
 531 
 532     if (ret) {
 533         printk(KERN_WARNING "ftl: Error writing new LogicalEUN in copy_erase_unit\n");
 534         return ret;
 535     }
 536 
 537 
 538     /* Update the maps and usage stats*/
 539     swap(xfer->EraseCount, eun->EraseCount);
 540     swap(xfer->Offset, eun->Offset);
 541     part->FreeTotal -= eun->Free;
 542     part->FreeTotal += free;
 543     eun->Free = free;
 544     eun->Deleted = 0;
 545 
 546     /* Now, the cache should be valid for the new block */
 547     part->bam_index = srcunit;
 548 
 549     return 0;
 550 } /* copy_erase_unit */
 551 
 552 /*======================================================================
 553 
 554     reclaim_block() picks a full erase unit and a transfer unit and
 555     then calls copy_erase_unit() to copy one to the other.  Then, it
 556     schedules an erase on the expired block.
 557 
 558     What's a good way to decide which transfer unit and which erase
 559     unit to use?  Beats me.  My way is to always pick the transfer
 560     unit with the fewest erases, and usually pick the data unit with
 561     the most deleted blocks.  But with a small probability, pick the
 562     oldest data unit instead.  This means that we generally postpone
 563     the next reclamation as long as possible, but shuffle static
 564     stuff around a bit for wear leveling.
 565 
 566 ======================================================================*/
 567 
 568 static int reclaim_block(partition_t *part)
 569 {
 570     uint16_t i, eun, xfer;
 571     uint32_t best;
 572     int queued, ret;
 573 
 574     pr_debug("ftl_cs: reclaiming space...\n");
 575     pr_debug("NumTransferUnits == %x\n", part->header.NumTransferUnits);
 576     /* Pick the least erased transfer unit */
 577     best = 0xffffffff; xfer = 0xffff;
 578     do {
 579         queued = 0;
 580         for (i = 0; i < part->header.NumTransferUnits; i++) {
 581             int n=0;
 582             if (part->XferInfo[i].state == XFER_UNKNOWN) {
 583                 pr_debug("XferInfo[%d].state == XFER_UNKNOWN\n",i);
 584                 n=1;
 585                 erase_xfer(part, i);
 586             }
 587             if (part->XferInfo[i].state == XFER_ERASING) {
 588                 pr_debug("XferInfo[%d].state == XFER_ERASING\n",i);
 589                 n=1;
 590                 queued = 1;
 591             }
 592             else if (part->XferInfo[i].state == XFER_ERASED) {
 593                 pr_debug("XferInfo[%d].state == XFER_ERASED\n",i);
 594                 n=1;
 595                 prepare_xfer(part, i);
 596             }
 597             if (part->XferInfo[i].state == XFER_PREPARED) {
 598                 pr_debug("XferInfo[%d].state == XFER_PREPARED\n",i);
 599                 n=1;
 600                 if (part->XferInfo[i].EraseCount <= best) {
 601                     best = part->XferInfo[i].EraseCount;
 602                     xfer = i;
 603                 }
 604             }
 605                 if (!n)
 606                     pr_debug("XferInfo[%d].state == %x\n",i, part->XferInfo[i].state);
 607 
 608         }
 609         if (xfer == 0xffff) {
 610             if (queued) {
 611                 pr_debug("ftl_cs: waiting for transfer "
 612                       "unit to be prepared...\n");
 613                 mtd_sync(part->mbd.mtd);
 614             } else {
 615                 static int ne = 0;
 616                 if (++ne < 5)
 617                     printk(KERN_NOTICE "ftl_cs: reclaim failed: no "
 618                            "suitable transfer units!\n");
 619                 else
 620                     pr_debug("ftl_cs: reclaim failed: no "
 621                           "suitable transfer units!\n");
 622 
 623                 return -EIO;
 624             }
 625         }
 626     } while (xfer == 0xffff);
 627 
 628     eun = 0;
 629     if ((jiffies % shuffle_freq) == 0) {
 630         pr_debug("ftl_cs: recycling freshest block...\n");
 631         best = 0xffffffff;
 632         for (i = 0; i < part->DataUnits; i++)
 633             if (part->EUNInfo[i].EraseCount <= best) {
 634                 best = part->EUNInfo[i].EraseCount;
 635                 eun = i;
 636             }
 637     } else {
 638         best = 0;
 639         for (i = 0; i < part->DataUnits; i++)
 640             if (part->EUNInfo[i].Deleted >= best) {
 641                 best = part->EUNInfo[i].Deleted;
 642                 eun = i;
 643             }
 644         if (best == 0) {
 645             static int ne = 0;
 646             if (++ne < 5)
 647                 printk(KERN_NOTICE "ftl_cs: reclaim failed: "
 648                        "no free blocks!\n");
 649             else
 650                 pr_debug("ftl_cs: reclaim failed: "
 651                        "no free blocks!\n");
 652 
 653             return -EIO;
 654         }
 655     }
 656     ret = copy_erase_unit(part, eun, xfer);
 657     if (!ret)
 658         erase_xfer(part, xfer);
 659     else
 660         printk(KERN_NOTICE "ftl_cs: copy_erase_unit failed!\n");
 661     return ret;
 662 } /* reclaim_block */
 663 
 664 /*======================================================================
 665 
 666     Find_free() searches for a free block.  If necessary, it updates
 667     the BAM cache for the erase unit containing the free block.  It
 668     returns the block index -- the erase unit is just the currently
 669     cached unit.  If there are no free blocks, it returns 0 -- this
 670     is never a valid data block because it contains the header.
 671 
 672 ======================================================================*/
 673 
 674 #ifdef PSYCHO_DEBUG
 675 static void dump_lists(partition_t *part)
 676 {
 677     int i;
 678     printk(KERN_DEBUG "ftl_cs: Free total = %d\n", part->FreeTotal);
 679     for (i = 0; i < part->DataUnits; i++)
 680         printk(KERN_DEBUG "ftl_cs:   unit %d: %d phys, %d free, "
 681                "%d deleted\n", i,
 682                part->EUNInfo[i].Offset >> part->header.EraseUnitSize,
 683                part->EUNInfo[i].Free, part->EUNInfo[i].Deleted);
 684 }
 685 #endif
 686 
 687 static uint32_t find_free(partition_t *part)
 688 {
 689     uint16_t stop, eun;
 690     uint32_t blk;
 691     size_t retlen;
 692     int ret;
 693 
 694     /* Find an erase unit with some free space */
 695     stop = (part->bam_index == 0xffff) ? 0 : part->bam_index;
 696     eun = stop;
 697     do {
 698         if (part->EUNInfo[eun].Free != 0) break;
 699         /* Wrap around at end of table */
 700         if (++eun == part->DataUnits) eun = 0;
 701     } while (eun != stop);
 702 
 703     if (part->EUNInfo[eun].Free == 0)
 704         return 0;
 705 
 706     /* Is this unit's BAM cached? */
 707     if (eun != part->bam_index) {
 708         /* Invalidate cache */
 709         part->bam_index = 0xffff;
 710 
 711         ret = mtd_read(part->mbd.mtd,
 712                        part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset),
 713                        part->BlocksPerUnit * sizeof(uint32_t),
 714                        &retlen,
 715                        (u_char *)(part->bam_cache));
 716 
 717         if (ret) {
 718             printk(KERN_WARNING"ftl: Error reading BAM in find_free\n");
 719             return 0;
 720         }
 721         part->bam_index = eun;
 722     }
 723 
 724     /* Find a free block */
 725     for (blk = 0; blk < part->BlocksPerUnit; blk++)
 726         if (BLOCK_FREE(le32_to_cpu(part->bam_cache[blk]))) break;
 727     if (blk == part->BlocksPerUnit) {
 728 #ifdef PSYCHO_DEBUG
 729         static int ne = 0;
 730         if (++ne == 1)
 731             dump_lists(part);
 732 #endif
 733         printk(KERN_NOTICE "ftl_cs: bad free list!\n");
 734         return 0;
 735     }
 736     pr_debug("ftl_cs: found free block at %d in %d\n", blk, eun);
 737     return blk;
 738 
 739 } /* find_free */
 740 
 741 
 742 /*======================================================================
 743 
 744     Read a series of sectors from an FTL partition.
 745 
 746 ======================================================================*/
 747 
 748 static int ftl_read(partition_t *part, caddr_t buffer,
 749                     u_long sector, u_long nblocks)
 750 {
 751     uint32_t log_addr, bsize;
 752     u_long i;
 753     int ret;
 754     size_t offset, retlen;
 755 
 756     pr_debug("ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n",
 757           part, sector, nblocks);
 758     if (!(part->state & FTL_FORMATTED)) {
 759         printk(KERN_NOTICE "ftl_cs: bad partition\n");
 760         return -EIO;
 761     }
 762     bsize = 1 << part->header.EraseUnitSize;
 763 
 764     for (i = 0; i < nblocks; i++) {
 765         if (((sector+i) * SECTOR_SIZE) >= le32_to_cpu(part->header.FormattedSize)) {
 766             printk(KERN_NOTICE "ftl_cs: bad read offset\n");
 767             return -EIO;
 768         }
 769         log_addr = part->VirtualBlockMap[sector+i];
 770         if (log_addr == 0xffffffff)
 771             memset(buffer, 0, SECTOR_SIZE);
 772         else {
 773             offset = (part->EUNInfo[log_addr / bsize].Offset
 774                           + (log_addr % bsize));
 775             ret = mtd_read(part->mbd.mtd, offset, SECTOR_SIZE, &retlen,
 776                            (u_char *)buffer);
 777 
 778             if (ret) {
 779                 printk(KERN_WARNING "Error reading MTD device in ftl_read()\n");
 780                 return ret;
 781             }
 782         }
 783         buffer += SECTOR_SIZE;
 784     }
 785     return 0;
 786 } /* ftl_read */
 787 
 788 /*======================================================================
 789 
 790     Write a series of sectors to an FTL partition
 791 
 792 ======================================================================*/
 793 
 794 static int set_bam_entry(partition_t *part, uint32_t log_addr,
 795                          uint32_t virt_addr)
 796 {
 797     uint32_t bsize, blk, le_virt_addr;
 798 #ifdef PSYCHO_DEBUG
 799     uint32_t old_addr;
 800 #endif
 801     uint16_t eun;
 802     int ret;
 803     size_t retlen, offset;
 804 
 805     pr_debug("ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n",
 806           part, log_addr, virt_addr);
 807     bsize = 1 << part->header.EraseUnitSize;
 808     eun = log_addr / bsize;
 809     blk = (log_addr % bsize) / SECTOR_SIZE;
 810     offset = (part->EUNInfo[eun].Offset + blk * sizeof(uint32_t) +
 811                   le32_to_cpu(part->header.BAMOffset));
 812 
 813 #ifdef PSYCHO_DEBUG
 814     ret = mtd_read(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
 815                    (u_char *)&old_addr);
 816     if (ret) {
 817         printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret);
 818         return ret;
 819     }
 820     old_addr = le32_to_cpu(old_addr);
 821 
 822     if (((virt_addr == 0xfffffffe) && !BLOCK_FREE(old_addr)) ||
 823         ((virt_addr == 0) && (BLOCK_TYPE(old_addr) != BLOCK_DATA)) ||
 824         (!BLOCK_DELETED(virt_addr) && (old_addr != 0xfffffffe))) {
 825         static int ne = 0;
 826         if (++ne < 5) {
 827             printk(KERN_NOTICE "ftl_cs: set_bam_entry() inconsistency!\n");
 828             printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, old = 0x%x"
 829                    ", new = 0x%x\n", log_addr, old_addr, virt_addr);
 830         }
 831         return -EIO;
 832     }
 833 #endif
 834     le_virt_addr = cpu_to_le32(virt_addr);
 835     if (part->bam_index == eun) {
 836 #ifdef PSYCHO_DEBUG
 837         if (le32_to_cpu(part->bam_cache[blk]) != old_addr) {
 838             static int ne = 0;
 839             if (++ne < 5) {
 840                 printk(KERN_NOTICE "ftl_cs: set_bam_entry() "
 841                        "inconsistency!\n");
 842                 printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, cache"
 843                        " = 0x%x\n",
 844                        le32_to_cpu(part->bam_cache[blk]), old_addr);
 845             }
 846             return -EIO;
 847         }
 848 #endif
 849         part->bam_cache[blk] = le_virt_addr;
 850     }
 851     ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
 852                     (u_char *)&le_virt_addr);
 853 
 854     if (ret) {
 855         printk(KERN_NOTICE "ftl_cs: set_bam_entry() failed!\n");
 856         printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, new = 0x%x\n",
 857                log_addr, virt_addr);
 858     }
 859     return ret;
 860 } /* set_bam_entry */
 861 
 862 static int ftl_write(partition_t *part, caddr_t buffer,
 863                      u_long sector, u_long nblocks)
 864 {
 865     uint32_t bsize, log_addr, virt_addr, old_addr, blk;
 866     u_long i;
 867     int ret;
 868     size_t retlen, offset;
 869 
 870     pr_debug("ftl_cs: ftl_write(0x%p, %ld, %ld)\n",
 871           part, sector, nblocks);
 872     if (!(part->state & FTL_FORMATTED)) {
 873         printk(KERN_NOTICE "ftl_cs: bad partition\n");
 874         return -EIO;
 875     }
 876     /* See if we need to reclaim space, before we start */
 877     while (part->FreeTotal < nblocks) {
 878         ret = reclaim_block(part);
 879         if (ret)
 880             return ret;
 881     }
 882 
 883     bsize = 1 << part->header.EraseUnitSize;
 884 
 885     virt_addr = sector * SECTOR_SIZE | BLOCK_DATA;
 886     for (i = 0; i < nblocks; i++) {
 887         if (virt_addr >= le32_to_cpu(part->header.FormattedSize)) {
 888             printk(KERN_NOTICE "ftl_cs: bad write offset\n");
 889             return -EIO;
 890         }
 891 
 892         /* Grab a free block */
 893         blk = find_free(part);
 894         if (blk == 0) {
 895             static int ne = 0;
 896             if (++ne < 5)
 897                 printk(KERN_NOTICE "ftl_cs: internal error: "
 898                        "no free blocks!\n");
 899             return -ENOSPC;
 900         }
 901 
 902         /* Tag the BAM entry, and write the new block */
 903         log_addr = part->bam_index * bsize + blk * SECTOR_SIZE;
 904         part->EUNInfo[part->bam_index].Free--;
 905         part->FreeTotal--;
 906         if (set_bam_entry(part, log_addr, 0xfffffffe))
 907             return -EIO;
 908         part->EUNInfo[part->bam_index].Deleted++;
 909         offset = (part->EUNInfo[part->bam_index].Offset +
 910                       blk * SECTOR_SIZE);
 911         ret = mtd_write(part->mbd.mtd, offset, SECTOR_SIZE, &retlen, buffer);
 912 
 913         if (ret) {
 914             printk(KERN_NOTICE "ftl_cs: block write failed!\n");
 915             printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, virt_addr"
 916                    " = 0x%x, Offset = 0x%zx\n", log_addr, virt_addr,
 917                    offset);
 918             return -EIO;
 919         }
 920 
 921         /* Only delete the old entry when the new entry is ready */
 922         old_addr = part->VirtualBlockMap[sector+i];
 923         if (old_addr != 0xffffffff) {
 924             part->VirtualBlockMap[sector+i] = 0xffffffff;
 925             part->EUNInfo[old_addr/bsize].Deleted++;
 926             if (set_bam_entry(part, old_addr, 0))
 927                 return -EIO;
 928         }
 929 
 930         /* Finally, set up the new pointers */
 931         if (set_bam_entry(part, log_addr, virt_addr))
 932             return -EIO;
 933         part->VirtualBlockMap[sector+i] = log_addr;
 934         part->EUNInfo[part->bam_index].Deleted--;
 935 
 936         buffer += SECTOR_SIZE;
 937         virt_addr += SECTOR_SIZE;
 938     }
 939     return 0;
 940 } /* ftl_write */
 941 
 942 static int ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
 943 {
 944         partition_t *part = (void *)dev;
 945         u_long sect;
 946 
 947         /* Sort of arbitrary: round size down to 4KiB boundary */
 948         sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE;
 949 
 950         geo->heads = 1;
 951         geo->sectors = 8;
 952         geo->cylinders = sect >> 3;
 953 
 954         return 0;
 955 }
 956 
 957 static int ftl_readsect(struct mtd_blktrans_dev *dev,
 958                               unsigned long block, char *buf)
 959 {
 960         return ftl_read((void *)dev, buf, block, 1);
 961 }
 962 
 963 static int ftl_writesect(struct mtd_blktrans_dev *dev,
 964                               unsigned long block, char *buf)
 965 {
 966         return ftl_write((void *)dev, buf, block, 1);
 967 }
 968 
 969 static int ftl_discardsect(struct mtd_blktrans_dev *dev,
 970                            unsigned long sector, unsigned nr_sects)
 971 {
 972         partition_t *part = (void *)dev;
 973         uint32_t bsize = 1 << part->header.EraseUnitSize;
 974 
 975         pr_debug("FTL erase sector %ld for %d sectors\n",
 976               sector, nr_sects);
 977 
 978         while (nr_sects) {
 979                 uint32_t old_addr = part->VirtualBlockMap[sector];
 980                 if (old_addr != 0xffffffff) {
 981                         part->VirtualBlockMap[sector] = 0xffffffff;
 982                         part->EUNInfo[old_addr/bsize].Deleted++;
 983                         if (set_bam_entry(part, old_addr, 0))
 984                                 return -EIO;
 985                 }
 986                 nr_sects--;
 987                 sector++;
 988         }
 989 
 990         return 0;
 991 }
 992 /*====================================================================*/
 993 
 994 static void ftl_freepart(partition_t *part)
 995 {
 996         vfree(part->VirtualBlockMap);
 997         part->VirtualBlockMap = NULL;
 998         kfree(part->EUNInfo);
 999         part->EUNInfo = NULL;
1000         kfree(part->XferInfo);
1001         part->XferInfo = NULL;
1002         kfree(part->bam_cache);
1003         part->bam_cache = NULL;
1004 } /* ftl_freepart */
1005 
1006 static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
1007 {
1008         partition_t *partition;
1009 
1010         partition = kzalloc(sizeof(partition_t), GFP_KERNEL);
1011 
1012         if (!partition) {
1013                 printk(KERN_WARNING "No memory to scan for FTL on %s\n",
1014                        mtd->name);
1015                 return;
1016         }
1017 
1018         partition->mbd.mtd = mtd;
1019 
1020         if ((scan_header(partition) == 0) &&
1021             (build_maps(partition) == 0)) {
1022 
1023                 partition->state = FTL_FORMATTED;
1024 #ifdef PCMCIA_DEBUG
1025                 printk(KERN_INFO "ftl_cs: opening %d KiB FTL partition\n",
1026                        le32_to_cpu(partition->header.FormattedSize) >> 10);
1027 #endif
1028                 partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9;
1029 
1030                 partition->mbd.tr = tr;
1031                 partition->mbd.devnum = -1;
1032                 if (!add_mtd_blktrans_dev((void *)partition))
1033                         return;
1034         }
1035 
1036         kfree(partition);
1037 }
1038 
1039 static void ftl_remove_dev(struct mtd_blktrans_dev *dev)
1040 {
1041         del_mtd_blktrans_dev(dev);
1042         ftl_freepart((partition_t *)dev);
1043 }
1044 
1045 static struct mtd_blktrans_ops ftl_tr = {
1046         .name           = "ftl",
1047         .major          = FTL_MAJOR,
1048         .part_bits      = PART_BITS,
1049         .blksize        = SECTOR_SIZE,
1050         .readsect       = ftl_readsect,
1051         .writesect      = ftl_writesect,
1052         .discard        = ftl_discardsect,
1053         .getgeo         = ftl_getgeo,
1054         .add_mtd        = ftl_add_mtd,
1055         .remove_dev     = ftl_remove_dev,
1056         .owner          = THIS_MODULE,
1057 };
1058 
1059 static int __init init_ftl(void)
1060 {
1061         return register_mtd_blktrans(&ftl_tr);
1062 }
1063 
1064 static void __exit cleanup_ftl(void)
1065 {
1066         deregister_mtd_blktrans(&ftl_tr);
1067 }
1068 
1069 module_init(init_ftl);
1070 module_exit(cleanup_ftl);
1071 
1072 
1073 MODULE_LICENSE("Dual MPL/GPL");
1074 MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
1075 MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices");

/* [<][>][^][v][top][bottom][index][help] */