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