/*
 * kernel/power/tuxonice_prepare_image.c
 *
 * Copyright (C) 2003-2015 Nigel Cunningham (nigel at nigelcunningham com au)
 *
 * This file is released under the GPLv2.
 *
 * We need to eat memory until we can:
 * 1. Perform the save without changing anything (RAM_NEEDED < #pages)
 * 2. Fit it all in available space (toiActiveAllocator->available_space() >=
 *    main_storage_needed())
 * 3. Reload the pagedir and pageset1 to places that don't collide with their
 *    final destinations, not knowing to what extent the resumed kernel will
 *    overlap with the one loaded at boot time. I think the resumed kernel
 *    should overlap completely, but I don't want to rely on this as it is
 *    an unproven assumption. We therefore assume there will be no overlap at
 *    all (worse case).
 * 4. Meet the user's requested limit (if any) on the size of the image.
 *    The limit is in MB, so pages/256 (assuming 4K pages).
 *
 */

#include <linux/highmem.h>
#include <linux/freezer.h>
#include <linux/hardirq.h>
#include <linux/mmzone.h>
#include <linux/console.h>
#include <linux/tuxonice.h>

#include "tuxonice_pageflags.h"
#include "tuxonice_modules.h"
#include "tuxonice_io.h"
#include "tuxonice_ui.h"
#include "tuxonice_prepare_image.h"
#include "tuxonice.h"
#include "tuxonice_extent.h"
#include "tuxonice_checksum.h"
#include "tuxonice_sysfs.h"
#include "tuxonice_alloc.h"
#include "tuxonice_atomic_copy.h"
#include "tuxonice_builtin.h"

static unsigned long num_nosave, main_storage_allocated, storage_limit,
            header_storage_needed;
unsigned long extra_pd1_pages_allowance =
        CONFIG_TOI_DEFAULT_EXTRA_PAGES_ALLOWANCE;
long image_size_limit = CONFIG_TOI_DEFAULT_IMAGE_SIZE_LIMIT;
static int no_ps2_needed;

struct attention_list {
        struct task_struct *task;
        struct attention_list *next;
};

static struct attention_list *attention_list;

#define PAGESET1 0
#define PAGESET2 1

void free_attention_list(void)
{
        struct attention_list *last = NULL;

        while (attention_list) {
                last = attention_list;
                attention_list = attention_list->next;
                toi_kfree(6, last, sizeof(*last));
        }
}

static int build_attention_list(void)
{
        int i, task_count = 0;
        struct task_struct *p;
        struct attention_list *next;

        /*
         * Count all userspace process (with task->mm) marked PF_NOFREEZE.
         */
        toi_read_lock_tasklist();
        for_each_process(p)
                if ((p->flags & PF_NOFREEZE) || p == current)
                        task_count++;
        toi_read_unlock_tasklist();

        /*
         * Allocate attention list structs.
         */
        for (i = 0; i < task_count; i++) {
                struct attention_list *this =
                        toi_kzalloc(6, sizeof(struct attention_list),
                                        TOI_WAIT_GFP);
                if (!this) {
                        printk(KERN_INFO "Failed to allocate slab for "
                                        "attention list.\n");
                        free_attention_list();
                        return 1;
                }
                this->next = NULL;
                if (attention_list)
                        this->next = attention_list;
                attention_list = this;
        }

        next = attention_list;
        toi_read_lock_tasklist();
        for_each_process(p)
                if ((p->flags & PF_NOFREEZE) || p == current) {
                        next->task = p;
                        next = next->next;
                }
        toi_read_unlock_tasklist();
        return 0;
}

static void pageset2_full(void)
{
        struct zone *zone;
        struct page *page;
        unsigned long flags;
        int i;

        toi_trace_index++;

        for_each_populated_zone(zone) {
                spin_lock_irqsave(&zone->lru_lock, flags);
                for_each_lru(i) {
                        if (!zone_page_state(zone, NR_LRU_BASE + i))
                                continue;

                        list_for_each_entry(page, &zone->lruvec.lists[i], lru) {
                                struct address_space *mapping;

                                mapping = page_mapping(page);
                                if (!mapping || !mapping->host ||
                                    !(mapping->host->i_flags & S_ATOMIC_COPY)) {
                                    if (PageTOI_RO(page) && test_result_state(TOI_KEPT_IMAGE)) {
                                        TOI_TRACE_DEBUG(page_to_pfn(page), "_Pageset2 unmodified.");
                                    } else {
                                        TOI_TRACE_DEBUG(page_to_pfn(page), "_Pageset2 pageset2_full.");
                                        SetPagePageset2(page);
                                    }
                                }
                        }
                }
                spin_unlock_irqrestore(&zone->lru_lock, flags);
        }
}

/*
 * toi_mark_task_as_pageset
 * Functionality   : Marks all the saveable pages belonging to a given process
 *                      as belonging to a particular pageset.
 */

static void toi_mark_task_as_pageset(struct task_struct *t, int pageset2)
{
        struct vm_area_struct *vma;
        struct mm_struct *mm;

        mm = t->active_mm;

        if (!mm || !mm->mmap)
                return;

        toi_trace_index++;

        if (!irqs_disabled())
                down_read(&mm->mmap_sem);

        for (vma = mm->mmap; vma; vma = vma->vm_next) {
                unsigned long posn;

                if (!vma->vm_start ||
                    vma->vm_flags & VM_PFNMAP)
                        continue;

                for (posn = vma->vm_start; posn < vma->vm_end;
                                posn += PAGE_SIZE) {
                        struct page *page = follow_page(vma, posn, 0);
                        struct address_space *mapping;

                        if (!page || !pfn_valid(page_to_pfn(page)))
                                continue;

                        mapping = page_mapping(page);
                        if (mapping && mapping->host &&
                            mapping->host->i_flags & S_ATOMIC_COPY && pageset2)
                                continue;

                        if (PageTOI_RO(page) && test_result_state(TOI_KEPT_IMAGE)) {
                                TOI_TRACE_DEBUG(page_to_pfn(page), "_Unmodified %d", pageset2 ? 1 : 2);
                                continue;
                        }

                        if (pageset2) {
                                TOI_TRACE_DEBUG(page_to_pfn(page), "_MarkTaskAsPageset 1");
                                SetPagePageset2(page);
                        } else {
                                TOI_TRACE_DEBUG(page_to_pfn(page), "_MarkTaskAsPageset 2");
                                ClearPagePageset2(page);
                                SetPagePageset1(page);
                        }
                }
        }

        if (!irqs_disabled())
                up_read(&mm->mmap_sem);
}

static void mark_tasks(int pageset)
{
        struct task_struct *p;

        toi_read_lock_tasklist();
        for_each_process(p) {
                if (!p->mm)
                        continue;

                if (p->flags & PF_KTHREAD)
                        continue;

                toi_mark_task_as_pageset(p, pageset);
        }
        toi_read_unlock_tasklist();

}

/* mark_pages_for_pageset2
 *
 * Description:        Mark unshared pages in processes not needed for hibernate as
 *                 being able to be written out in a separate pagedir.
 *                 HighMem pages are simply marked as pageset2. They won't be
 *                 needed during hibernate.
 */

static void toi_mark_pages_for_pageset2(void)
{
        struct attention_list *this = attention_list;

        memory_bm_clear(pageset2_map);

        if (test_action_state(TOI_NO_PAGESET2) || no_ps2_needed)
                return;

        if (test_action_state(TOI_PAGESET2_FULL))
                pageset2_full();
        else
                mark_tasks(PAGESET2);

        /*
         * Because the tasks in attention_list are ones related to hibernating,
         * we know that they won't go away under us.
         */

        while (this) {
                if (!test_result_state(TOI_ABORTED))
                        toi_mark_task_as_pageset(this->task, PAGESET1);
                this = this->next;
        }
}

/*
 * The atomic copy of pageset1 is stored in pageset2 pages.
 * But if pageset1 is larger (normally only just after boot),
 * we need to allocate extra pages to store the atomic copy.
 * The following data struct and functions are used to handle
 * the allocation and freeing of that memory.
 */

static unsigned long extra_pages_allocated;

struct extras {
        struct page *page;
        int order;
        struct extras *next;
};

static struct extras *extras_list;

/* toi_free_extra_pagedir_memory
 *
 * Description:        Free previously allocated extra pagedir memory.
 */
void toi_free_extra_pagedir_memory(void)
{
        /* Free allocated pages */
        while (extras_list) {
                struct extras *this = extras_list;
                int i;

                extras_list = this->next;

                for (i = 0; i < (1 << this->order); i++)
                        ClearPageNosave(this->page + i);

                toi_free_pages(9, this->page, this->order);
                toi_kfree(7, this, sizeof(*this));
        }

        extra_pages_allocated = 0;
}

/* toi_allocate_extra_pagedir_memory
 *
 * Description:        Allocate memory for making the atomic copy of pagedir1 in the
 *                 case where it is bigger than pagedir2.
 * Arguments:        int        num_to_alloc: Number of extra pages needed.
 * Result:        int.         Number of extra pages we now have allocated.
 */
static int toi_allocate_extra_pagedir_memory(int extra_pages_needed)
{
        int j, order, num_to_alloc = extra_pages_needed - extra_pages_allocated;
        gfp_t flags = TOI_ATOMIC_GFP;

        if (num_to_alloc < 1)
                return 0;

        order = fls(num_to_alloc);
        if (order >= MAX_ORDER)
                order = MAX_ORDER - 1;

        while (num_to_alloc) {
                struct page *newpage;
                unsigned long virt;
                struct extras *extras_entry;

                while ((1 << order) > num_to_alloc)
                        order--;

                extras_entry = (struct extras *) toi_kzalloc(7,
                        sizeof(struct extras), TOI_ATOMIC_GFP);

                if (!extras_entry)
                        return extra_pages_allocated;

                virt = toi_get_free_pages(9, flags, order);
                while (!virt && order) {
                        order--;
                        virt = toi_get_free_pages(9, flags, order);
                }

                if (!virt) {
                        toi_kfree(7, extras_entry, sizeof(*extras_entry));
                        return extra_pages_allocated;
                }

                newpage = virt_to_page(virt);

                extras_entry->page = newpage;
                extras_entry->order = order;
                extras_entry->next = extras_list;

                extras_list = extras_entry;

                for (j = 0; j < (1 << order); j++) {
                        SetPageNosave(newpage + j);
                        SetPagePageset1Copy(newpage + j);
                }

                extra_pages_allocated += (1 << order);
                num_to_alloc -= (1 << order);
        }

        return extra_pages_allocated;
}

/*
 * real_nr_free_pages: Count pcp pages for a zone type or all zones
 * (-1 for all, otherwise zone_idx() result desired).
 */
unsigned long real_nr_free_pages(unsigned long zone_idx_mask)
{
        struct zone *zone;
        int result = 0, cpu;

        /* PCP lists */
        for_each_populated_zone(zone) {
                if (!(zone_idx_mask & (1 << zone_idx(zone))))
                        continue;

                for_each_online_cpu(cpu) {
                        struct per_cpu_pageset *pset =
                                per_cpu_ptr(zone->pageset, cpu);
                        struct per_cpu_pages *pcp = &pset->pcp;
                        result += pcp->count;
                }

                result += zone_page_state(zone, NR_FREE_PAGES);
        }
        return result;
}

/*
 * Discover how much extra memory will be required by the drivers
 * when they're asked to hibernate. We can then ensure that amount
 * of memory is available when we really want it.
 */
static void get_extra_pd1_allowance(void)
{
        unsigned long orig_num_free = real_nr_free_pages(all_zones_mask), final;

        toi_prepare_status(CLEAR_BAR, "Finding allowance for drivers.");

        if (toi_go_atomic(PMSG_FREEZE, 1))
                return;

        final = real_nr_free_pages(all_zones_mask);
        toi_end_atomic(ATOMIC_ALL_STEPS, 1, 0);

        extra_pd1_pages_allowance = (orig_num_free > final) ?
                orig_num_free - final + MIN_EXTRA_PAGES_ALLOWANCE :
                MIN_EXTRA_PAGES_ALLOWANCE;
}

/*
 * Amount of storage needed, possibly taking into account the
 * expected compression ratio and possibly also ignoring our
 * allowance for extra pages.
 */
static unsigned long main_storage_needed(int use_ecr,
                int ignore_extra_pd1_allow)
{
        return (pagedir1.size + pagedir2.size +
          (ignore_extra_pd1_allow ? 0 : extra_pd1_pages_allowance)) *
         (use_ecr ? toi_expected_compression_ratio() : 100) / 100;
}

/*
 * Storage needed for the image header, in bytes until the return.
 */
unsigned long get_header_storage_needed(void)
{
        unsigned long bytes = sizeof(struct toi_header) +
                        toi_header_storage_for_modules() +
                        toi_pageflags_space_needed() +
                        fs_info_space_needed();

        return DIV_ROUND_UP(bytes, PAGE_SIZE);
}

/*
 * When freeing memory, pages from either pageset might be freed.
 *
 * When seeking to free memory to be able to hibernate, for every ps1 page
 * freed, we need 2 less pages for the atomic copy because there is one less
 * page to copy and one more page into which data can be copied.
 *
 * Freeing ps2 pages saves us nothing directly. No more memory is available
 * for the atomic copy. Indirectly, a ps1 page might be freed (slab?), but
 * that's too much work to figure out.
 *
 * => ps1_to_free functions
 *
 * Of course if we just want to reduce the image size, because of storage
 * limitations or an image size limit either ps will do.
 *
 * => any_to_free function
 */

static unsigned long lowpages_usable_for_highmem_copy(void)
{
        unsigned long needed = get_lowmem_size(pagedir1) +
                        extra_pd1_pages_allowance + MIN_FREE_RAM +
                        toi_memory_for_modules(0),
                available = get_lowmem_size(pagedir2) +
                         real_nr_free_low_pages() + extra_pages_allocated;

        return available > needed ? available - needed : 0;
}

static unsigned long highpages_ps1_to_free(void)
{
        unsigned long need = get_highmem_size(pagedir1),
                      available = get_highmem_size(pagedir2) +
                              real_nr_free_high_pages() +
                              lowpages_usable_for_highmem_copy();

        return need > available ? DIV_ROUND_UP(need - available, 2) : 0;
}

static unsigned long lowpages_ps1_to_free(void)
{
        unsigned long needed = get_lowmem_size(pagedir1) +
                        extra_pd1_pages_allowance + MIN_FREE_RAM +
                        toi_memory_for_modules(0),
                available = get_lowmem_size(pagedir2) +
                         real_nr_free_low_pages() + extra_pages_allocated;

        return needed > available ? DIV_ROUND_UP(needed - available, 2) : 0;
}

static unsigned long current_image_size(void)
{
        return pagedir1.size + pagedir2.size + header_storage_needed;
}

static unsigned long storage_still_required(void)
{
        unsigned long needed = main_storage_needed(1, 1);
        return needed > storage_limit ? needed - storage_limit : 0;
}

static unsigned long ram_still_required(void)
{
        unsigned long needed = MIN_FREE_RAM + toi_memory_for_modules(0) +
                2 * extra_pd1_pages_allowance,
                  available = real_nr_free_low_pages() + extra_pages_allocated;
        return needed > available ? needed - available : 0;
}

unsigned long any_to_free(int use_image_size_limit)
{
        int use_soft_limit = use_image_size_limit && image_size_limit > 0;
        unsigned long current_size = current_image_size(),
                      soft_limit = use_soft_limit ? (image_size_limit << 8) : 0,
                      to_free = use_soft_limit ? (current_size > soft_limit ?
                                      current_size - soft_limit : 0) : 0,
                      storage_limit = storage_still_required(),
                      ram_limit = ram_still_required(),
                      first_max = max(to_free, storage_limit);

        return max(first_max, ram_limit);
}

static int need_pageset2(void)
{
        return (real_nr_free_low_pages() + extra_pages_allocated -
                2 * extra_pd1_pages_allowance - MIN_FREE_RAM -
                 toi_memory_for_modules(0) - pagedir1.size) < pagedir2.size;
}

/* amount_needed
 *
 * Calculates the amount by which the image size needs to be reduced to meet
 * our constraints.
 */
static unsigned long amount_needed(int use_image_size_limit)
{
        return max(highpages_ps1_to_free() + lowpages_ps1_to_free(),
                        any_to_free(use_image_size_limit));
}

static int image_not_ready(int use_image_size_limit)
{
        toi_message(TOI_EAT_MEMORY, TOI_LOW, 1,
                "Amount still needed (%lu) > 0:%u,"
                " Storage allocd: %lu < %lu: %u.\n",
                        amount_needed(use_image_size_limit),
                        (amount_needed(use_image_size_limit) > 0),
                        main_storage_allocated,
                        main_storage_needed(1, 1),
                        main_storage_allocated < main_storage_needed(1, 1));

        toi_cond_pause(0, NULL);

        return (amount_needed(use_image_size_limit) > 0) ||
                 main_storage_allocated < main_storage_needed(1, 1);
}

static void display_failure_reason(int tries_exceeded)
{
        unsigned long storage_required = storage_still_required(),
            ram_required = ram_still_required(),
            high_ps1 = highpages_ps1_to_free(),
            low_ps1 = lowpages_ps1_to_free();

        printk(KERN_INFO "Failed to prepare the image because...\n");

        if (!storage_limit) {
                printk(KERN_INFO "- You need some storage available to be "
                                "able to hibernate.\n");
                return;
        }

        if (tries_exceeded)
                printk(KERN_INFO "- The maximum number of iterations was "
                                "reached without successfully preparing the "
                                "image.\n");

        if (storage_required) {
                printk(KERN_INFO " - We need at least %lu pages of storage "
                                "(ignoring the header), but only have %lu.\n",
                                main_storage_needed(1, 1),
                                main_storage_allocated);
                set_abort_result(TOI_INSUFFICIENT_STORAGE);
        }

        if (ram_required) {
                printk(KERN_INFO " - We need %lu more free pages of low "
                                "memory.\n", ram_required);
                printk(KERN_INFO "     Minimum free     : %8d\n", MIN_FREE_RAM);
                printk(KERN_INFO "   + Reqd. by modules : %8lu\n",
                                toi_memory_for_modules(0));
                printk(KERN_INFO "   + 2 * extra allow  : %8lu\n",
                                2 * extra_pd1_pages_allowance);
                printk(KERN_INFO "   - Currently free   : %8lu\n",
                                real_nr_free_low_pages());
                printk(KERN_INFO "   - Pages allocd     : %8lu\n",
                                extra_pages_allocated);
                printk(KERN_INFO "                      : ========\n");
                printk(KERN_INFO "     Still needed     : %8lu\n",
                                ram_required);

                /* Print breakdown of memory needed for modules */
                toi_memory_for_modules(1);
                set_abort_result(TOI_UNABLE_TO_FREE_ENOUGH_MEMORY);
        }

        if (high_ps1) {
                printk(KERN_INFO "- We need to free %lu highmem pageset 1 "
                                "pages.\n", high_ps1);
                set_abort_result(TOI_UNABLE_TO_FREE_ENOUGH_MEMORY);
        }

        if (low_ps1) {
                printk(KERN_INFO " - We need to free %ld lowmem pageset 1 "
                                "pages.\n", low_ps1);
                set_abort_result(TOI_UNABLE_TO_FREE_ENOUGH_MEMORY);
        }
}

static void display_stats(int always, int sub_extra_pd1_allow)
{
        char buffer[255];
        snprintf(buffer, 254,
                "Free:%lu(%lu). Sets:%lu(%lu),%lu(%lu). "
                "Nosave:%lu-%lu=%lu. Storage:%lu/%lu(%lu=>%lu). "
                "Needed:%lu,%lu,%lu(%u,%lu,%lu,%ld) (PS2:%s)\n",

                /* Free */
                real_nr_free_pages(all_zones_mask),
                real_nr_free_low_pages(),

                /* Sets */
                pagedir1.size, pagedir1.size - get_highmem_size(pagedir1),
                pagedir2.size, pagedir2.size - get_highmem_size(pagedir2),

                /* Nosave */
                num_nosave, extra_pages_allocated,
                num_nosave - extra_pages_allocated,

                /* Storage */
                main_storage_allocated,
                storage_limit,
                main_storage_needed(1, sub_extra_pd1_allow),
                main_storage_needed(1, 1),

                /* Needed */
                lowpages_ps1_to_free(), highpages_ps1_to_free(),
                any_to_free(1),
                MIN_FREE_RAM, toi_memory_for_modules(0),
                extra_pd1_pages_allowance,
                image_size_limit,

                need_pageset2() ? "yes" : "no");

        if (always)
                printk("%s", buffer);
        else
                toi_message(TOI_EAT_MEMORY, TOI_MEDIUM, 1, buffer);
}

