cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ftl.c (31140B)


      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' */
     80static int shuffle_freq = 50;
     81module_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 */
    110typedef 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
    151static 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
    192static 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
    309out_bam_cache:
    310    kfree(part->bam_cache);
    311out_VirtualBlockMap:
    312    vfree(part->VirtualBlockMap);
    313out_XferInfo:
    314    kfree(part->XferInfo);
    315out_EUNInfo:
    316    kfree(part->EUNInfo);
    317out:
    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
    328static 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
    370static 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
    428static 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
    568static 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
    675static 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
    687static 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
    748static 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
    794static 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
    862static 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
    942static 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
    957static 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
    963static 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
    969static 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
    994static 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
   1006static 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(&partition->mbd))
   1033			return;
   1034	}
   1035
   1036	kfree(partition);
   1037}
   1038
   1039static void ftl_remove_dev(struct mtd_blktrans_dev *dev)
   1040{
   1041	del_mtd_blktrans_dev(dev);
   1042	ftl_freepart((partition_t *)dev);
   1043}
   1044
   1045static 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
   1059module_mtd_blktrans(ftl_tr);
   1060
   1061MODULE_LICENSE("Dual MPL/GPL");
   1062MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
   1063MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices");