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
scan_header(partition_t * part)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
build_maps(partition_t * part)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_objs(struct eun_info_t, part->DataUnits);
205 if (!part->EUNInfo)
206 goto out;
207 for (i = 0; i < part->DataUnits; i++)
208 part->EUNInfo[i].Offset = 0xffffffff;
209 part->XferInfo =
210 kmalloc_objs(struct xfer_info_t, part->header.NumTransferUnits);
211 if (!part->XferInfo)
212 goto out_EUNInfo;
213
214 xvalid = xtrans = 0;
215 for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) {
216 offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN))
217 << part->header.EraseUnitSize);
218 ret = mtd_read(part->mbd.mtd, offset, sizeof(header), &retval,
219 (unsigned char *)&header);
220
221 if (ret)
222 goto out_XferInfo;
223
224 ret = -1;
225 /* Is this a transfer partition? */
226 hdr_ok = (strcmp(header.DataOrgTuple+3, "FTL100") == 0);
227 if (hdr_ok && (le16_to_cpu(header.LogicalEUN) < part->DataUnits) &&
228 (part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset == 0xffffffff)) {
229 part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset = offset;
230 part->EUNInfo[le16_to_cpu(header.LogicalEUN)].EraseCount =
231 le32_to_cpu(header.EraseCount);
232 xvalid++;
233 } else {
234 if (xtrans == part->header.NumTransferUnits) {
235 printk(KERN_NOTICE "ftl_cs: format error: too many "
236 "transfer units!\n");
237 goto out_XferInfo;
238 }
239 if (hdr_ok && (le16_to_cpu(header.LogicalEUN) == 0xffff)) {
240 part->XferInfo[xtrans].state = XFER_PREPARED;
241 part->XferInfo[xtrans].EraseCount = le32_to_cpu(header.EraseCount);
242 } else {
243 part->XferInfo[xtrans].state = XFER_UNKNOWN;
244 /* Pick anything reasonable for the erase count */
245 part->XferInfo[xtrans].EraseCount =
246 le32_to_cpu(part->header.EraseCount);
247 }
248 part->XferInfo[xtrans].Offset = offset;
249 xtrans++;
250 }
251 }
252 /* Check for format trouble */
253 header = part->header;
254 if ((xtrans != header.NumTransferUnits) ||
255 (xvalid+xtrans != le16_to_cpu(header.NumEraseUnits))) {
256 printk(KERN_NOTICE "ftl_cs: format error: erase units "
257 "don't add up!\n");
258 goto out_XferInfo;
259 }
260
261 /* Set up virtual page map */
262 blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize;
263 part->VirtualBlockMap = vmalloc_array(blocks, sizeof(uint32_t));
264 if (!part->VirtualBlockMap)
265 goto out_XferInfo;
266
267 memset(part->VirtualBlockMap, 0xff, blocks * sizeof(uint32_t));
268 part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize;
269
270 part->bam_cache = kmalloc_array(part->BlocksPerUnit, sizeof(uint32_t),
271 GFP_KERNEL);
272 if (!part->bam_cache)
273 goto out_VirtualBlockMap;
274
275 part->bam_index = 0xffff;
276 part->FreeTotal = 0;
277
278 for (i = 0; i < part->DataUnits; i++) {
279 part->EUNInfo[i].Free = 0;
280 part->EUNInfo[i].Deleted = 0;
281 offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset);
282
283 ret = mtd_read(part->mbd.mtd, offset,
284 part->BlocksPerUnit * sizeof(uint32_t), &retval,
285 (unsigned char *)part->bam_cache);
286
287 if (ret)
288 goto out_bam_cache;
289
290 for (j = 0; j < part->BlocksPerUnit; j++) {
291 if (BLOCK_FREE(le32_to_cpu(part->bam_cache[j]))) {
292 part->EUNInfo[i].Free++;
293 part->FreeTotal++;
294 } else if ((BLOCK_TYPE(le32_to_cpu(part->bam_cache[j])) == BLOCK_DATA) &&
295 (BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j])) < blocks))
296 part->VirtualBlockMap[BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j]))] =
297 (i << header.EraseUnitSize) + (j << header.BlockSize);
298 else if (BLOCK_DELETED(le32_to_cpu(part->bam_cache[j])))
299 part->EUNInfo[i].Deleted++;
300 }
301 }
302
303 ret = 0;
304 goto out;
305
306 out_bam_cache:
307 kfree(part->bam_cache);
308 out_VirtualBlockMap:
309 vfree(part->VirtualBlockMap);
310 out_XferInfo:
311 kfree(part->XferInfo);
312 out_EUNInfo:
313 kfree(part->EUNInfo);
314 out:
315 return ret;
316 } /* build_maps */
317
318 /*======================================================================
319
320 Erase_xfer() schedules an asynchronous erase operation for a
321 transfer unit.
322
323 ======================================================================*/
324
erase_xfer(partition_t * part,uint16_t xfernum)325 static int erase_xfer(partition_t *part,
326 uint16_t xfernum)
327 {
328 int ret;
329 struct xfer_info_t *xfer;
330 struct erase_info *erase;
331
332 xfer = &part->XferInfo[xfernum];
333 pr_debug("ftl_cs: erasing xfer unit at 0x%x\n", xfer->Offset);
334 xfer->state = XFER_ERASING;
335
336 /* Is there a free erase slot? Always in MTD. */
337
338
339 erase=kmalloc_obj(struct erase_info);
340 if (!erase)
341 return -ENOMEM;
342
343 erase->addr = xfer->Offset;
344 erase->len = 1ULL << part->header.EraseUnitSize;
345
346 ret = mtd_erase(part->mbd.mtd, erase);
347 if (!ret) {
348 xfer->state = XFER_ERASED;
349 xfer->EraseCount++;
350 } else {
351 xfer->state = XFER_FAILED;
352 pr_notice("ftl_cs: erase failed: err = %d\n", ret);
353 }
354
355 kfree(erase);
356
357 return ret;
358 } /* erase_xfer */
359
360 /*======================================================================
361
362 Prepare_xfer() takes a freshly erased transfer unit and gives
363 it an appropriate header.
364
365 ======================================================================*/
366
prepare_xfer(partition_t * part,int i)367 static int prepare_xfer(partition_t *part, int i)
368 {
369 erase_unit_header_t header;
370 struct xfer_info_t *xfer;
371 int nbam, ret;
372 uint32_t ctl;
373 ssize_t retlen;
374 loff_t offset;
375
376 xfer = &part->XferInfo[i];
377 xfer->state = XFER_FAILED;
378
379 pr_debug("ftl_cs: preparing xfer unit at 0x%x\n", xfer->Offset);
380
381 /* Write the transfer unit header */
382 header = part->header;
383 header.LogicalEUN = cpu_to_le16(0xffff);
384 header.EraseCount = cpu_to_le32(xfer->EraseCount);
385
386 ret = mtd_write(part->mbd.mtd, xfer->Offset, sizeof(header), &retlen,
387 (u_char *)&header);
388
389 if (ret) {
390 return ret;
391 }
392
393 /* Write the BAM stub */
394 nbam = DIV_ROUND_UP(part->BlocksPerUnit * sizeof(uint32_t) +
395 le32_to_cpu(part->header.BAMOffset), SECTOR_SIZE);
396
397 offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset);
398 ctl = cpu_to_le32(BLOCK_CONTROL);
399
400 for (i = 0; i < nbam; i++, offset += sizeof(uint32_t)) {
401
402 ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
403 (u_char *)&ctl);
404
405 if (ret)
406 return ret;
407 }
408 xfer->state = XFER_PREPARED;
409 return 0;
410
411 } /* prepare_xfer */
412
413 /*======================================================================
414
415 Copy_erase_unit() takes a full erase block and a transfer unit,
416 copies everything to the transfer unit, then swaps the block
417 pointers.
418
419 All data blocks are copied to the corresponding blocks in the
420 target unit, so the virtual block map does not need to be
421 updated.
422
423 ======================================================================*/
424
copy_erase_unit(partition_t * part,uint16_t srcunit,uint16_t xferunit)425 static int copy_erase_unit(partition_t *part, uint16_t srcunit,
426 uint16_t xferunit)
427 {
428 u_char buf[SECTOR_SIZE];
429 struct eun_info_t *eun;
430 struct xfer_info_t *xfer;
431 uint32_t src, dest, free, i;
432 uint16_t unit;
433 int ret;
434 ssize_t retlen;
435 loff_t offset;
436 uint16_t srcunitswap = cpu_to_le16(srcunit);
437
438 eun = &part->EUNInfo[srcunit];
439 xfer = &part->XferInfo[xferunit];
440 pr_debug("ftl_cs: copying block 0x%x to 0x%x\n",
441 eun->Offset, xfer->Offset);
442
443
444 /* Read current BAM */
445 if (part->bam_index != srcunit) {
446
447 offset = eun->Offset + le32_to_cpu(part->header.BAMOffset);
448
449 ret = mtd_read(part->mbd.mtd, offset,
450 part->BlocksPerUnit * sizeof(uint32_t), &retlen,
451 (u_char *)(part->bam_cache));
452
453 /* mark the cache bad, in case we get an error later */
454 part->bam_index = 0xffff;
455
456 if (ret) {
457 printk( KERN_WARNING "ftl: Failed to read BAM cache in copy_erase_unit()!\n");
458 return ret;
459 }
460 }
461
462 /* Write the LogicalEUN for the transfer unit */
463 xfer->state = XFER_UNKNOWN;
464 offset = xfer->Offset + 20; /* Bad! */
465 unit = cpu_to_le16(0x7fff);
466
467 ret = mtd_write(part->mbd.mtd, offset, sizeof(uint16_t), &retlen,
468 (u_char *)&unit);
469
470 if (ret) {
471 printk( KERN_WARNING "ftl: Failed to write back to BAM cache in copy_erase_unit()!\n");
472 return ret;
473 }
474
475 /* Copy all data blocks from source unit to transfer unit */
476 src = eun->Offset; dest = xfer->Offset;
477
478 free = 0;
479 ret = 0;
480 for (i = 0; i < part->BlocksPerUnit; i++) {
481 switch (BLOCK_TYPE(le32_to_cpu(part->bam_cache[i]))) {
482 case BLOCK_CONTROL:
483 /* This gets updated later */
484 break;
485 case BLOCK_DATA:
486 case BLOCK_REPLACEMENT:
487 ret = mtd_read(part->mbd.mtd, src, SECTOR_SIZE, &retlen,
488 (u_char *)buf);
489 if (ret) {
490 printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n");
491 return ret;
492 }
493
494
495 ret = mtd_write(part->mbd.mtd, dest, SECTOR_SIZE, &retlen,
496 (u_char *)buf);
497 if (ret) {
498 printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n");
499 return ret;
500 }
501
502 break;
503 default:
504 /* All other blocks must be free */
505 part->bam_cache[i] = cpu_to_le32(0xffffffff);
506 free++;
507 break;
508 }
509 src += SECTOR_SIZE;
510 dest += SECTOR_SIZE;
511 }
512
513 /* Write the BAM to the transfer unit */
514 ret = mtd_write(part->mbd.mtd,
515 xfer->Offset + le32_to_cpu(part->header.BAMOffset),
516 part->BlocksPerUnit * sizeof(int32_t),
517 &retlen,
518 (u_char *)part->bam_cache);
519 if (ret) {
520 printk( KERN_WARNING "ftl: Error writing BAM in copy_erase_unit\n");
521 return ret;
522 }
523
524
525 /* All clear? Then update the LogicalEUN again */
526 ret = mtd_write(part->mbd.mtd, xfer->Offset + 20, sizeof(uint16_t),
527 &retlen, (u_char *)&srcunitswap);
528
529 if (ret) {
530 printk(KERN_WARNING "ftl: Error writing new LogicalEUN in copy_erase_unit\n");
531 return ret;
532 }
533
534
535 /* Update the maps and usage stats*/
536 swap(xfer->EraseCount, eun->EraseCount);
537 swap(xfer->Offset, eun->Offset);
538 part->FreeTotal -= eun->Free;
539 part->FreeTotal += free;
540 eun->Free = free;
541 eun->Deleted = 0;
542
543 /* Now, the cache should be valid for the new block */
544 part->bam_index = srcunit;
545
546 return 0;
547 } /* copy_erase_unit */
548
549 /*======================================================================
550
551 reclaim_block() picks a full erase unit and a transfer unit and
552 then calls copy_erase_unit() to copy one to the other. Then, it
553 schedules an erase on the expired block.
554
555 What's a good way to decide which transfer unit and which erase
556 unit to use? Beats me. My way is to always pick the transfer
557 unit with the fewest erases, and usually pick the data unit with
558 the most deleted blocks. But with a small probability, pick the
559 oldest data unit instead. This means that we generally postpone
560 the next reclamation as long as possible, but shuffle static
561 stuff around a bit for wear leveling.
562
563 ======================================================================*/
564
reclaim_block(partition_t * part)565 static int reclaim_block(partition_t *part)
566 {
567 uint16_t i, eun, xfer;
568 uint32_t best;
569 int queued, ret;
570
571 pr_debug("ftl_cs: reclaiming space...\n");
572 pr_debug("NumTransferUnits == %x\n", part->header.NumTransferUnits);
573 /* Pick the least erased transfer unit */
574 best = 0xffffffff; xfer = 0xffff;
575 do {
576 queued = 0;
577 for (i = 0; i < part->header.NumTransferUnits; i++) {
578 int n=0;
579 if (part->XferInfo[i].state == XFER_UNKNOWN) {
580 pr_debug("XferInfo[%d].state == XFER_UNKNOWN\n",i);
581 n=1;
582 erase_xfer(part, i);
583 }
584 if (part->XferInfo[i].state == XFER_ERASING) {
585 pr_debug("XferInfo[%d].state == XFER_ERASING\n",i);
586 n=1;
587 queued = 1;
588 }
589 else if (part->XferInfo[i].state == XFER_ERASED) {
590 pr_debug("XferInfo[%d].state == XFER_ERASED\n",i);
591 n=1;
592 prepare_xfer(part, i);
593 }
594 if (part->XferInfo[i].state == XFER_PREPARED) {
595 pr_debug("XferInfo[%d].state == XFER_PREPARED\n",i);
596 n=1;
597 if (part->XferInfo[i].EraseCount <= best) {
598 best = part->XferInfo[i].EraseCount;
599 xfer = i;
600 }
601 }
602 if (!n)
603 pr_debug("XferInfo[%d].state == %x\n",i, part->XferInfo[i].state);
604
605 }
606 if (xfer == 0xffff) {
607 if (queued) {
608 pr_debug("ftl_cs: waiting for transfer "
609 "unit to be prepared...\n");
610 mtd_sync(part->mbd.mtd);
611 } else {
612 static int ne = 0;
613 if (++ne < 5)
614 printk(KERN_NOTICE "ftl_cs: reclaim failed: no "
615 "suitable transfer units!\n");
616 else
617 pr_debug("ftl_cs: reclaim failed: no "
618 "suitable transfer units!\n");
619
620 return -EIO;
621 }
622 }
623 } while (xfer == 0xffff);
624
625 eun = 0;
626 if ((jiffies % shuffle_freq) == 0) {
627 pr_debug("ftl_cs: recycling freshest block...\n");
628 best = 0xffffffff;
629 for (i = 0; i < part->DataUnits; i++)
630 if (part->EUNInfo[i].EraseCount <= best) {
631 best = part->EUNInfo[i].EraseCount;
632 eun = i;
633 }
634 } else {
635 best = 0;
636 for (i = 0; i < part->DataUnits; i++)
637 if (part->EUNInfo[i].Deleted >= best) {
638 best = part->EUNInfo[i].Deleted;
639 eun = i;
640 }
641 if (best == 0) {
642 static int ne = 0;
643 if (++ne < 5)
644 printk(KERN_NOTICE "ftl_cs: reclaim failed: "
645 "no free blocks!\n");
646 else
647 pr_debug("ftl_cs: reclaim failed: "
648 "no free blocks!\n");
649
650 return -EIO;
651 }
652 }
653 ret = copy_erase_unit(part, eun, xfer);
654 if (!ret)
655 erase_xfer(part, xfer);
656 else
657 printk(KERN_NOTICE "ftl_cs: copy_erase_unit failed!\n");
658 return ret;
659 } /* reclaim_block */
660
661 /*======================================================================
662
663 Find_free() searches for a free block. If necessary, it updates
664 the BAM cache for the erase unit containing the free block. It
665 returns the block index -- the erase unit is just the currently
666 cached unit. If there are no free blocks, it returns 0 -- this
667 is never a valid data block because it contains the header.
668
669 ======================================================================*/
670
671 #ifdef PSYCHO_DEBUG
dump_lists(partition_t * part)672 static void dump_lists(partition_t *part)
673 {
674 int i;
675 printk(KERN_DEBUG "ftl_cs: Free total = %d\n", part->FreeTotal);
676 for (i = 0; i < part->DataUnits; i++)
677 printk(KERN_DEBUG "ftl_cs: unit %d: %d phys, %d free, "
678 "%d deleted\n", i,
679 part->EUNInfo[i].Offset >> part->header.EraseUnitSize,
680 part->EUNInfo[i].Free, part->EUNInfo[i].Deleted);
681 }
682 #endif
683
find_free(partition_t * part)684 static uint32_t find_free(partition_t *part)
685 {
686 uint16_t stop, eun;
687 uint32_t blk;
688 size_t retlen;
689 int ret;
690
691 /* Find an erase unit with some free space */
692 stop = (part->bam_index == 0xffff) ? 0 : part->bam_index;
693 eun = stop;
694 do {
695 if (part->EUNInfo[eun].Free != 0) break;
696 /* Wrap around at end of table */
697 if (++eun == part->DataUnits) eun = 0;
698 } while (eun != stop);
699
700 if (part->EUNInfo[eun].Free == 0)
701 return 0;
702
703 /* Is this unit's BAM cached? */
704 if (eun != part->bam_index) {
705 /* Invalidate cache */
706 part->bam_index = 0xffff;
707
708 ret = mtd_read(part->mbd.mtd,
709 part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset),
710 part->BlocksPerUnit * sizeof(uint32_t),
711 &retlen,
712 (u_char *)(part->bam_cache));
713
714 if (ret) {
715 printk(KERN_WARNING"ftl: Error reading BAM in find_free\n");
716 return 0;
717 }
718 part->bam_index = eun;
719 }
720
721 /* Find a free block */
722 for (blk = 0; blk < part->BlocksPerUnit; blk++)
723 if (BLOCK_FREE(le32_to_cpu(part->bam_cache[blk]))) break;
724 if (blk == part->BlocksPerUnit) {
725 #ifdef PSYCHO_DEBUG
726 static int ne = 0;
727 if (++ne == 1)
728 dump_lists(part);
729 #endif
730 printk(KERN_NOTICE "ftl_cs: bad free list!\n");
731 return 0;
732 }
733 pr_debug("ftl_cs: found free block at %d in %d\n", blk, eun);
734 return blk;
735
736 } /* find_free */
737
738
739 /*======================================================================
740
741 Read a series of sectors from an FTL partition.
742
743 ======================================================================*/
744
ftl_read(partition_t * part,caddr_t buffer,u_long sector,u_long nblocks)745 static int ftl_read(partition_t *part, caddr_t buffer,
746 u_long sector, u_long nblocks)
747 {
748 uint32_t log_addr, bsize;
749 u_long i;
750 int ret;
751 size_t offset, retlen;
752
753 pr_debug("ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n",
754 part, sector, nblocks);
755 if (!(part->state & FTL_FORMATTED)) {
756 printk(KERN_NOTICE "ftl_cs: bad partition\n");
757 return -EIO;
758 }
759 bsize = 1 << part->header.EraseUnitSize;
760
761 for (i = 0; i < nblocks; i++) {
762 if (((sector+i) * SECTOR_SIZE) >= le32_to_cpu(part->header.FormattedSize)) {
763 printk(KERN_NOTICE "ftl_cs: bad read offset\n");
764 return -EIO;
765 }
766 log_addr = part->VirtualBlockMap[sector+i];
767 if (log_addr == 0xffffffff)
768 memset(buffer, 0, SECTOR_SIZE);
769 else {
770 offset = (part->EUNInfo[log_addr / bsize].Offset
771 + (log_addr % bsize));
772 ret = mtd_read(part->mbd.mtd, offset, SECTOR_SIZE, &retlen,
773 (u_char *)buffer);
774
775 if (ret) {
776 printk(KERN_WARNING "Error reading MTD device in ftl_read()\n");
777 return ret;
778 }
779 }
780 buffer += SECTOR_SIZE;
781 }
782 return 0;
783 } /* ftl_read */
784
785 /*======================================================================
786
787 Write a series of sectors to an FTL partition
788
789 ======================================================================*/
790
set_bam_entry(partition_t * part,uint32_t log_addr,uint32_t virt_addr)791 static int set_bam_entry(partition_t *part, uint32_t log_addr,
792 uint32_t virt_addr)
793 {
794 uint32_t bsize, blk, le_virt_addr;
795 #ifdef PSYCHO_DEBUG
796 uint32_t old_addr;
797 #endif
798 uint16_t eun;
799 int ret;
800 size_t retlen, offset;
801
802 pr_debug("ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n",
803 part, log_addr, virt_addr);
804 bsize = 1 << part->header.EraseUnitSize;
805 eun = log_addr / bsize;
806 blk = (log_addr % bsize) / SECTOR_SIZE;
807 offset = (part->EUNInfo[eun].Offset + blk * sizeof(uint32_t) +
808 le32_to_cpu(part->header.BAMOffset));
809
810 #ifdef PSYCHO_DEBUG
811 ret = mtd_read(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
812 (u_char *)&old_addr);
813 if (ret) {
814 printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret);
815 return ret;
816 }
817 old_addr = le32_to_cpu(old_addr);
818
819 if (((virt_addr == 0xfffffffe) && !BLOCK_FREE(old_addr)) ||
820 ((virt_addr == 0) && (BLOCK_TYPE(old_addr) != BLOCK_DATA)) ||
821 (!BLOCK_DELETED(virt_addr) && (old_addr != 0xfffffffe))) {
822 static int ne = 0;
823 if (++ne < 5) {
824 printk(KERN_NOTICE "ftl_cs: set_bam_entry() inconsistency!\n");
825 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, old = 0x%x"
826 ", new = 0x%x\n", log_addr, old_addr, virt_addr);
827 }
828 return -EIO;
829 }
830 #endif
831 le_virt_addr = cpu_to_le32(virt_addr);
832 if (part->bam_index == eun) {
833 #ifdef PSYCHO_DEBUG
834 if (le32_to_cpu(part->bam_cache[blk]) != old_addr) {
835 static int ne = 0;
836 if (++ne < 5) {
837 printk(KERN_NOTICE "ftl_cs: set_bam_entry() "
838 "inconsistency!\n");
839 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, cache"
840 " = 0x%x\n",
841 le32_to_cpu(part->bam_cache[blk]), old_addr);
842 }
843 return -EIO;
844 }
845 #endif
846 part->bam_cache[blk] = le_virt_addr;
847 }
848 ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
849 (u_char *)&le_virt_addr);
850
851 if (ret) {
852 printk(KERN_NOTICE "ftl_cs: set_bam_entry() failed!\n");
853 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, new = 0x%x\n",
854 log_addr, virt_addr);
855 }
856 return ret;
857 } /* set_bam_entry */
858
ftl_write(partition_t * part,caddr_t buffer,u_long sector,u_long nblocks)859 static int ftl_write(partition_t *part, caddr_t buffer,
860 u_long sector, u_long nblocks)
861 {
862 uint32_t bsize, log_addr, virt_addr, old_addr, blk;
863 u_long i;
864 int ret;
865 size_t retlen, offset;
866
867 pr_debug("ftl_cs: ftl_write(0x%p, %ld, %ld)\n",
868 part, sector, nblocks);
869 if (!(part->state & FTL_FORMATTED)) {
870 printk(KERN_NOTICE "ftl_cs: bad partition\n");
871 return -EIO;
872 }
873 /* See if we need to reclaim space, before we start */
874 while (part->FreeTotal < nblocks) {
875 ret = reclaim_block(part);
876 if (ret)
877 return ret;
878 }
879
880 bsize = 1 << part->header.EraseUnitSize;
881
882 virt_addr = sector * SECTOR_SIZE | BLOCK_DATA;
883 for (i = 0; i < nblocks; i++) {
884 if (virt_addr >= le32_to_cpu(part->header.FormattedSize)) {
885 printk(KERN_NOTICE "ftl_cs: bad write offset\n");
886 return -EIO;
887 }
888
889 /* Grab a free block */
890 blk = find_free(part);
891 if (blk == 0) {
892 static int ne = 0;
893 if (++ne < 5)
894 printk(KERN_NOTICE "ftl_cs: internal error: "
895 "no free blocks!\n");
896 return -ENOSPC;
897 }
898
899 /* Tag the BAM entry, and write the new block */
900 log_addr = part->bam_index * bsize + blk * SECTOR_SIZE;
901 part->EUNInfo[part->bam_index].Free--;
902 part->FreeTotal--;
903 if (set_bam_entry(part, log_addr, 0xfffffffe))
904 return -EIO;
905 part->EUNInfo[part->bam_index].Deleted++;
906 offset = (part->EUNInfo[part->bam_index].Offset +
907 blk * SECTOR_SIZE);
908 ret = mtd_write(part->mbd.mtd, offset, SECTOR_SIZE, &retlen, buffer);
909
910 if (ret) {
911 printk(KERN_NOTICE "ftl_cs: block write failed!\n");
912 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, virt_addr"
913 " = 0x%x, Offset = 0x%zx\n", log_addr, virt_addr,
914 offset);
915 return -EIO;
916 }
917
918 /* Only delete the old entry when the new entry is ready */
919 old_addr = part->VirtualBlockMap[sector+i];
920 if (old_addr != 0xffffffff) {
921 part->VirtualBlockMap[sector+i] = 0xffffffff;
922 part->EUNInfo[old_addr/bsize].Deleted++;
923 if (set_bam_entry(part, old_addr, 0))
924 return -EIO;
925 }
926
927 /* Finally, set up the new pointers */
928 if (set_bam_entry(part, log_addr, virt_addr))
929 return -EIO;
930 part->VirtualBlockMap[sector+i] = log_addr;
931 part->EUNInfo[part->bam_index].Deleted--;
932
933 buffer += SECTOR_SIZE;
934 virt_addr += SECTOR_SIZE;
935 }
936 return 0;
937 } /* ftl_write */
938
ftl_getgeo(struct mtd_blktrans_dev * dev,struct hd_geometry * geo)939 static int ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
940 {
941 partition_t *part = container_of(dev, struct partition_t, mbd);
942 u_long sect;
943
944 /* Sort of arbitrary: round size down to 4KiB boundary */
945 sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE;
946
947 geo->heads = 1;
948 geo->sectors = 8;
949 geo->cylinders = sect >> 3;
950
951 return 0;
952 }
953
ftl_readsect(struct mtd_blktrans_dev * dev,unsigned long block,char * buf)954 static int ftl_readsect(struct mtd_blktrans_dev *dev,
955 unsigned long block, char *buf)
956 {
957 return ftl_read((void *)dev, buf, block, 1);
958 }
959
ftl_writesect(struct mtd_blktrans_dev * dev,unsigned long block,char * buf)960 static int ftl_writesect(struct mtd_blktrans_dev *dev,
961 unsigned long block, char *buf)
962 {
963 return ftl_write((void *)dev, buf, block, 1);
964 }
965
ftl_discardsect(struct mtd_blktrans_dev * dev,unsigned long sector,unsigned nr_sects)966 static int ftl_discardsect(struct mtd_blktrans_dev *dev,
967 unsigned long sector, unsigned nr_sects)
968 {
969 partition_t *part = container_of(dev, struct partition_t, mbd);
970 uint32_t bsize = 1 << part->header.EraseUnitSize;
971
972 pr_debug("FTL erase sector %ld for %d sectors\n",
973 sector, nr_sects);
974
975 while (nr_sects) {
976 uint32_t old_addr = part->VirtualBlockMap[sector];
977 if (old_addr != 0xffffffff) {
978 part->VirtualBlockMap[sector] = 0xffffffff;
979 part->EUNInfo[old_addr/bsize].Deleted++;
980 if (set_bam_entry(part, old_addr, 0))
981 return -EIO;
982 }
983 nr_sects--;
984 sector++;
985 }
986
987 return 0;
988 }
989 /*====================================================================*/
990
ftl_freepart(partition_t * part)991 static void ftl_freepart(partition_t *part)
992 {
993 vfree(part->VirtualBlockMap);
994 part->VirtualBlockMap = NULL;
995 kfree(part->EUNInfo);
996 part->EUNInfo = NULL;
997 kfree(part->XferInfo);
998 part->XferInfo = NULL;
999 kfree(part->bam_cache);
1000 part->bam_cache = NULL;
1001 } /* ftl_freepart */
1002
ftl_add_mtd(struct mtd_blktrans_ops * tr,struct mtd_info * mtd)1003 static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
1004 {
1005 partition_t *partition;
1006
1007 partition = kzalloc_obj(partition_t);
1008
1009 if (!partition) {
1010 printk(KERN_WARNING "No memory to scan for FTL on %s\n",
1011 mtd->name);
1012 return;
1013 }
1014
1015 partition->mbd.mtd = mtd;
1016
1017 if ((scan_header(partition) == 0) &&
1018 (build_maps(partition) == 0)) {
1019
1020 partition->state = FTL_FORMATTED;
1021 #ifdef PCMCIA_DEBUG
1022 printk(KERN_INFO "ftl_cs: opening %d KiB FTL partition\n",
1023 le32_to_cpu(partition->header.FormattedSize) >> 10);
1024 #endif
1025 partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9;
1026
1027 partition->mbd.tr = tr;
1028 partition->mbd.devnum = -1;
1029 if (!add_mtd_blktrans_dev(&partition->mbd))
1030 return;
1031 }
1032
1033 kfree(partition);
1034 }
1035
ftl_remove_dev(struct mtd_blktrans_dev * dev)1036 static void ftl_remove_dev(struct mtd_blktrans_dev *dev)
1037 {
1038 del_mtd_blktrans_dev(dev);
1039 ftl_freepart((partition_t *)dev);
1040 }
1041
1042 static struct mtd_blktrans_ops ftl_tr = {
1043 .name = "ftl",
1044 .major = FTL_MAJOR,
1045 .part_bits = PART_BITS,
1046 .blksize = SECTOR_SIZE,
1047 .readsect = ftl_readsect,
1048 .writesect = ftl_writesect,
1049 .discard = ftl_discardsect,
1050 .getgeo = ftl_getgeo,
1051 .add_mtd = ftl_add_mtd,
1052 .remove_dev = ftl_remove_dev,
1053 .owner = THIS_MODULE,
1054 };
1055
1056 module_mtd_blktrans(ftl_tr);
1057
1058 MODULE_LICENSE("Dual MPL/GPL");
1059 MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
1060 MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices");
1061