/* flag_image_pages
 *
 * This routine generates our lists of pages to be stored in each
 * pageset. Since we store the data using extents, and adding new
 * extents might allocate a new extent page, this routine may well
 * be called more than once.
 */
static void flag_image_pages(int atomic_copy)
{
        int num_free = 0, num_unmodified = 0;
        unsigned long loop;
        struct zone *zone;

        pagedir1.size = 0;
        pagedir2.size = 0;

        set_highmem_size(pagedir1, 0);
        set_highmem_size(pagedir2, 0);

        num_nosave = 0;
        toi_trace_index++;

        memory_bm_clear(pageset1_map);

        toi_generate_free_page_map();

        /*
         * Pages not to be saved are marked Nosave irrespective of being
         * reserved.
         */
        for_each_populated_zone(zone) {
                int highmem = is_highmem(zone);

                for (loop = 0; loop < zone->spanned_pages; loop++) {
                        unsigned long pfn = zone->zone_start_pfn + loop;
                        struct page *page;
                        int chunk_size;

                        if (!pfn_valid(pfn)) {
                            TOI_TRACE_DEBUG(pfn, "_Flag Invalid");
                            continue;
                        }

                        chunk_size = toi_size_of_free_region(zone, pfn);
                        if (chunk_size) {
                            unsigned long y;
                            for (y = pfn; y < pfn + chunk_size; y++) {
                                page = pfn_to_page(y);
                                TOI_TRACE_DEBUG(y, "_Flag Free");
                                ClearPagePageset1(page);
                                ClearPagePageset2(page);
                            }
                                num_free += chunk_size;
                                loop += chunk_size - 1;
                                continue;
                        }

                        page = pfn_to_page(pfn);

                        if (PageNosave(page)) {
                            char *desc = PagePageset1Copy(page) ? "Pageset1Copy" : "NoSave";
                            TOI_TRACE_DEBUG(pfn, "_Flag %s", desc);
                            num_nosave++;
                            continue;
                        }

                        page = highmem ? saveable_highmem_page(zone, pfn) :
                                saveable_page(zone, pfn);

                        if (!page) {
                                TOI_TRACE_DEBUG(pfn, "_Flag Nosave2");
                                num_nosave++;
                                continue;
                        }

                        if (PageTOI_RO(page) && test_result_state(TOI_KEPT_IMAGE)) {
                            TOI_TRACE_DEBUG(pfn, "_Unmodified");
                            num_unmodified++;
                            continue;
                        }

                        if (PagePageset2(page)) {
                                pagedir2.size++;
                                TOI_TRACE_DEBUG(pfn, "_Flag PS2");
                                if (PageHighMem(page))
                                        inc_highmem_size(pagedir2);
                                else
                                        SetPagePageset1Copy(page);
                                if (PageResave(page)) {
                                        SetPagePageset1(page);
                                        ClearPagePageset1Copy(page);
                                        pagedir1.size++;
                                        if (PageHighMem(page))
                                                inc_highmem_size(pagedir1);
                                }
                        } else {
                                pagedir1.size++;
                                TOI_TRACE_DEBUG(pfn, "_Flag PS1");
                                SetPagePageset1(page);
                                if (PageHighMem(page))
                                        inc_highmem_size(pagedir1);
                        }
                }
        }

        if (!atomic_copy)
                toi_message(TOI_EAT_MEMORY, TOI_MEDIUM, 0,
                        "Count data pages: Set1 (%d) + Set2 (%d) + Nosave (%ld)"
                                    " + Unmodified (%d) + NumFree (%d) = %d.\n",
                        pagedir1.size, pagedir2.size, num_nosave, num_unmodified,
                        num_free, pagedir1.size + pagedir2.size + num_nosave + num_free);
}

