Commit | Line | Data |
---|---|---|
6fa3eb70 S |
1 | /* |
2 | * kernel/power/tuxonice_extent.c | |
3 | * | |
4 | * Copyright (C) 2003-2010 Nigel Cunningham (nigel at tuxonice net) | |
5 | * | |
6 | * Distributed under GPLv2. | |
7 | * | |
8 | * These functions encapsulate the manipulation of storage metadata. | |
9 | */ | |
10 | ||
11 | #include <linux/suspend.h> | |
12 | #include "tuxonice_modules.h" | |
13 | #include "tuxonice_extent.h" | |
14 | #include "tuxonice_alloc.h" | |
15 | #include "tuxonice_ui.h" | |
16 | #include "tuxonice.h" | |
17 | ||
18 | /** | |
19 | * toi_get_extent - return a free extent | |
20 | * | |
21 | * May fail, returning NULL instead. | |
22 | **/ | |
23 | static struct hibernate_extent *toi_get_extent(void) | |
24 | { | |
25 | return (struct hibernate_extent *)toi_kzalloc(2, | |
26 | sizeof(struct hibernate_extent), | |
27 | TOI_ATOMIC_GFP); | |
28 | } | |
29 | ||
30 | /** | |
31 | * toi_put_extent_chain - free a whole chain of extents | |
32 | * @chain: Chain to free. | |
33 | **/ | |
34 | void toi_put_extent_chain(struct hibernate_extent_chain *chain) | |
35 | { | |
36 | struct hibernate_extent *this; | |
37 | ||
38 | this = chain->first; | |
39 | ||
40 | while (this) { | |
41 | struct hibernate_extent *next = this->next; | |
42 | toi_kfree(2, this, sizeof(*this)); | |
43 | chain->num_extents--; | |
44 | this = next; | |
45 | } | |
46 | ||
47 | chain->first = NULL; | |
48 | chain->last_touched = NULL; | |
49 | chain->current_extent = NULL; | |
50 | chain->size = 0; | |
51 | } | |
52 | EXPORT_SYMBOL_GPL(toi_put_extent_chain); | |
53 | ||
54 | /** | |
55 | * toi_add_to_extent_chain - add an extent to an existing chain | |
56 | * @chain: Chain to which the extend should be added | |
57 | * @start: Start of the extent (first physical block) | |
58 | * @end: End of the extent (last physical block) | |
59 | * | |
60 | * The chain information is updated if the insertion is successful. | |
61 | **/ | |
62 | int toi_add_to_extent_chain(struct hibernate_extent_chain *chain, | |
63 | unsigned long start, unsigned long end) | |
64 | { | |
65 | struct hibernate_extent *new_ext = NULL, *cur_ext = NULL; | |
66 | ||
67 | toi_message(TOI_IO, TOI_VERBOSE, 0, | |
68 | "Adding extent %lu-%lu to chain %p.\n", start, end, chain); | |
69 | ||
70 | /* Find the right place in the chain */ | |
71 | if (chain->last_touched && chain->last_touched->start < start) | |
72 | cur_ext = chain->last_touched; | |
73 | else if (chain->first && chain->first->start < start) | |
74 | cur_ext = chain->first; | |
75 | ||
76 | if (cur_ext) { | |
77 | while (cur_ext->next && cur_ext->next->start < start) | |
78 | cur_ext = cur_ext->next; | |
79 | ||
80 | if (cur_ext->end == (start - 1)) { | |
81 | struct hibernate_extent *next_ext = cur_ext->next; | |
82 | cur_ext->end = end; | |
83 | ||
84 | /* Merge with the following one? */ | |
85 | if (next_ext && cur_ext->end + 1 == next_ext->start) { | |
86 | cur_ext->end = next_ext->end; | |
87 | cur_ext->next = next_ext->next; | |
88 | toi_kfree(2, next_ext, sizeof(*next_ext)); | |
89 | chain->num_extents--; | |
90 | } | |
91 | ||
92 | chain->last_touched = cur_ext; | |
93 | chain->size += (end - start + 1); | |
94 | ||
95 | return 0; | |
96 | } | |
97 | } | |
98 | ||
99 | new_ext = toi_get_extent(); | |
100 | if (!new_ext) { | |
101 | printk(KERN_INFO "Error unable to append a new extent to the " "chain.\n"); | |
102 | return -ENOMEM; | |
103 | } | |
104 | ||
105 | chain->num_extents++; | |
106 | chain->size += (end - start + 1); | |
107 | new_ext->start = start; | |
108 | new_ext->end = end; | |
109 | ||
110 | chain->last_touched = new_ext; | |
111 | ||
112 | if (cur_ext) { | |
113 | new_ext->next = cur_ext->next; | |
114 | cur_ext->next = new_ext; | |
115 | } else { | |
116 | if (chain->first) | |
117 | new_ext->next = chain->first; | |
118 | chain->first = new_ext; | |
119 | } | |
120 | ||
121 | return 0; | |
122 | } | |
123 | EXPORT_SYMBOL_GPL(toi_add_to_extent_chain); |