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