void toi_recalculate_image_contents(int atomic_copy)
{
        memory_bm_clear(pageset1_map);
        if (!atomic_copy) {
                unsigned long pfn;
                memory_bm_position_reset(pageset2_map);
                for (pfn = memory_bm_next_pfn(pageset2_map, 0);
                                pfn != BM_END_OF_MAP;
                                pfn = memory_bm_next_pfn(pageset2_map, 0))
                        ClearPagePageset1Copy(pfn_to_page(pfn));
                /* Need to call this before getting pageset1_size! */
                toi_mark_pages_for_pageset2();
        }
        memory_bm_position_reset(pageset2_map);
        flag_image_pages(atomic_copy);

        if (!atomic_copy) {
                storage_limit = toiActiveAllocator->storage_available();
                display_stats(0, 0);
        }
}

int try_allocate_extra_memory(void)
{
        unsigned long wanted = pagedir1.size +  extra_pd1_pages_allowance -
                get_lowmem_size(pagedir2);
        if (wanted > extra_pages_allocated) {
                unsigned long got = toi_allocate_extra_pagedir_memory(wanted);
                if (wanted < got) {
                        toi_message(TOI_EAT_MEMORY, TOI_LOW, 1,
                                "Want %d extra pages for pageset1, got %d.\n",
                                wanted, got);
                        return 1;
                }
        }
        return 0;
}

/* update_image
 *
 * Allocate [more] memory and storage for the image.
 */
static void update_image(int ps2_recalc)
{
        int old_header_req;
        unsigned long seek;

        if (try_allocate_extra_memory())
                return;

        if (ps2_recalc)
                goto recalc;

        thaw_kernel_threads();

        /*
         * Allocate remaining storage space, if possible, up to the
         * maximum we know we'll need. It's okay to allocate the
         * maximum if the writer is the swapwriter, but
         * we don't want to grab all available space on an NFS share.
         * We therefore ignore the expected compression ratio here,
         * thereby trying to allocate the maximum image size we could
         * need (assuming compression doesn't expand the image), but
         * don't complain if we can't get the full amount we're after.
         */

        do {
                int result;

                old_header_req = header_storage_needed;
                toiActiveAllocator->reserve_header_space(header_storage_needed);

                /* How much storage is free with the reservation applied? */
                storage_limit = toiActiveAllocator->storage_available();
                seek = min(storage_limit, main_storage_needed(0, 0));

                result = toiActiveAllocator->allocate_storage(seek);
                if (result)
                        printk("Failed to allocate storage (%d).\n", result);

                main_storage_allocated =
                        toiActiveAllocator->storage_allocated();

                /* Need more header because more storage allocated? */
                header_storage_needed = get_header_storage_needed();

        } while (header_storage_needed > old_header_req);

        if (freeze_kernel_threads())
                set_abort_result(TOI_FREEZING_FAILED);

recalc:
        toi_recalculate_image_contents(0);
}

/* attempt_to_freeze
 *
 * Try to freeze processes.
 */

static int attempt_to_freeze(void)
{
        int result;

        /* Stop processes before checking again */
        toi_prepare_status(CLEAR_BAR, "Freezing processes & syncing "
                        "filesystems.");
        result = freeze_processes();

        if (result)
                set_abort_result(TOI_FREEZING_FAILED);

        result = freeze_kernel_threads();

        if (result)
                set_abort_result(TOI_FREEZING_FAILED);

        return result;
}

/* eat_memory
 *
 * Try to free some memory, either to meet hard or soft constraints on the image
 * characteristics.
 *
 * Hard constraints:
 * - Pageset1 must be < half of memory;
 * - We must have enough memory free at resume time to have pageset1
 *   be able to be loaded in pages that don't conflict with where it has to
 *   be restored.
 * Soft constraints
 * - User specificied image size limit.
 */
