xref: /linux/drivers/mtd/ftl.c (revision 32a92f8c89326985e05dce8b22d3f0aa07a3e1bd)
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