From: Nigel Cunningham <nigel@xxxxxxxxxxxx> Separate out the extent storage and manipulation into a separate file and make them more generic, so we can also use extents for recording what sectors are used and (later) support multiple storage devices more easily. Signed-off-by: Nigel Cunningham <nigel@xxxxxxxxxxxx> --- kernel/power/Makefile | 2 +- kernel/power/extents.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++++ kernel/power/extents.h | 33 +++++++++++++ kernel/power/swap.c | 117 ++++++----------------------------------------- 4 files changed, 166 insertions(+), 104 deletions(-) create mode 100644 kernel/power/extents.c create mode 100644 kernel/power/extents.h diff --git a/kernel/power/Makefile b/kernel/power/Makefile index 524e058..eac3541 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_FREEZER) += process.o obj-$(CONFIG_SUSPEND) += suspend.o obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ - block_io.o + block_io.o extents.o obj-$(CONFIG_HIBERNATION_NVS) += hibernate_nvs.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o diff --git a/kernel/power/extents.c b/kernel/power/extents.c new file mode 100644 index 0000000..a0c30fe --- /dev/null +++ b/kernel/power/extents.c @@ -0,0 +1,118 @@ +/* + * linux/kernel/power/extents.c + * + * This file provides functions for storing and using a series of + * extents. + * + * Copyright (C) 1998,2001-2005 Pavel Machek <pavel@xxxxxxx> + * Copyright (C) 2006 Rafael J. Wysocki <rjw@xxxxxxx> + * Copyright (C) 2010 Nigel Cunningham <nigel@xxxxxxxxxxxx> + * + * This file is released under the GPLv2. + * + */ + +#include <linux/slab.h> +#include "extents.h" + +int hib_extents_empty(struct hib_extent_state *pos) +{ + return RB_EMPTY_ROOT(&pos->root); +} + +void hib_reset_extent_pos(struct hib_extent_state *pos) +{ + if (hib_extents_empty(pos)) + return; + + pos->node = rb_first(&pos->root); + + if (!pos->node) { + pos->cur_ext = NULL; + pos->offset = 0; + return; + } + + pos->cur_ext = container_of(pos->node, struct hib_extent, node); + pos->offset = pos->cur_ext->start; +} + +unsigned long hib_extent_next(struct hib_extent_state *pos) +{ + if (!pos->node) + return 0; + + if (pos->cur_ext->end >= pos->offset) + return pos->offset++; + + pos->node = rb_next(pos->node); + if (!pos->node) + return 0; + + pos->cur_ext = container_of(pos->node, struct hib_extent, node); + pos->offset = pos->cur_ext->start; + return pos->offset; +} + +int hib_extents_insert(struct hib_extent_state *pos, unsigned long value) +{ + struct rb_node **new = &(pos->root.rb_node); + struct rb_node *parent = NULL; + struct hib_extent *ext; + + /* Figure out where to put the new node */ + while (*new) { + ext = container_of(*new, struct hib_extent, node); + parent = *new; + if (value < ext->start) { + /* Try to merge */ + if (value == ext->start - 1) { + ext->start--; + return 0; + } + new = &((*new)->rb_left); + } else if (value > ext->end) { + /* Try to merge */ + if (value == ext->end + 1) { + ext->end++; + return 0; + } + new = &((*new)->rb_right); + } else { + /* It already is in the tree */ + return -EINVAL; + } + } + /* Add the new node and rebalance the tree. */ + ext = kzalloc(sizeof(struct hib_extent), GFP_KERNEL); + if (!ext) + return -ENOMEM; + + ext->start = value; + ext->end = value; + rb_link_node(&ext->node, parent, new); + rb_insert_color(&ext->node, &pos->root); + return 0; +} + +/** + * free_all_swap_pages - free swap pages allocated for saving image data. + * It also frees the extents used to register which swap entres had been + * allocated. + */ + +void hib_extents_clear(struct hib_extent_state *pos) +{ + struct rb_node *node; + + if (hib_extents_empty(pos)) + return; + + while ((node = pos->root.rb_node)) { + struct hib_extent *ext; + + ext = container_of(node, struct hib_extent, node); + rb_erase(node, &pos->root); + kfree(ext); + } +} diff --git a/kernel/power/extents.h b/kernel/power/extents.h new file mode 100644 index 0000000..e292719 --- /dev/null +++ b/kernel/power/extents.h @@ -0,0 +1,33 @@ +/* + * linux/kernel/power/extents.h + * + * Copyright (C) 2010 Nigel Cunningham <nigel@xxxxxxxxxxxx> + * + * This file is released under the GPLv2. + * + */ + +#include <linux/rbtree.h> + +struct hib_extent { + struct rb_node node; + unsigned long start; + unsigned long end; +}; + +struct hib_extent_state { + /* Tree */ + struct rb_root root; + + /* Current position */ + struct rb_node *node; + struct hib_extent *cur_ext; + unsigned long offset; +}; + + +void hib_reset_extent_pos(struct hib_extent_state *pos); +unsigned long hib_extent_next(struct hib_extent_state *pos); +int hib_extents_insert(struct hib_extent_state *pos, unsigned long value); +void hib_extents_clear(struct hib_extent_state *pos); +int hib_extents_empty(struct hib_extent_state *pos); diff --git a/kernel/power/swap.c b/kernel/power/swap.c index 1e6e5b6..15cbe70 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -23,9 +23,9 @@ #include <linux/swap.h> #include <linux/swapops.h> #include <linux/pm.h> -#include <linux/slab.h> #include "power.h" +#include "extents.h" #define SWSUSP_SIG "S1SUSPEND" @@ -75,97 +75,14 @@ struct swsusp_header { static struct swsusp_header *swsusp_header; -/** - * The following functions are used for tracing the allocated - * swap pages, so that they can be freed in case of an error. - */ - -struct swsusp_extent { - struct rb_node node; - unsigned long start; - unsigned long end; -}; - -static struct rb_root swsusp_extents = RB_ROOT; - -struct storage_position { - struct rb_node *node; - struct swsusp_extent *cur_ext; - unsigned long offset; -}; - -static struct storage_position pos; - -static void reset_storage_pos(void) -{ - pos.node = rb_first(&swsusp_extents); - - if (!pos.node) { - pos.cur_ext = NULL; - pos.offset = 0; - return; - } - - pos.cur_ext = container_of(pos.node, struct swsusp_extent, node); - pos.offset = pos.cur_ext->start; -} +static struct hib_extent_state swap_extents; static sector_t next_swapdev_block(void) { - if (!pos.node) - return 0; - - if (pos.cur_ext->end >= pos.offset) - return pos.offset++; - - pos.node = rb_next(pos.node); - if (!pos.node) - return 0; - - pos.cur_ext = container_of(pos.node, struct swsusp_extent, node); - pos.offset = pos.cur_ext->start; - return pos.offset; -} - -static int swsusp_extents_insert(unsigned long swap_offset) -{ - struct rb_node **new = &(swsusp_extents.rb_node); - struct rb_node *parent = NULL; - struct swsusp_extent *ext; - - /* Figure out where to put the new node */ - while (*new) { - ext = container_of(*new, struct swsusp_extent, node); - parent = *new; - if (swap_offset < ext->start) { - /* Try to merge */ - if (swap_offset == ext->start - 1) { - ext->start--; - return 0; - } - new = &((*new)->rb_left); - } else if (swap_offset > ext->end) { - /* Try to merge */ - if (swap_offset == ext->end + 1) { - ext->end++; - return 0; - } - new = &((*new)->rb_right); - } else { - /* It already is in the tree */ - return -EINVAL; - } - } - /* Add the new node and rebalance the tree. */ - ext = kzalloc(sizeof(struct swsusp_extent), GFP_KERNEL); - if (!ext) - return -ENOMEM; - - ext->start = swap_offset; - ext->end = swap_offset; - rb_link_node(&ext->node, parent, new); - rb_insert_color(&ext->node, &swsusp_extents); - return 0; + unsigned long res = hib_extent_next(&swap_extents); + if (res) + res = swapdev_block(root_swap, res); + return res; } /** @@ -179,7 +96,7 @@ sector_t alloc_swapdev_block(int swap) offset = swp_offset(get_swap_page_of_type(swap)); if (offset) { - if (swsusp_extents_insert(offset)) + if (hib_extents_insert(&swap_extents, offset)) swap_free(swp_entry(swap, offset)); else return swapdev_block(swap, offset); @@ -223,7 +140,7 @@ static int allocate_swap(unsigned int nr_pages) return 0; } - reset_storage_pos(); + hib_reset_extent_pos(&swap_extents); return 1; } @@ -235,24 +152,18 @@ static int allocate_swap(unsigned int nr_pages) void free_all_swap_pages(int swap) { - struct rb_node *node; - - while ((node = swsusp_extents.rb_node)) { - struct swsusp_extent *ext; - unsigned long offset; + unsigned long offset; - ext = container_of(node, struct swsusp_extent, node); - rb_erase(node, &swsusp_extents); - for (offset = ext->start; offset <= ext->end; offset++) - swap_free(swp_entry(swap, offset)); + hib_reset_extent_pos(&swap_extents); + while ((offset = hib_extent_next(&swap_extents))) + swap_free(swp_entry(swap, offset)); - kfree(ext); - } + hib_extents_clear(&swap_extents); } int swsusp_swap_in_use(void) { - return (swsusp_extents.rb_node != NULL); + return (!hib_extents_empty(&swap_extents)); } /* -- 1.7.0.4 _______________________________________________ linux-pm mailing list linux-pm@xxxxxxxxxxxxxxxxxxxxxxxxxx https://lists.linux-foundation.org/mailman/listinfo/linux-pm