static void eat_memory(void)
{
        unsigned long amount_wanted = 0;
        int did_eat_memory = 0;

        /*
         * Note that if we have enough storage space and enough free memory, we
         * may exit without eating anything. We give up when the last 10
         * iterations ate no extra pages because we're not going to get much
         * more anyway, but the few pages we get will take a lot of time.
         *
         * We freeze processes before beginning, and then unfreeze them if we
         * need to eat memory until we think we have enough. If our attempts
         * to freeze fail, we give up and abort.
         */

        amount_wanted = amount_needed(1);

        switch (image_size_limit) {
        case -1: /* Don't eat any memory */
                if (amount_wanted > 0) {
                        set_abort_result(TOI_WOULD_EAT_MEMORY);
                        return;
                }
                break;
        case -2:  /* Free caches only */
                drop_pagecache();
                toi_recalculate_image_contents(0);
                amount_wanted = amount_needed(1);
                break;
        default:
                break;
        }

        if (amount_wanted > 0 && !test_result_state(TOI_ABORTED) &&
                        image_size_limit != -1) {
                unsigned long request = amount_wanted;
                unsigned long high_req = max(highpages_ps1_to_free(),
                                any_to_free(1));
                unsigned long low_req = lowpages_ps1_to_free();
                unsigned long got = 0;

                toi_prepare_status(CLEAR_BAR,
                                "Seeking to free %ldMB of memory.",
                                MB(amount_wanted));

                thaw_kernel_threads();

                /*
                 * Ask for too many because shrink_memory_mask doesn't
                 * currently return enough most of the time.
                 */
                
                if (low_req)
                        got = shrink_memory_mask(low_req, GFP_KERNEL);
                if (high_req)
                        shrink_memory_mask(high_req - got, GFP_HIGHUSER);

                did_eat_memory = 1;

                toi_recalculate_image_contents(0);

                amount_wanted = amount_needed(1);

                printk(KERN_DEBUG "Asked shrink_memory_mask for %ld low pages &"
                                " %ld pages from anywhere, got %ld.\n",
                                high_req, low_req,
                                request - amount_wanted);

                toi_cond_pause(0, NULL);

                if (freeze_kernel_threads())
                        set_abort_result(TOI_FREEZING_FAILED);
        }

        if (did_eat_memory)
                toi_recalculate_image_contents(0);
}

/* toi_prepare_image
 *
 * Entry point to the whole image preparation section.
 *
 * We do four things:
 * - Freeze processes;
 * - Ensure image size constraints are met;
 * - Complete all the preparation for saving the image,
 *   including allocation of storage. The only memory
 *   that should be needed when we're finished is that
 *   for actually storing the image (and we know how
 *   much is needed for that because the modules tell
 *   us).
 * - Make sure that all dirty buffers are written out.
 */
#define MAX_TRIES 2
int toi_prepare_image(void)
{
        int result = 1, tries = 1;

        main_storage_allocated = 0;
        no_ps2_needed = 0;

        if (attempt_to_freeze())
                return 1;

        lock_device_hotplug();
        set_toi_state(TOI_DEVICE_HOTPLUG_LOCKED);

        if (!extra_pd1_pages_allowance)
                get_extra_pd1_allowance();

        storage_limit = toiActiveAllocator->storage_available();

        if (!storage_limit) {
                printk(KERN_INFO "No storage available. Didn't try to prepare "
                                "an image.\n");
                display_failure_reason(0);
                set_abort_result(TOI_NOSTORAGE_AVAILABLE);
                return 1;
        }

        if (build_attention_list()) {
                abort_hibernate(TOI_UNABLE_TO_PREPARE_IMAGE,
                                "Unable to successfully prepare the image.\n");
                return 1;
        }

        toi_recalculate_image_contents(0);

        do {
                toi_prepare_status(CLEAR_BAR,
                                "Preparing Image. Try %d.", tries);

                eat_memory();

                if (test_result_state(TOI_ABORTED))
                        break;

                update_image(0);

                tries++;

        } while (image_not_ready(1) && tries <= MAX_TRIES &&
                        !test_result_state(TOI_ABORTED));

        result = image_not_ready(0);

        /* TODO: Handle case where need to remove existing image and resave
         * instead of adding to incremental image. */

        if (!test_result_state(TOI_ABORTED)) {
                if (result) {
                        display_stats(1, 0);
                        display_failure_reason(tries > MAX_TRIES);
                        abort_hibernate(TOI_UNABLE_TO_PREPARE_IMAGE,
                                "Unable to successfully prepare the image.\n");
                } else {
                        /* Pageset 2 needed? */
                        if (!need_pageset2() &&
                                  test_action_state(TOI_NO_PS2_IF_UNNEEDED)) {
                                no_ps2_needed = 1;
                                toi_recalculate_image_contents(0);
                                update_image(1);
                        }

                        toi_cond_pause(1, "Image preparation complete.");
                }
        }

        return result ? result : allocate_checksum_pages();
}