Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(96)

Side by Side Diff: storage/browser/blob/blob_memory_controller.cc

Issue 2857283002: Add memory pressure listener to Blob storage (Closed)
Patch Set: ratio and pressue arg. Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "storage/browser/blob/blob_memory_controller.h" 5 #include "storage/browser/blob/blob_memory_controller.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <numeric> 8 #include <numeric>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
(...skipping 23 matching lines...) Expand all
34 #include "storage/browser/blob/shareable_file_reference.h" 34 #include "storage/browser/blob/shareable_file_reference.h"
35 #include "storage/common/data_element.h" 35 #include "storage/common/data_element.h"
36 36
37 using base::File; 37 using base::File;
38 using base::FilePath; 38 using base::FilePath;
39 39
40 namespace storage { 40 namespace storage {
41 namespace { 41 namespace {
42 constexpr int64_t kUnknownDiskAvailability = -1ll; 42 constexpr int64_t kUnknownDiskAvailability = -1ll;
43 constexpr uint64_t kMegabyte = 1024ull * 1024; 43 constexpr uint64_t kMegabyte = 1024ull * 1024;
44 const int64_t kMinSecondsForPressureEvictions = 30;
44 45
45 using FileCreationInfo = BlobMemoryController::FileCreationInfo; 46 using FileCreationInfo = BlobMemoryController::FileCreationInfo;
46 using MemoryAllocation = BlobMemoryController::MemoryAllocation; 47 using MemoryAllocation = BlobMemoryController::MemoryAllocation;
47 using QuotaAllocationTask = BlobMemoryController::QuotaAllocationTask; 48 using QuotaAllocationTask = BlobMemoryController::QuotaAllocationTask;
48 using DiskSpaceFuncPtr = BlobMemoryController::DiskSpaceFuncPtr; 49 using DiskSpaceFuncPtr = BlobMemoryController::DiskSpaceFuncPtr;
49 50
50 // CrOS: 51 // CrOS:
51 // * Ram - 20% 52 // * Ram - 20%
52 // * Disk - 50% 53 // * Disk - 50%
53 // Note: The disk is the user partition, so the operating system can still 54 // Note: The disk is the user partition, so the operating system can still
(...skipping 444 matching lines...) Expand 10 before | Expand all | Expand 10 after
498 499
499 BlobMemoryController::BlobMemoryController( 500 BlobMemoryController::BlobMemoryController(
500 const base::FilePath& storage_directory, 501 const base::FilePath& storage_directory,
501 scoped_refptr<base::TaskRunner> file_runner) 502 scoped_refptr<base::TaskRunner> file_runner)
502 : file_paging_enabled_(file_runner.get() != nullptr), 503 : file_paging_enabled_(file_runner.get() != nullptr),
503 blob_storage_dir_(storage_directory), 504 blob_storage_dir_(storage_directory),
504 file_runner_(std::move(file_runner)), 505 file_runner_(std::move(file_runner)),
505 disk_space_function_(&base::SysInfo::AmountOfFreeDiskSpace), 506 disk_space_function_(&base::SysInfo::AmountOfFreeDiskSpace),
506 populated_memory_items_( 507 populated_memory_items_(
507 base::MRUCache<uint64_t, ShareableBlobDataItem*>::NO_AUTO_EVICT), 508 base::MRUCache<uint64_t, ShareableBlobDataItem*>::NO_AUTO_EVICT),
509 memory_pressure_listener_(
510 base::Bind(&BlobMemoryController::OnMemoryPressure,
511 base::Unretained(this))),
508 weak_factory_(this) {} 512 weak_factory_(this) {}
509 513
510 BlobMemoryController::~BlobMemoryController() {} 514 BlobMemoryController::~BlobMemoryController() {}
511 515
512 void BlobMemoryController::DisableFilePaging(base::File::Error reason) { 516 void BlobMemoryController::DisableFilePaging(base::File::Error reason) {
513 UMA_HISTOGRAM_ENUMERATION("Storage.Blob.PagingDisabled", -reason, 517 UMA_HISTOGRAM_ENUMERATION("Storage.Blob.PagingDisabled", -reason,
514 -File::FILE_ERROR_MAX); 518 -File::FILE_ERROR_MAX);
515 DLOG(ERROR) << "Blob storage paging disabled, reason: " << reason; 519 DLOG(ERROR) << "Blob storage paging disabled, reason: " << reason;
516 file_paging_enabled_ = false; 520 file_paging_enabled_ = false;
517 in_flight_memory_used_ = 0; 521 in_flight_memory_used_ = 0;
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
597 // more paging for any more pending blobs. 601 // more paging for any more pending blobs.
598 if (!pending_memory_quota_tasks_.empty()) { 602 if (!pending_memory_quota_tasks_.empty()) {
599 return AppendMemoryTask(total_bytes_needed, 603 return AppendMemoryTask(total_bytes_needed,
600 std::move(unreserved_memory_items), done_callback); 604 std::move(unreserved_memory_items), done_callback);
601 } 605 }
602 606
603 // Store right away if we can. 607 // Store right away if we can.
604 if (total_bytes_needed <= GetAvailableMemoryForBlobs()) { 608 if (total_bytes_needed <= GetAvailableMemoryForBlobs()) {
605 GrantMemoryAllocations(&unreserved_memory_items, 609 GrantMemoryAllocations(&unreserved_memory_items,
606 static_cast<size_t>(total_bytes_needed)); 610 static_cast<size_t>(total_bytes_needed));
607 MaybeScheduleEvictionUntilSystemHealthy(); 611 MaybeScheduleEvictionUntilSystemHealthy(
612 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE);
608 done_callback.Run(true); 613 done_callback.Run(true);
609 return base::WeakPtr<QuotaAllocationTask>(); 614 return base::WeakPtr<QuotaAllocationTask>();
610 } 615 }
611 616
612 // Size is larger than available memory. 617 // Size is larger than available memory.
613 DCHECK(pending_memory_quota_tasks_.empty()); 618 DCHECK(pending_memory_quota_tasks_.empty());
614 DCHECK_EQ(0u, pending_memory_quota_total_size_); 619 DCHECK_EQ(0u, pending_memory_quota_total_size_);
615 620
616 auto weak_ptr = AppendMemoryTask( 621 auto weak_ptr = AppendMemoryTask(
617 total_bytes_needed, std::move(unreserved_memory_items), done_callback); 622 total_bytes_needed, std::move(unreserved_memory_items), done_callback);
618 MaybeScheduleEvictionUntilSystemHealthy(); 623 MaybeScheduleEvictionUntilSystemHealthy(
624 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE);
619 return weak_ptr; 625 return weak_ptr;
620 } 626 }
621 627
622 base::WeakPtr<QuotaAllocationTask> BlobMemoryController::ReserveFileQuota( 628 base::WeakPtr<QuotaAllocationTask> BlobMemoryController::ReserveFileQuota(
623 std::vector<scoped_refptr<ShareableBlobDataItem>> unreserved_file_items, 629 std::vector<scoped_refptr<ShareableBlobDataItem>> unreserved_file_items,
624 const FileQuotaRequestCallback& done_callback) { 630 const FileQuotaRequestCallback& done_callback) {
625 pending_file_quota_tasks_.push_back(base::MakeUnique<FileQuotaAllocationTask>( 631 pending_file_quota_tasks_.push_back(base::MakeUnique<FileQuotaAllocationTask>(
626 this, disk_space_function_, std::move(unreserved_file_items), 632 this, disk_space_function_, std::move(unreserved_file_items),
627 done_callback)); 633 done_callback));
628 pending_file_quota_tasks_.back()->set_my_list_position( 634 pending_file_quota_tasks_.back()->set_my_list_position(
(...skipping 13 matching lines...) Expand all
642 items_paging_to_file_.end()) { 648 items_paging_to_file_.end()) {
643 return; 649 return;
644 } 650 }
645 auto iterator = populated_memory_items_.Get(item->item_id()); 651 auto iterator = populated_memory_items_.Get(item->item_id());
646 if (iterator == populated_memory_items_.end()) { 652 if (iterator == populated_memory_items_.end()) {
647 populated_memory_items_bytes_ += 653 populated_memory_items_bytes_ +=
648 static_cast<size_t>(item->item()->length()); 654 static_cast<size_t>(item->item()->length());
649 populated_memory_items_.Put(item->item_id(), item.get()); 655 populated_memory_items_.Put(item->item_id(), item.get());
650 } 656 }
651 } 657 }
652 MaybeScheduleEvictionUntilSystemHealthy(); 658 MaybeScheduleEvictionUntilSystemHealthy(
659 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE);
653 } 660 }
654 661
655 void BlobMemoryController::CalculateBlobStorageLimits() { 662 void BlobMemoryController::CalculateBlobStorageLimits() {
656 if (file_runner_) { 663 if (file_runner_) {
657 PostTaskAndReplyWithResult( 664 PostTaskAndReplyWithResult(
658 file_runner_.get(), FROM_HERE, 665 file_runner_.get(), FROM_HERE,
659 base::Bind(&CalculateBlobStorageLimitsImpl, blob_storage_dir_, true), 666 base::Bind(&CalculateBlobStorageLimitsImpl, blob_storage_dir_, true),
660 base::Bind(&BlobMemoryController::OnStorageLimitsCalculated, 667 base::Bind(&BlobMemoryController::OnStorageLimitsCalculated,
661 weak_factory_.GetWeakPtr())); 668 weak_factory_.GetWeakPtr()));
662 } else { 669 } else {
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
764 std::unique_ptr<MemoryQuotaAllocationTask> memory_task = 771 std::unique_ptr<MemoryQuotaAllocationTask> memory_task =
765 std::move(pending_memory_quota_tasks_.front()); 772 std::move(pending_memory_quota_tasks_.front());
766 pending_memory_quota_tasks_.pop_front(); 773 pending_memory_quota_tasks_.pop_front();
767 pending_memory_quota_total_size_ -= memory_task->allocation_size(); 774 pending_memory_quota_total_size_ -= memory_task->allocation_size();
768 memory_task->RunDoneCallback(true); 775 memory_task->RunDoneCallback(true);
769 } 776 }
770 RecordTracingCounters(); 777 RecordTracingCounters();
771 } 778 }
772 779
773 size_t BlobMemoryController::CollectItemsForEviction( 780 size_t BlobMemoryController::CollectItemsForEviction(
774 std::vector<scoped_refptr<ShareableBlobDataItem>>* output) { 781 std::vector<scoped_refptr<ShareableBlobDataItem>>* output,
782 uint64_t min_page_file_size) {
775 base::CheckedNumeric<size_t> total_items_size = 0; 783 base::CheckedNumeric<size_t> total_items_size = 0;
776 // Process the recent item list and remove items until we have at least a 784 // Process the recent item list and remove items until we have at least a
777 // minimum file size or we're at the end of our items to page to disk. 785 // minimum file size or we're at the end of our items to page to disk.
778 while (total_items_size.ValueOrDie() < limits_.min_page_file_size && 786 while (total_items_size.ValueOrDie() < min_page_file_size &&
779 !populated_memory_items_.empty()) { 787 !populated_memory_items_.empty()) {
780 auto iterator = --populated_memory_items_.end(); 788 auto iterator = --populated_memory_items_.end();
781 ShareableBlobDataItem* item = iterator->second; 789 ShareableBlobDataItem* item = iterator->second;
782 DCHECK_EQ(item->item()->type(), DataElement::TYPE_BYTES); 790 DCHECK_EQ(item->item()->type(), DataElement::TYPE_BYTES);
783 populated_memory_items_.Erase(iterator); 791 populated_memory_items_.Erase(iterator);
784 size_t size = base::checked_cast<size_t>(item->item()->length()); 792 size_t size = base::checked_cast<size_t>(item->item()->length());
785 populated_memory_items_bytes_ -= size; 793 populated_memory_items_bytes_ -= size;
786 total_items_size += size; 794 total_items_size += size;
787 output->push_back(make_scoped_refptr(item)); 795 output->push_back(make_scoped_refptr(item));
788 } 796 }
789 return total_items_size.ValueOrDie(); 797 return total_items_size.ValueOrDie();
790 } 798 }
791 799
792 void BlobMemoryController::MaybeScheduleEvictionUntilSystemHealthy() { 800 void BlobMemoryController::MaybeScheduleEvictionUntilSystemHealthy(
801 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
793 // Don't do eviction when others are happening, as we don't change our 802 // Don't do eviction when others are happening, as we don't change our
794 // pending_memory_quota_total_size_ value until after the paging files have 803 // pending_memory_quota_total_size_ value until after the paging files have
795 // been written. 804 // been written.
796 if (pending_evictions_ != 0 || !file_paging_enabled_) 805 if (pending_evictions_ != 0 || !file_paging_enabled_)
797 return; 806 return;
798 807
799 uint64_t total_memory_usage = 808 uint64_t total_memory_usage =
800 static_cast<uint64_t>(pending_memory_quota_total_size_) + 809 static_cast<uint64_t>(pending_memory_quota_total_size_) +
801 blob_memory_used_; 810 blob_memory_used_;
802 811
812 size_t in_memory_limit = limits_.memory_limit_before_paging();
813 uint64_t min_page_file_size = limits_.min_page_file_size;
814 if (memory_pressure_level !=
815 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) {
816 in_memory_limit = 0;
817 // Use lower page file size to reduce using more memory for writing under
818 // pressure.
819 min_page_file_size = limits_.max_blob_in_memory_space *
820 limits_.min_page_file_size_ratio_under_pressure;
821 }
822
803 // We try to page items to disk until our current system size + requested 823 // We try to page items to disk until our current system size + requested
804 // memory is below our size limit. 824 // memory is below our size limit.
805 // Size limit is a lower |memory_limit_before_paging()| if we have disk space. 825 // Size limit is a lower |memory_limit_before_paging()| if we have disk space.
806 while (total_memory_usage > limits_.effective_max_disk_space || 826 while (total_memory_usage > limits_.effective_max_disk_space ||
807 (disk_used_ < limits_.effective_max_disk_space && 827 (disk_used_ < limits_.effective_max_disk_space &&
808 total_memory_usage > limits_.memory_limit_before_paging())) { 828 total_memory_usage > in_memory_limit)) {
829 const char* reason = nullptr;
830 if (memory_pressure_level !=
831 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) {
832 reason = "OnMemoryPressure";
833 } else if (total_memory_usage > limits_.effective_max_disk_space) {
834 reason = "SizeExceededMaxDiskSpace";
835 } else {
836 reason = "SizeExceededInMemoryLimit";
837 }
838
809 // We only page when we have enough items to fill a whole page file. 839 // We only page when we have enough items to fill a whole page file.
810 if (populated_memory_items_bytes_ < limits_.min_page_file_size) 840 if (populated_memory_items_bytes_ < min_page_file_size)
811 break; 841 break;
812 DCHECK_LE(limits_.min_page_file_size, 842 DCHECK_LE(min_page_file_size, static_cast<uint64_t>(blob_memory_used_));
813 static_cast<uint64_t>(blob_memory_used_));
814 843
815 std::vector<scoped_refptr<ShareableBlobDataItem>> items_to_swap; 844 std::vector<scoped_refptr<ShareableBlobDataItem>> items_to_swap;
816 size_t total_items_size = CollectItemsForEviction(&items_to_swap); 845
846 size_t total_items_size =
847 CollectItemsForEviction(&items_to_swap, min_page_file_size);
817 if (total_items_size == 0) 848 if (total_items_size == 0)
818 break; 849 break;
819 850
820 std::vector<DataElement*> items_for_paging; 851 std::vector<DataElement*> items_for_paging;
821 for (auto& shared_blob_item : items_to_swap) { 852 for (auto& shared_blob_item : items_to_swap) {
822 items_paging_to_file_.insert(shared_blob_item->item_id()); 853 items_paging_to_file_.insert(shared_blob_item->item_id());
823 items_for_paging.push_back(shared_blob_item->item()->data_element_ptr()); 854 items_for_paging.push_back(shared_blob_item->item()->data_element_ptr());
824 } 855 }
825 856
826 // Update our bookkeeping. 857 // Update our bookkeeping.
(...skipping 15 matching lines...) Expand all
842 873
843 // Post the file writing task. 874 // Post the file writing task.
844 base::PostTaskAndReplyWithResult( 875 base::PostTaskAndReplyWithResult(
845 file_runner_.get(), FROM_HERE, 876 file_runner_.get(), FROM_HERE,
846 base::Bind(&CreateFileAndWriteItems, blob_storage_dir_, 877 base::Bind(&CreateFileAndWriteItems, blob_storage_dir_,
847 disk_space_function_, base::Passed(&page_file_path), 878 disk_space_function_, base::Passed(&page_file_path),
848 file_runner_, base::Passed(&items_for_paging), 879 file_runner_, base::Passed(&items_for_paging),
849 total_items_size), 880 total_items_size),
850 base::Bind(&BlobMemoryController::OnEvictionComplete, 881 base::Bind(&BlobMemoryController::OnEvictionComplete,
851 weak_factory_.GetWeakPtr(), base::Passed(&file_reference), 882 weak_factory_.GetWeakPtr(), base::Passed(&file_reference),
852 base::Passed(&items_to_swap), total_items_size)); 883 base::Passed(&items_to_swap), total_items_size, reason,
884 total_memory_usage));
885
886 last_eviction_time_ = base::TimeTicks::Now();
853 } 887 }
888
dmurph 2017/05/10 19:13:34 nit: remove newline
ssid 2017/05/12 01:38:01 Done.
854 RecordTracingCounters(); 889 RecordTracingCounters();
855 } 890 }
856 891
857 void BlobMemoryController::OnEvictionComplete( 892 void BlobMemoryController::OnEvictionComplete(
858 scoped_refptr<ShareableFileReference> file_reference, 893 scoped_refptr<ShareableFileReference> file_reference,
859 std::vector<scoped_refptr<ShareableBlobDataItem>> items, 894 std::vector<scoped_refptr<ShareableBlobDataItem>> items,
860 size_t total_items_size, 895 size_t total_items_size,
896 const char* evict_reason,
897 size_t memory_usage_before_eviction,
861 std::pair<FileCreationInfo, int64_t /* avail_disk */> result) { 898 std::pair<FileCreationInfo, int64_t /* avail_disk */> result) {
862 if (!file_paging_enabled_) 899 if (!file_paging_enabled_)
863 return; 900 return;
864 901
865 FileCreationInfo& file_info = std::get<0>(result); 902 FileCreationInfo& file_info = std::get<0>(result);
866 int64_t avail_disk_space = std::get<1>(result); 903 int64_t avail_disk_space = std::get<1>(result);
867 904
868 if (file_info.error != File::FILE_OK) { 905 if (file_info.error != File::FILE_OK) {
869 DisableFilePaging(file_info.error); 906 DisableFilePaging(file_info.error);
870 return; 907 return;
(...skipping 15 matching lines...) Expand all
886 file_reference->path(), offset, shareable_item->item()->length(), 923 file_reference->path(), offset, shareable_item->item()->length(),
887 file_info.last_modified); 924 file_info.last_modified);
888 DCHECK(shareable_item->memory_allocation_); 925 DCHECK(shareable_item->memory_allocation_);
889 shareable_item->set_memory_allocation(nullptr); 926 shareable_item->set_memory_allocation(nullptr);
890 shareable_item->set_item(new_item); 927 shareable_item->set_item(new_item);
891 items_paging_to_file_.erase(shareable_item->item_id()); 928 items_paging_to_file_.erase(shareable_item->item_id());
892 offset += shareable_item->item()->length(); 929 offset += shareable_item->item()->length();
893 } 930 }
894 in_flight_memory_used_ -= total_items_size; 931 in_flight_memory_used_ -= total_items_size;
895 932
933 // Record change in memory usage at the last eviction reply.
934 size_t total_usage = blob_memory_used_ + pending_memory_quota_total_size_;
935 if (!pending_evictions_ && memory_usage_before_eviction >= total_usage) {
936 std::string full_histogram_name =
937 std::string("Storage.Blob.SizeEvictedToDiskInKB.") + evict_reason;
938 base::HistogramBase* histogram = base::Histogram::FactoryGet(
939 full_histogram_name, 1, 100000, 50,
940 base::HistogramBase::kUmaTargetedHistogramFlag);
Ilya Sherman 2017/05/11 20:13:39 Please use one of the base::UmaHistogram... functi
ssid 2017/05/12 01:38:01 Ah thanks! I couldn't find these functions when I
941 if (histogram)
942 histogram->Add((memory_usage_before_eviction - total_usage) / 1024);
943 }
944
896 // We want callback on blobs up to the amount we've freed. 945 // We want callback on blobs up to the amount we've freed.
897 MaybeGrantPendingMemoryRequests(); 946 MaybeGrantPendingMemoryRequests();
898 947
899 // If we still have more blobs waiting and we're not waiting on more paging 948 // If we still have more blobs waiting and we're not waiting on more paging
900 // operations, schedule more. 949 // operations, schedule more.
901 MaybeScheduleEvictionUntilSystemHealthy(); 950 MaybeScheduleEvictionUntilSystemHealthy(
951 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE);
952 }
953
954 void BlobMemoryController::OnMemoryPressure(
955 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
956 auto time_from_last_evicion = base::TimeTicks::Now() - last_eviction_time_;
957 if (time_from_last_evicion.InSeconds() < kMinSecondsForPressureEvictions)
958 return;
959
960 MaybeScheduleEvictionUntilSystemHealthy(memory_pressure_level);
902 } 961 }
903 962
904 FilePath BlobMemoryController::GenerateNextPageFileName() { 963 FilePath BlobMemoryController::GenerateNextPageFileName() {
905 std::string file_name = base::Uint64ToString(current_file_num_++); 964 std::string file_name = base::Uint64ToString(current_file_num_++);
906 return blob_storage_dir_.Append(FilePath::FromUTF8Unsafe(file_name)); 965 return blob_storage_dir_.Append(FilePath::FromUTF8Unsafe(file_name));
907 } 966 }
908 967
909 void BlobMemoryController::RecordTracingCounters() const { 968 void BlobMemoryController::RecordTracingCounters() const {
910 TRACE_COUNTER2("Blob", "MemoryUsage", "TotalStorage", blob_memory_used_, 969 TRACE_COUNTER2("Blob", "MemoryUsage", "TotalStorage", blob_memory_used_,
911 "InFlightToDisk", in_flight_memory_used_); 970 "InFlightToDisk", in_flight_memory_used_);
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
977 MaybeGrantPendingMemoryRequests(); 1036 MaybeGrantPendingMemoryRequests();
978 } 1037 }
979 1038
980 void BlobMemoryController::OnBlobFileDelete(uint64_t size, 1039 void BlobMemoryController::OnBlobFileDelete(uint64_t size,
981 const FilePath& path) { 1040 const FilePath& path) {
982 DCHECK_LE(size, disk_used_); 1041 DCHECK_LE(size, disk_used_);
983 disk_used_ -= size; 1042 disk_used_ -= size;
984 } 1043 }
985 1044
986 } // namespace storage 1045 } // namespace storage
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698