[PATCH 06/21] Hibernation: Generic extents support.

From: Nigel Cunningham
Date: Wed Jun 02 2010 - 08:20:20 EST


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 | 125 ++++++++++++++++++++++++++++++++++++++++++++++++
kernel/power/extents.h | 35 +++++++++++++
kernel/power/swap.c | 117 +++++---------------------------------------
4 files changed, 175 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..172322d
--- /dev/null
+++ b/kernel/power/extents.c
@@ -0,0 +1,125 @@
+/*
+ * 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_current(struct hib_extent_state *pos)
+{
+ return (pos->node) ? pos->offset : 0;
+}
+
+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);
+ pos->num_extents++;
+ 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);
+ pos->num_extents--;
+ kfree(ext);
+ }
+}
diff --git a/kernel/power/extents.h b/kernel/power/extents.h
new file mode 100644
index 0000000..0b69b8e
--- /dev/null
+++ b/kernel/power/extents.h
@@ -0,0 +1,35 @@
+/*
+ * 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;
+ int num_extents;
+
+ /* 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_current(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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/