Revision 893a9cb4 block/cow.c
b/block/cow.c | ||
---|---|---|
21 | 21 |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
22 | 22 |
* THE SOFTWARE. |
23 | 23 |
*/ |
24 |
#ifndef _WIN32 |
|
25 | 24 |
#include "qemu-common.h" |
26 | 25 |
#include "block_int.h" |
27 | 26 |
#include "module.h" |
28 |
#include <sys/mman.h> |
|
29 | 27 |
|
30 | 28 |
/**************************************************************/ |
31 | 29 |
/* COW block driver using file system holes */ |
... | ... | |
45 | 43 |
|
46 | 44 |
typedef struct BDRVCowState { |
47 | 45 |
int fd; |
48 |
uint8_t *cow_bitmap; /* if non NULL, COW mappings are used first */ |
|
49 |
uint8_t *cow_bitmap_addr; /* mmap address of cow_bitmap */ |
|
50 |
int cow_bitmap_size; |
|
51 | 46 |
int64_t cow_sectors_offset; |
52 | 47 |
} BDRVCowState; |
53 | 48 |
|
... | ... | |
68 | 63 |
BDRVCowState *s = bs->opaque; |
69 | 64 |
int fd; |
70 | 65 |
struct cow_header_v2 cow_header; |
66 |
int bitmap_size; |
|
71 | 67 |
int64_t size; |
72 | 68 |
|
73 | 69 |
fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); |
... | ... | |
94 | 90 |
pstrcpy(bs->backing_file, sizeof(bs->backing_file), |
95 | 91 |
cow_header.backing_file); |
96 | 92 |
|
97 |
/* mmap the bitmap */ |
|
98 |
s->cow_bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header); |
|
99 |
s->cow_bitmap_addr = (void *)mmap(get_mmap_addr(s->cow_bitmap_size), |
|
100 |
s->cow_bitmap_size, |
|
101 |
PROT_READ | PROT_WRITE, |
|
102 |
MAP_SHARED, s->fd, 0); |
|
103 |
if (s->cow_bitmap_addr == MAP_FAILED) |
|
104 |
goto fail; |
|
105 |
s->cow_bitmap = s->cow_bitmap_addr + sizeof(cow_header); |
|
106 |
s->cow_sectors_offset = (s->cow_bitmap_size + 511) & ~511; |
|
93 |
bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header); |
|
94 |
s->cow_sectors_offset = (bitmap_size + 511) & ~511; |
|
107 | 95 |
return 0; |
108 | 96 |
fail: |
109 | 97 |
close(fd); |
110 | 98 |
return -1; |
111 | 99 |
} |
112 | 100 |
|
113 |
static inline void cow_set_bit(uint8_t *bitmap, int64_t bitnum) |
|
101 |
/* |
|
102 |
* XXX(hch): right now these functions are extremly ineffcient. |
|
103 |
* We should just read the whole bitmap we'll need in one go instead. |
|
104 |
*/ |
|
105 |
static inline int cow_set_bit(BlockDriverState *bs, int64_t bitnum) |
|
114 | 106 |
{ |
115 |
bitmap[bitnum / 8] |= (1 << (bitnum%8)); |
|
107 |
BDRVCowState *s = bs->opaque; |
|
108 |
uint64_t offset = sizeof(struct cow_header_v2) + bitnum / 8; |
|
109 |
uint8_t bitmap; |
|
110 |
|
|
111 |
if (pread(s->fd, &bitmap, sizeof(bitmap), offset) != |
|
112 |
sizeof(bitmap)) { |
|
113 |
return -errno; |
|
114 |
} |
|
115 |
|
|
116 |
bitmap |= (1 << (bitnum % 8)); |
|
117 |
|
|
118 |
if (pwrite(s->fd, &bitmap, sizeof(bitmap), offset) != |
|
119 |
sizeof(bitmap)) { |
|
120 |
return -errno; |
|
121 |
} |
|
122 |
return 0; |
|
116 | 123 |
} |
117 | 124 |
|
118 |
static inline int is_bit_set(const uint8_t *bitmap, int64_t bitnum)
|
|
125 |
static inline int is_bit_set(BlockDriverState *bs, int64_t bitnum)
|
|
119 | 126 |
{ |
120 |
return !!(bitmap[bitnum / 8] & (1 << (bitnum%8))); |
|
121 |
} |
|
127 |
BDRVCowState *s = bs->opaque; |
|
128 |
uint64_t offset = sizeof(struct cow_header_v2) + bitnum / 8; |
|
129 |
uint8_t bitmap; |
|
122 | 130 |
|
131 |
if (pread(s->fd, &bitmap, sizeof(bitmap), offset) != |
|
132 |
sizeof(bitmap)) { |
|
133 |
return -errno; |
|
134 |
} |
|
135 |
|
|
136 |
return !!(bitmap & (1 << (bitnum % 8))); |
|
137 |
} |
|
123 | 138 |
|
124 | 139 |
/* Return true if first block has been changed (ie. current version is |
125 | 140 |
* in COW file). Set the number of continuous blocks for which that |
126 | 141 |
* is true. */ |
127 |
static inline int is_changed(uint8_t *bitmap, |
|
128 |
int64_t sector_num, int nb_sectors, |
|
129 |
int *num_same) |
|
142 |
static int cow_is_allocated(BlockDriverState *bs, int64_t sector_num, |
|
143 |
int nb_sectors, int *num_same) |
|
130 | 144 |
{ |
131 | 145 |
int changed; |
132 | 146 |
|
133 |
if (!bitmap || nb_sectors == 0) {
|
|
147 |
if (nb_sectors == 0) { |
|
134 | 148 |
*num_same = nb_sectors; |
135 | 149 |
return 0; |
136 | 150 |
} |
137 | 151 |
|
138 |
changed = is_bit_set(bitmap, sector_num); |
|
152 |
changed = is_bit_set(bs, sector_num); |
|
153 |
if (changed < 0) { |
|
154 |
return 0; /* XXX: how to return I/O errors? */ |
|
155 |
} |
|
156 |
|
|
139 | 157 |
for (*num_same = 1; *num_same < nb_sectors; (*num_same)++) { |
140 |
if (is_bit_set(bitmap, sector_num + *num_same) != changed)
|
|
158 |
if (is_bit_set(bs, sector_num + *num_same) != changed)
|
|
141 | 159 |
break; |
142 | 160 |
} |
143 | 161 |
|
144 | 162 |
return changed; |
145 | 163 |
} |
146 | 164 |
|
147 |
static int cow_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
|
148 |
int nb_sectors, int *pnum)
|
|
165 |
static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num,
|
|
166 |
int nb_sectors)
|
|
149 | 167 |
{ |
150 |
BDRVCowState *s = bs->opaque; |
|
151 |
return is_changed(s->cow_bitmap, sector_num, nb_sectors, pnum); |
|
168 |
int error = 0; |
|
169 |
int i; |
|
170 |
|
|
171 |
for (i = 0; i < nb_sectors; i++) { |
|
172 |
error = cow_set_bit(bs, sector_num + i); |
|
173 |
if (error) { |
|
174 |
break; |
|
175 |
} |
|
176 |
} |
|
177 |
|
|
178 |
return error; |
|
152 | 179 |
} |
153 | 180 |
|
154 | 181 |
static int cow_read(BlockDriverState *bs, int64_t sector_num, |
... | ... | |
158 | 185 |
int ret, n; |
159 | 186 |
|
160 | 187 |
while (nb_sectors > 0) { |
161 |
if (is_changed(s->cow_bitmap, sector_num, nb_sectors, &n)) {
|
|
188 |
if (cow_is_allocated(bs, sector_num, nb_sectors, &n)) {
|
|
162 | 189 |
ret = pread(s->fd, buf, n * 512, |
163 | 190 |
s->cow_sectors_offset + sector_num * 512); |
164 | 191 |
if (ret != n * 512) |
... | ... | |
184 | 211 |
const uint8_t *buf, int nb_sectors) |
185 | 212 |
{ |
186 | 213 |
BDRVCowState *s = bs->opaque; |
187 |
int ret, i;
|
|
214 |
int ret; |
|
188 | 215 |
|
189 | 216 |
ret = pwrite(s->fd, buf, nb_sectors * 512, |
190 | 217 |
s->cow_sectors_offset + sector_num * 512); |
191 | 218 |
if (ret != nb_sectors * 512) |
192 | 219 |
return -1; |
193 |
for (i = 0; i < nb_sectors; i++) |
|
194 |
cow_set_bit(s->cow_bitmap, sector_num + i); |
|
195 |
return 0; |
|
220 |
|
|
221 |
return cow_update_bitmap(bs, sector_num, nb_sectors); |
|
196 | 222 |
} |
197 | 223 |
|
198 | 224 |
static void cow_close(BlockDriverState *bs) |
199 | 225 |
{ |
200 | 226 |
BDRVCowState *s = bs->opaque; |
201 |
munmap((void *)s->cow_bitmap_addr, s->cow_bitmap_size); |
|
202 | 227 |
close(s->fd); |
203 | 228 |
} |
204 | 229 |
|
... | ... | |
308 | 333 |
} |
309 | 334 |
|
310 | 335 |
block_init(bdrv_cow_init); |
311 |
#endif |
Also available in: Unified diff