1 // Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "net/disk_cache/block_files.h" 6 7 #include "base/file_util.h" 8 #include "base/metrics/histogram.h" 9 #include "base/string_util.h" 10 #include "base/stringprintf.h" 11 #include "base/threading/thread_checker.h" 12 #include "base/time.h" 13 #include "net/disk_cache/cache_util.h" 14 #include "net/disk_cache/file_lock.h" 15 #include "net/disk_cache/trace.h" 16 17 using base::TimeTicks; 18 19 namespace { 20 21 const char* kBlockName = "data_"; 22 23 // This array is used to perform a fast lookup of the nibble bit pattern to the 24 // type of entry that can be stored there (number of consecutive blocks). 25 const char s_types[16] = {4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}; 26 27 // Returns the type of block (number of consecutive blocks that can be stored) 28 // for a given nibble of the bitmap. 29 inline int GetMapBlockType(uint8 value) { 30 value &= 0xf; 31 return s_types[value]; 32 } 33 34 void FixAllocationCounters(disk_cache::BlockFileHeader* header); 35 36 // Creates a new entry on the allocation map, updating the apropriate counters. 37 // target is the type of block to use (number of empty blocks), and size is the 38 // actual number of blocks to use. 39 bool CreateMapBlock(int target, int size, disk_cache::BlockFileHeader* header, 40 int* index) { 41 if (target <= 0 || target > disk_cache::kMaxNumBlocks || 42 size <= 0 || size > disk_cache::kMaxNumBlocks) { 43 NOTREACHED(); 44 return false; 45 } 46 47 TimeTicks start = TimeTicks::Now(); 48 // We are going to process the map on 32-block chunks (32 bits), and on every 49 // chunk, iterate through the 8 nibbles where the new block can be located. 50 int current = header->hints[target - 1]; 51 for (int i = 0; i < header->max_entries / 32; i++, current++) { 52 if (current == header->max_entries / 32) 53 current = 0; 54 uint32 map_block = header->allocation_map[current]; 55 56 for (int j = 0; j < 8; j++, map_block >>= 4) { 57 if (GetMapBlockType(map_block) != target) 58 continue; 59 60 disk_cache::FileLock lock(header); 61 int index_offset = j * 4 + 4 - target; 62 *index = current * 32 + index_offset; 63 DCHECK_EQ(*index / 4, (*index + size - 1) / 4); 64 uint32 to_add = ((1 << size) - 1) << index_offset; 65 header->allocation_map[current] |= to_add; 66 67 header->hints[target - 1] = current; 68 header->empty[target - 1]--; 69 DCHECK(header->empty[target - 1] >= 0); 70 header->num_entries++; 71 if (target != size) { 72 header->empty[target - size - 1]++; 73 } 74 HISTOGRAM_TIMES("DiskCache.CreateBlock", TimeTicks::Now() - start); 75 return true; 76 } 77 } 78 79 // It is possible to have an undetected corruption (for example when the OS 80 // crashes), fix it here. 81 LOG(ERROR) << "Failing CreateMapBlock"; 82 FixAllocationCounters(header); 83 return false; 84 } 85 86 // Deletes the block pointed by index from allocation_map, and updates the 87 // relevant counters on the header. 88 void DeleteMapBlock(int index, int size, disk_cache::BlockFileHeader* header) { 89 if (size < 0 || size > disk_cache::kMaxNumBlocks) { 90 NOTREACHED(); 91 return; 92 } 93 TimeTicks start = TimeTicks::Now(); 94 int byte_index = index / 8; 95 uint8* byte_map = reinterpret_cast<uint8*>(header->allocation_map); 96 uint8 map_block = byte_map[byte_index]; 97 98 if (index % 8 >= 4) 99 map_block >>= 4; 100 101 // See what type of block will be availabe after we delete this one. 102 int bits_at_end = 4 - size - index % 4; 103 uint8 end_mask = (0xf << (4 - bits_at_end)) & 0xf; 104 bool update_counters = (map_block & end_mask) == 0; 105 uint8 new_value = map_block & ~(((1 << size) - 1) << (index % 4)); 106 int new_type = GetMapBlockType(new_value); 107 108 disk_cache::FileLock lock(header); 109 DCHECK((((1 << size) - 1) << (index % 8)) < 0x100); 110 uint8 to_clear = ((1 << size) - 1) << (index % 8); 111 DCHECK((byte_map[byte_index] & to_clear) == to_clear); 112 byte_map[byte_index] &= ~to_clear; 113 114 if (update_counters) { 115 if (bits_at_end) 116 header->empty[bits_at_end - 1]--; 117 header->empty[new_type - 1]++; 118 DCHECK(header->empty[bits_at_end - 1] >= 0); 119 } 120 header->num_entries--; 121 DCHECK(header->num_entries >= 0); 122 HISTOGRAM_TIMES("DiskCache.DeleteBlock", TimeTicks::Now() - start); 123 } 124 125 #ifndef NDEBUG 126 // Returns true if the specified block is used. Note that this is a simplified 127 // version of DeleteMapBlock(). 128 bool UsedMapBlock(int index, int size, disk_cache::BlockFileHeader* header) { 129 if (size < 0 || size > disk_cache::kMaxNumBlocks) { 130 NOTREACHED(); 131 return false; 132 } 133 int byte_index = index / 8; 134 uint8* byte_map = reinterpret_cast<uint8*>(header->allocation_map); 135 uint8 map_block = byte_map[byte_index]; 136 137 if (index % 8 >= 4) 138 map_block >>= 4; 139 140 DCHECK((((1 << size) - 1) << (index % 8)) < 0x100); 141 uint8 to_clear = ((1 << size) - 1) << (index % 8); 142 return ((byte_map[byte_index] & to_clear) == to_clear); 143 } 144 #endif // NDEBUG 145 146 // Restores the "empty counters" and allocation hints. 147 void FixAllocationCounters(disk_cache::BlockFileHeader* header) { 148 for (int i = 0; i < disk_cache::kMaxNumBlocks; i++) { 149 header->hints[i] = 0; 150 header->empty[i] = 0; 151 } 152 153 for (int i = 0; i < header->max_entries / 32; i++) { 154 uint32 map_block = header->allocation_map[i]; 155 156 for (int j = 0; j < 8; j++, map_block >>= 4) { 157 int type = GetMapBlockType(map_block); 158 if (type) 159 header->empty[type -1]++; 160 } 161 } 162 } 163 164 // Returns true if the current block file should not be used as-is to store more 165 // records. |block_count| is the number of blocks to allocate. 166 bool NeedToGrowBlockFile(const disk_cache::BlockFileHeader* header, 167 int block_count) { 168 bool have_space = false; 169 int empty_blocks = 0; 170 for (int i = 0; i < disk_cache::kMaxNumBlocks; i++) { 171 empty_blocks += header->empty[i] * (i + 1); 172 if (i >= block_count - 1 && header->empty[i]) 173 have_space = true; 174 } 175 176 if (header->next_file && (empty_blocks < disk_cache::kMaxBlocks / 10)) { 177 // This file is almost full but we already created another one, don't use 178 // this file yet so that it is easier to find empty blocks when we start 179 // using this file again. 180 return true; 181 } 182 return !have_space; 183 } 184 185 } // namespace 186 187 namespace disk_cache { 188 189 BlockFiles::BlockFiles(const FilePath& path) 190 : init_(false), zero_buffer_(NULL), path_(path) { 191 } 192 193 BlockFiles::~BlockFiles() { 194 if (zero_buffer_) 195 delete[] zero_buffer_; 196 CloseFiles(); 197 } 198 199 bool BlockFiles::Init(bool create_files) { 200 DCHECK(!init_); 201 if (init_) 202 return false; 203 204 thread_checker_.reset(new base::ThreadChecker); 205 206 block_files_.resize(kFirstAdditionalBlockFile); 207 for (int i = 0; i < kFirstAdditionalBlockFile; i++) { 208 if (create_files) 209 if (!CreateBlockFile(i, static_cast<FileType>(i + 1), true)) 210 return false; 211 212 if (!OpenBlockFile(i)) 213 return false; 214 215 // Walk this chain of files removing empty ones. 216 RemoveEmptyFile(static_cast<FileType>(i + 1)); 217 } 218 219 init_ = true; 220 return true; 221 } 222 223 MappedFile* BlockFiles::GetFile(Addr address) { 224 DCHECK(thread_checker_->CalledOnValidThread()); 225 DCHECK(block_files_.size() >= 4); 226 DCHECK(address.is_block_file() || !address.is_initialized()); 227 if (!address.is_initialized()) 228 return NULL; 229 230 int file_index = address.FileNumber(); 231 if (static_cast<unsigned int>(file_index) >= block_files_.size() || 232 !block_files_[file_index]) { 233 // We need to open the file 234 if (!OpenBlockFile(file_index)) 235 return NULL; 236 } 237 DCHECK(block_files_.size() >= static_cast<unsigned int>(file_index)); 238 return block_files_[file_index]; 239 } 240 241 bool BlockFiles::CreateBlock(FileType block_type, int block_count, 242 Addr* block_address) { 243 DCHECK(thread_checker_->CalledOnValidThread()); 244 if (block_type < RANKINGS || block_type > BLOCK_4K || 245 block_count < 1 || block_count > 4) 246 return false; 247 if (!init_) 248 return false; 249 250 MappedFile* file = FileForNewBlock(block_type, block_count); 251 if (!file) 252 return false; 253 254 BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer()); 255 256 int target_size = 0; 257 for (int i = block_count; i <= 4; i++) { 258 if (header->empty[i - 1]) { 259 target_size = i; 260 break; 261 } 262 } 263 264 DCHECK(target_size); 265 int index; 266 if (!CreateMapBlock(target_size, block_count, header, &index)) 267 return false; 268 269 Addr address(block_type, block_count, header->this_file, index); 270 block_address->set_value(address.value()); 271 Trace("CreateBlock 0x%x", address.value()); 272 return true; 273 } 274 275 void BlockFiles::DeleteBlock(Addr address, bool deep) { 276 DCHECK(thread_checker_->CalledOnValidThread()); 277 if (!address.is_initialized() || address.is_separate_file()) 278 return; 279 280 if (!zero_buffer_) { 281 zero_buffer_ = new char[Addr::BlockSizeForFileType(BLOCK_4K) * 4]; 282 memset(zero_buffer_, 0, Addr::BlockSizeForFileType(BLOCK_4K) * 4); 283 } 284 MappedFile* file = GetFile(address); 285 if (!file) 286 return; 287 288 Trace("DeleteBlock 0x%x", address.value()); 289 290 size_t size = address.BlockSize() * address.num_blocks(); 291 size_t offset = address.start_block() * address.BlockSize() + 292 kBlockHeaderSize; 293 if (deep) 294 file->Write(zero_buffer_, size, offset); 295 296 BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer()); 297 DeleteMapBlock(address.start_block(), address.num_blocks(), header); 298 299 if (!header->num_entries) { 300 // This file is now empty. Let's try to delete it. 301 FileType type = Addr::RequiredFileType(header->entry_size); 302 if (Addr::BlockSizeForFileType(RANKINGS) == header->entry_size) 303 type = RANKINGS; 304 RemoveEmptyFile(type); 305 } 306 } 307 308 void BlockFiles::CloseFiles() { 309 if (init_) { 310 DCHECK(thread_checker_->CalledOnValidThread()); 311 } 312 init_ = false; 313 for (unsigned int i = 0; i < block_files_.size(); i++) { 314 if (block_files_[i]) { 315 block_files_[i]->Release(); 316 block_files_[i] = NULL; 317 } 318 } 319 block_files_.clear(); 320 } 321 322 void BlockFiles::ReportStats() { 323 DCHECK(thread_checker_->CalledOnValidThread()); 324 int used_blocks[kFirstAdditionalBlockFile]; 325 int load[kFirstAdditionalBlockFile]; 326 for (int i = 0; i < kFirstAdditionalBlockFile; i++) { 327 GetFileStats(i, &used_blocks[i], &load[i]); 328 } 329 UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_0", used_blocks[0]); 330 UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_1", used_blocks[1]); 331 UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_2", used_blocks[2]); 332 UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_3", used_blocks[3]); 333 334 UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_0", load[0], 101); 335 UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_1", load[1], 101); 336 UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_2", load[2], 101); 337 UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_3", load[3], 101); 338 } 339 340 bool BlockFiles::IsValid(Addr address) { 341 #ifdef NDEBUG 342 return true; 343 #else 344 if (!address.is_initialized() || address.is_separate_file()) 345 return false; 346 347 MappedFile* file = GetFile(address); 348 if (!file) 349 return false; 350 351 BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer()); 352 bool rv = UsedMapBlock(address.start_block(), address.num_blocks(), header); 353 DCHECK(rv); 354 355 static bool read_contents = false; 356 if (read_contents) { 357 scoped_array<char> buffer; 358 buffer.reset(new char[Addr::BlockSizeForFileType(BLOCK_4K) * 4]); 359 size_t size = address.BlockSize() * address.num_blocks(); 360 size_t offset = address.start_block() * address.BlockSize() + 361 kBlockHeaderSize; 362 bool ok = file->Read(buffer.get(), size, offset); 363 DCHECK(ok); 364 } 365 366 return rv; 367 #endif 368 } 369 370 bool BlockFiles::CreateBlockFile(int index, FileType file_type, bool force) { 371 FilePath name = Name(index); 372 int flags = 373 force ? base::PLATFORM_FILE_CREATE_ALWAYS : base::PLATFORM_FILE_CREATE; 374 flags |= base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_EXCLUSIVE_WRITE; 375 376 scoped_refptr<File> file(new File( 377 base::CreatePlatformFile(name, flags, NULL, NULL))); 378 if (!file->IsValid()) 379 return false; 380 381 BlockFileHeader header; 382 header.entry_size = Addr::BlockSizeForFileType(file_type); 383 header.this_file = static_cast<int16>(index); 384 DCHECK(index <= kint16max && index >= 0); 385 386 return file->Write(&header, sizeof(header), 0); 387 } 388 389 bool BlockFiles::OpenBlockFile(int index) { 390 if (block_files_.size() - 1 < static_cast<unsigned int>(index)) { 391 DCHECK(index > 0); 392 int to_add = index - static_cast<int>(block_files_.size()) + 1; 393 block_files_.resize(block_files_.size() + to_add); 394 } 395 396 FilePath name = Name(index); 397 scoped_refptr<MappedFile> file(new MappedFile()); 398 399 if (!file->Init(name, kBlockHeaderSize)) { 400 LOG(ERROR) << "Failed to open " << name.value(); 401 return false; 402 } 403 404 size_t file_len = file->GetLength(); 405 if (file_len < static_cast<size_t>(kBlockHeaderSize)) { 406 LOG(ERROR) << "File too small " << name.value(); 407 return false; 408 } 409 410 BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer()); 411 if (kBlockMagic != header->magic || kCurrentVersion != header->version) { 412 LOG(ERROR) << "Invalid file version or magic"; 413 return false; 414 } 415 416 if (header->updating) { 417 // Last instance was not properly shutdown. 418 if (!FixBlockFileHeader(file)) 419 return false; 420 } 421 422 if (static_cast<int>(file_len) < 423 header->max_entries * header->entry_size + kBlockHeaderSize) { 424 LOG(ERROR) << "File too small " << name.value(); 425 return false; 426 } 427 428 if (index == 0) { 429 // Load the links file into memory with a single read. 430 scoped_array<char> buf(new char[file_len]); 431 if (!file->Read(buf.get(), file_len, 0)) 432 return false; 433 } 434 435 DCHECK(!block_files_[index]); 436 file.swap(&block_files_[index]); 437 return true; 438 } 439 440 bool BlockFiles::GrowBlockFile(MappedFile* file, BlockFileHeader* header) { 441 if (kMaxBlocks == header->max_entries) 442 return false; 443 444 DCHECK(!header->empty[3]); 445 int new_size = header->max_entries + 1024; 446 if (new_size > kMaxBlocks) 447 new_size = kMaxBlocks; 448 449 int new_size_bytes = new_size * header->entry_size + sizeof(*header); 450 451 FileLock lock(header); 452 if (!file->SetLength(new_size_bytes)) { 453 // Most likely we are trying to truncate the file, so the header is wrong. 454 if (header->updating < 10 && !FixBlockFileHeader(file)) { 455 // If we can't fix the file increase the lock guard so we'll pick it on 456 // the next start and replace it. 457 header->updating = 100; 458 return false; 459 } 460 return (header->max_entries >= new_size); 461 } 462 463 header->empty[3] = (new_size - header->max_entries) / 4; // 4 blocks entries 464 header->max_entries = new_size; 465 466 return true; 467 } 468 469 MappedFile* BlockFiles::FileForNewBlock(FileType block_type, int block_count) { 470 COMPILE_ASSERT(RANKINGS == 1, invalid_fily_type); 471 MappedFile* file = block_files_[block_type - 1]; 472 BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer()); 473 474 TimeTicks start = TimeTicks::Now(); 475 while (NeedToGrowBlockFile(header, block_count)) { 476 if (kMaxBlocks == header->max_entries) { 477 file = NextFile(file); 478 if (!file) 479 return NULL; 480 header = reinterpret_cast<BlockFileHeader*>(file->buffer()); 481 continue; 482 } 483 484 if (!GrowBlockFile(file, header)) 485 return NULL; 486 break; 487 } 488 HISTOGRAM_TIMES("DiskCache.GetFileForNewBlock", TimeTicks::Now() - start); 489 return file; 490 } 491 492 MappedFile* BlockFiles::NextFile(const MappedFile* file) { 493 BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer()); 494 int new_file = header->next_file; 495 if (!new_file) { 496 // RANKINGS is not reported as a type for small entries, but we may be 497 // extending the rankings block file. 498 FileType type = Addr::RequiredFileType(header->entry_size); 499 if (header->entry_size == Addr::BlockSizeForFileType(RANKINGS)) 500 type = RANKINGS; 501 502 new_file = CreateNextBlockFile(type); 503 if (!new_file) 504 return NULL; 505 506 FileLock lock(header); 507 header->next_file = new_file; 508 } 509 510 // Only the block_file argument is relevant for what we want. 511 Addr address(BLOCK_256, 1, new_file, 0); 512 return GetFile(address); 513 } 514 515 int BlockFiles::CreateNextBlockFile(FileType block_type) { 516 for (int i = kFirstAdditionalBlockFile; i <= kMaxBlockFile; i++) { 517 if (CreateBlockFile(i, block_type, false)) 518 return i; 519 } 520 return 0; 521 } 522 523 // We walk the list of files for this particular block type, deleting the ones 524 // that are empty. 525 void BlockFiles::RemoveEmptyFile(FileType block_type) { 526 MappedFile* file = block_files_[block_type - 1]; 527 BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer()); 528 529 while (header->next_file) { 530 // Only the block_file argument is relevant for what we want. 531 Addr address(BLOCK_256, 1, header->next_file, 0); 532 MappedFile* next_file = GetFile(address); 533 if (!next_file) 534 return; 535 536 BlockFileHeader* next_header = 537 reinterpret_cast<BlockFileHeader*>(next_file->buffer()); 538 if (!next_header->num_entries) { 539 DCHECK_EQ(next_header->entry_size, header->entry_size); 540 // Delete next_file and remove it from the chain. 541 int file_index = header->next_file; 542 header->next_file = next_header->next_file; 543 DCHECK(block_files_.size() >= static_cast<unsigned int>(file_index)); 544 545 // We get a new handle to the file and release the old one so that the 546 // file gets unmmaped... so we can delete it. 547 FilePath name = Name(file_index); 548 scoped_refptr<File> this_file(new File(false)); 549 this_file->Init(name); 550 block_files_[file_index]->Release(); 551 block_files_[file_index] = NULL; 552 553 int failure = DeleteCacheFile(name) ? 0 : 1; 554 UMA_HISTOGRAM_COUNTS("DiskCache.DeleteFailed2", failure); 555 if (failure) 556 LOG(ERROR) << "Failed to delete " << name.value() << " from the cache."; 557 continue; 558 } 559 560 header = next_header; 561 file = next_file; 562 } 563 } 564 565 bool BlockFiles::FixBlockFileHeader(MappedFile* file) { 566 BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer()); 567 int file_size = static_cast<int>(file->GetLength()); 568 if (file_size < static_cast<int>(sizeof(*header))) 569 return false; // file_size > 2GB is also an error. 570 571 int expected = header->entry_size * header->max_entries + sizeof(*header); 572 if (file_size != expected) { 573 int max_expected = header->entry_size * kMaxBlocks + sizeof(*header); 574 if (file_size < expected || header->empty[3] || file_size > max_expected) { 575 NOTREACHED(); 576 LOG(ERROR) << "Unexpected file size"; 577 return false; 578 } 579 // We were in the middle of growing the file. 580 int num_entries = (file_size - sizeof(*header)) / header->entry_size; 581 header->max_entries = num_entries; 582 } 583 584 FixAllocationCounters(header); 585 header->updating = 0; 586 return true; 587 } 588 589 // We are interested in the total number of blocks used by this file type, and 590 // the max number of blocks that we can store (reported as the percentage of 591 // used blocks). In order to find out the number of used blocks, we have to 592 // substract the empty blocks from the total blocks for each file in the chain. 593 void BlockFiles::GetFileStats(int index, int* used_count, int* load) { 594 int max_blocks = 0; 595 *used_count = 0; 596 *load = 0; 597 for (;;) { 598 if (!block_files_[index] && !OpenBlockFile(index)) 599 return; 600 601 BlockFileHeader* header = 602 reinterpret_cast<BlockFileHeader*>(block_files_[index]->buffer()); 603 604 max_blocks += header->max_entries; 605 int used = header->max_entries; 606 for (int i = 0; i < 4; i++) { 607 used -= header->empty[i] * (i + 1); 608 DCHECK_GE(used, 0); 609 } 610 *used_count += used; 611 612 if (!header->next_file) 613 break; 614 index = header->next_file; 615 } 616 if (max_blocks) 617 *load = *used_count * 100 / max_blocks; 618 } 619 620 FilePath BlockFiles::Name(int index) { 621 // The file format allows for 256 files. 622 DCHECK(index < 256 || index >= 0); 623 std::string tmp = base::StringPrintf("%s%d", kBlockName, index); 624 return path_.AppendASCII(tmp); 625 } 626 627 } // namespace disk_cache 628