- Main Page
- Related Pages
- Data Structures
- Files

- File List
- Globals

# drivers/block/journal.c

Go to the documentation of this file.

    00001
    00045 #include <debug.h>
    00046
    00047 #include <block/journal.h>
    00048 #include <checksum/crc8.h>
    00049 #include <dmapool.h>
    00050 #include <workqueue.h>
    00051 #include <app/workqueue.h>
    00052 #include <string.h>
    00053 #include <status-codes.h>
    00054
    00055 #define JOURNAL_MAGIC0          0x42
    00056 #define JOURNAL_CARD0_MAGIC     0x1234
    00057 #define JOURNAL_CARD1_MAGIC     0x5678
    00058
    00059 enum journal_search_state {
    00060         JOURNAL_SEARCH_STARTING,
    00061         JOURNAL_SEARCH_FOR_BLOCK,
    00062         JOURNAL_SEARCH_FOR_ENTRY,
    00063 };
    00064
    00065 #define JOURNAL_ENTRY_FULL_LENGTH       sizeof(struct journal_entry)
    00066 /* Length of the first part of a journal entry (ie. without the
    00067  * committed card magics
    00068  */
    00069 #define JOURNAL_ENTRY_START_LENGTH      (sizeof(struct journal_entry) - 4)
    00070
    00084 static unsigned int journal_get_next_address(
    00085                 struct journal *media, unsigned int prev_addr)
    00086 {
    00087         unsigned int next_addr = prev_addr + JOURNAL_ENTRY_FULL_LENGTH;
    00088
    00089         /* If the next entry is going to cross a page boundary, move it to
    00090          * the start of the next page.
    00091          */
    00092         if (dataflash_get_page_offset(media->storage, next_addr)
    00093                         + JOURNAL_ENTRY_FULL_LENGTH
    00094                         > dataflash_get_page_size(media->storage)) {
    00095                 next_addr = (dataflash_get_page_index(media->storage,
    00096                                         next_addr) + 1)
    00097                                 * dataflash_get_page_size(media->storage);
    00098         }
    00099         return next_addr;
    00100 }
    00101
    00102 static void journal_open_entry_done(struct dataflash_request *request,
    00103                 void *context)
    00104 {
    00105         struct journal *media = context;
    00106
    00107         if (request->status != STATUS_OK) {
    00108                 media->status = JOURNAL_ERROR;
    00109                 dbg_error("Journal ERROR: Dataflash error\n");
    00110         } else if (media->current_entry) {
    00111                 media->status = JOURNAL_ENTRY_OPEN;
    00112         } else {
    00113                 media->status = JOURNAL_IDLE;
    00114         }
    00115
    00116         if (media->flags & JOURNAL_EMPTY) {
    00117                 media->flags &= ~JOURNAL_EMPTY;
    00118         } else if (dataflash_get_block_index(media->storage,
    00119                                 media->last_location)
    00120                         != dataflash_get_block_index(media->storage,
    00121                                 media->current_location)) {
    00122                 /* First entry started in new block, erase previous block */
    00123                 dbg_info("Journal: Done with block %x, erasing\n",
    00124                         dataflash_get_block_index(media->storage,
    00125                                 media->last_location));
    00126
    00127                 media->erase_request.address = dataflash_get_block_index(
    00128                                 media->storage,
    00129                                 media->last_location);
    00130                 dataflash_erase_blocks(&media->erase_request);
    00131         }
    00132
    00133         media->last_location = media->current_location;
    00134         media->current_location = journal_get_next_address(media,
    00135                         media->last_location);
    00136
    00137         if (media->current_location + JOURNAL_ENTRY_FULL_LENGTH >
    00138                         media->start + media->length) {
    00139                 /* Next entry will not fit in journal space. Rewind to start.
    00140                  * We do not need to worry about erasing the used block here,
    00141                  * that will be handled when the first entry is written in the
    00142                  * new block
    00143                  */
    00144                 media->current_location = media->start;
    00145         }
    00146
    00147         if (media->callback)
    00148                 media->callback(media, media->current_entry, media->context);
    00149 }
    00150
    00164 void journal_open_entry(
    00165                 struct journal *media,
    00166                 struct journal_entry *entry,
    00167                 journal_callback_t entry_stored,
    00168                 void *context)
    00169 {
    00170         struct dataflash_request *request;
    00171
    00172         assert(media);
    00173         assert(entry);
    00174         assert(entry->magic0 != 0xffff);
    00175         assert(media->status == JOURNAL_IDLE);
    00176         media->status = JOURNAL_BUSY;
    00177
    00178         request = &media->request;
    00179         media->current_entry = entry;
    00180         media->callback = entry_stored;
    00181         media->context = context;
    00182
    00183         buffer_init_tx(&media->entry_buffer, entry,
    00184                         JOURNAL_ENTRY_START_LENGTH);
    00185
    00186         request->address = media->current_location;
    00187         request->request_done = journal_open_entry_done;
    00188
    00189         dataflash_write_array(request);
    00190 }
    00191
    00192 static void journal_clean_entry_done(struct dataflash_request *request,
    00193                 void *context)
    00194 {
    00195         dma_free(request->data->addr.ptr, sizeof(struct journal_entry));
    00196         journal_open_entry_done(request, context);
    00197 }
    00198
    00217 void journal_clean_last_entry(
    00218                 struct journal *media,
    00219                 journal_callback_t entry_cleaned,
    00220                 void *context)
    00221 {
    00222         struct journal_entry    *entry;
    00223         struct dataflash_request *request;
    00224         phys_addr_t             entry_phys;
    00225
    00226         assert(media);
    00227         assert(media->status == JOURNAL_IDLE);
    00228
    00229         entry = dma_alloc(&entry_phys, sizeof(struct journal_entry));
    00230         if (!entry) {
    00231                 dbg_error("journal: failed to allocate dummy entry\n");
    00232                 if (entry_cleaned)
    00233                         entry_cleaned(media, NULL, context);
    00234                 return;
    00235         }
    00236
    00237         media->status = JOURNAL_BUSY;
    00238         buffer_init_tx_mapped(&media->entry_buffer, entry, entry_phys,
    00239                         sizeof(struct journal_entry));
    00240
    00241         journal_prepare_entry(entry, 0, 0, 0);
    00242
    00243         request = &media->request;
    00244         media->current_entry = NULL;
    00245         media->callback = entry_cleaned;
    00246         media->context = context;
    00247
    00248         request->address = media->current_location;
    00249         request->request_done = journal_clean_entry_done;
    00250
    00251         dataflash_write_array(request);
    00252 }
    00253
    00254 static void journal_commit_card_done(
    00255                 struct dataflash_request *request,
    00256                 void *context)
    00257 {
    00258         struct journal *media = context;
    00259
    00260         if (request->status != STATUS_OK) {
    00261                 media->status = JOURNAL_ERROR;
    00262                 dbg_error("Journal ERROR: Dataflash error\n");
    00263         } else if (media->flags & JOURNAL_CARD0_COMMITTED &&
    00264                         media->flags & JOURNAL_CARD1_COMMITTED) {
    00265                 media->flags &= ~(JOURNAL_CARD0_COMMITTED
    00266                                 | JOURNAL_CARD1_COMMITTED);
    00267                 media->status = JOURNAL_IDLE;
    00268         } else {
    00269                 media->status = JOURNAL_ENTRY_OPEN;
    00270         }
    00271
    00272         if (media->callback)
    00273                 media->callback(media, media->current_entry, media->context);
    00274 }
    00275
    00290 void journal_commit_card(
    00291                 struct journal *media,
    00292                 struct journal_entry *entry,
    00293                 unsigned int card_nr,
    00294                 journal_callback_t card_committed,
    00295                 void *context)
    00296 {
    00297         struct dataflash_request *request;
    00298
    00299         assert(media);
    00300         assert(entry);
    00301         assert(entry == media->current_entry);
    00302         assert(card_nr <= 1);
    00303         assert(media->status == JOURNAL_ENTRY_OPEN);
    00304
    00305         media->status = JOURNAL_BUSY;
    00306         request = &media->request;
    00307         media->callback = card_committed;
    00308         media->context = context;
    00309
    00310         if (card_nr == 0) {
    00311                 buffer_init_tx(&media->entry_buffer,
    00312                                 &entry->committed_card0_magic, 2);
    00313                 request->address = media->last_location +
    00314                                 JOURNAL_ENTRY_START_LENGTH;
    00315                 media->flags |= JOURNAL_CARD0_COMMITTED;
    00316         } else if (card_nr == 1) {
    00317                 buffer_init_tx(&media->entry_buffer,
    00318                                 &entry->committed_card1_magic, 2);
    00319                 request->address = media->last_location +
    00320                                 JOURNAL_ENTRY_START_LENGTH + 2;
    00321                 media->flags |= JOURNAL_CARD1_COMMITTED;
    00322         }
    00323
    00324         request->request_done = journal_commit_card_done;
    00325         dataflash_write_array(request);
    00326 }
    00327
    00338 void journal_close_entry(struct journal *media)
    00339 {
    00340         assert(media);
    00341
    00342         media->flags &= ~(JOURNAL_CARD0_COMMITTED
    00343                         | JOURNAL_CARD1_COMMITTED);
    00344         media->status = JOURNAL_IDLE;
    00345 }
    00346
    00347 static void journal_get_last_entry_done(
    00348                 struct dataflash_request *request,
    00349                 void *context)
    00350 {
    00351         struct journal *media = context;
    00352
    00353         if (request->status != STATUS_OK) {
    00354                 media->status = JOURNAL_ERROR;
    00355                 dbg_error("Journal ERROR: Dataflash error\n");
    00356         } else {
    00357                 media->status = JOURNAL_IDLE;
    00358         }
    00359
    00360         if (media->callback) {
    00361                 if (media->flags & JOURNAL_EMPTY)
    00362                         media->callback(media, NULL, media->context);
    00363                 else
    00364                         media->callback(media, media->current_entry,
    00365                                         media->context);
    00366         }
    00367 }
    00368
    00369 static void journal_read_last_entry(
    00370                 struct journal *media)
    00371 {
    00372         buffer_init_rx(&media->entry_buffer, media->current_entry,
    00373                         JOURNAL_ENTRY_FULL_LENGTH);
    00374
    00375         media->request.address = media->last_location;
    00376         media->request.request_done = journal_get_last_entry_done;
    00377         dataflash_read_array(&media->request);
    00378 }
    00379
    00380 static void journal_run_find_last_entry(
    00381                 struct dataflash_request *request,
    00382                 void *context)
    00383 {
    00384         struct journal *media = context;
    00385
    00386         workqueue_add_item(&journal_workqueue, &media->workqueue_item);
    00387 }
    00388
    00389 static void journal_reerase_previous_block(struct journal *media)
    00390 {
    00391         unsigned int current_block;
    00392         unsigned int first_block;
    00393         unsigned int last_block;
    00394
    00395         /* Return if our entry is not the first in a block */
    00396         if (dataflash_get_block_index(media->storage, media->last_location)
    00397                         == dataflash_get_block_index(media->storage,
    00398                                         media->last_location - 1)) {
    00399                 return;
    00400         }
    00401         /* Return if card0 was committed */
    00402         if (media->current_entry->committed_card0_magic
    00403                         == JOURNAL_CARD0_MAGIC)
    00404                 return;
    00405         /* If we get here the last entry is the first on a block and we lost
    00406          * power before card0 was committed. This could imply that the
    00407          * previous block did not get properly erased; we will erase it again
    00408          * just to be sure.
    00409          */
    00410         current_block = dataflash_get_block_index(
    00411                         media->storage,
    00412                         media->last_location);
    00413         first_block = dataflash_get_block_index(
    00414                         media->storage,
    00415                         media->start);
    00416         last_block = dataflash_get_block_index(
    00417                         media->storage,
    00418                         media->start + media->length - 1);
    00419         if (current_block == first_block)
    00420                 media->erase_request.address = last_block;
    00421         else
    00422                 media->erase_request.address = current_block - 1;
    00423         dataflash_erase_blocks(&media->erase_request);
    00424 }
    00425
    00438 static void journal_find_last_entry(void *data)
    00439 {
    00440         struct journal *media = data;
    00441
    00442         /* Request is NULL when the read starts, not-NULL for each later read */
    00443         switch (media->search_mode) {
    00444         case JOURNAL_SEARCH_STARTING:
    00445                 media->current_location = media->start;
    00446                 media->search_mode = JOURNAL_SEARCH_FOR_BLOCK;
    00447                 break;
    00448         case JOURNAL_SEARCH_FOR_BLOCK:
    00449                 if (media->magic_number == 0xffff) {
    00450                         /* Block is empty, jump to next */
    00451                         media->current_location += dataflash_get_block_size(
    00452                                         media->storage);
    00453                         break;
    00454                 } else {
    00455                         /* Found used block, scan for last entry */
    00456                         media->search_mode = JOURNAL_SEARCH_FOR_ENTRY;
    00457                         media->flags &= ~JOURNAL_EMPTY;
    00458                         /* Fall through */
    00459                 }
    00460         case JOURNAL_SEARCH_FOR_ENTRY:
    00461                 if (media->magic_number == 0xffff) {
    00462                         /* Found end of journal */
    00463                         journal_read_last_entry(media);
    00464                         journal_reerase_previous_block(media);
    00465                         return;
    00466                 } else {
    00467                         media->last_location = media->current_location;
    00468                         media->current_location = journal_get_next_address(
    00469                                         media, media->current_location);
    00470                 }
    00471                 break;
    00472         default:
    00473                 unhandled_case(media->search_mode);
    00474         }
    00475
    00476         if (media->current_location + JOURNAL_ENTRY_FULL_LENGTH
    00477                         > media->start + media->length) {
    00478                 /* Last entry is at end of media, we should write the next
    00479                  * to the start */
    00480                 media->current_location = media->start;
    00481                 journal_read_last_entry(media);
    00482                 return;
    00483         }
    00484
    00485         buffer_init_rx(&media->entry_buffer, &media->magic_number, 2);
    00486         media->request.address = media->current_location;
    00487         dataflash_read_array(&media->request);
    00488 }
    00489
    00503 void journal_get_last_entry(
    00504                 struct journal *media,
    00505                 struct journal_entry *entry,
    00506                 journal_callback_t entry_retrieved,
    00507                 void *context)
    00508 {
    00509         assert(media);
    00510         assert(media->status == JOURNAL_IDLE
    00511                         || media->status == JOURNAL_END_NOT_LOCALIZED);
    00512
    00513         media->status = JOURNAL_BUSY;
    00514         media->callback = entry_retrieved;
    00515         media->context = context;
    00516         media->current_entry = entry;
    00517         /* Assume journal is empty until we find an entry */
    00518         media->flags |= JOURNAL_EMPTY;
    00519         media->search_mode = JOURNAL_SEARCH_STARTING;
    00520
    00521         media->request.request_done = journal_run_find_last_entry;
    00522         journal_find_last_entry(media);
    00523 }
    00524
    00538 void journal_prepare_entry(
    00539                 struct journal_entry *entry,
    00540                 uint32_t card_id,
    00541                 uint32_t block,
    00542                 uint16_t length)
    00543 {
    00544         uint8_t journal_checksum;
    00545
    00546         assert(entry);
    00547
    00548         entry->card_id = card_id;
    00549         entry->block = block;
    00550         entry->length = length;
    00551
    00552         /* Store checksum of non-magic part of journal entry */
    00553         journal_checksum = checksum_crc8((uint8_t *)&entry->length, 10);
    00554         entry->magic0 = JOURNAL_MAGIC0 << 8 | journal_checksum;
    00555         entry->committed_card0_magic = JOURNAL_CARD0_MAGIC;
    00556         entry->committed_card1_magic = JOURNAL_CARD1_MAGIC;
    00557 }
    00558
    00574 bool journal_entry_card_is_intact(
    00575                 struct journal_entry *entry,
    00576                 unsigned int card_nr)
    00577 {
    00578         uint8_t checksum;
    00579
    00580         assert(entry);
    00581         assert(card_nr <= 1);
    00582
    00583         checksum = checksum_crc8((uint8_t *)&entry->length, 10);
    00584
    00585         if (entry->magic0 != (JOURNAL_MAGIC0 << 8 | checksum)) {
    00586                 /* Journal entry not opened successfully so no data
    00587                  * has been written to cards: Both cards are intact
    00588                  */
    00589                 return true;
    00590         } else if (entry->committed_card0_magic != JOURNAL_CARD0_MAGIC) {
    00591                 /* Card0 has not been committed and might be corrupt,
    00592                  * no data written to card1; copy from card1 to card0 */
    00593                 if (card_nr == 0)
    00594                         return false;
    00595                 else
    00596                         return true;
    00597         } else if (entry->committed_card1_magic != JOURNAL_CARD1_MAGIC) {
    00598                 /* Card0 has been successfully written, but card1 might
    00599                  * be corrupt; copy from card0 to card1
    00600                  */
    00601                 if (card_nr == 0)
    00602                         return true;
    00603                 else
    00604                         return false;
    00605         } else {
    00606                 /* Both cards have been successfully written */
    00607                 return true;
    00608         }
    00609 }
    00610
    00623 bool journal_entry_is_consistent(struct journal_entry *entry)
    00624 {
    00625         uint8_t checksum;
    00626
    00627         assert(entry);
    00628
    00629         checksum = checksum_crc8((uint8_t *)&entry->length, 10);
    00630
    00631         /* If the checksum does not match, there should not be any data in
    00632          * any of the commit fields. If we find anything here without a
    00633          * correct checksum, something is very wrong; most probably the
    00634          * storage media is failing...
    00635          */
    00636         if (entry->magic0 != (JOURNAL_MAGIC0 << 8 | checksum)) {
    00637                 if (entry->committed_card0_magic != 0xffff ||
    00638                                 entry->committed_card1_magic != 0xffff)
    00639                         return false;
    00640         }
    00641         return true;
    00642 }
    00643
    00657 void journal_init(
    00658                 struct journal *media,
    00659                 struct dataflash_device *storage,
    00660                 unsigned int start_addr,
    00661                 unsigned int length)
    00662 {
    00663         assert(media);
    00664         assert(storage);
    00665
    00666         memset(media, 0, sizeof(struct journal));
    00667         media->status = JOURNAL_END_NOT_LOCALIZED;
    00668
    00669         if (dataflash_get_block_index(storage, start_addr - 1)
    00670                         == dataflash_get_block_index(storage, start_addr)) {
    00671                 /* Not aligned with block boundary, start at next block */
    00672                 start_addr = dataflash_get_block_size(storage) *
    00673                         (dataflash_get_block_index(storage, start_addr) + 1);
    00674                 dbg_warning("Journal: Moving start address to %x to align "
    00675                                 "with block boundary (block %x)\n",
    00676                                 start_addr,
    00677                                 dataflash_get_block_index(storage,
    00678                                         start_addr));
    00679         }
    00680         assert(start_addr + length <= dataflash_get_device_size(storage));
    00681
    00682         media->start = start_addr;
    00683         media->length = length;
    00684         media->storage = storage;
    00685         workqueue_init_item(&media->workqueue_item,
    00686                         journal_find_last_entry, media);
    00687
    00688         /* Set up the request for later use */
    00689         media->request.data = &media->entry_buffer;
    00690         media->request.device = media->storage;
    00691         media->request.context = media;
    00692
    00693         media->erase_request.device = media->storage;
    00694         media->erase_request.length = 1;
    00695 }