| /* |
| * kernel/power/tuxonice_bio_devinfo.c |
| * |
| * Copyright (C) 2009-2010 Nigel Cunningham (nigel at tuxonice net) |
| * |
| * Distributed under GPLv2. |
| * |
| */ |
| |
| #include <linux/mm_types.h> |
| #include "tuxonice_bio.h" |
| #include "tuxonice_bio_internal.h" |
| #include "tuxonice_alloc.h" |
| #include "tuxonice_ui.h" |
| #include "tuxonice.h" |
| #include "tuxonice_io.h" |
| |
| static struct toi_bdev_info *prio_chain_head; |
| static int num_chains; |
| |
| /* Pointer to current entry being loaded/saved. */ |
| struct toi_extent_iterate_state toi_writer_posn; |
| |
| #define metadata_size (sizeof(struct toi_bdev_info) - \ |
| offsetof(struct toi_bdev_info, uuid)) |
| |
| /* |
| * After section 0 (header) comes 2 => next_section[0] = 2 |
| */ |
| static int next_section[3] = { 2, 3, 1 }; |
| |
| /** |
| * dump_block_chains - print the contents of the bdev info array. |
| **/ |
| void dump_block_chains(void) |
| { |
| int i = 0; |
| int j; |
| struct toi_bdev_info *cur_chain = prio_chain_head; |
| |
| while (cur_chain) { |
| struct hibernate_extent *this = cur_chain->blocks.first; |
| |
| printk(KERN_DEBUG "Chain %d (prio %d):", i, cur_chain->prio); |
| |
| while (this) { |
| printk(KERN_CONT " [%lu-%lu]%s", this->start, |
| this->end, this->next ? "," : ""); |
| this = this->next; |
| } |
| |
| printk("\n"); |
| cur_chain = cur_chain->next; |
| i++; |
| } |
| |
| printk(KERN_DEBUG "Saved states:\n"); |
| for (i = 0; i < 4; i++) { |
| printk(KERN_DEBUG "Slot %d: Chain %d.\n", i, toi_writer_posn.saved_chain_number[i]); |
| |
| cur_chain = prio_chain_head; |
| j = 0; |
| while (cur_chain) { |
| printk(KERN_DEBUG " Chain %d: Extent %d. Offset %lu.\n", |
| j, cur_chain->saved_state[i].extent_num, |
| cur_chain->saved_state[i].offset); |
| cur_chain = cur_chain->next; |
| j++; |
| } |
| printk(KERN_CONT "\n"); |
| } |
| } |
| |
| /** |
| * |
| **/ |
| static void toi_extent_chain_next(void) |
| { |
| struct toi_bdev_info *this = toi_writer_posn.current_chain; |
| |
| if (!this->blocks.current_extent) |
| return; |
| |
| if (this->blocks.current_offset == this->blocks.current_extent->end) { |
| if (this->blocks.current_extent->next) { |
| this->blocks.current_extent = this->blocks.current_extent->next; |
| this->blocks.current_offset = this->blocks.current_extent->start; |
| } else { |
| this->blocks.current_extent = NULL; |
| this->blocks.current_offset = 0; |
| } |
| } else |
| this->blocks.current_offset++; |
| } |
| |
| /** |
| * |
| */ |
| |
| static struct toi_bdev_info *__find_next_chain_same_prio(void) |
| { |
| struct toi_bdev_info *start_chain = toi_writer_posn.current_chain; |
| struct toi_bdev_info *this = start_chain; |
| int orig_prio = this->prio; |
| |
| do { |
| this = this->next; |
| |
| if (!this) |
| this = prio_chain_head; |
| |
| /* Back on original chain? Use it again. */ |
| if (this == start_chain) |
| return start_chain; |
| |
| } while (!this->blocks.current_extent || this->prio != orig_prio); |
| |
| return this; |
| } |
| |
| static void find_next_chain(void) |
| { |
| struct toi_bdev_info *this; |
| |
| this = __find_next_chain_same_prio(); |
| |
| /* |
| * If we didn't get another chain of the same priority that we |
| * can use, look for the next priority. |
| */ |
| while (this && !this->blocks.current_extent) |
| this = this->next; |
| |
| toi_writer_posn.current_chain = this; |
| } |
| |
| /** |
| * toi_extent_state_next - go to the next extent |
| * @blocks: The number of values to progress. |
| * @stripe_mode: Whether to spread usage across all chains. |
| * |
| * Given a state, progress to the next valid entry. We may begin in an |
| * invalid state, as we do when invoked after extent_state_goto_start below. |
| * |
| * When using compression and expected_compression > 0, we let the image size |
| * be larger than storage, so we can validly run out of data to return. |
| **/ |
| static unsigned long toi_extent_state_next(int blocks, int current_stream) |
| { |
| int i; |
| |
| if (!toi_writer_posn.current_chain) |
| return -ENOSPC; |
| |
| /* Assume chains always have lengths that are multiples of @blocks */ |
| for (i = 0; i < blocks; i++) |
| toi_extent_chain_next(); |
| |
| /* The header stream is not striped */ |
| if (current_stream || !toi_writer_posn.current_chain->blocks.current_extent) |
| find_next_chain(); |
| |
| return toi_writer_posn.current_chain ? 0 : -ENOSPC; |
| } |
| |
| static void toi_insert_chain_in_prio_list(struct toi_bdev_info *this) |
| { |
| struct toi_bdev_info **prev_ptr; |
| struct toi_bdev_info *cur; |
| |
| /* Loop through the existing chain, finding where to insert it */ |
| prev_ptr = &prio_chain_head; |
| cur = prio_chain_head; |
| |
| while (cur && cur->prio >= this->prio) { |
| prev_ptr = &cur->next; |
| cur = cur->next; |
| } |
| |
| this->next = *prev_ptr; |
| *prev_ptr = this; |
| |
| this = prio_chain_head; |
| while (this) |
| this = this->next; |
| num_chains++; |
| } |
| |
| /** |
| * toi_extent_state_goto_start - reinitialize an extent chain iterator |
| * @state: Iterator to reinitialize |
| **/ |
| void toi_extent_state_goto_start(void) |
| { |
| struct toi_bdev_info *this = prio_chain_head; |
| |
| while (this) { |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, |
| "Setting current extent to %p.", this->blocks.first); |
| this->blocks.current_extent = this->blocks.first; |
| if (this->blocks.current_extent) { |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, |
| "Setting current offset to %lu.", |
| this->blocks.current_extent->start); |
| this->blocks.current_offset = this->blocks.current_extent->start; |
| } |
| |
| this = this->next; |
| } |
| |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Setting current chain to %p.", prio_chain_head); |
| toi_writer_posn.current_chain = prio_chain_head; |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Leaving extent state goto start."); |
| } |
| |
| /** |
| * toi_extent_state_save - save state of the iterator |
| * @state: Current state of the chain |
| * @saved_state: Iterator to populate |
| * |
| * Given a state and a struct hibernate_extent_state_store, save the current |
| * position in a format that can be used with relocated chains (at |
| * resume time). |
| **/ |
| void toi_extent_state_save(int slot) |
| { |
| struct toi_bdev_info *cur_chain = prio_chain_head; |
| struct hibernate_extent *extent; |
| struct hibernate_extent_saved_state *chain_state; |
| int i = 0; |
| |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "toi_extent_state_save, slot %d.", slot); |
| |
| if (!toi_writer_posn.current_chain) { |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "No current chain => " "chain_num = -1."); |
| toi_writer_posn.saved_chain_number[slot] = -1; |
| return; |
| } |
| |
| while (cur_chain) { |
| i++; |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Saving chain %d (%p) " |
| "state, slot %d.", i, cur_chain, slot); |
| |
| chain_state = &cur_chain->saved_state[slot]; |
| |
| chain_state->offset = cur_chain->blocks.current_offset; |
| |
| if (toi_writer_posn.current_chain == cur_chain) { |
| toi_writer_posn.saved_chain_number[slot] = i; |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "This is the chain " |
| "we were on => chain_num is %d.", i); |
| } |
| |
| if (!cur_chain->blocks.current_extent) { |
| chain_state->extent_num = 0; |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "No current extent " |
| "for this chain => extent_num %d is 0.", i); |
| cur_chain = cur_chain->next; |
| continue; |
| } |
| |
| extent = cur_chain->blocks.first; |
| chain_state->extent_num = 1; |
| |
| while (extent != cur_chain->blocks.current_extent) { |
| chain_state->extent_num++; |
| extent = extent->next; |
| } |
| |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "extent num %d is %d.", i, |
| chain_state->extent_num); |
| |
| cur_chain = cur_chain->next; |
| } |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Completed saving extent state slot %d.", slot); |
| } |
| |
| /** |
| * toi_extent_state_restore - restore the position saved by extent_state_save |
| * @state: State to populate |
| * @saved_state: Iterator saved to restore |
| **/ |
| void toi_extent_state_restore(int slot) |
| { |
| int i = 0; |
| struct toi_bdev_info *cur_chain = prio_chain_head; |
| struct hibernate_extent_saved_state *chain_state; |
| |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "toi_extent_state_restore - slot %d.", slot); |
| |
| if (toi_writer_posn.saved_chain_number[slot] == -1) { |
| toi_writer_posn.current_chain = NULL; |
| return; |
| } |
| |
| while (cur_chain) { |
| int posn; |
| int j; |
| i++; |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Restoring chain %d (%p) " |
| "state, slot %d.", i, cur_chain, slot); |
| |
| chain_state = &cur_chain->saved_state[slot]; |
| |
| posn = chain_state->extent_num; |
| |
| cur_chain->blocks.current_extent = cur_chain->blocks.first; |
| cur_chain->blocks.current_offset = chain_state->offset; |
| |
| if (i == toi_writer_posn.saved_chain_number[slot]) { |
| toi_writer_posn.current_chain = cur_chain; |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Found current chain."); |
| } |
| |
| for (j = 0; j < 4; j++) |
| if (i == toi_writer_posn.saved_chain_number[j]) { |
| toi_writer_posn.saved_chain_ptr[j] = cur_chain; |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, |
| "Found saved chain ptr %d (%p) (offset" |
| " %d).", j, cur_chain, |
| cur_chain->saved_state[j].offset); |
| } |
| |
| if (posn) { |
| while (--posn) |
| cur_chain->blocks.current_extent = |
| cur_chain->blocks.current_extent->next; |
| } else |
| cur_chain->blocks.current_extent = NULL; |
| |
| cur_chain = cur_chain->next; |
| } |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Done."); |
| if (test_action_state(TOI_LOGALL)) |
| dump_block_chains(); |
| } |
| |
| /* |
| * Storage needed |
| * |
| * Returns amount of space in the image header required |
| * for the chain data. This ignores the links between |
| * pages, which we factor in when allocating the space. |
| */ |
| int toi_bio_devinfo_storage_needed(void) |
| { |
| int result = sizeof(num_chains); |
| struct toi_bdev_info *chain = prio_chain_head; |
| |
| while (chain) { |
| result += metadata_size; |
| |
| /* Chain size */ |
| result += sizeof(int); |
| |
| /* Extents */ |
| result += (2 * sizeof(unsigned long) * chain->blocks.num_extents); |
| |
| chain = chain->next; |
| } |
| |
| result += 4 * sizeof(int); |
| return result; |
| } |
| |
| static unsigned long chain_pages_used(struct toi_bdev_info *chain) |
| { |
| struct hibernate_extent *this = chain->blocks.first; |
| struct hibernate_extent_saved_state *state = &chain->saved_state[3]; |
| unsigned long size = 0; |
| int extent_idx = 1; |
| |
| if (!state->extent_num) { |
| if (!this) |
| return 0; |
| else |
| return chain->blocks.size; |
| } |
| |
| while (extent_idx < state->extent_num) { |
| size += (this->end - this->start + 1); |
| this = this->next; |
| extent_idx++; |
| } |
| |
| /* We didn't use the one we're sitting on, so don't count it */ |
| return size + state->offset - this->start; |
| } |
| |
| /** |
| * toi_serialise_extent_chain - write a chain in the image |
| * @chain: Chain to write. |
| **/ |
| static int toi_serialise_extent_chain(struct toi_bdev_info *chain) |
| { |
| struct hibernate_extent *this; |
| int ret; |
| int i = 1; |
| |
| chain->pages_used = chain_pages_used(chain); |
| |
| if (test_action_state(TOI_LOGALL)) |
| dump_block_chains(); |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Serialising chain (dev_t %lx).", chain->dev_t); |
| /* Device info - dev_t, prio, bmap_shift, blocks per page, positions */ |
| ret = toiActiveAllocator->rw_header_chunk(WRITE, &toi_blockwriter_ops, |
| (char *)&chain->uuid, metadata_size); |
| if (ret) |
| return ret; |
| |
| /* Num extents */ |
| ret = toiActiveAllocator->rw_header_chunk(WRITE, &toi_blockwriter_ops, |
| (char *)&chain->blocks.num_extents, sizeof(int)); |
| if (ret) |
| return ret; |
| |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "%d extents.", chain->blocks.num_extents); |
| |
| this = chain->blocks.first; |
| while (this) { |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Extent %d.", i); |
| ret = toiActiveAllocator->rw_header_chunk(WRITE, |
| &toi_blockwriter_ops, |
| (char *)this, 2 * sizeof(this->start)); |
| if (ret) |
| return ret; |
| this = this->next; |
| i++; |
| } |
| |
| return ret; |
| } |
| |
| int toi_serialise_extent_chains(void) |
| { |
| struct toi_bdev_info *this = prio_chain_head; |
| int result; |
| |
| /* Write the number of chains */ |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Write number of chains (%d)", num_chains); |
| result = toiActiveAllocator->rw_header_chunk(WRITE, |
| &toi_blockwriter_ops, (char *)&num_chains, |
| sizeof(int)); |
| if (result) |
| return result; |
| |
| /* Then the chains themselves */ |
| while (this) { |
| result = toi_serialise_extent_chain(this); |
| if (result) |
| return result; |
| this = this->next; |
| } |
| |
| /* |
| * Finally, the chain we should be on at the start of each |
| * section. |
| */ |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Saved chain numbers."); |
| result = toiActiveAllocator->rw_header_chunk(WRITE, |
| &toi_blockwriter_ops, |
| (char *)&toi_writer_posn.saved_chain_number[0], |
| 4 * sizeof(int)); |
| |
| return result; |
| } |
| |
| int toi_register_storage_chain(struct toi_bdev_info *new) |
| { |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Inserting chain %p into list.", new); |
| toi_insert_chain_in_prio_list(new); |
| return 0; |
| } |
| |
| static void free_bdev_info(struct toi_bdev_info *chain) |
| { |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Free chain %p.", chain); |
| |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, " - Block extents."); |
| toi_put_extent_chain(&chain->blocks); |
| |
| /* |
| * The allocator may need to do more than just free the chains |
| * (swap_free, for example). Don't call from boot kernel. |
| */ |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, " - Allocator extents."); |
| if (chain->allocator) |
| chain->allocator->bio_allocator_ops->free_storage(chain); |
| |
| /* |
| * Dropping out of reading atomic copy? Need to undo |
| * toi_open_by_devnum. |
| */ |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, " - Bdev."); |
| if (chain->bdev && !IS_ERR(chain->bdev) && |
| chain->bdev != resume_block_device && |
| chain->bdev != header_block_device && test_toi_state(TOI_TRYING_TO_RESUME)) |
| toi_close_bdev(chain->bdev); |
| |
| /* Poison */ |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, " - Struct."); |
| toi_kfree(39, chain, sizeof(*chain)); |
| |
| if (prio_chain_head == chain) |
| prio_chain_head = NULL; |
| |
| num_chains--; |
| } |
| |
| void free_all_bdev_info(void) |
| { |
| struct toi_bdev_info *this = prio_chain_head; |
| |
| while (this) { |
| struct toi_bdev_info *next = this->next; |
| free_bdev_info(this); |
| this = next; |
| } |
| |
| memset((char *)&toi_writer_posn, 0, sizeof(toi_writer_posn)); |
| prio_chain_head = NULL; |
| } |
| |
| static void set_up_start_position(void) |
| { |
| toi_writer_posn.current_chain = prio_chain_head; |
| go_next_page(0, 0); |
| } |
| |
| /** |
| * toi_load_extent_chain - read back a chain saved in the image |
| * @chain: Chain to load |
| * |
| * The linked list of extents is reconstructed from the disk. chain will point |
| * to the first entry. |
| **/ |
| int toi_load_extent_chain(int index, int *num_loaded) |
| { |
| struct toi_bdev_info *chain = toi_kzalloc(39, |
| sizeof(struct toi_bdev_info), GFP_ATOMIC); |
| struct hibernate_extent *this, *last = NULL; |
| int i, ret; |
| |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Loading extent chain %d.", index); |
| /* Get dev_t, prio, bmap_shift, blocks per page, positions */ |
| ret = toiActiveAllocator->rw_header_chunk_noreadahead(READ, NULL, |
| (char *)&chain->uuid, metadata_size); |
| |
| if (ret) { |
| printk(KERN_ERR "Failed to read the size of extent chain.\n"); |
| toi_kfree(39, chain, sizeof(*chain)); |
| return 1; |
| } |
| |
| toi_bkd.pages_used[index] = chain->pages_used; |
| |
| ret = toiActiveAllocator->rw_header_chunk_noreadahead(READ, NULL, |
| (char *)&chain->blocks.num_extents, |
| sizeof(int)); |
| if (ret) { |
| printk(KERN_ERR "Failed to read the size of extent chain.\n"); |
| toi_kfree(39, chain, sizeof(*chain)); |
| return 1; |
| } |
| |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "%d extents.", chain->blocks.num_extents); |
| |
| for (i = 0; i < chain->blocks.num_extents; i++) { |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Extent %d.", i + 1); |
| |
| this = toi_kzalloc(2, sizeof(struct hibernate_extent), TOI_ATOMIC_GFP); |
| if (!this) { |
| printk(KERN_INFO "Failed to allocate a new extent.\n"); |
| free_bdev_info(chain); |
| return -ENOMEM; |
| } |
| this->next = NULL; |
| /* Get the next page */ |
| ret = toiActiveAllocator->rw_header_chunk_noreadahead(READ, |
| NULL, (char *)this, |
| 2 * sizeof(this->start)); |
| if (ret) { |
| printk(KERN_INFO "Failed to read an extent.\n"); |
| toi_kfree(2, this, sizeof(struct hibernate_extent)); |
| free_bdev_info(chain); |
| return 1; |
| } |
| |
| if (last) |
| last->next = this; |
| else { |
| char b1[32], b2[32], b3[32]; |
| /* |
| * Open the bdev |
| */ |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, |
| "Chain dev_t is %s. Resume dev t is %s. Header" |
| " bdev_t is %s.\n", |
| format_dev_t(b1, chain->dev_t), |
| format_dev_t(b2, resume_dev_t), |
| format_dev_t(b3, toi_sig_data->header_dev_t)); |
| |
| if (chain->dev_t == resume_dev_t) |
| chain->bdev = resume_block_device; |
| else if (chain->dev_t == toi_sig_data->header_dev_t) |
| chain->bdev = header_block_device; |
| else { |
| chain->bdev = toi_open_bdev(chain->uuid, chain->dev_t, 1); |
| if (IS_ERR(chain->bdev)) { |
| free_bdev_info(chain); |
| return -ENODEV; |
| } |
| } |
| |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Chain bmap shift " |
| "is %d and blocks per page is %d.", |
| chain->bmap_shift, chain->blocks_per_page); |
| |
| chain->blocks.first = this; |
| |
| /* |
| * Couldn't do this earlier, but can't do |
| * goto_start now - we may have already used blocks |
| * in the first chain. |
| */ |
| chain->blocks.current_extent = this; |
| chain->blocks.current_offset = this->start; |
| |
| /* |
| * Can't wait until we've read the whole chain |
| * before we insert it in the list. We might need |
| * this chain to read the next page in the header |
| */ |
| toi_insert_chain_in_prio_list(chain); |
| } |
| |
| /* |
| * We have to wait until 2 extents are loaded before setting up |
| * properly because if the first extent has only one page, we |
| * will need to put the position on the second extent. Sounds |
| * obvious, but it wasn't! |
| */ |
| (*num_loaded)++; |
| if ((*num_loaded) == 2) |
| set_up_start_position(); |
| last = this; |
| } |
| |
| /* |
| * Shouldn't get empty chains, but it's not impossible. Link them in so |
| * they get freed properly later. |
| */ |
| if (!chain->blocks.num_extents) |
| toi_insert_chain_in_prio_list(chain); |
| |
| if (!chain->blocks.current_extent) { |
| chain->blocks.current_extent = chain->blocks.first; |
| if (chain->blocks.current_extent) |
| chain->blocks.current_offset = chain->blocks.current_extent->start; |
| } |
| return 0; |
| } |
| |
| int toi_load_extent_chains(void) |
| { |
| int result; |
| int to_load; |
| int i; |
| int extents_loaded = 0; |
| |
| result = toiActiveAllocator->rw_header_chunk_noreadahead(READ, NULL, |
| (char *)&to_load, sizeof(int)); |
| if (result) |
| return result; |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "%d chains to read.", to_load); |
| |
| for (i = 0; i < to_load; i++) { |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, " >> Loading chain %d/%d.", i, to_load); |
| result = toi_load_extent_chain(i, &extents_loaded); |
| if (result) |
| return result; |
| } |
| |
| /* If we never got to a second extent, we still need to do this. */ |
| if (extents_loaded == 1) |
| set_up_start_position(); |
| |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Save chain numbers."); |
| result = toiActiveAllocator->rw_header_chunk_noreadahead(READ, |
| &toi_blockwriter_ops, |
| (char *)&toi_writer_posn. |
| saved_chain_number[0], |
| 4 * sizeof(int)); |
| |
| return result; |
| } |
| |
| static int toi_end_of_stream(int writing, int section_barrier) |
| { |
| struct toi_bdev_info *cur_chain = toi_writer_posn.current_chain; |
| int compare_to = next_section[current_stream]; |
| struct toi_bdev_info *compare_chain = toi_writer_posn.saved_chain_ptr[compare_to]; |
| int compare_offset = compare_chain ? compare_chain->saved_state[compare_to].offset : 0; |
| |
| if (!section_barrier) |
| return 0; |
| |
| if (!cur_chain) |
| return 1; |
| |
| if (cur_chain == compare_chain && cur_chain->blocks.current_offset == compare_offset) { |
| if (writing) { |
| if (!current_stream) { |
| debug_broken_header(); |
| return 1; |
| } |
| } else { |
| more_readahead = 0; |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, |
| "Reached the end of stream %d " |
| "(not an error).", current_stream); |
| return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * go_next_page - skip blocks to the start of the next page |
| * @writing: Whether we're reading or writing the image. |
| * |
| * Go forward one page. |
| **/ |
| int go_next_page(int writing, int section_barrier) |
| { |
| struct toi_bdev_info *cur_chain = toi_writer_posn.current_chain; |
| int max = cur_chain ? cur_chain->blocks_per_page : 1; |
| |
| /* Nope. Go foward a page - or maybe two. Don't stripe the header, |
| * so that bad fragmentation doesn't put the extent data containing |
| * the location of the second page out of the first header page. |
| */ |
| if (toi_extent_state_next(max, current_stream)) { |
| /* Don't complain if readahead falls off the end */ |
| if (writing && section_barrier) { |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Extent state eof. " |
| "Expected compression ratio too optimistic?"); |
| if (test_action_state(TOI_LOGALL)) |
| dump_block_chains(); |
| } |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Ran out of extents to " |
| "read/write. (Not necessarily a fatal error."); |
| return -ENOSPC; |
| } |
| |
| return 0; |
| } |
| |
| int devices_of_same_priority(struct toi_bdev_info *this) |
| { |
| struct toi_bdev_info *check = prio_chain_head; |
| int i = 0; |
| |
| while (check) { |
| if (check->prio == this->prio) |
| i++; |
| check = check->next; |
| } |
| |
| return i; |
| } |
| |
| /** |
| * toi_bio_rw_page - do i/o on the next disk page in the image |
| * @writing: Whether reading or writing. |
| * @page: Page to do i/o on. |
| * @is_readahead: Whether we're doing readahead |
| * @free_group: The group used in allocating the page |
| * |
| * Submit a page for reading or writing, possibly readahead. |
| * Pass the group used in allocating the page as well, as it should |
| * be freed on completion of the bio if we're writing the page. |
| **/ |
| int toi_bio_rw_page(int writing, struct page *page, int is_readahead, int free_group) |
| { |
| int result = toi_end_of_stream(writing, 1); |
| struct toi_bdev_info *dev_info = toi_writer_posn.current_chain; |
| |
| if (result) { |
| if (writing) |
| abort_hibernate(TOI_INSUFFICIENT_STORAGE, |
| "Insufficient storage for your image."); |
| else |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Seeking to " |
| "read/write another page when stream has " "ended."); |
| return -ENOSPC; |
| } |
| |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, |
| "%s %lx:%ld", |
| writing ? "Write" : "Read", dev_info->dev_t, dev_info->blocks.current_offset); |
| |
| result = toi_do_io(writing, dev_info->bdev, |
| dev_info->blocks.current_offset << dev_info->bmap_shift, |
| page, is_readahead, 0, free_group); |
| |
| /* Ignore the result here - will check end of stream if come in again */ |
| go_next_page(writing, 1); |
| |
| if (result) |
| printk(KERN_ERR "toi_do_io returned %d.\n", result); |
| return result; |
| } |
| |
| dev_t get_header_dev_t(void) |
| { |
| return prio_chain_head->dev_t; |
| } |
| |
| struct block_device *get_header_bdev(void) |
| { |
| return prio_chain_head->bdev; |
| } |
| |
| unsigned long get_headerblock(void) |
| { |
| return prio_chain_head->blocks.first->start << prio_chain_head->bmap_shift; |
| } |
| |
| int get_main_pool_phys_params(void) |
| { |
| struct toi_bdev_info *this = prio_chain_head; |
| int result; |
| |
| while (this) { |
| result = this->allocator->bio_allocator_ops->bmap(this); |
| if (result) |
| return result; |
| this = this->next; |
| } |
| |
| return 0; |
| } |
| |
| static int apply_header_reservation(void) |
| { |
| int i; |
| |
| if (!header_pages_reserved) { |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "No header pages reserved at the moment."); |
| return 0; |
| } |
| |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Applying header reservation."); |
| |
| /* Apply header space reservation */ |
| toi_extent_state_goto_start(); |
| |
| for (i = 0; i < header_pages_reserved; i++) |
| if (go_next_page(1, 0)) |
| return -ENOSPC; |
| |
| /* The end of header pages will be the start of pageset 2 */ |
| toi_extent_state_save(2); |
| |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Finished applying header reservation."); |
| return 0; |
| } |
| |
| static int toi_bio_register_storage(void) |
| { |
| int result = 0; |
| struct toi_module_ops *this_module; |
| |
| list_for_each_entry(this_module, &toi_modules, module_list) { |
| if (!this_module->enabled || this_module->type != BIO_ALLOCATOR_MODULE) |
| continue; |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, |
| "Registering storage from %s.", this_module->name); |
| result = this_module->bio_allocator_ops->register_storage(); |
| if (result) |
| break; |
| } |
| |
| return result; |
| } |
| |
| int toi_bio_allocate_storage(unsigned long request) |
| { |
| struct toi_bdev_info *chain = prio_chain_head; |
| unsigned long to_get = request; |
| unsigned long extra_pages, needed; |
| int no_free = 0; |
| |
| if (!chain) { |
| int result = toi_bio_register_storage(); |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "toi_bio_allocate_storage: " |
| "Registering storage."); |
| if (result) |
| return 0; |
| chain = prio_chain_head; |
| if (!chain) { |
| printk("TuxOnIce: No storage was registered.\n"); |
| return 0; |
| } |
| } |
| |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "toi_bio_allocate_storage: " |
| "Request is %lu pages.", request); |
| extra_pages = DIV_ROUND_UP(request * (sizeof(unsigned long) |
| + sizeof(int)), PAGE_SIZE); |
| needed = request + extra_pages + header_pages_reserved; |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Adding %lu extra pages and %lu " |
| "for header => %lu.", extra_pages, header_pages_reserved, needed); |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Already allocated %lu pages.", raw_pages_allocd); |
| |
| to_get = needed > raw_pages_allocd ? needed - raw_pages_allocd : 0; |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Need to get %lu pages.", to_get); |
| |
| if (!to_get) |
| return apply_header_reservation(); |
| |
| while (to_get && chain) { |
| int num_group = devices_of_same_priority(chain); |
| int divisor = num_group - no_free; |
| int i; |
| unsigned long portion = DIV_ROUND_UP(to_get, divisor); |
| unsigned long got = 0; |
| unsigned long got_this_round = 0; |
| struct toi_bdev_info *top = chain; |
| |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, |
| " Start of loop. To get is %lu. Divisor is %d.", to_get, divisor); |
| no_free = 0; |
| |
| /* |
| * We're aiming to spread the allocated storage as evenly |
| * as possible, but we also want to get all the storage we |
| * can off this priority. |
| */ |
| for (i = 0; i < num_group; i++) { |
| struct toi_bio_allocator_ops *ops = chain->allocator->bio_allocator_ops; |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, |
| " Asking for %lu pages from chain %p.", portion, chain); |
| got = ops->allocate_storage(chain, portion); |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, |
| " Got %lu pages from allocator %p.", got, chain); |
| if (!got) |
| no_free++; |
| got_this_round += got; |
| chain = chain->next; |
| } |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, " Loop finished. Got a " |
| "total of %lu pages from %d allocators.", |
| got_this_round, divisor - no_free); |
| |
| raw_pages_allocd += got_this_round; |
| to_get = needed > raw_pages_allocd ? needed - raw_pages_allocd : 0; |
| |
| /* |
| * If we got anything from chains of this priority and we |
| * still have storage to allocate, go over this priority |
| * again. |
| */ |
| if (got_this_round && to_get) |
| chain = top; |
| else |
| no_free = 0; |
| } |
| |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Finished allocating. Calling " |
| "get_main_pool_phys_params"); |
| /* Now let swap allocator bmap the pages */ |
| get_main_pool_phys_params(); |
| |
| toi_message(TOI_BIO, TOI_VERBOSE, 0, "Done. Reserving header."); |
| return apply_header_reservation(); |
| } |
| |
| void toi_bio_chains_post_atomic(struct toi_boot_kernel_data *bkd) |
| { |
| int i = 0; |
| struct toi_bdev_info *cur_chain = prio_chain_head; |
| |
| while (cur_chain) { |
| cur_chain->pages_used = bkd->pages_used[i]; |
| cur_chain = cur_chain->next; |
| i++; |
| } |
| } |
| |
| int toi_bio_chains_debug_info(char *buffer, int size) |
| { |
| /* Show what we actually used */ |
| struct toi_bdev_info *cur_chain = prio_chain_head; |
| int len = 0; |
| |
| while (cur_chain) { |
| len += scnprintf(buffer + len, size - len, " Used %lu pages " |
| "from %s.\n", cur_chain->pages_used, cur_chain->name); |
| cur_chain = cur_chain->next; |
| } |
| |
| return len; |
| } |