diff options
Diffstat (limited to 'drivers/md/dm-snap-persistent.c')
-rw-r--r-- | drivers/md/dm-snap-persistent.c | 88 |
1 files changed, 53 insertions, 35 deletions
diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c index 6e3fe4f14934..d5b2e08750d5 100644 --- a/drivers/md/dm-snap-persistent.c +++ b/drivers/md/dm-snap-persistent.c | |||
@@ -106,6 +106,13 @@ struct pstore { | |||
106 | void *zero_area; | 106 | void *zero_area; |
107 | 107 | ||
108 | /* | 108 | /* |
109 | * An area used for header. The header can be written | ||
110 | * concurrently with metadata (when invalidating the snapshot), | ||
111 | * so it needs a separate buffer. | ||
112 | */ | ||
113 | void *header_area; | ||
114 | |||
115 | /* | ||
109 | * Used to keep track of which metadata area the data in | 116 | * Used to keep track of which metadata area the data in |
110 | * 'chunk' refers to. | 117 | * 'chunk' refers to. |
111 | */ | 118 | */ |
@@ -148,16 +155,27 @@ static int alloc_area(struct pstore *ps) | |||
148 | */ | 155 | */ |
149 | ps->area = vmalloc(len); | 156 | ps->area = vmalloc(len); |
150 | if (!ps->area) | 157 | if (!ps->area) |
151 | return r; | 158 | goto err_area; |
152 | 159 | ||
153 | ps->zero_area = vmalloc(len); | 160 | ps->zero_area = vmalloc(len); |
154 | if (!ps->zero_area) { | 161 | if (!ps->zero_area) |
155 | vfree(ps->area); | 162 | goto err_zero_area; |
156 | return r; | ||
157 | } | ||
158 | memset(ps->zero_area, 0, len); | 163 | memset(ps->zero_area, 0, len); |
159 | 164 | ||
165 | ps->header_area = vmalloc(len); | ||
166 | if (!ps->header_area) | ||
167 | goto err_header_area; | ||
168 | |||
160 | return 0; | 169 | return 0; |
170 | |||
171 | err_header_area: | ||
172 | vfree(ps->zero_area); | ||
173 | |||
174 | err_zero_area: | ||
175 | vfree(ps->area); | ||
176 | |||
177 | err_area: | ||
178 | return r; | ||
161 | } | 179 | } |
162 | 180 | ||
163 | static void free_area(struct pstore *ps) | 181 | static void free_area(struct pstore *ps) |
@@ -169,6 +187,10 @@ static void free_area(struct pstore *ps) | |||
169 | if (ps->zero_area) | 187 | if (ps->zero_area) |
170 | vfree(ps->zero_area); | 188 | vfree(ps->zero_area); |
171 | ps->zero_area = NULL; | 189 | ps->zero_area = NULL; |
190 | |||
191 | if (ps->header_area) | ||
192 | vfree(ps->header_area); | ||
193 | ps->header_area = NULL; | ||
172 | } | 194 | } |
173 | 195 | ||
174 | struct mdata_req { | 196 | struct mdata_req { |
@@ -188,7 +210,8 @@ static void do_metadata(struct work_struct *work) | |||
188 | /* | 210 | /* |
189 | * Read or write a chunk aligned and sized block of data from a device. | 211 | * Read or write a chunk aligned and sized block of data from a device. |
190 | */ | 212 | */ |
191 | static int chunk_io(struct pstore *ps, chunk_t chunk, int rw, int metadata) | 213 | static int chunk_io(struct pstore *ps, void *area, chunk_t chunk, int rw, |
214 | int metadata) | ||
192 | { | 215 | { |
193 | struct dm_io_region where = { | 216 | struct dm_io_region where = { |
194 | .bdev = ps->store->cow->bdev, | 217 | .bdev = ps->store->cow->bdev, |
@@ -198,7 +221,7 @@ static int chunk_io(struct pstore *ps, chunk_t chunk, int rw, int metadata) | |||
198 | struct dm_io_request io_req = { | 221 | struct dm_io_request io_req = { |
199 | .bi_rw = rw, | 222 | .bi_rw = rw, |
200 | .mem.type = DM_IO_VMA, | 223 | .mem.type = DM_IO_VMA, |
201 | .mem.ptr.vma = ps->area, | 224 | .mem.ptr.vma = area, |
202 | .client = ps->io_client, | 225 | .client = ps->io_client, |
203 | .notify.fn = NULL, | 226 | .notify.fn = NULL, |
204 | }; | 227 | }; |
@@ -240,7 +263,7 @@ static int area_io(struct pstore *ps, int rw) | |||
240 | 263 | ||
241 | chunk = area_location(ps, ps->current_area); | 264 | chunk = area_location(ps, ps->current_area); |
242 | 265 | ||
243 | r = chunk_io(ps, chunk, rw, 0); | 266 | r = chunk_io(ps, ps->area, chunk, rw, 0); |
244 | if (r) | 267 | if (r) |
245 | return r; | 268 | return r; |
246 | 269 | ||
@@ -254,20 +277,7 @@ static void zero_memory_area(struct pstore *ps) | |||
254 | 277 | ||
255 | static int zero_disk_area(struct pstore *ps, chunk_t area) | 278 | static int zero_disk_area(struct pstore *ps, chunk_t area) |
256 | { | 279 | { |
257 | struct dm_io_region where = { | 280 | return chunk_io(ps, ps->zero_area, area_location(ps, area), WRITE, 0); |
258 | .bdev = ps->store->cow->bdev, | ||
259 | .sector = ps->store->chunk_size * area_location(ps, area), | ||
260 | .count = ps->store->chunk_size, | ||
261 | }; | ||
262 | struct dm_io_request io_req = { | ||
263 | .bi_rw = WRITE, | ||
264 | .mem.type = DM_IO_VMA, | ||
265 | .mem.ptr.vma = ps->zero_area, | ||
266 | .client = ps->io_client, | ||
267 | .notify.fn = NULL, | ||
268 | }; | ||
269 | |||
270 | return dm_io(&io_req, 1, &where, NULL); | ||
271 | } | 281 | } |
272 | 282 | ||
273 | static int read_header(struct pstore *ps, int *new_snapshot) | 283 | static int read_header(struct pstore *ps, int *new_snapshot) |
@@ -276,6 +286,7 @@ static int read_header(struct pstore *ps, int *new_snapshot) | |||
276 | struct disk_header *dh; | 286 | struct disk_header *dh; |
277 | chunk_t chunk_size; | 287 | chunk_t chunk_size; |
278 | int chunk_size_supplied = 1; | 288 | int chunk_size_supplied = 1; |
289 | char *chunk_err; | ||
279 | 290 | ||
280 | /* | 291 | /* |
281 | * Use default chunk size (or hardsect_size, if larger) if none supplied | 292 | * Use default chunk size (or hardsect_size, if larger) if none supplied |
@@ -297,11 +308,11 @@ static int read_header(struct pstore *ps, int *new_snapshot) | |||
297 | if (r) | 308 | if (r) |
298 | return r; | 309 | return r; |
299 | 310 | ||
300 | r = chunk_io(ps, 0, READ, 1); | 311 | r = chunk_io(ps, ps->header_area, 0, READ, 1); |
301 | if (r) | 312 | if (r) |
302 | goto bad; | 313 | goto bad; |
303 | 314 | ||
304 | dh = (struct disk_header *) ps->area; | 315 | dh = ps->header_area; |
305 | 316 | ||
306 | if (le32_to_cpu(dh->magic) == 0) { | 317 | if (le32_to_cpu(dh->magic) == 0) { |
307 | *new_snapshot = 1; | 318 | *new_snapshot = 1; |
@@ -319,20 +330,25 @@ static int read_header(struct pstore *ps, int *new_snapshot) | |||
319 | ps->version = le32_to_cpu(dh->version); | 330 | ps->version = le32_to_cpu(dh->version); |
320 | chunk_size = le32_to_cpu(dh->chunk_size); | 331 | chunk_size = le32_to_cpu(dh->chunk_size); |
321 | 332 | ||
322 | if (!chunk_size_supplied || ps->store->chunk_size == chunk_size) | 333 | if (ps->store->chunk_size == chunk_size) |
323 | return 0; | 334 | return 0; |
324 | 335 | ||
325 | DMWARN("chunk size %llu in device metadata overrides " | 336 | if (chunk_size_supplied) |
326 | "table chunk size of %llu.", | 337 | DMWARN("chunk size %llu in device metadata overrides " |
327 | (unsigned long long)chunk_size, | 338 | "table chunk size of %llu.", |
328 | (unsigned long long)ps->store->chunk_size); | 339 | (unsigned long long)chunk_size, |
340 | (unsigned long long)ps->store->chunk_size); | ||
329 | 341 | ||
330 | /* We had a bogus chunk_size. Fix stuff up. */ | 342 | /* We had a bogus chunk_size. Fix stuff up. */ |
331 | free_area(ps); | 343 | free_area(ps); |
332 | 344 | ||
333 | ps->store->chunk_size = chunk_size; | 345 | r = dm_exception_store_set_chunk_size(ps->store, chunk_size, |
334 | ps->store->chunk_mask = chunk_size - 1; | 346 | &chunk_err); |
335 | ps->store->chunk_shift = ffs(chunk_size) - 1; | 347 | if (r) { |
348 | DMERR("invalid on-disk chunk size %llu: %s.", | ||
349 | (unsigned long long)chunk_size, chunk_err); | ||
350 | return r; | ||
351 | } | ||
336 | 352 | ||
337 | r = dm_io_client_resize(sectors_to_pages(ps->store->chunk_size), | 353 | r = dm_io_client_resize(sectors_to_pages(ps->store->chunk_size), |
338 | ps->io_client); | 354 | ps->io_client); |
@@ -351,15 +367,15 @@ static int write_header(struct pstore *ps) | |||
351 | { | 367 | { |
352 | struct disk_header *dh; | 368 | struct disk_header *dh; |
353 | 369 | ||
354 | memset(ps->area, 0, ps->store->chunk_size << SECTOR_SHIFT); | 370 | memset(ps->header_area, 0, ps->store->chunk_size << SECTOR_SHIFT); |
355 | 371 | ||
356 | dh = (struct disk_header *) ps->area; | 372 | dh = ps->header_area; |
357 | dh->magic = cpu_to_le32(SNAP_MAGIC); | 373 | dh->magic = cpu_to_le32(SNAP_MAGIC); |
358 | dh->valid = cpu_to_le32(ps->valid); | 374 | dh->valid = cpu_to_le32(ps->valid); |
359 | dh->version = cpu_to_le32(ps->version); | 375 | dh->version = cpu_to_le32(ps->version); |
360 | dh->chunk_size = cpu_to_le32(ps->store->chunk_size); | 376 | dh->chunk_size = cpu_to_le32(ps->store->chunk_size); |
361 | 377 | ||
362 | return chunk_io(ps, 0, WRITE, 1); | 378 | return chunk_io(ps, ps->header_area, 0, WRITE, 1); |
363 | } | 379 | } |
364 | 380 | ||
365 | /* | 381 | /* |
@@ -679,6 +695,8 @@ static int persistent_ctr(struct dm_exception_store *store, | |||
679 | ps->valid = 1; | 695 | ps->valid = 1; |
680 | ps->version = SNAPSHOT_DISK_VERSION; | 696 | ps->version = SNAPSHOT_DISK_VERSION; |
681 | ps->area = NULL; | 697 | ps->area = NULL; |
698 | ps->zero_area = NULL; | ||
699 | ps->header_area = NULL; | ||
682 | ps->next_free = 2; /* skipping the header and first area */ | 700 | ps->next_free = 2; /* skipping the header and first area */ |
683 | ps->current_committed = 0; | 701 | ps->current_committed = 0; |
684 | 702 | ||