source: rtems/cpukit/libblock/src/flashdisk.c @ b3142bd

4.10
Last change on this file since b3142bd was b3142bd, checked in by Sebastian Huber <sebastian.huber@…>, on 03/14/12 at 07:45:53

PR2040: libblock: Avoid erased blocks starvation

The compaction process needs erased blocks. It is only possible to
erase an entire segment. Thus in order to make a progress we always
need enough erased blocks to empty a used or available segment which can
be erased in turn. A (possibly the worst case) lower bound of erased
blocks is the block count of the largest segment. The number of
unavailable blocks specified by the configuration will be used to
determine the erase blocks starvation situation. The number of
unavailable blocks must be greater than or equal to the number of blocks
in the largest segment.

  • Property mode set to 100644
File size: 75.7 KB
Line 
1/*
2 * flashdisk.c -- Flash disk block device implementation
3 *
4 * Copyright (C) 2007 Chris Johns
5 *
6 * The license and distribution terms for this file may be
7 * found in the file LICENSE in this distribution or at
8 * http://www.rtems.com/license/LICENSE.
9 *
10 * $Id$
11 */
12/**
13 * @file
14 *
15 * Flash disk driver for RTEMS provides support for block based
16 * file systems on flash devices. The driver is not a flash file
17 * system nor does it try to compete with flash file systems. It
18 * currently does not journal how-ever block sequence numbering
19 * could be added to allow recovery of a past positions if
20 * a power down occurred while being updated.
21 *
22 * This flash driver provides block device support for most flash
23 * devices. The driver has been tested on NOR type devices such
24 * as the AMLV160 or M28W160. Support for NAND type devices may
25 * require driver changes to allow speedy recover of the block
26 * mapping data and to also handle the current use of word programming.
27 * Currently the page descriptors are stored in the first few pages
28 * of each segment.
29 *
30 * The driver supports devices, segments and pages. You provide
31 * to the driver the device descriptions as a table of device
32 * descriptors. Each device descriptor contain a table of
33 * segment descriptions or segment descriptors. The driver uses
34 * this information to manage the devices.
35 *
36 * A device is made up of segments. These are also called
37 * sectors or blocks. It is the smallest erasable part of a device.
38 * A device can have differing size segments at different
39 * offsets in the device. The segment descriptors support repeating
40 * segments that are continous in the device. The driver breaks the
41 * segments up into pages. The first pages of a segment contain
42 * the page descriptors. A page descriptor hold the page flags,
43 * a CRC for the page of data and the block number the page
44 * holds. The block can appear in any order in the devices. A
45 * page is active if it hold a current block of data. If the
46 * used bit is set the page is counted as used. A page moves
47 * from erased to active to used then back to erased. If a block
48 * is written that is already in a page, the block is written to
49 * a new page the old page is flagged as used.
50 *
51 * At initialisation time each segment's page descriptors are
52 * read into memory and scanned to determine the active pages,
53 * the used pages and the bad pages. If a segment has any erased
54 * pages it is queue on the available queue. If the segment has
55 * no erased pages it is queue on the used queue.
56 *
57 * The available queue is sorted from the least number available
58 * to the most number of available pages. A segment that has just
59 * been erased will placed at the end of the queue. A segment that
60 * has only a few available pages will be used sooner and once
61 * there are no available pages it is queued on the used queue.
62 * The used queue hold segments that have no available pages and
63 * is sorted from the least number of active pages to the most
64 * number of active pages.
65 *
66 * The driver is required to compact segments. Compacting takes
67 * the segment with the most number of available pages from the
68 * available queue then takes segments with the least number of
69 * active pages from the used queue until it has enough pages
70 * to fill the empty segment. As the active pages are moved
71 * they flagged as used and once the segment has only used pages
72 * it is erased.
73 *
74 * A flash block driver like this never knows if a page is not
75 * being used by the file-system. A typical file system is not
76 * design with the idea of erasing a block on a disk once it is
77 * not being used. The file-system will normally use a flag
78 * or a location as a marker to say that part of the disk is
79 * no longer in use. This means a number of blocks could be
80 * held in active pages but are no in use by the file system.
81 * The file system may also read blocks that have never been
82 * written to disk. This complicates the driver and may make
83 * the wear, usage and erase patterns harsher than a flash
84 * file system. The driver may also suffer from problems if
85 * power is lost.
86 *
87 * @note
88 *
89 * The use of pages can vary. The rtems_fdisk_seg_*_page set
90 * routines use an absolute page number relative to the segment
91 * while all other page numbera are relative to the number of
92 * page descriptor pages a segment has. You need to add the
93 * number of page descriptor pages (pages_desc) to the page number
94 * when call the rtems_fdisk_seg_*_page functions.
95 *
96 * You must always show the page number as relative in any trace
97 * or error message as device-segment-page and if you have to
98 * the page number as absolute use device-segment~page. This
99 * can be seen in the page copy routine.
100 *
101 * The code is like this to avoid needing the pass the pages_desc
102 * value around. It is only used in selected places and so the
103 * extra parameter was avoided.
104 */
105
106#if HAVE_CONFIG_H
107#include "config.h"
108#endif
109
110#include <rtems.h>
111#include <rtems/libio.h>
112#include <errno.h>
113#include <stdlib.h>
114#include <stdio.h>
115#include <string.h>
116#include <inttypes.h>
117
118#include "rtems/blkdev.h"
119#include "rtems/diskdevs.h"
120#include "rtems/flashdisk.h"
121
122/**
123 * Control tracing. It can be compiled out of the code for small
124 * footprint targets. Leave in by default.
125 */
126#if !defined (RTEMS_FDISK_TRACE)
127#define RTEMS_FDISK_TRACE 1
128#endif
129
130/**
131 * The start of a segment has a segment control table. This hold the CRC and
132 * block number for the page.
133 *
134 * @todo A sequence number for the block could be added. This would
135 *       mean a larger descriptor size. Need to make the sequence
136 *       large like 20+ bits so a large file system would not have
137 *       more blocks available than the sequence number.
138 */
139typedef struct rtems_fdisk_page_desc
140{
141  uint16_t crc;       /**< The page's checksum. */
142  uint16_t flags;     /**< The flags for the page. */
143  uint32_t block;     /**< The block number. */
144} rtems_fdisk_page_desc;
145
146/**
147 * Flag the page as active.
148 */
149#define RTEMS_FDISK_PAGE_ACTIVE (1 << 0)
150
151/**
152 * Flag the page as used.
153 */
154#define RTEMS_FDISK_PAGE_USED (1 << 1)
155
156/**
157 * Flash Segment Control holds the pointer to the segment, number of
158 * pages, various page stats and the memory copy of the page descriptors.
159 */
160typedef struct rtems_fdisk_segment_ctl
161{
162  /**
163   * Segments with available pages are maintained as a linked list.
164   */
165  struct rtems_fdisk_segment_ctl* next;
166
167  /**
168   * The descriptor provided by the low-level driver.
169   */
170  const rtems_fdisk_segment_desc* descriptor;
171
172  /**
173   * The device this segment resides on.
174   */
175  uint32_t device;
176
177  /**
178   * The segment in the device. This must be within the
179   * segment descriptor.
180   */
181  uint32_t segment;
182
183  /**
184   * The in-memory ocpy of the page descriptors found at
185   * the start of the segment in the flash device.
186   */
187  rtems_fdisk_page_desc* page_descriptors;
188
189  /*
190   * Page stats.
191   *
192   * A bad page does not checksum or is not erased or has invalid flags.
193   */
194  uint32_t pages;         /**< Total number of pages in the segment. */
195  uint32_t pages_desc;    /**< Number of pages used for page descriptors. */
196  uint32_t pages_active;  /**< Number of pages flagged as active. */
197  uint32_t pages_used;    /**< Number of pages flagged as used. */
198  uint32_t pages_bad;     /**< Number of pages detected as bad. */
199
200  uint32_t failed;        /**< The segment has failed. */
201
202  uint32_t erased;        /**< Counter to debugging. Wear support would
203                               remove this. */
204} rtems_fdisk_segment_ctl;
205
206/**
207 * Segment control table queue.
208 */
209typedef struct rtems_fdisk_segment_ctl_queue
210{
211  rtems_fdisk_segment_ctl* head;
212  rtems_fdisk_segment_ctl* tail;
213  uint32_t                 count;
214} rtems_fdisk_segment_ctl_queue;
215
216/**
217 * Flash Device Control holds the segment controls
218 */
219typedef struct rtems_fdisk_device_ctl
220{
221  rtems_fdisk_segment_ctl*       segments;      /**< Segment controls. */
222  uint32_t                       segment_count; /**< Segment control count. */
223  const rtems_fdisk_device_desc* descriptor;    /**< Device descriptor. */
224} rtems_fdisk_device_ctl;
225
226/**
227 * The Block control holds the segment and page with the data.
228 */
229typedef struct rtems_fdisk_block_ctl
230{
231  rtems_fdisk_segment_ctl* segment; /**< The segment with the block. */
232  uint32_t                 page;    /**< The page in the segment. */
233} rtems_fdisk_block_ctl;
234
235/**
236 * The virtual block table holds the mapping for blocks as seen by the disk
237 * drivers to the device, segment and page numbers of the physical device.
238 */
239typedef struct rtems_flashdisk
240{
241  rtems_device_major_number major;         /**< The driver's major number. */
242  rtems_device_minor_number minor;         /**< The driver's minor number. */
243
244  uint32_t flags;                          /**< configuration flags. */
245
246  uint32_t compact_segs;                   /**< Max segs to compact at once. */
247  uint32_t avail_compact_segs;             /**< The number of segments when
248                                                compaction occurs when writing. */
249
250  uint32_t               block_size;       /**< The block size for this disk. */
251  rtems_fdisk_block_ctl* blocks;           /**< The block to segment-page
252                                                mappings. */
253  uint32_t block_count;                    /**< The number of avail. blocks. */
254  uint32_t unavail_blocks;                 /**< The number of unavail blocks. */
255  uint32_t erased_blocks;                  /**< The number of erased blocks. */
256
257  rtems_fdisk_device_ctl* devices;         /**< The flash devices for this
258                                                disk. */
259  uint32_t                device_count;    /**< The number of flash devices. */
260
261  rtems_fdisk_segment_ctl_queue available; /**< The queue of segments with
262                                                available pages. */
263  rtems_fdisk_segment_ctl_queue used;      /**< The list of segments with all
264                                                pages used. */
265  rtems_fdisk_segment_ctl_queue erase;     /**< The list of segments to be
266                                                erased. */
267  rtems_fdisk_segment_ctl_queue failed;    /**< The list of segments that failed
268                                                when being erased. */
269  rtems_id lock;                           /**< Mutex for threading protection.*/
270
271  uint8_t* copy_buffer;                    /**< Copy buf used during compacting */
272
273  uint32_t info_level;                     /**< The info trace level. */
274} rtems_flashdisk;
275
276/**
277 * The array of flash disks we support.
278 */
279static rtems_flashdisk* rtems_flashdisks;
280
281/**
282 * The number of flash disks we have.
283 */
284static uint32_t rtems_flashdisk_count;
285
286/**
287 * The CRC16 factor table. Created during initialisation.
288 */
289static uint16_t* rtems_fdisk_crc16_factor;
290
291/**
292 * Calculate the CRC16 checksum.
293 *
294 * @param _b The byte to checksum.
295 * @param _c The current checksum.
296 */
297#define rtems_fdisk_calc_crc16(_b, _c) \
298  rtems_fdisk_crc16_factor[((_b) ^ ((_c) & 0xff)) & 0xff] ^ (((_c) >> 8) & 0xff)
299
300/**
301 * Generate the CRC table.
302 *
303 * @param pattern The seed pattern for the table of factors.
304 * @relval RTEMS_SUCCESSFUL The table was generated.
305 * @retval RTEMS_NO_MEMORY The table could not be allocated from the heap.
306 */
307rtems_status_code
308rtems_fdisk_crc16_gen_factors (uint16_t pattern)
309{
310  uint32_t b;
311
312  rtems_fdisk_crc16_factor = malloc (sizeof (uint16_t) * 256);
313  if (!rtems_fdisk_crc16_factor)
314    return RTEMS_NO_MEMORY;
315
316  for (b = 0; b < 256; b++)
317  {
318    uint32_t i;
319    uint16_t v = b;
320    for (i = 8; i--;)
321      v = v & 1 ? (v >> 1) ^ pattern : v >> 1;
322    rtems_fdisk_crc16_factor[b] = v & 0xffff;
323  }
324  return RTEMS_SUCCESSFUL;
325}
326
327#if RTEMS_FDISK_TRACE
328/**
329 * Print a message to the flash disk output and flush it.
330 *
331 * @param fd The flashdisk control structure.
332 * @param format The format string. See printf for details.
333 * @param ... The arguments for the format text.
334 * @return int The number of bytes written to the output.
335 */
336static int
337rtems_fdisk_printf (const rtems_flashdisk* fd, const char *format, ...)
338{
339  int ret = 0;
340  if (fd->info_level >= 3)
341  {
342    va_list args;
343    va_start (args, format);
344    fprintf (stdout, "fdisk:");
345    ret =  vfprintf (stdout, format, args);
346    fprintf (stdout, "\n");
347    fflush (stdout);
348    va_end (args);
349  }
350  return ret;
351}
352
353static bool
354rtems_fdisk_is_erased_blocks_starvation (const rtems_flashdisk* fd)
355{
356  return fd->erased_blocks < fd->unavail_blocks;
357}
358
359/**
360 * Print a info message to the flash disk output and flush it.
361 *
362 * @param fd The flashdisk control structure.
363 * @param format The format string. See printf for details.
364 * @param ... The arguments for the format text.
365 * @return int The number of bytes written to the output.
366 */
367static int
368rtems_fdisk_info (const rtems_flashdisk* fd, const char *format, ...)
369{
370  int ret = 0;
371  if (fd->info_level >= 2)
372  {
373    va_list args;
374    va_start (args, format);
375    fprintf (stdout, "fdisk:");
376    ret =  vfprintf (stdout, format, args);
377    fprintf (stdout, "\n");
378    fflush (stdout);
379    va_end (args);
380  }
381  return ret;
382}
383
384/**
385 * Print a warning to the flash disk output and flush it.
386 *
387 * @param fd The flashdisk control structure.
388 * @param format The format string. See printf for details.
389 * @param ... The arguments for the format text.
390 * @return int The number of bytes written to the output.
391 */
392static int
393rtems_fdisk_warning (const rtems_flashdisk* fd, const char *format, ...)
394{
395  int ret = 0;
396  if (fd->info_level >= 1)
397  {
398    va_list args;
399    va_start (args, format);
400    fprintf (stdout, "fdisk:warning:");
401    ret =  vfprintf (stdout, format, args);
402    fprintf (stdout, "\n");
403    fflush (stdout);
404    va_end (args);
405  }
406  return ret;
407}
408#endif
409
410/**
411 * Print an error to the flash disk output and flush it.
412 *
413 * @param format The format string. See printf for details.
414 * @param ... The arguments for the format text.
415 * @return int The number of bytes written to the output.
416 */
417static int
418rtems_fdisk_error (const char *format, ...)
419{
420  int ret;
421  va_list args;
422  va_start (args, format);
423  fprintf (stderr, "fdisk:error:");
424  ret =  vfprintf (stderr, format, args);
425  fprintf (stderr, "\n");
426  fflush (stderr);
427  va_end (args);
428  return ret;
429}
430
431/**
432 * Print an abort message, flush it then abort the program.
433 *
434 * @param format The format string. See printf for details.
435 * @param ... The arguments for the format text.
436 */
437static void
438rtems_fdisk_abort (const char *format, ...)
439{
440  va_list args;
441  va_start (args, format);
442  fprintf (stderr, "fdisk:abort:");
443  vfprintf (stderr, format, args);
444  fprintf (stderr, "\n");
445  fflush (stderr);
446  va_end (args);
447  exit (1);
448}
449
450/**
451 * Initialise the segment control queue.
452 */
453static void
454rtems_fdisk_segment_queue_init (rtems_fdisk_segment_ctl_queue* queue)
455{
456  queue->head = queue->tail = 0;
457  queue->count = 0;
458}
459
460/**
461 * Push to the head of the segment control queue.
462 */
463static void
464rtems_fdisk_segment_queue_push_head (rtems_fdisk_segment_ctl_queue* queue,
465                                     rtems_fdisk_segment_ctl*       sc)
466{
467  if (sc)
468  {
469    sc->next = queue->head;
470    queue->head = sc;
471
472    if (queue->tail == 0)
473      queue->tail = sc;
474    queue->count++;
475  }
476}
477
478/**
479 * Pop the head of the segment control queue.
480 */
481static rtems_fdisk_segment_ctl*
482rtems_fdisk_segment_queue_pop_head (rtems_fdisk_segment_ctl_queue* queue)
483{
484  if (queue->head)
485  {
486    rtems_fdisk_segment_ctl* sc = queue->head;
487
488    queue->head = sc->next;
489    if (!queue->head)
490      queue->tail = 0;
491
492    queue->count--;
493
494    sc->next = 0;
495
496    return sc;
497  }
498
499  return 0;
500}
501
502/**
503 * Push to the tail of the segment control queue.
504 */
505static void
506rtems_fdisk_segment_queue_push_tail (rtems_fdisk_segment_ctl_queue* queue,
507                                     rtems_fdisk_segment_ctl*       sc)
508{
509  if (sc)
510  {
511    sc->next = 0;
512
513    if (queue->head)
514    {
515      queue->tail->next = sc;
516      queue->tail       = sc;
517    }
518    else
519    {
520      queue->head = queue->tail = sc;
521    }
522
523    queue->count++;
524  }
525}
526
527/**
528 * Remove from the segment control queue.
529 */
530static void
531rtems_fdisk_segment_queue_remove (rtems_fdisk_segment_ctl_queue* queue,
532                                  rtems_fdisk_segment_ctl*       sc)
533{
534  rtems_fdisk_segment_ctl* prev = 0;
535  rtems_fdisk_segment_ctl* it = queue->head;
536
537  /*
538   * Do not change sc->next as sc could be on another queue.
539   */
540
541  while (it)
542  {
543    if (sc == it)
544    {
545      if (prev == 0)
546      {
547        queue->head = sc->next;
548        if (queue->head == 0)
549          queue->tail = 0;
550      }
551      else
552      {
553        prev->next = sc->next;
554        if (queue->tail == sc)
555          queue->tail = prev;
556      }
557      sc->next = 0;
558      queue->count--;
559      break;
560    }
561
562    prev = it;
563    it = it->next;
564  }
565}
566
567/**
568 * Insert into the segment control queue before the specific
569 * segment control item.
570 */
571static void
572rtems_fdisk_segment_queue_insert_before (rtems_fdisk_segment_ctl_queue* queue,
573                                         rtems_fdisk_segment_ctl*       item,
574                                         rtems_fdisk_segment_ctl*       sc)
575{
576  if (item)
577  {
578    rtems_fdisk_segment_ctl** prev = &queue->head;
579    rtems_fdisk_segment_ctl*  it = queue->head;
580
581    while (it)
582    {
583      if (item == it)
584      {
585        sc->next = item;
586        *prev = sc;
587        queue->count++;
588        return;
589      }
590
591      prev = &it->next;
592      it = it->next;
593    }
594  }
595
596  rtems_fdisk_segment_queue_push_tail (queue, sc);
597}
598
599/**
600 * Count the number of elements on the list.
601 */
602static uint32_t
603rtems_fdisk_segment_queue_count (rtems_fdisk_segment_ctl_queue* queue)
604{
605  return queue->count;
606}
607
608/**
609 * Count the number of elements on the list.
610 */
611static uint32_t
612rtems_fdisk_segment_count_queue (rtems_fdisk_segment_ctl_queue* queue)
613{
614  rtems_fdisk_segment_ctl* sc = queue->head;
615  uint32_t                 count = 0;
616
617  while (sc)
618  {
619    count++;
620    sc = sc->next;
621  }
622
623  return count;
624}
625
626/**
627 * See if a segment control is present on this queue.
628 */
629static bool
630rtems_fdisk_segment_queue_present (rtems_fdisk_segment_ctl_queue* queue,
631                                   rtems_fdisk_segment_ctl*       sc)
632{
633  rtems_fdisk_segment_ctl*  it = queue->head;
634
635  while (it)
636  {
637    if (it == sc)
638      return true;
639    it = it->next;
640  }
641
642  return false;
643}
644
645/**
646 * Format a string with the queue status.
647 */
648static void
649rtems_fdisk_queue_status (rtems_flashdisk*         fd,
650                          rtems_fdisk_segment_ctl* sc,
651                          char                     queues[5])
652{
653  queues[0] = rtems_fdisk_segment_queue_present (&fd->available, sc) ? 'A' : '-';
654  queues[1] = rtems_fdisk_segment_queue_present (&fd->used, sc)      ? 'U' : '-';
655  queues[2] = rtems_fdisk_segment_queue_present (&fd->erase, sc)     ? 'E' : '-';
656  queues[3] = rtems_fdisk_segment_queue_present (&fd->failed, sc)    ? 'F' : '-';
657  queues[4] = '\0';
658}
659
660/**
661 * Check if the page descriptor is erased.
662 */
663static bool
664rtems_fdisk_page_desc_erased (const rtems_fdisk_page_desc* pd)
665{
666  return ((pd->crc == 0xffff) &&
667          (pd->flags == 0xffff) &&
668          (pd->block == 0xffffffff)) ? true : false;
669}
670
671/**
672 * Check if the flags are set. The flags are inverted as we can
673 * only set a flag by changing it from 1 to 0.
674 */
675static bool
676rtems_fdisk_page_desc_flags_set (rtems_fdisk_page_desc* pd, uint16_t flags)
677{
678  return (pd->flags & flags) == 0 ? true : false;
679}
680
681/**
682 * Check if the flags are clear. The flags are inverted as we can
683 * only set a flag by changing it from 1 to 0.
684 */
685static bool
686rtems_fdisk_page_desc_flags_clear (rtems_fdisk_page_desc* pd, uint16_t flags)
687{
688  return (pd->flags & flags) == flags ? true : false;
689}
690
691/**
692 * Set the flags. Setting means clear the bit to 0.
693 */
694static void
695rtems_fdisk_page_desc_set_flags (rtems_fdisk_page_desc* pd, uint16_t flags)
696{
697  pd->flags &= ~flags;
698}
699
700/**
701 * Get the segment descriptor for a device and segment. There are
702 * no range checks.
703 */
704static const rtems_fdisk_segment_desc*
705rtems_fdisk_seg_descriptor (const rtems_flashdisk* fd,
706                            uint32_t               device,
707                            uint32_t               segment)
708{
709  return fd->devices[device].segments[segment].descriptor;
710}
711
712/**
713 * Count the segments for a device.
714 */
715static uint32_t
716rtems_fdisk_count_segments (const rtems_fdisk_device_desc* dd)
717{
718  uint32_t count = 0;
719  uint32_t segment;
720  for (segment = 0; segment < dd->segment_count; segment++)
721    count += dd->segments[segment].count;
722  return count;
723}
724
725/**
726 * Calculate the pages in a segment give the segment size and the
727 * page size.
728 *
729 * @param sd The segment descriptor.
730 * @param page_size The page size in bytes.
731 */
732static uint32_t
733rtems_fdisk_pages_in_segment (const rtems_fdisk_segment_desc* sd,
734                              uint32_t                        page_size)
735{
736  return sd->size / page_size;
737}
738
739/**
740 * Calculate the number of pages needed to hold the page descriptors.
741 * The calculation need to round up.
742 *
743 * The segment control contains the number of pages used as descriptors
744 * and should be used rather than this call where possible.
745 */
746static uint32_t
747rtems_fdisk_page_desc_pages (const rtems_fdisk_segment_desc* sd,
748                             uint32_t                        page_size)
749{
750  uint32_t pages = rtems_fdisk_pages_in_segment (sd, page_size);
751  uint32_t bytes = pages * sizeof (rtems_fdisk_page_desc);
752  return ((bytes - 1) / page_size) + 1;
753}
754
755/**
756 * The number of available pages is the total pages less the
757 * active, used and bad pages.
758 */
759static uint32_t
760rtems_fdisk_seg_pages_available (const rtems_fdisk_segment_ctl* sc)
761{
762  return sc->pages - (sc->pages_active + sc->pages_used + sc->pages_bad);
763}
764/**
765 * Find the next available page in a segment.
766 */
767static uint32_t
768rtems_fdisk_seg_next_available_page (rtems_fdisk_segment_ctl* sc)
769{
770  rtems_fdisk_page_desc* pd = &sc->page_descriptors[0];
771  uint32_t               page;
772
773  for (page = 0; page < sc->pages; page++, pd++)
774    if (rtems_fdisk_page_desc_erased (pd))
775      break;
776
777  return page;
778}
779
780/**
781 * Find the segment on the queue that has the most free pages.
782 */
783static rtems_fdisk_segment_ctl*
784rtems_fdisk_seg_most_available (const rtems_fdisk_segment_ctl_queue* queue)
785{
786  rtems_fdisk_segment_ctl* sc      = queue->head;
787  rtems_fdisk_segment_ctl* biggest = queue->head;
788
789  while (sc)
790  {
791    if (rtems_fdisk_seg_pages_available (sc) >
792        rtems_fdisk_seg_pages_available (biggest))
793      biggest = sc;
794    sc = sc->next;
795  }
796
797  return biggest;
798}
799
800/**
801 * Is the segment all used ?
802 */
803#if 0
804static bool
805rtems_fdisk_seg_pages_all_used (const rtems_fdisk_segment_ctl* sc)
806{
807  return sc->pages == (sc->pages_used + sc->pages_bad) ? true : false;
808}
809#endif
810
811/**
812 * Calculate the blocks in a device. This is the number of
813 * pages less the pages hold page descriptors. This call be used
814 * early in the initialisation process and does not rely on
815 * the system being fully initialised.
816 *
817 * @param dd The device descriptor.
818 * @param page_size The page size in bytes.
819 */
820static uint32_t
821rtems_fdisk_blocks_in_device (const rtems_fdisk_device_desc* dd,
822                              uint32_t                       page_size)
823{
824  uint32_t count = 0;
825  uint32_t s;
826  for (s = 0; s < dd->segment_count; s++)
827  {
828    const rtems_fdisk_segment_desc* sd = &dd->segments[s];
829    count +=
830      (rtems_fdisk_pages_in_segment (sd, page_size) -
831       rtems_fdisk_page_desc_pages (sd, page_size)) * sd->count;
832  }
833  return count;
834}
835
836/**
837 * Read a block of data from a segment.
838 */
839static int
840rtems_fdisk_seg_read (const rtems_flashdisk*         fd,
841                      const rtems_fdisk_segment_ctl* sc,
842                      uint32_t                       offset,
843                      void*                          buffer,
844                      uint32_t                       size)
845{
846  uint32_t                           device;
847  uint32_t                           segment;
848  const rtems_fdisk_segment_desc*    sd;
849  const rtems_fdisk_driver_handlers* ops;
850  device = sc->device;
851  segment = sc->segment;
852  sd = rtems_fdisk_seg_descriptor (fd, device, segment);
853  ops = fd->devices[device].descriptor->flash_ops;
854#if RTEMS_FDISK_TRACE
855  rtems_fdisk_printf (fd, "  seg-read: %02d-%03d: o=%08x s=%d",
856                      device, segment, offset, size);
857#endif
858  return ops->read (sd, device, segment, offset, buffer, size);
859}
860
861/**
862 * Write a block of data to a segment. It is assumed the
863 * location in the segment is erased and able to take the
864 * data.
865 */
866static int
867rtems_fdisk_seg_write (const rtems_flashdisk*   fd,
868                       rtems_fdisk_segment_ctl* sc,
869                       uint32_t                 offset,
870                       const void*              buffer,
871                       uint32_t                 size)
872{
873  int ret;
874  uint32_t                           device;
875  uint32_t                           segment;
876  const rtems_fdisk_segment_desc*    sd;
877  const rtems_fdisk_driver_handlers* ops;
878  device = sc->device;
879  segment = sc->segment;
880  sd = rtems_fdisk_seg_descriptor (fd, device, segment);
881  ops = fd->devices[device].descriptor->flash_ops;
882#if RTEMS_FDISK_TRACE
883  rtems_fdisk_printf (fd, "  seg-write: %02d-%03d: o=%08x s=%d",
884                      device, segment, offset, size);
885#endif
886  ret = ops->write (sd, device, segment, offset, buffer, size);
887  if (ret)
888    sc->failed = true;
889
890  return ret;
891}
892
893/**
894 * Blank check the area of a segment.
895 */
896static int
897rtems_fdisk_seg_blank_check (const rtems_flashdisk*   fd,
898                             rtems_fdisk_segment_ctl* sc,
899                             uint32_t                 offset,
900                             uint32_t                 size)
901{
902  uint32_t                           device;
903  uint32_t                           segment;
904  const rtems_fdisk_segment_desc*    sd;
905  const rtems_fdisk_driver_handlers* ops;
906  device = sc->device;
907  segment = sc->segment;
908  sd = rtems_fdisk_seg_descriptor (fd, device, segment);
909  ops = fd->devices[device].descriptor->flash_ops;
910#if RTEMS_FDISK_TRACE
911  rtems_fdisk_printf (fd, "  seg-blank: %02d-%03d: o=%08x s=%d",
912                      device, segment, offset, size);
913#endif
914  return ops->blank (sd, device, segment, offset, size);
915}
916/**
917 * Verify the data with the data in a segment.
918 */
919static int
920rtems_fdisk_seg_verify (const rtems_flashdisk* fd,
921                        uint32_t               device,
922                        uint32_t               segment,
923                        uint32_t               offset,
924                        const void*            buffer,
925                        uint32_t               size)
926{
927  const rtems_fdisk_segment_desc*    sd;
928  const rtems_fdisk_driver_handlers* ops;
929  sd  = rtems_fdisk_seg_descriptor (fd, device, segment);
930  ops = fd->devices[device].descriptor->flash_ops;
931#if RTEMS_FDISK_TRACE
932  rtems_fdisk_printf (fd, "  seg-verify: %02d-%03d: o=%08x s=%d",
933                      device, segment, offset, size);
934#endif
935  return ops->verify (sd, device, segment, offset, buffer, size);
936}
937
938/**
939 * Blank check a page of data in a segment.
940 */
941static int
942rtems_fdisk_seg_blank_check_page (const rtems_flashdisk*   fd,
943                                  rtems_fdisk_segment_ctl* sc,
944                                  uint32_t                 page)
945{
946  return rtems_fdisk_seg_blank_check (fd, sc,
947                                      page * fd->block_size, fd->block_size);
948}
949
950/**
951 * Read a page of data from a segment.
952 */
953static int
954rtems_fdisk_seg_read_page (const rtems_flashdisk*   fd,
955                           rtems_fdisk_segment_ctl* sc,
956                           uint32_t                 page,
957                           void*                    buffer)
958{
959  return rtems_fdisk_seg_read (fd, sc,
960                               page * fd->block_size, buffer, fd->block_size);
961}
962
963/**
964 * Write a page of data to a segment.
965 */
966static int
967rtems_fdisk_seg_write_page (rtems_flashdisk*         fd,
968                            rtems_fdisk_segment_ctl* sc,
969                            uint32_t                 page,
970                            const void*              buffer)
971{
972  if ((fd->flags & RTEMS_FDISK_BLANK_CHECK_BEFORE_WRITE))
973  {
974    int ret = rtems_fdisk_seg_blank_check_page (fd, sc, page);
975    if (ret)
976      return ret;
977  }
978  --fd->erased_blocks;
979  return rtems_fdisk_seg_write (fd, sc,
980                                page * fd->block_size, buffer, fd->block_size);
981}
982
983/**
984 * Verify a page of data with the data in the segment.
985 */
986static int
987rtems_fdisk_seg_verify_page (const rtems_flashdisk* fd,
988                             uint32_t               device,
989                             uint32_t               segment,
990                             uint32_t               page,
991                             const void*            buffer)
992{
993  return rtems_fdisk_seg_verify (fd, device, segment,
994                                 page * fd->block_size, buffer, fd->block_size);
995}
996
997/**
998 * Copy a page of data from one segment to another segment.
999 */
1000static int
1001rtems_fdisk_seg_copy_page (rtems_flashdisk*         fd,
1002                           rtems_fdisk_segment_ctl* src_sc,
1003                           uint32_t                 src_page,
1004                           rtems_fdisk_segment_ctl* dst_sc,
1005                           uint32_t                 dst_page)
1006{
1007  int ret;
1008#if RTEMS_FDISK_TRACE
1009  rtems_fdisk_printf (fd, "  seg-copy-page: %02d-%03d~%03d=>%02d-%03d~%03d",
1010                      src_sc->device, src_sc->segment, src_page,
1011                      dst_sc->device, dst_sc->segment, dst_page);
1012#endif
1013  ret = rtems_fdisk_seg_read_page (fd, src_sc, src_page,
1014                                   fd->copy_buffer);
1015  if (ret)
1016    return ret;
1017  return rtems_fdisk_seg_write_page (fd, dst_sc, dst_page,
1018                                     fd->copy_buffer);
1019}
1020
1021/**
1022 * Write the page descriptor to a segment. This code assumes the page
1023 * descriptors are located at offset 0 in the segment.
1024 */
1025static int
1026rtems_fdisk_seg_write_page_desc (const rtems_flashdisk*       fd,
1027                                 rtems_fdisk_segment_ctl*     sc,
1028                                 uint32_t                     page,
1029                                 const rtems_fdisk_page_desc* page_desc)
1030{
1031  uint32_t offset = page * sizeof (rtems_fdisk_page_desc);
1032  if ((fd->flags & RTEMS_FDISK_BLANK_CHECK_BEFORE_WRITE))
1033  {
1034    int ret = rtems_fdisk_seg_blank_check (fd, sc,
1035                                           offset,
1036                                           sizeof (rtems_fdisk_page_desc));
1037    if (ret)
1038      return ret;
1039  }
1040  return rtems_fdisk_seg_write (fd, sc, offset,
1041                                page_desc, sizeof (rtems_fdisk_page_desc));
1042}
1043
1044/**
1045 * Write the page descriptor flags to a segment. This code assumes the page
1046 * descriptors are located at offset 0 in the segment.
1047 */
1048static int
1049rtems_fdisk_seg_write_page_desc_flags (const rtems_flashdisk*       fd,
1050                                       rtems_fdisk_segment_ctl*     sc,
1051                                       uint32_t                     page,
1052                                       const rtems_fdisk_page_desc* page_desc)
1053{
1054  uint32_t offset = ((page * sizeof (rtems_fdisk_page_desc)) +
1055                     ((uint8_t*) &page_desc->flags) - ((uint8_t*) page_desc));
1056  if ((fd->flags & RTEMS_FDISK_BLANK_CHECK_BEFORE_WRITE))
1057  {
1058    uint16_t flash_flags;
1059    int      ret;
1060    ret = rtems_fdisk_seg_read (fd, sc, offset,
1061                                &flash_flags, sizeof (flash_flags));
1062    if (ret)
1063      return ret;
1064    if ((flash_flags & page_desc->flags) != page_desc->flags)
1065    {
1066      rtems_fdisk_error ("  seg-write-page-flags: %02d-%03d-%03d: "
1067                         "flags not erased: 0x%04 -> 0x%04x",
1068                         sc->device, sc->segment, page,
1069                         flash_flags, page_desc->flags);
1070      return ret;
1071    }
1072  }
1073  return rtems_fdisk_seg_write (fd, sc, offset,
1074                                &page_desc->flags, sizeof (page_desc->flags));
1075}
1076
1077/**
1078 * Erase a device.
1079 */
1080static int
1081rtems_fdisk_device_erase (const rtems_flashdisk* fd, uint32_t device)
1082{
1083  const rtems_fdisk_driver_handlers* ops;
1084  ops = fd->devices[device].descriptor->flash_ops;
1085#if RTEMS_FDISK_TRACE
1086  rtems_fdisk_printf (fd, " device-erase: %02d", device);
1087#endif
1088  return ops->erase_device (fd->devices[device].descriptor, device);
1089}
1090
1091/**
1092 * Erase all flash.
1093 */
1094static int
1095rtems_fdisk_erase_flash (const rtems_flashdisk* fd)
1096{
1097  uint32_t device;
1098  for (device = 0; device < fd->device_count; device++)
1099  {
1100    int ret;
1101
1102#if RTEMS_FDISK_TRACE
1103    rtems_fdisk_info (fd, " erase-flash:%02d", device);
1104#endif
1105
1106    ret = rtems_fdisk_device_erase (fd, device);
1107
1108    if (ret != 0)
1109      return ret;
1110  }
1111  return 0;
1112}
1113
1114/**
1115 * Calculate the checksum of a page in a segment.
1116 */
1117static uint16_t
1118rtems_fdisk_page_checksum (const uint8_t* buffer, uint32_t page_size)
1119{
1120  uint16_t cs = 0xffff;
1121  uint32_t i;
1122
1123  for (i = 0; i < page_size; i++, buffer++)
1124    cs = rtems_fdisk_calc_crc16 (cs, *buffer);
1125
1126  return cs;
1127}
1128
1129/**
1130 * Erase the segment.
1131 */
1132static int
1133rtems_fdisk_erase_segment (rtems_flashdisk* fd, rtems_fdisk_segment_ctl* sc)
1134{
1135  int                                ret;
1136  uint32_t                           device;
1137  uint32_t                           segment;
1138  const rtems_fdisk_segment_desc*    sd;
1139  const rtems_fdisk_driver_handlers* ops;
1140  device = sc->device;
1141  segment = sc->segment;
1142  sd = rtems_fdisk_seg_descriptor (fd, device, segment);
1143  ops = fd->devices[device].descriptor->flash_ops;
1144  ret = ops->erase (sd, device, segment);
1145  if (ret)
1146  {
1147    rtems_fdisk_error (" erase-segment:%02d-%03d: "      \
1148                       "segment erase failed: %s (%d)",
1149                       sc->device, sc->segment, strerror (ret), ret);
1150    sc->failed = true;
1151    if (!rtems_fdisk_segment_queue_present (&fd->failed, sc))
1152      rtems_fdisk_segment_queue_push_tail (&fd->failed, sc);
1153    return ret;
1154  }
1155
1156  fd->erased_blocks += sc->pages;
1157  sc->erased++;
1158
1159  memset (sc->page_descriptors, 0xff, sc->pages_desc * fd->block_size);
1160
1161  sc->pages_active = 0;
1162  sc->pages_used   = 0;
1163  sc->pages_bad    = 0;
1164
1165  sc->failed = false;
1166
1167  /*
1168   * Push to the tail of the available queue. It is a very
1169   * simple type of wear reduction. Every other available
1170   * segment will now get a go.
1171   */
1172  rtems_fdisk_segment_queue_push_tail (&fd->available, sc);
1173
1174  return 0;
1175}
1176
1177/**
1178 * Erase used segment.
1179 */
1180static int
1181rtems_fdisk_erase_used (rtems_flashdisk* fd)
1182{
1183  rtems_fdisk_segment_ctl* sc;
1184  int                      latched_ret = 0;
1185
1186  while ((sc = rtems_fdisk_segment_queue_pop_head (&fd->erase)))
1187  {
1188    /*
1189     * The segment will either end up on the available queue or
1190     * the failed queue.
1191     */
1192    int ret = rtems_fdisk_erase_segment (fd, sc);
1193    if (ret && !latched_ret)
1194      latched_ret = ret;
1195  }
1196
1197  return latched_ret;
1198}
1199
1200/**
1201 * Queue a segment. This is done after some of the stats for the segment
1202 * have been changed and this may effect the order the segment pages have in
1203 * the queue of available pages.
1204 *
1205 * @param fd The flash disk control table.
1206 * @param sc The segment control table to be reallocated
1207 */
1208static void
1209rtems_fdisk_queue_segment (rtems_flashdisk* fd, rtems_fdisk_segment_ctl* sc)
1210{
1211#if RTEMS_FDISK_TRACE
1212  rtems_fdisk_info (fd, " queue-seg:%02d-%03d: p=%d a=%d u=%d b=%d f=%s n=%s",
1213                    sc->device, sc->segment,
1214                    sc->pages, sc->pages_active, sc->pages_used, sc->pages_bad,
1215                    sc->failed ? "FAILED" : "no", sc->next ? "set" : "null");
1216#endif
1217
1218  /*
1219   * If the segment has failed then check the failed queue and append
1220   * if not failed.
1221   */
1222  if (sc->failed)
1223  {
1224    if (!rtems_fdisk_segment_queue_present (&fd->failed, sc))
1225      rtems_fdisk_segment_queue_push_tail (&fd->failed, sc);
1226    return;
1227  }
1228
1229  /*
1230   * Remove the queue from the available or used queue.
1231   */
1232  rtems_fdisk_segment_queue_remove (&fd->available, sc);
1233  rtems_fdisk_segment_queue_remove (&fd->used, sc);
1234
1235  /*
1236   * Are all the pages in the segment used ?
1237   * If they are and the driver has been configured to background
1238   * erase place the segment on the used queue. If not configured
1239   * to background erase perform the erase now.
1240   *
1241   */
1242  if (rtems_fdisk_seg_pages_available (sc) == 0)
1243  {
1244    if (sc->pages_active)
1245    {
1246      /*
1247       * Keep the used queue sorted by the most number of used
1248       * pages. When we compact we want to move the pages into
1249       * a new segment and cover more than one segment.
1250       */
1251      rtems_fdisk_segment_ctl* seg = fd->used.head;
1252
1253      while (seg)
1254      {
1255        if (sc->pages_used > seg->pages_used)
1256          break;
1257        seg = seg->next;
1258      }
1259
1260      if (seg)
1261        rtems_fdisk_segment_queue_insert_before (&fd->used, seg, sc);
1262      else
1263        rtems_fdisk_segment_queue_push_tail (&fd->used, sc);
1264    }
1265    else
1266    {
1267      if ((fd->flags & RTEMS_FDISK_BACKGROUND_ERASE))
1268        rtems_fdisk_segment_queue_push_tail (&fd->erase, sc);
1269      else
1270        rtems_fdisk_erase_segment (fd, sc);
1271    }
1272  }
1273  else
1274  {
1275    /*
1276     * The segment has pages available so place back onto the
1277     * available list. The list is sorted from the least number
1278     * of available pages to the most. This approach means
1279     * the pages of a partially filled segment will be filled
1280     * before moving onto another emptier segment. This keeps
1281     * empty segments longer aiding compaction.
1282     *
1283     * The down side is the wear effect as a single segment
1284     * could be used more than segment. This will not be
1285     * addressed until wear support is added.
1286     *
1287     * @note Wear support can be added by having counts for
1288     * for the number of times a segment is erased. This
1289     * available list is then sorted on the least number
1290     * of available pages then empty segments are sorted
1291     * on the least number of erases the segment has.
1292     *
1293     * The erase count can be stored in specially flaged
1294     * pages and contain a counter (32bits?) and 32 bits
1295     * for each segment. When a segment is erased a
1296     * bit is cleared for that segment. When 32 erasers
1297     * has occurred the page is re-written to the flash
1298     * with all the counters updated with the number of
1299     * bits cleared and all bits set back to 1.
1300     */
1301    rtems_fdisk_segment_ctl* seg = fd->available.head;
1302
1303    while (seg)
1304    {
1305      if (rtems_fdisk_seg_pages_available (sc) <
1306          rtems_fdisk_seg_pages_available (seg))
1307        break;
1308      seg = seg->next;
1309    }
1310
1311    if (seg)
1312      rtems_fdisk_segment_queue_insert_before (&fd->available, seg, sc);
1313    else
1314      rtems_fdisk_segment_queue_push_tail (&fd->available, sc);
1315  }
1316}
1317
1318static int
1319rtems_fdisk_recycle_segment (rtems_flashdisk*         fd,
1320                                    rtems_fdisk_segment_ctl* ssc,
1321                                    rtems_fdisk_segment_ctl* dsc,
1322                                    uint32_t *pages)
1323{
1324  int      ret;
1325  uint32_t spage;
1326  uint32_t used = 0;
1327  uint32_t active = 0;
1328
1329  for (spage = 0; spage < ssc->pages; spage++)
1330  {
1331    uint32_t               dst_pages;
1332    rtems_fdisk_page_desc* spd = &ssc->page_descriptors[spage];
1333
1334    if (rtems_fdisk_page_desc_flags_set (spd, RTEMS_FDISK_PAGE_ACTIVE) &&
1335        !rtems_fdisk_page_desc_flags_set (spd, RTEMS_FDISK_PAGE_USED))
1336    {
1337      rtems_fdisk_page_desc* dpd;
1338      uint32_t               dpage;
1339
1340      dpage = rtems_fdisk_seg_next_available_page (dsc);
1341      dpd   = &dsc->page_descriptors[dpage];
1342
1343      active++;
1344
1345      if (dpage >= dsc->pages)
1346      {
1347        rtems_fdisk_error ("recycle: %02d-%03d: " \
1348                           "no page desc available: %d",
1349                           dsc->device, dsc->segment,
1350                           rtems_fdisk_seg_pages_available (dsc));
1351        dsc->failed = true;
1352        rtems_fdisk_queue_segment (fd, dsc);
1353        rtems_fdisk_segment_queue_push_head (&fd->used, ssc);
1354        return EIO;
1355      }
1356
1357#if RTEMS_FDISK_TRACE
1358      rtems_fdisk_info (fd, "recycle: %02d-%03d-%03d=>%02d-%03d-%03d",
1359                        ssc->device, ssc->segment, spage,
1360                        dsc->device, dsc->segment, dpage);
1361#endif
1362      ret = rtems_fdisk_seg_copy_page (fd, ssc,
1363                                       spage + ssc->pages_desc,
1364                                       dsc,
1365                                       dpage + dsc->pages_desc);
1366      if (ret)
1367      {
1368        rtems_fdisk_error ("recycle: %02d-%03d-%03d=>" \
1369                           "%02d-%03d-%03d: "             \
1370                           "copy page failed: %s (%d)",
1371                           ssc->device, ssc->segment, spage,
1372                           dsc->device, dsc->segment, dpage,
1373                           strerror (ret), ret);
1374        rtems_fdisk_queue_segment (fd, dsc);
1375        rtems_fdisk_segment_queue_push_head (&fd->used, ssc);
1376        return ret;
1377      }
1378
1379      *dpd = *spd;
1380
1381      ret = rtems_fdisk_seg_write_page_desc (fd,
1382                                             dsc,
1383                                             dpage, dpd);
1384
1385      if (ret)
1386      {
1387        rtems_fdisk_error ("recycle: %02d-%03d-%03d=>"   \
1388                           "%02d-%03d-%03d: copy pd failed: %s (%d)",
1389                           ssc->device, ssc->segment, spage,
1390                           dsc->device, dsc->segment, dpage,
1391                           strerror (ret), ret);
1392        rtems_fdisk_queue_segment (fd, dsc);
1393        rtems_fdisk_segment_queue_push_head (&fd->used, ssc);
1394        return ret;
1395      }
1396
1397      dsc->pages_active++;
1398
1399      /*
1400       * No need to set the used bit on the source page as the
1401       * segment will be erased. Power down could be a problem.
1402       * We do the stats to make sure everything is as it should
1403       * be.
1404       */
1405
1406      ssc->pages_active--;
1407      ssc->pages_used++;
1408
1409      fd->blocks[spd->block].segment = dsc;
1410      fd->blocks[spd->block].page    = dpage;
1411
1412      /*
1413       * Place the segment on to the correct queue.
1414       */
1415      rtems_fdisk_queue_segment (fd, dsc);
1416
1417      /*
1418       * Get new destination segment if necessary.
1419       */
1420      dst_pages = rtems_fdisk_seg_pages_available (dsc);
1421      if (dst_pages == 0)
1422      {
1423        dsc = rtems_fdisk_seg_most_available (&fd->available);
1424        if (!dsc)
1425        {
1426          rtems_fdisk_error ("recycle: no available dst segment");
1427          return EIO;
1428        }
1429      }
1430
1431      (*pages)--;
1432    }
1433    else if (rtems_fdisk_page_desc_erased (spd))
1434    {
1435      --fd->erased_blocks;
1436    }
1437    else
1438    {
1439      used++;
1440    }
1441  }
1442
1443#if RTEMS_FDISK_TRACE
1444  rtems_fdisk_printf (fd, "ssc end: %d-%d: p=%ld, a=%ld, u=%ld",
1445                      ssc->device, ssc->segment,
1446                      pages, active, used);
1447#endif
1448  if (ssc->pages_active != 0)
1449  {
1450    rtems_fdisk_error ("compacting: ssc pages not 0: %d",
1451                       ssc->pages_active);
1452  }
1453
1454  ret = rtems_fdisk_erase_segment (fd, ssc);
1455
1456  return ret;
1457}
1458
1459/**
1460 * Compact the used segments to free what is available. Find the segment
1461 * with the most available number of pages and see if we have
1462 * used segments that will fit. The used queue is sorted on the least
1463 * number of active pages.
1464 */
1465static int
1466rtems_fdisk_compact (rtems_flashdisk* fd)
1467{
1468  int ret;
1469  rtems_fdisk_segment_ctl* dsc;
1470  rtems_fdisk_segment_ctl* ssc;
1471  uint32_t compacted_segs = 0;
1472  uint32_t pages;
1473
1474  if (rtems_fdisk_is_erased_blocks_starvation (fd))
1475  {
1476    ssc = rtems_fdisk_segment_queue_pop_head (&fd->used);
1477    if (!ssc)
1478      ssc = rtems_fdisk_segment_queue_pop_head (&fd->available);
1479
1480    if (ssc)
1481    {
1482      dsc = rtems_fdisk_seg_most_available (&fd->available);
1483      if (dsc)
1484      {
1485        ret = rtems_fdisk_recycle_segment (fd, ssc, dsc, &pages);
1486        if (ret)
1487          return ret;
1488      }
1489      else
1490      {
1491        rtems_fdisk_error ("compacting: starvation");
1492        return EIO;
1493      }
1494    }
1495    else
1496    {
1497      rtems_fdisk_error ("compacting: nothing to recycle");
1498      return EIO;
1499    }
1500  }
1501
1502  while (fd->used.head)
1503  {
1504    uint32_t                 dst_pages;
1505    uint32_t                 segments;
1506
1507#if RTEMS_FDISK_TRACE
1508    rtems_fdisk_printf (fd, " compacting");
1509#endif
1510
1511    dsc = rtems_fdisk_seg_most_available (&fd->available);
1512
1513    if (dsc == 0)
1514    {
1515      rtems_fdisk_error ("compacting: no available segments to compact too");
1516      return EIO;
1517    }
1518
1519    ssc = fd->used.head;
1520    dst_pages = rtems_fdisk_seg_pages_available (dsc);
1521    segments = 0;
1522    pages = 0;
1523
1524#if RTEMS_FDISK_TRACE
1525    rtems_fdisk_printf (fd, " dsc:%02d-%03d: most available",
1526                        dsc->device, dsc->segment);
1527#endif
1528
1529    /*
1530     * Count the number of segments that have active pages that fit into
1531     * the destination segment. Also limit the number of segments that
1532     * we handle during one compaction. A lower number means less aggressive
1533     * compaction or less delay when compacting but it may mean the disk
1534     * will fill.
1535     */
1536
1537    while (ssc &&
1538           ((pages + ssc->pages_active) < dst_pages) &&
1539           ((compacted_segs + segments) < fd->compact_segs))
1540    {
1541      pages += ssc->pages_active;
1542      segments++;
1543      ssc = ssc->next;
1544    }
1545
1546    /*
1547     * We need a source segment and have pages to copy and
1548     * compacting one segment to another is silly. Compaction needs
1549     * to free at least one more segment.
1550     */
1551
1552    if (!ssc || (pages == 0) || ((compacted_segs + segments) == 1))
1553      break;
1554
1555#if RTEMS_FDISK_TRACE
1556    rtems_fdisk_printf (fd, " ssc scan: %d-%d: p=%ld, seg=%ld",
1557                        ssc->device, ssc->segment,
1558                        pages, segments);
1559#endif
1560
1561    rtems_fdisk_segment_queue_remove (&fd->available, dsc);
1562
1563    /*
1564     * We now copy the pages to the new segment.
1565     */
1566
1567    while (pages)
1568    {
1569      ssc = rtems_fdisk_segment_queue_pop_head (&fd->used);
1570
1571      if (ssc)
1572      {
1573        ret = rtems_fdisk_recycle_segment (fd, ssc, dsc, &pages);
1574        if (ret)
1575          return ret;
1576      }
1577    }
1578
1579    compacted_segs += segments;
1580  }
1581
1582  return 0;
1583}
1584
1585/**
1586 * Recover the block mappings from the devices.
1587 */
1588static int
1589rtems_fdisk_recover_block_mappings (rtems_flashdisk* fd)
1590{
1591  uint32_t device;
1592
1593  /*
1594   * Clear the queues.
1595   */
1596  rtems_fdisk_segment_queue_init (&fd->available);
1597  rtems_fdisk_segment_queue_init (&fd->used);
1598  rtems_fdisk_segment_queue_init (&fd->erase);
1599  rtems_fdisk_segment_queue_init (&fd->failed);
1600
1601  /*
1602   * Clear the lock mappings.
1603   */
1604  memset (fd->blocks, 0, fd->block_count * sizeof (rtems_fdisk_block_ctl));
1605
1606  /*
1607   * Scan each segment or each device recovering the valid pages.
1608   */
1609  fd->erased_blocks = 0;
1610  for (device = 0; device < fd->device_count; device++)
1611  {
1612    uint32_t segment;
1613    for (segment = 0; segment < fd->devices[device].segment_count; segment++)
1614    {
1615      rtems_fdisk_segment_ctl*        sc = &fd->devices[device].segments[segment];
1616      const rtems_fdisk_segment_desc* sd = sc->descriptor;
1617      rtems_fdisk_page_desc*          pd;
1618      uint32_t                        page;
1619      int                             ret;
1620
1621#if RTEMS_FDISK_TRACE
1622      rtems_fdisk_info (fd, "recover-block-mappings:%02d-%03d", device, segment);
1623#endif
1624
1625      sc->pages_desc = rtems_fdisk_page_desc_pages (sd, fd->block_size);
1626      sc->pages =
1627        rtems_fdisk_pages_in_segment (sd, fd->block_size) - sc->pages_desc;
1628
1629      sc->pages_active = 0;
1630      sc->pages_used   = 0;
1631      sc->pages_bad    = 0;
1632
1633      sc->failed = false;
1634
1635      if (!sc->page_descriptors)
1636        sc->page_descriptors = malloc (sc->pages_desc * fd->block_size);
1637
1638      if (!sc->page_descriptors)
1639        rtems_fdisk_abort ("no memory for page descriptors");
1640
1641      pd = sc->page_descriptors;
1642
1643      /*
1644       * The page descriptors are always at the start of the segment. Read
1645       * the descriptors off the device into the segment control page
1646       * descriptors.
1647       *
1648       * @todo It may be better to ask the driver to get these value
1649       *       so NAND flash could be better supported.
1650       */
1651      ret = rtems_fdisk_seg_read (fd, sc, 0, (void*) pd,
1652                                  sc->pages_desc * fd->block_size);
1653
1654      if (ret)
1655      {
1656        rtems_fdisk_error ("recover-block-mappings:%02d-%03d: " \
1657                           "read page desc failed: %s (%d)",
1658                           device, segment, strerror (ret), ret);
1659        return ret;
1660      }
1661
1662      /*
1663       * Check each page in the segement for valid pages.
1664       * Update the stats for the segment so we know how many pages
1665       * are active and how many are used.
1666       *
1667       * If the page is active see if the block is with-in range and
1668       * if the block is a duplicate.
1669       */
1670      for (page = 0; page < sc->pages; page++, pd++)
1671      {
1672        if (rtems_fdisk_page_desc_erased (pd))
1673        {
1674          /*
1675           * Is the page erased ?
1676           */
1677          ret = rtems_fdisk_seg_blank_check_page (fd, sc,
1678                                                  page + sc->pages_desc);
1679
1680          if (ret == 0)
1681          {
1682            ++fd->erased_blocks;
1683          }
1684          else
1685          {
1686#if RTEMS_FDISK_TRACE
1687            rtems_fdisk_warning (fd, "page not blank: %d-%d-%d",
1688                                 device, segment, page, pd->block);
1689#endif
1690            rtems_fdisk_page_desc_set_flags (pd, RTEMS_FDISK_PAGE_USED);
1691
1692            ret = rtems_fdisk_seg_write_page_desc (fd, sc,
1693                                                   page, pd);
1694
1695            if (ret)
1696            {
1697              rtems_fdisk_error ("forcing page to used failed: %d-%d-%d",
1698                                 device, segment, page);
1699            }
1700
1701            sc->pages_used++;
1702          }
1703        }
1704        else
1705        {
1706          if (rtems_fdisk_page_desc_flags_set (pd, RTEMS_FDISK_PAGE_USED))
1707          {
1708            sc->pages_used++;
1709          }
1710          else if (rtems_fdisk_page_desc_flags_set (pd, RTEMS_FDISK_PAGE_ACTIVE))
1711          {
1712            if (pd->block >= fd->block_count)
1713            {
1714#if RTEMS_FDISK_TRACE
1715              rtems_fdisk_warning (fd,
1716                                   "invalid block number: %d-%d-%d: block: %d",
1717                                   device, segment, page, pd->block);
1718#endif
1719              sc->pages_bad++;
1720            }
1721            else if (fd->blocks[pd->block].segment)
1722            {
1723              /**
1724               * @todo
1725               * This may need more work later. Maybe a counter is stored with
1726               * each block so we can tell which is the later block when
1727               * duplicates appear. A power down with a failed wirte could cause
1728               * a duplicate.
1729               */
1730              const rtems_fdisk_segment_ctl* bsc = fd->blocks[pd->block].segment;
1731              rtems_fdisk_error ("duplicate block: %d-%d-%d: " \
1732                                 "duplicate: %d-%d-%d",
1733                                 bsc->device, bsc->segment,
1734                                 fd->blocks[pd->block].page,
1735                                 device, segment, page);
1736              sc->pages_bad++;
1737            }
1738            else
1739            {
1740              /**
1741               * @todo
1742               * Add start up crc checks here.
1743               */
1744              fd->blocks[pd->block].segment = sc;
1745              fd->blocks[pd->block].page    = page;
1746
1747              /*
1748               * The page is active.
1749               */
1750              sc->pages_active++;
1751            }
1752          }
1753          else
1754            sc->pages_bad++;
1755        }
1756      }
1757
1758      /*
1759       * Place the segment on to the correct queue.
1760       */
1761      rtems_fdisk_queue_segment (fd, sc);
1762    }
1763  }
1764
1765  return 0;
1766}
1767
1768/**
1769 * Read a block. The block is checked to see if the page referenced
1770 * is valid and the page has a valid crc.
1771 *
1772 * @param fd The rtems_flashdisk control table.
1773 * @param block The block number to read.
1774 * @param buffer The buffer to write the data into.
1775 * @return 0 No error.
1776 * @return EIO Invalid block size, block number, segment pointer, crc,
1777 *             page flags.
1778 */
1779static bool
1780rtems_fdisk_read_block (rtems_flashdisk* fd,
1781                        uint32_t         block,
1782                        uint8_t*         buffer)
1783{
1784  rtems_fdisk_block_ctl*   bc;
1785  rtems_fdisk_segment_ctl* sc;
1786  rtems_fdisk_page_desc*   pd;
1787
1788#if RTEMS_FDISK_TRACE
1789  rtems_fdisk_info (fd, "read-block:%d", block);
1790#endif
1791
1792  /*
1793   * Broken out to allow info messages when testing.
1794   */
1795
1796  if (block >= (fd->block_count - fd->unavail_blocks))
1797  {
1798    rtems_fdisk_error ("read-block: block out of range: %d", block);
1799    return EIO;
1800  }
1801
1802  bc = &fd->blocks[block];
1803
1804  if (!bc->segment)
1805  {
1806#if RTEMS_FDISK_TRACE
1807    rtems_fdisk_info (fd, "read-block: no segment mapping: %d", block);
1808#endif
1809    memset (buffer, 0xff, fd->block_size);
1810    return 0;
1811  }
1812
1813  sc = fd->blocks[block].segment;
1814  pd = &sc->page_descriptors[bc->page];
1815
1816#if RTEMS_FDISK_TRACE
1817  rtems_fdisk_info (fd,
1818                    " read:%d=>%02d-%03d-%03d: p=%d a=%d u=%d b=%d n=%s: " \
1819                    "f=%04x c=%04x b=%d",
1820                    block, sc->device, sc->segment, bc->page,
1821                    sc->pages, sc->pages_active, sc->pages_used, sc->pages_bad,
1822                    sc->next ? "set" : "null",
1823                    pd->flags, pd->crc, pd->block);
1824#endif
1825
1826  if (rtems_fdisk_page_desc_flags_set (pd, RTEMS_FDISK_PAGE_ACTIVE))
1827  {
1828    if (rtems_fdisk_page_desc_flags_clear (pd, RTEMS_FDISK_PAGE_USED))
1829    {
1830      uint16_t cs;
1831
1832      /*
1833       * We use the segment page offset not the page number used in the
1834       * driver. This skips the page descriptors.
1835       */
1836      int ret = rtems_fdisk_seg_read_page (fd, sc,
1837                                           bc->page + sc->pages_desc, buffer);
1838
1839      if (ret)
1840      {
1841#if RTEMS_FDISK_TRACE
1842        rtems_fdisk_info (fd,
1843                          "read-block:%02d-%03d-%03d: read page failed: %s (%d)",
1844                          sc->device, sc->segment, bc->page,
1845                          strerror (ret), ret);
1846#endif
1847        return ret;
1848      }
1849
1850      cs = rtems_fdisk_page_checksum (buffer, fd->block_size);
1851
1852      if (cs == pd->crc)
1853        return 0;
1854
1855      rtems_fdisk_error ("read-block: crc failure: %d: buffer:%04x page:%04x",
1856                         block, cs, pd->crc);
1857    }
1858    else
1859    {
1860      rtems_fdisk_error ("read-block: block points to used page: %d: %d-%d-%d",
1861                         block, sc->device, sc->segment, bc->page);
1862    }
1863  }
1864  else
1865  {
1866    rtems_fdisk_error ("read-block: block page not active: %d: %d-%d-%d",
1867                       block, sc->device, sc->segment, bc->page);
1868  }
1869
1870  return EIO;
1871}
1872
1873/**
1874 * Write a block. The block:
1875 *
1876 *  # May never have existed in flash before this write.
1877 *  # Exists and needs to be moved to a new page.
1878 *
1879 * If the block does not exist in flash we need to get the next
1880 * segment available to place the page into. The segments with
1881 * available pages are held on the avaliable list sorted on least
1882 * number of available pages as the primary key. Currently there
1883 * is no secondary key. Empty segments are at the end of the list.
1884 *
1885 * If the block already exists we need to set the USED bit in the
1886 * current page's flags. This is a single byte which changes a 1 to
1887 * a 0 and can be done with a single 16 bit write. The driver for
1888 * 8 bit devices should only attempt the write on the changed bit.
1889 *
1890 * @param fd The rtems_flashdisk control table.
1891 * @param block The block number to read.
1892 * @param block_size The size of the block. Must match what we have.
1893 * @param buffer The buffer to write the data into.
1894 * @return 0 No error.
1895 * @return EIO Invalid block size, block number, segment pointer, crc,
1896 *             page flags.
1897 */
1898static int
1899rtems_fdisk_write_block (rtems_flashdisk* fd,
1900                         uint32_t         block,
1901                         const uint8_t*   buffer)
1902{
1903  rtems_fdisk_block_ctl*   bc;
1904  rtems_fdisk_segment_ctl* sc;
1905  rtems_fdisk_page_desc*   pd;
1906  uint32_t                 page;
1907  int                      ret;
1908
1909#if RTEMS_FDISK_TRACE
1910  rtems_fdisk_info (fd, "write-block:%d", block);
1911#endif
1912
1913  /*
1914   * Broken out to allow info messages when testing.
1915   */
1916
1917  if (block >= (fd->block_count - fd->unavail_blocks))
1918  {
1919    rtems_fdisk_error ("write-block: block out of range: %d", block);
1920    return EIO;
1921  }
1922
1923  bc = &fd->blocks[block];
1924
1925  /*
1926   * Does the page exist in flash ?
1927   */
1928  if (bc->segment)
1929  {
1930    sc = bc->segment;
1931    pd = &sc->page_descriptors[bc->page];
1932
1933#if RTEMS_FDISK_TRACE
1934    rtems_fdisk_info (fd, " write:%02d-%03d-%03d: flag used",
1935                      sc->device, sc->segment, bc->page);
1936#endif
1937
1938    /*
1939     * The page exists in flash so see if the page has been changed.
1940     */
1941    if (rtems_fdisk_seg_verify_page (fd, sc->device, sc->segment,
1942                                     bc->page + sc->pages_desc, buffer) == 0)
1943    {
1944#if RTEMS_FDISK_TRACE
1945      rtems_fdisk_info (fd, "write-block:%d=>%02d-%03d-%03d: page verified",
1946                        block, sc->device, sc->segment, bc->page);
1947#endif
1948      return 0;
1949    }
1950
1951    /*
1952     * The page exists in flash so we need to set the used flag
1953     * in the page descriptor. The descriptor is in memory with the
1954     * segment control block. We can assume this memory copy
1955     * matches the flash device.
1956     */
1957
1958    rtems_fdisk_page_desc_set_flags (pd, RTEMS_FDISK_PAGE_USED);
1959
1960    ret = rtems_fdisk_seg_write_page_desc_flags (fd, sc, bc->page, pd);
1961
1962    if (ret)
1963    {
1964#if RTEMS_FDISK_TRACE
1965      rtems_fdisk_info (fd, " write:%02d-%03d-%03d: "      \
1966                        "write used page desc failed: %s (%d)",
1967                        sc->device, sc->segment, bc->page,
1968                        strerror (ret), ret);
1969#endif
1970    }
1971    else
1972    {
1973      sc->pages_active--;
1974      sc->pages_used++;
1975    }
1976
1977    /*
1978     * If possible reuse this segment. This will mean the segment
1979     * needs to be removed from the available list and placed
1980     * back if space is still available.
1981     */
1982    rtems_fdisk_queue_segment (fd, sc);
1983
1984    /*
1985     * If no background compacting then compact in the forground.
1986     * If we compact we ignore the error as there is little we
1987     * can do from here. The write may will work.
1988     */
1989    if ((fd->flags & RTEMS_FDISK_BACKGROUND_COMPACT) == 0)
1990      rtems_fdisk_compact (fd);
1991  }
1992
1993  /*
1994   * Is it time to compact the disk ?
1995   *
1996   * We override the background compaction configruation.
1997   */
1998  if (rtems_fdisk_segment_count_queue (&fd->available) <=
1999      fd->avail_compact_segs)
2000    rtems_fdisk_compact (fd);
2001
2002  /*
2003   * Get the next avaliable segment.
2004   */
2005  sc = rtems_fdisk_segment_queue_pop_head (&fd->available);
2006
2007  /*
2008   * Is the flash disk full ?
2009   */
2010  if (!sc)
2011  {
2012    /*
2013     * If compacting is configured for the background do it now
2014     * to see if we can get some space back.
2015     */
2016    if ((fd->flags & RTEMS_FDISK_BACKGROUND_COMPACT))
2017      rtems_fdisk_compact (fd);
2018
2019    /*
2020     * Try again for some free space.
2021     */
2022    sc = rtems_fdisk_segment_queue_pop_head (&fd->available);
2023
2024    if (!sc)
2025    {
2026      rtems_fdisk_error ("write-block: no available pages");
2027      return ENOSPC;
2028    }
2029  }
2030
2031#if RTEMS_FDISK_TRACE
2032  if (fd->info_level >= 3)
2033  {
2034    char queues[5];
2035    rtems_fdisk_queue_status (fd, sc, queues);
2036    rtems_fdisk_info (fd, " write:%d=>%02d-%03d: queue check: %s",
2037                      block, sc->device, sc->segment, queues);
2038  }
2039#endif
2040
2041  /*
2042   * Find the next avaliable page in the segment.
2043   */
2044
2045  pd = sc->page_descriptors;
2046
2047  for (page = 0; page < sc->pages; page++, pd++)
2048  {
2049    if (rtems_fdisk_page_desc_erased (pd))
2050    {
2051      pd->crc   = rtems_fdisk_page_checksum (buffer, fd->block_size);
2052      pd->block = block;
2053
2054      bc->segment = sc;
2055      bc->page    = page;
2056
2057      rtems_fdisk_page_desc_set_flags (pd, RTEMS_FDISK_PAGE_ACTIVE);
2058
2059#if RTEMS_FDISK_TRACE
2060      rtems_fdisk_info (fd, " write:%d=>%02d-%03d-%03d: write: " \
2061                        "p=%d a=%d u=%d b=%d n=%s: f=%04x c=%04x b=%d",
2062                        block, sc->device, sc->segment, page,
2063                        sc->pages, sc->pages_active, sc->pages_used,
2064                        sc->pages_bad, sc->next ? "set" : "null",
2065                        pd->flags, pd->crc, pd->block);
2066#endif
2067
2068      /*
2069       * We use the segment page offset not the page number used in the
2070       * driver. This skips the page descriptors.
2071       */
2072      ret = rtems_fdisk_seg_write_page (fd, sc, page + sc->pages_desc, buffer);
2073      if (ret)
2074      {
2075#if RTEMS_FDISK_TRACE
2076        rtems_fdisk_info (fd, "write-block:%02d-%03d-%03d: write page failed: " \
2077                          "%s (%d)", sc->device, sc->segment, page,
2078                          strerror (ret), ret);
2079#endif
2080      }
2081      else
2082      {
2083        ret = rtems_fdisk_seg_write_page_desc (fd, sc, page, pd);
2084        if (ret)
2085        {
2086#if RTEMS_FDISK_TRACE
2087          rtems_fdisk_info (fd, "write-block:%02d-%03d-%03d: "  \
2088                            "write page desc failed: %s (%d)",
2089                            sc->device, sc->segment, bc->page,
2090                            strerror (ret), ret);
2091#endif
2092        }
2093        else
2094        {
2095          sc->pages_active++;
2096        }
2097      }
2098
2099      rtems_fdisk_queue_segment (fd, sc);
2100
2101      if (rtems_fdisk_is_erased_blocks_starvation (fd))
2102        rtems_fdisk_compact (fd);
2103
2104      return ret;
2105    }
2106  }
2107
2108  rtems_fdisk_error ("write-block: no erased page descs in segment: %d-%d",
2109                     sc->device, sc->segment);
2110
2111  sc->failed = true;
2112  rtems_fdisk_queue_segment (fd, sc);
2113
2114  return EIO;
2115}
2116
2117/**
2118 * Disk READ request handler. This primitive copies data from the
2119 * flash disk to the supplied buffer and invoke the callout function
2120 * to inform upper layer that reading is completed.
2121 *
2122 * @param req Pointer to the READ block device request info.
2123 * @retval int The ioctl return value.
2124 */
2125static int
2126rtems_fdisk_read (rtems_flashdisk* fd, rtems_blkdev_request* req)
2127{
2128  rtems_blkdev_sg_buffer* sg = req->bufs;
2129  uint32_t                buf;
2130  int                     ret = 0;
2131
2132  for (buf = 0; (ret == 0) && (buf < req->bufnum); buf++, sg++)
2133  {
2134    uint8_t* data;
2135    uint32_t fb;
2136    uint32_t b;
2137    fb = sg->length / fd->block_size;
2138    data = sg->buffer;
2139    for (b = 0; b < fb; b++, data += fd->block_size)
2140    {
2141      ret = rtems_fdisk_read_block (fd, sg->block + b, data);
2142      if (ret)
2143        break;
2144    }
2145  }
2146
2147  req->status = ret ? RTEMS_IO_ERROR : RTEMS_SUCCESSFUL;
2148  req->req_done (req->done_arg, req->status);
2149
2150  return ret;
2151}
2152
2153/**
2154 * Flash disk WRITE request handler. This primitive copies data from
2155 * supplied buffer to flash disk and invoke the callout function to inform
2156 * upper layer that writing is completed.
2157 *
2158 * @param req Pointers to the WRITE block device request info.
2159 * @retval int The ioctl return value.
2160 */
2161static int
2162rtems_fdisk_write (rtems_flashdisk* fd, rtems_blkdev_request* req)
2163{
2164  rtems_blkdev_sg_buffer* sg = req->bufs;
2165  uint32_t                buf;
2166  int                     ret = 0;
2167
2168  for (buf = 0; (ret == 0) && (buf < req->bufnum); buf++, sg++)
2169  {
2170    uint8_t* data;
2171    uint32_t fb;
2172    uint32_t b;
2173    fb = sg->length / fd->block_size;
2174    data = sg->buffer;
2175    for (b = 0; b < fb; b++, data += fd->block_size)
2176    {
2177      ret = rtems_fdisk_write_block (fd, sg->block + b, data);
2178      if (ret)
2179        break;
2180    }
2181  }
2182
2183  req->status = ret ? RTEMS_IO_ERROR : RTEMS_SUCCESSFUL;
2184  req->req_done (req->done_arg, req->status);
2185
2186  return 0;
2187}
2188
2189/**
2190 * Flash disk erase disk.
2191 *
2192 * @param fd The flashdisk data.
2193 * @retval int The ioctl return value.
2194 */
2195static int
2196rtems_fdisk_erase_disk (rtems_flashdisk* fd)
2197{
2198  uint32_t device;
2199  int      ret;
2200
2201#if RTEMS_FDISK_TRACE
2202  rtems_fdisk_info (fd, "erase-disk");
2203#endif
2204
2205  ret = rtems_fdisk_erase_flash (fd);
2206
2207  if (ret == 0)
2208  {
2209    for (device = 0; device < fd->device_count; device++)
2210    {
2211      if (!fd->devices[device].segments)
2212        return ENOMEM;
2213
2214      ret = rtems_fdisk_recover_block_mappings (fd);
2215      if (ret)
2216        break;
2217    }
2218  }
2219
2220  return ret;
2221}
2222
2223/**
2224 * Flash Disk Monitoring data is return in the monitoring data
2225 * structure.
2226 */
2227static int
2228rtems_fdisk_monitoring_data (rtems_flashdisk*          fd,
2229                             rtems_fdisk_monitor_data* data)
2230{
2231  uint32_t i;
2232  uint32_t j;
2233
2234  data->block_size     = fd->block_size;
2235  data->block_count    = fd->block_count;
2236  data->unavail_blocks = fd->unavail_blocks;
2237  data->device_count   = fd->device_count;
2238
2239  data->blocks_used = 0;
2240  for (i = 0; i < fd->block_count; i++)
2241    if (fd->blocks[i].segment)
2242      data->blocks_used++;
2243
2244  data->segs_available = rtems_fdisk_segment_count_queue (&fd->available);
2245  data->segs_used      = rtems_fdisk_segment_count_queue (&fd->used);
2246  data->segs_failed    = rtems_fdisk_segment_count_queue (&fd->failed);
2247
2248  data->segment_count = 0;
2249  data->page_count    = 0;
2250  data->pages_desc    = 0;
2251  data->pages_active  = 0;
2252  data->pages_used    = 0;
2253  data->pages_bad     = 0;
2254  data->seg_erases    = 0;
2255
2256  for (i = 0; i < fd->device_count; i++)
2257  {
2258    data->segment_count += fd->devices[i].segment_count;
2259
2260    for (j = 0; j < fd->devices[i].segment_count; j++)
2261    {
2262      rtems_fdisk_segment_ctl* sc = &fd->devices[i].segments[j];
2263
2264      data->page_count   += sc->pages;
2265      data->pages_desc   += sc->pages_desc;
2266      data->pages_active += sc->pages_active;
2267      data->pages_used   += sc->pages_used;
2268      data->pages_bad    += sc->pages_bad;
2269      data->seg_erases   += sc->erased;
2270    }
2271  }
2272
2273  data->info_level = fd->info_level;
2274  return 0;
2275}
2276
2277/**
2278 * Print to stdout the status of the driver. This is a debugging aid.
2279 */
2280static int
2281rtems_fdisk_print_status (rtems_flashdisk* fd)
2282{
2283#if RTEMS_FDISK_TRACE
2284  uint32_t current_info_level = fd->info_level;
2285  uint32_t total;
2286  uint32_t count;
2287  uint32_t device;
2288
2289  fd->info_level = 3;
2290
2291  rtems_fdisk_printf (fd,
2292                      "Flash Disk Driver Status : %d.%d", fd->major, fd->minor);
2293
2294  rtems_fdisk_printf (fd, "Block count\t%d", fd->block_count);
2295  rtems_fdisk_printf (fd, "Unavail blocks\t%d", fd->unavail_blocks);
2296  count = rtems_fdisk_segment_count_queue (&fd->available);
2297  total = count;
2298  rtems_fdisk_printf (fd, "Available queue\t%ld (%ld)",
2299                      count, rtems_fdisk_segment_queue_count (&fd->available));
2300  count = rtems_fdisk_segment_count_queue (&fd->used);
2301  total += count;
2302  rtems_fdisk_printf (fd, "Used queue\t%ld (%ld)",
2303                      count, rtems_fdisk_segment_queue_count (&fd->used));
2304  count = rtems_fdisk_segment_count_queue (&fd->erase);
2305  total += count;
2306  rtems_fdisk_printf (fd, "Erase queue\t%ld (%ld)",
2307                      count, rtems_fdisk_segment_queue_count (&fd->erase));
2308  count = rtems_fdisk_segment_count_queue (&fd->failed);
2309  total += count;
2310  rtems_fdisk_printf (fd, "Failed queue\t%ld (%ld)",
2311                      count, rtems_fdisk_segment_queue_count (&fd->failed));
2312
2313  count = 0;
2314  for (device = 0; device < fd->device_count; device++)
2315    count += fd->devices[device].segment_count;
2316
2317  rtems_fdisk_printf (fd, "Queue total\t%ld of %ld, %s", total, count,
2318                      total == count ? "ok" : "MISSING");
2319
2320  rtems_fdisk_printf (fd, "Device count\t%d", fd->device_count);
2321
2322  for (device = 0; device < fd->device_count; device++)
2323  {
2324    uint32_t block;
2325    uint32_t seg;
2326
2327    rtems_fdisk_printf (fd, " Device\t\t%ld", device);
2328    rtems_fdisk_printf (fd, "  Segment count\t%ld",
2329                        fd->devices[device].segment_count);
2330
2331    for (seg = 0; seg < fd->devices[device].segment_count; seg++)
2332    {
2333      rtems_fdisk_segment_ctl* sc = &fd->devices[device].segments[seg];
2334      uint32_t                 page;
2335      uint32_t                 erased = 0;
2336      uint32_t                 active = 0;
2337      uint32_t                 used = 0;
2338      bool                     is_active = false;
2339      char                     queues[5];
2340
2341      rtems_fdisk_queue_status (fd, sc, queues);
2342
2343      for (page = 0; page < sc->pages; page++)
2344      {
2345        if (rtems_fdisk_page_desc_erased (&sc->page_descriptors[page]))
2346          erased++;
2347        else if (rtems_fdisk_page_desc_flags_set (&sc->page_descriptors[page],
2348                                                  RTEMS_FDISK_PAGE_ACTIVE))
2349        {
2350          if (rtems_fdisk_page_desc_flags_set (&sc->page_descriptors[page],
2351                                               RTEMS_FDISK_PAGE_USED))
2352            used++;
2353          else
2354          {
2355            active++;
2356            is_active = true;
2357          }
2358        }
2359
2360        for (block = 0; block < fd->block_count; block++)
2361        {
2362          if ((fd->blocks[block].segment == sc) &&
2363              (fd->blocks[block].page == page) && !is_active)
2364            rtems_fdisk_printf (fd,
2365                                "    %ld\t not active when mapped by block %ld",
2366                                page, block);
2367        }
2368      }
2369
2370      count = 0;
2371      for (block = 0; block < fd->block_count; block++)
2372      {
2373        if (fd->blocks[block].segment == sc)
2374          count++;
2375      }
2376
2377      rtems_fdisk_printf (fd, "  %3ld %s p:%3ld a:%3ld/%3ld" \
2378                          " u:%3ld/%3ld e:%3ld/%3ld br:%ld",
2379                          seg, queues,
2380                          sc->pages, sc->pages_active, active,
2381                          sc->pages_used, used, erased,
2382                          sc->pages - (sc->pages_active +
2383                                       sc->pages_used + sc->pages_bad),
2384                          count);
2385    }
2386  }
2387
2388  {
2389    rtems_fdisk_segment_ctl* sc = fd->used.head;
2390    int count = 0;
2391    rtems_fdisk_printf (fd, "Used List:");
2392    while (sc)
2393    {
2394      rtems_fdisk_printf (fd, "  %3d %02d:%03d u:%3ld",
2395                          count, sc->device, sc->segment, sc->pages_used);
2396      sc = sc->next;
2397      count++;
2398    }
2399  }
2400  fd->info_level = current_info_level;
2401
2402  return 0;
2403#else
2404  return ENOSYS;
2405#endif
2406}
2407
2408/**
2409 * Flash disk IOCTL handler.
2410 *
2411 * @param dd Disk device.
2412 * @param req IOCTL request code.
2413 * @param argp IOCTL argument.
2414 * @retval The IOCTL return value
2415 */
2416static int
2417rtems_fdisk_ioctl (rtems_disk_device *dd, uint32_t req, void* argp)
2418{
2419  dev_t                     dev = rtems_disk_get_device_identifier (dd);
2420  rtems_device_minor_number minor = rtems_filesystem_dev_minor_t (dev);
2421  rtems_blkdev_request*     r = argp;
2422  rtems_status_code         sc;
2423
2424  errno = 0;
2425
2426  sc = rtems_semaphore_obtain (rtems_flashdisks[minor].lock, RTEMS_WAIT, 0);
2427  if (sc != RTEMS_SUCCESSFUL)
2428    errno = EIO;
2429  else
2430  {
2431    errno = 0;
2432    switch (req)
2433    {
2434      case RTEMS_BLKIO_REQUEST:
2435        if ((minor >= rtems_flashdisk_count) ||
2436            (rtems_flashdisks[minor].device_count == 0))
2437        {
2438          errno = ENODEV;
2439        }
2440        else
2441        {
2442          switch (r->req)
2443          {
2444            case RTEMS_BLKDEV_REQ_READ:
2445              errno = rtems_fdisk_read (&rtems_flashdisks[minor], r);
2446              break;
2447
2448            case RTEMS_BLKDEV_REQ_WRITE:
2449              errno = rtems_fdisk_write (&rtems_flashdisks[minor], r);
2450              break;
2451
2452            default:
2453              errno = EINVAL;
2454              break;
2455          }
2456        }
2457        break;
2458
2459      case RTEMS_FDISK_IOCTL_ERASE_DISK:
2460        errno = rtems_fdisk_erase_disk (&rtems_flashdisks[minor]);
2461        break;
2462
2463      case RTEMS_FDISK_IOCTL_COMPACT:
2464        errno = rtems_fdisk_compact (&rtems_flashdisks[minor]);
2465        break;
2466
2467      case RTEMS_FDISK_IOCTL_ERASE_USED:
2468        errno = rtems_fdisk_erase_used (&rtems_flashdisks[minor]);
2469        break;
2470
2471      case RTEMS_FDISK_IOCTL_MONITORING:
2472        errno = rtems_fdisk_monitoring_data (&rtems_flashdisks[minor],
2473                                             (rtems_fdisk_monitor_data*) argp);
2474        break;
2475
2476      case RTEMS_FDISK_IOCTL_INFO_LEVEL:
2477        rtems_flashdisks[minor].info_level = (uintptr_t) argp;
2478        break;
2479
2480      case RTEMS_FDISK_IOCTL_PRINT_STATUS:
2481        errno = rtems_fdisk_print_status (&rtems_flashdisks[minor]);
2482        break;
2483
2484      default:
2485        rtems_blkdev_ioctl (dd, req, argp);
2486        break;
2487    }
2488
2489    sc = rtems_semaphore_release (rtems_flashdisks[minor].lock);
2490    if (sc != RTEMS_SUCCESSFUL)
2491      errno = EIO;
2492  }
2493
2494  return errno == 0 ? 0 : -1;
2495}
2496
2497/**
2498 * Flash disk device driver initialization.
2499 *
2500 * @todo Memory clean up on error is really badly handled.
2501 *
2502 * @param major Flash disk major device number.
2503 * @param minor Minor device number, not applicable.
2504 * @param arg Initialization argument, not applicable.
2505 */
2506rtems_device_driver
2507rtems_fdisk_initialize (rtems_device_major_number major,
2508                        rtems_device_minor_number minor,
2509                        void*                     arg __attribute__((unused)))
2510{
2511  const rtems_flashdisk_config* c = rtems_flashdisk_configuration;
2512  rtems_flashdisk*              fd;
2513  rtems_status_code             sc;
2514
2515  sc = rtems_disk_io_initialize ();
2516  if (sc != RTEMS_SUCCESSFUL)
2517    return sc;
2518
2519  sc = rtems_fdisk_crc16_gen_factors (0x8408);
2520  if (sc != RTEMS_SUCCESSFUL)
2521      return sc;
2522
2523  rtems_flashdisks = calloc (rtems_flashdisk_configuration_size,
2524                             sizeof (rtems_flashdisk));
2525
2526  if (!rtems_flashdisks)
2527    return RTEMS_NO_MEMORY;
2528
2529  for (minor = 0; minor < rtems_flashdisk_configuration_size; minor++, c++)
2530  {
2531    char     name[] = RTEMS_FLASHDISK_DEVICE_BASE_NAME "a";
2532    dev_t    dev = rtems_filesystem_make_dev_t (major, minor);
2533    uint32_t device;
2534    uint32_t blocks = 0;
2535    int      ret;
2536
2537    fd = &rtems_flashdisks[minor];
2538
2539    name [sizeof(RTEMS_FLASHDISK_DEVICE_BASE_NAME)] += minor;
2540
2541    fd->major              = major;
2542    fd->minor              = minor;
2543    fd->flags              = c->flags;
2544    fd->compact_segs       = c->compact_segs;
2545    fd->avail_compact_segs = c->avail_compact_segs;
2546    fd->block_size         = c->block_size;
2547    fd->unavail_blocks     = c->unavail_blocks;
2548    fd->info_level         = c->info_level;
2549
2550    for (device = 0; device < c->device_count; device++)
2551      blocks += rtems_fdisk_blocks_in_device (&c->devices[device],
2552                                              c->block_size);
2553
2554    /*
2555     * One copy buffer of a page size.
2556     */
2557    fd->copy_buffer = malloc (c->block_size);
2558    if (!fd->copy_buffer)
2559      return RTEMS_NO_MEMORY;
2560
2561    fd->blocks = calloc (blocks, sizeof (rtems_fdisk_block_ctl));
2562    if (!fd->blocks)
2563      return RTEMS_NO_MEMORY;
2564
2565    fd->block_count = blocks;
2566
2567    fd->devices = calloc (c->device_count, sizeof (rtems_fdisk_device_ctl));
2568    if (!fd->devices)
2569      return RTEMS_NO_MEMORY;
2570
2571    sc = rtems_semaphore_create (rtems_build_name ('F', 'D', 'S', 'K'), 1,
2572                                 RTEMS_PRIORITY | RTEMS_BINARY_SEMAPHORE |
2573                                 RTEMS_INHERIT_PRIORITY, 0, &fd->lock);
2574    if (sc != RTEMS_SUCCESSFUL)
2575    {
2576      rtems_fdisk_error ("disk lock create failed");
2577      free (fd->copy_buffer);
2578      free (fd->blocks);
2579      free (fd->devices);
2580      return sc;
2581    }
2582
2583    sc = rtems_disk_create_phys(dev, c->block_size,
2584                                blocks - fd->unavail_blocks,
2585                                rtems_fdisk_ioctl, NULL, name);
2586    if (sc != RTEMS_SUCCESSFUL)
2587    {
2588      rtems_semaphore_delete (fd->lock);
2589      rtems_disk_delete (dev);
2590      free (fd->copy_buffer);
2591      free (fd->blocks);
2592      free (fd->devices);
2593      rtems_fdisk_error ("disk create phy failed");
2594      return sc;
2595    }
2596
2597    for (device = 0; device < c->device_count; device++)
2598    {
2599      rtems_fdisk_segment_ctl* sc;
2600      uint32_t                 segment_count;
2601      uint32_t                 segment;
2602
2603      segment_count = rtems_fdisk_count_segments (&c->devices[device]);
2604
2605      fd->devices[device].segments = calloc (segment_count,
2606                                             sizeof (rtems_fdisk_segment_ctl));
2607      if (!fd->devices[device].segments)
2608      {
2609        rtems_disk_delete (dev);
2610        rtems_semaphore_delete (fd->lock);
2611        free (fd->copy_buffer);
2612        free (fd->blocks);
2613        free (fd->devices);
2614        return RTEMS_NO_MEMORY;
2615      }
2616
2617      sc = fd->devices[device].segments;
2618
2619      for (segment = 0; segment < c->devices[device].segment_count; segment++)
2620      {
2621        const rtems_fdisk_segment_desc* sd;
2622        uint32_t                        seg_segment;
2623
2624        sd = &c->devices[device].segments[segment];
2625
2626        for (seg_segment = 0; seg_segment < sd->count; seg_segment++, sc++)
2627        {
2628          sc->descriptor = sd;
2629          sc->device     = device;
2630          sc->segment    = seg_segment;
2631          sc->erased     = 0;
2632        }
2633      }
2634
2635      fd->devices[device].segment_count = segment_count;
2636      fd->devices[device].descriptor    = &c->devices[device];
2637    }
2638
2639    fd->device_count = c->device_count;
2640
2641    ret = rtems_fdisk_recover_block_mappings (fd);
2642    if (ret)
2643    {
2644      rtems_disk_delete (dev);
2645      rtems_semaphore_delete (fd->lock);
2646      free (fd->copy_buffer);
2647      free (fd->blocks);
2648      free (fd->devices);
2649      rtems_fdisk_error ("recovery of disk failed: %s (%d)",
2650                         strerror (ret), ret);
2651      return ret;
2652    }
2653
2654    ret = rtems_fdisk_compact (fd);
2655    if (ret)
2656    {
2657      rtems_disk_delete (dev);
2658      rtems_semaphore_delete (fd->lock);
2659      free (fd->copy_buffer);
2660      free (fd->blocks);
2661      free (fd->devices);
2662      rtems_fdisk_error ("compacting of disk failed: %s (%d)",
2663                         strerror (ret), ret);
2664      return ret;
2665    }
2666  }
2667
2668  rtems_flashdisk_count = rtems_flashdisk_configuration_size;
2669
2670  return RTEMS_SUCCESSFUL;
2671}
Note: See TracBrowser for help on using the repository browser.