diff options
Diffstat (limited to 'fs_mgr/liblp/builder.cpp')
-rw-r--r-- | fs_mgr/liblp/builder.cpp | 78 |
1 files changed, 64 insertions, 14 deletions
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp index d3c785d33..eb429b930 100644 --- a/fs_mgr/liblp/builder.cpp +++ b/fs_mgr/liblp/builder.cpp | |||
@@ -48,10 +48,20 @@ bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device | |||
48 | PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed"; | 48 | PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed"; |
49 | return false; | 49 | return false; |
50 | } | 50 | } |
51 | if (ioctl(fd, BLKALIGNOFF, &device_info->alignment_offset) < 0) { | 51 | |
52 | int alignment_offset; | ||
53 | if (ioctl(fd, BLKALIGNOFF, &alignment_offset) < 0) { | ||
52 | PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed"; | 54 | PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed"; |
53 | return false; | 55 | return false; |
54 | } | 56 | } |
57 | int logical_block_size; | ||
58 | if (ioctl(fd, BLKSSZGET, &logical_block_size) < 0) { | ||
59 | PERROR << __PRETTY_FUNCTION__ << "BLKSSZGET failed"; | ||
60 | return false; | ||
61 | } | ||
62 | |||
63 | device_info->alignment_offset = static_cast<uint32_t>(alignment_offset); | ||
64 | device_info->logical_block_size = static_cast<uint32_t>(logical_block_size); | ||
55 | return true; | 65 | return true; |
56 | #else | 66 | #else |
57 | (void)block_device; | 67 | (void)block_device; |
@@ -178,6 +188,7 @@ bool MetadataBuilder::Init(const LpMetadata& metadata) { | |||
178 | 188 | ||
179 | device_info_.alignment = geometry_.alignment; | 189 | device_info_.alignment = geometry_.alignment; |
180 | device_info_.alignment_offset = geometry_.alignment_offset; | 190 | device_info_.alignment_offset = geometry_.alignment_offset; |
191 | device_info_.logical_block_size = geometry_.logical_block_size; | ||
181 | return true; | 192 | return true; |
182 | } | 193 | } |
183 | 194 | ||
@@ -201,6 +212,10 @@ bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata | |||
201 | LERROR << "Block device size must be a multiple of 512."; | 212 | LERROR << "Block device size must be a multiple of 512."; |
202 | return false; | 213 | return false; |
203 | } | 214 | } |
215 | if (device_info_.logical_block_size % LP_SECTOR_SIZE != 0) { | ||
216 | LERROR << "Logical block size must be a multiple of 512."; | ||
217 | return false; | ||
218 | } | ||
204 | if (device_info_.alignment_offset % LP_SECTOR_SIZE != 0) { | 219 | if (device_info_.alignment_offset % LP_SECTOR_SIZE != 0) { |
205 | LERROR << "Alignment offset is not sector-aligned."; | 220 | LERROR << "Alignment offset is not sector-aligned."; |
206 | return false; | 221 | return false; |
@@ -244,6 +259,18 @@ bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata | |||
244 | return false; | 259 | return false; |
245 | } | 260 | } |
246 | 261 | ||
262 | // Finally, the size of the allocatable space must be a multiple of the | ||
263 | // logical block size. If we have no more free space after this | ||
264 | // computation, then we abort. Note that the last sector is inclusive, | ||
265 | // so we have to account for that. | ||
266 | uint64_t num_free_sectors = last_sector - first_sector + 1; | ||
267 | uint64_t sectors_per_block = device_info_.logical_block_size / LP_SECTOR_SIZE; | ||
268 | if (num_free_sectors < sectors_per_block) { | ||
269 | LERROR << "Not enough space to allocate any partition tables."; | ||
270 | return false; | ||
271 | } | ||
272 | last_sector = first_sector + (num_free_sectors / sectors_per_block) * sectors_per_block - 1; | ||
273 | |||
247 | geometry_.first_logical_sector = first_sector; | 274 | geometry_.first_logical_sector = first_sector; |
248 | geometry_.last_logical_sector = last_sector; | 275 | geometry_.last_logical_sector = last_sector; |
249 | geometry_.metadata_max_size = metadata_max_size; | 276 | geometry_.metadata_max_size = metadata_max_size; |
@@ -251,6 +278,7 @@ bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata | |||
251 | geometry_.alignment = device_info_.alignment; | 278 | geometry_.alignment = device_info_.alignment; |
252 | geometry_.alignment_offset = device_info_.alignment_offset; | 279 | geometry_.alignment_offset = device_info_.alignment_offset; |
253 | geometry_.block_device_size = device_info_.size; | 280 | geometry_.block_device_size = device_info_.size; |
281 | geometry_.logical_block_size = device_info.logical_block_size; | ||
254 | return true; | 282 | return true; |
255 | } | 283 | } |
256 | 284 | ||
@@ -297,6 +325,7 @@ bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) | |||
297 | uint64_t end; | 325 | uint64_t end; |
298 | 326 | ||
299 | Interval(uint64_t start, uint64_t end) : start(start), end(end) {} | 327 | Interval(uint64_t start, uint64_t end) : start(start), end(end) {} |
328 | uint64_t length() const { return end - start; } | ||
300 | bool operator<(const Interval& other) const { return start < other.start; } | 329 | bool operator<(const Interval& other) const { return start < other.start; } |
301 | }; | 330 | }; |
302 | 331 | ||
@@ -343,31 +372,46 @@ bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) | |||
343 | free_regions.emplace_back(last_free_extent_start, geometry_.last_logical_sector + 1); | 372 | free_regions.emplace_back(last_free_extent_start, geometry_.last_logical_sector + 1); |
344 | } | 373 | } |
345 | 374 | ||
375 | const uint64_t sectors_per_block = device_info_.logical_block_size / LP_SECTOR_SIZE; | ||
376 | CHECK(sectors_needed % sectors_per_block == 0); | ||
377 | |||
346 | // Find gaps that we can use for new extents. Note we store new extents in a | 378 | // Find gaps that we can use for new extents. Note we store new extents in a |
347 | // temporary vector, and only commit them if we are guaranteed enough free | 379 | // temporary vector, and only commit them if we are guaranteed enough free |
348 | // space. | 380 | // space. |
349 | std::vector<std::unique_ptr<LinearExtent>> new_extents; | 381 | std::vector<std::unique_ptr<LinearExtent>> new_extents; |
350 | for (const auto& region : free_regions) { | 382 | for (auto& region : free_regions) { |
351 | // This gap is enough to hold the remainder of the space requested, so we | 383 | if (region.length() % sectors_per_block != 0) { |
352 | // can allocate what we need and return. | 384 | // This should never happen, because it would imply that we |
353 | if (region.end - region.start >= sectors_needed) { | 385 | // once allocated an extent that was not a multiple of the |
354 | auto extent = std::make_unique<LinearExtent>(sectors_needed, region.start); | 386 | // block size. That extent would be rejected by DM_TABLE_LOAD. |
355 | sectors_needed -= extent->num_sectors(); | 387 | LERROR << "Region " << region.start << ".." << region.end |
356 | new_extents.push_back(std::move(extent)); | 388 | << " is not a multiple of the block size, " << sectors_per_block; |
357 | break; | 389 | |
390 | // If for some reason the final region is mis-sized we still want | ||
391 | // to be able to grow partitions. So just to be safe, round the | ||
392 | // region down to the nearest block. | ||
393 | region.end = region.start + (region.length() / sectors_per_block) * sectors_per_block; | ||
394 | if (!region.length()) { | ||
395 | continue; | ||
396 | } | ||
358 | } | 397 | } |
359 | 398 | ||
360 | // This gap is not big enough to fit the remainder of the space requested, | 399 | uint64_t sectors = std::min(sectors_needed, region.length()); |
361 | // so consume the whole thing and keep looking for more. | 400 | CHECK(sectors % sectors_per_block == 0); |
362 | auto extent = std::make_unique<LinearExtent>(region.end - region.start, region.start); | 401 | |
363 | sectors_needed -= extent->num_sectors(); | 402 | auto extent = std::make_unique<LinearExtent>(sectors, region.start); |
364 | new_extents.push_back(std::move(extent)); | 403 | new_extents.push_back(std::move(extent)); |
404 | sectors_needed -= sectors; | ||
405 | if (!sectors_needed) { | ||
406 | break; | ||
407 | } | ||
365 | } | 408 | } |
366 | if (sectors_needed) { | 409 | if (sectors_needed) { |
367 | LERROR << "Not enough free space to expand partition: " << partition->name(); | 410 | LERROR << "Not enough free space to expand partition: " << partition->name(); |
368 | return false; | 411 | return false; |
369 | } | 412 | } |
370 | 413 | ||
414 | // Everything succeeded, so commit the new extents. | ||
371 | for (auto& extent : new_extents) { | 415 | for (auto& extent : new_extents) { |
372 | partition->AddExtent(std::move(extent)); | 416 | partition->AddExtent(std::move(extent)); |
373 | } | 417 | } |
@@ -435,6 +479,12 @@ uint64_t MetadataBuilder::AlignSector(uint64_t sector) { | |||
435 | void MetadataBuilder::set_block_device_info(const BlockDeviceInfo& device_info) { | 479 | void MetadataBuilder::set_block_device_info(const BlockDeviceInfo& device_info) { |
436 | device_info_.size = device_info.size; | 480 | device_info_.size = device_info.size; |
437 | 481 | ||
482 | // Note that if the logical block size changes, we're probably in trouble: | ||
483 | // we could have already built extents that will only work on the previous | ||
484 | // size. | ||
485 | DCHECK(partitions_.empty() || | ||
486 | device_info_.logical_block_size == device_info.logical_block_size); | ||
487 | |||
438 | // The kernel does not guarantee these values are present, so we only | 488 | // The kernel does not guarantee these values are present, so we only |
439 | // replace existing values if the new values are non-zero. | 489 | // replace existing values if the new values are non-zero. |
440 | if (device_info.alignment) { | 490 | if (device_info.alignment) { |
@@ -447,7 +497,7 @@ void MetadataBuilder::set_block_device_info(const BlockDeviceInfo& device_info) | |||
447 | 497 | ||
448 | bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size) { | 498 | bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size) { |
449 | // Align the space needed up to the nearest sector. | 499 | // Align the space needed up to the nearest sector. |
450 | uint64_t aligned_size = AlignTo(requested_size, LP_SECTOR_SIZE); | 500 | uint64_t aligned_size = AlignTo(requested_size, device_info_.logical_block_size); |
451 | 501 | ||
452 | if (aligned_size > partition->size()) { | 502 | if (aligned_size > partition->size()) { |
453 | return GrowPartition(partition, aligned_size); | 503 | return GrowPartition(partition, aligned_size); |