OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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/quota/quota_manager.h" | 5 #include "storage/browser/quota/quota_manager.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 #include <stdint.h> | 8 #include <stdint.h> |
9 #include <algorithm> | 9 #include <algorithm> |
10 #include <functional> | 10 #include <functional> |
11 #include <limits> | 11 #include <limits> |
12 #include <utility> | 12 #include <utility> |
13 | 13 |
14 #include "base/bind.h" | 14 #include "base/bind.h" |
15 #include "base/bind_helpers.h" | 15 #include "base/bind_helpers.h" |
16 #include "base/command_line.h" | 16 #include "base/command_line.h" |
17 #include "base/files/file_util.h" | 17 #include "base/files/file_util.h" |
18 #include "base/macros.h" | 18 #include "base/macros.h" |
19 #include "base/metrics/histogram.h" | 19 #include "base/metrics/histogram.h" |
20 #include "base/profiler/scoped_tracker.h" | 20 #include "base/profiler/scoped_tracker.h" |
21 #include "base/sequenced_task_runner.h" | 21 #include "base/sequenced_task_runner.h" |
22 #include "base/single_thread_task_runner.h" | 22 #include "base/single_thread_task_runner.h" |
23 #include "base/strings/string_number_conversions.h" | 23 #include "base/strings/string_number_conversions.h" |
24 #include "base/sys_info.h" | 24 #include "base/sys_info.h" |
25 #include "base/task_runner_util.h" | 25 #include "base/task_runner_util.h" |
26 #include "base/thread_task_runner_handle.h" | |
26 #include "base/time/time.h" | 27 #include "base/time/time.h" |
27 #include "base/trace_event/trace_event.h" | 28 #include "base/trace_event/trace_event.h" |
28 #include "net/base/url_util.h" | 29 #include "net/base/url_util.h" |
29 #include "storage/browser/quota/client_usage_tracker.h" | 30 #include "storage/browser/quota/client_usage_tracker.h" |
30 #include "storage/browser/quota/quota_manager_proxy.h" | 31 #include "storage/browser/quota/quota_manager_proxy.h" |
31 #include "storage/browser/quota/quota_temporary_storage_evictor.h" | 32 #include "storage/browser/quota/quota_temporary_storage_evictor.h" |
32 #include "storage/browser/quota/storage_monitor.h" | 33 #include "storage/browser/quota/storage_monitor.h" |
33 #include "storage/browser/quota/usage_tracker.h" | 34 #include "storage/browser/quota/usage_tracker.h" |
34 #include "storage/common/quota/quota_types.h" | 35 #include "storage/common/quota/quota_types.h" |
35 | 36 |
(...skipping 15 matching lines...) Expand all Loading... | |
51 1, 10 * 1024 * 1024 /* 10TB */, 100) | 52 1, 10 * 1024 * 1024 /* 10TB */, 100) |
52 | 53 |
53 namespace storage { | 54 namespace storage { |
54 | 55 |
55 namespace { | 56 namespace { |
56 | 57 |
57 const int64_t kMBytes = 1024 * 1024; | 58 const int64_t kMBytes = 1024 * 1024; |
58 const int kMinutesInMilliSeconds = 60 * 1000; | 59 const int kMinutesInMilliSeconds = 60 * 1000; |
59 | 60 |
60 const int64_t kReportHistogramInterval = 60 * 60 * 1000; // 1 hour | 61 const int64_t kReportHistogramInterval = 60 * 60 * 1000; // 1 hour |
61 const double kTemporaryQuotaRatioToAvail = 1.0 / 3.0; // 33% | 62 const double kTemporaryQuotaRatio = 1.0 / 3.0; // 33% |
jsbell
2016/03/23 18:08:28
While you're here, can you comment on what these a
michaeln
2016/04/07 00:32:34
Done, also moved these values to where they're use
| |
63 | |
64 #if defined(OS_ANDROID) | |
65 const uint64_t kOsAccomodation = 250 * kMBytes; | |
michaeln
2016/03/22 22:23:55
these specific numbers are just placeholders, this
| |
66 #elif defined(OS_CHROMEOS) | |
67 const uint64_t kOsAccomodation = 1000 * kMBytes; | |
68 #else | |
69 const uint64_t kOsAccomodation = 10000 * kMBytes; | |
70 #endif | |
62 | 71 |
63 } // namespace | 72 } // namespace |
64 | 73 |
65 // Arbitrary for now, but must be reasonably small so that | 74 // Arbitrary for now, but must be reasonably small so that |
66 // in-memory databases can fit. | 75 // in-memory databases can fit. |
67 // TODO(kinuko): Refer SysInfo::AmountOfPhysicalMemory() to determine this. | 76 // TODO(kinuko): Refer SysInfo::AmountOfPhysicalMemory() to determine this. |
68 const int64_t QuotaManager::kIncognitoDefaultQuotaLimit = 100 * kMBytes; | 77 const int64_t QuotaManager::kIncognitoDefaultQuotaLimit = 100 * kMBytes; |
69 | 78 |
70 const int64_t QuotaManager::kNoLimit = INT64_MAX; | 79 const int64_t QuotaManager::kNoLimit = INT64_MAX; |
71 | 80 |
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
236 } | 245 } |
237 | 246 |
238 bool UpdateModifiedTimeOnDBThread(const GURL& origin, | 247 bool UpdateModifiedTimeOnDBThread(const GURL& origin, |
239 StorageType type, | 248 StorageType type, |
240 base::Time modified_time, | 249 base::Time modified_time, |
241 QuotaDatabase* database) { | 250 QuotaDatabase* database) { |
242 DCHECK(database); | 251 DCHECK(database); |
243 return database->SetOriginLastModifiedTime(origin, type, modified_time); | 252 return database->SetOriginLastModifiedTime(origin, type, modified_time); |
244 } | 253 } |
245 | 254 |
246 int64_t CalculateTemporaryGlobalQuota(int64_t global_limited_usage, | |
247 int64_t available_space) { | |
248 DCHECK_GE(global_limited_usage, 0); | |
249 int64_t avail_space = available_space; | |
250 if (avail_space < | |
251 std::numeric_limits<int64_t>::max() - global_limited_usage) { | |
252 // We basically calculate the temporary quota by | |
253 // [available_space + space_used_for_temp] * kTempQuotaRatio, | |
254 // but make sure we'll have no overflow. | |
255 avail_space += global_limited_usage; | |
256 } | |
257 int64_t pool_size = avail_space * kTemporaryQuotaRatioToAvail; | |
258 UMA_HISTOGRAM_MBYTES("Quota.GlobalTemporaryPoolSize", pool_size); | |
259 return pool_size; | |
260 } | |
261 | |
262 void DispatchTemporaryGlobalQuotaCallback( | |
263 const QuotaCallback& callback, | |
264 QuotaStatusCode status, | |
265 const UsageAndQuota& usage_and_quota) { | |
266 if (status != kQuotaStatusOk) { | |
267 callback.Run(status, 0); | |
268 return; | |
269 } | |
270 | |
271 callback.Run(status, CalculateTemporaryGlobalQuota( | |
272 usage_and_quota.global_limited_usage, | |
273 usage_and_quota.available_disk_space)); | |
274 } | |
275 | |
276 int64_t CalculateQuotaWithDiskSpace(int64_t available_disk_space, | 255 int64_t CalculateQuotaWithDiskSpace(int64_t available_disk_space, |
277 int64_t usage, | 256 int64_t usage, |
278 int64_t quota) { | 257 int64_t quota) { |
279 if (available_disk_space < QuotaManager::kMinimumPreserveForSystem) { | 258 if (available_disk_space < QuotaManager::kMinimumPreserveForSystem) { |
280 LOG(WARNING) | 259 LOG(WARNING) |
281 << "Running out of disk space for profile." | 260 << "Running out of disk space for profile." |
282 << " QuotaManager starts forbidding further quota consumption."; | 261 << " QuotaManager starts forbidding further quota consumption."; |
283 return usage; | 262 return usage; |
284 } | 263 } |
285 | 264 |
286 if (quota < usage) { | 265 if (quota < usage) { |
287 // No more space; cap the quota to the current usage. | 266 // No more space; cap the quota to the current usage. |
288 return usage; | 267 return usage; |
289 } | 268 } |
290 | 269 |
291 available_disk_space -= QuotaManager::kMinimumPreserveForSystem; | 270 available_disk_space -= QuotaManager::kMinimumPreserveForSystem; |
292 if (available_disk_space < quota - usage) | 271 if (available_disk_space < quota - usage) |
293 return available_disk_space + usage; | 272 return available_disk_space + usage; |
294 | 273 |
295 return quota; | 274 return quota; |
296 } | 275 } |
297 | 276 |
298 int64_t CalculateTemporaryHostQuota(int64_t host_usage, | 277 int64_t CalculateTemporaryHostQuota(int64_t host_usage, |
299 int64_t global_quota, | 278 int64_t global_quota, |
300 int64_t global_limited_usage) { | 279 int64_t available_disk_space) { |
301 DCHECK_GE(global_limited_usage, 0); | 280 const int64_t desired_quota = global_quota / |
302 int64_t host_quota = global_quota / QuotaManager::kPerHostTemporaryPortion; | 281 QuotaManager::kPerHostTemporaryPortion; |
303 if (global_limited_usage > global_quota) | 282 |
michaeln
2016/03/22 21:40:15
This used to defend against filling the device.
| |
304 host_quota = std::min(host_quota, host_usage); | 283 // We define "too low" as not enough room for another hosts |
305 return host_quota; | 284 // nominal desired amount of data. (TODO: what should this be) |
michaeln
2016/03/22 21:40:15
~66 MBytes for small andoid devcices and ~66GB for
jsbell
2016/03/23 18:08:29
Would the "save a bit of room for non-Chrome usage
michaeln
2016/04/07 00:32:34
This is one of the two places where logic to "save
| |
285 const int64_t too_low_threshold = desired_quota; | |
286 | |
287 // If disk space is too low, cap usage at current levels. | |
288 if (available_disk_space < too_low_threshold) | |
michaeln
2016/03/22 21:40:15
That defense is replaced with this.
| |
289 return std::min(desired_quota, host_usage); | |
290 | |
291 // If its close to being too low, cap growth to avoid it getting too low. | |
292 int64_t remaining_quota = std::min(INT64_C(0), desired_quota - host_usage); | |
jsbell
2016/03/23 18:08:28
I think this might be clearer as:
return std::min
michaeln
2016/04/07 00:32:34
I think your right. I had thought a variable named
| |
293 if (available_disk_space < remaining_quota + too_low_threshold) { | |
294 remaining_quota = available_disk_space - too_low_threshold; | |
295 return host_usage + remaining_quota; | |
296 } | |
297 | |
298 return desired_quota; | |
306 } | 299 } |
307 | 300 |
308 void DispatchUsageAndQuotaForWebApps( | 301 void DispatchUsageAndQuotaForWebApps( |
309 StorageType type, | 302 StorageType type, |
310 bool is_incognito, | 303 bool is_incognito, |
311 bool is_unlimited, | 304 bool is_unlimited, |
312 bool can_query_disk_size, | 305 bool can_query_disk_size, |
313 const QuotaManager::GetUsageAndQuotaCallback& callback, | 306 const QuotaManager::GetUsageAndQuotaCallback& callback, |
314 QuotaStatusCode status, | 307 QuotaStatusCode status, |
315 const UsageAndQuota& usage_and_quota) { | 308 const UsageAndQuota& usage_and_quota) { |
316 if (status != kQuotaStatusOk) { | 309 if (status != kQuotaStatusOk) { |
317 callback.Run(status, 0, 0); | 310 callback.Run(status, 0, 0); |
318 return; | 311 return; |
319 } | 312 } |
320 | 313 |
321 int64_t usage = usage_and_quota.usage; | 314 int64_t host_usage = usage_and_quota.usage; |
322 int64_t quota = usage_and_quota.quota; | 315 int64_t global_quota = usage_and_quota.quota; |
316 int64_t host_quota = global_quota; | |
323 | 317 |
324 if (type == kStorageTypeTemporary && !is_unlimited) { | 318 if (type == kStorageTypeTemporary && !is_unlimited) { |
325 quota = CalculateTemporaryHostQuota( | 319 host_quota = CalculateTemporaryHostQuota( |
326 usage, quota, usage_and_quota.global_limited_usage); | 320 host_usage, global_quota, usage_and_quota.available_disk_space); |
327 } | 321 } |
328 | 322 |
329 if (is_incognito) { | 323 if (is_incognito) { |
330 quota = std::min(quota, QuotaManager::kIncognitoDefaultQuotaLimit); | 324 host_quota = std::min(host_quota, |
331 callback.Run(status, usage, quota); | 325 QuotaManager::kIncognitoDefaultQuotaLimit); |
326 callback.Run(status, host_usage, host_quota); | |
332 return; | 327 return; |
333 } | 328 } |
334 | 329 |
335 // For apps with unlimited permission or can_query_disk_size is true (and not | 330 // For apps with unlimited permission or can_query_disk_size is true (and not |
336 // in incognito mode). | 331 // in incognito mode). |
337 // We assume we can expose the actual disk size for them and cap the quota by | 332 // We assume we can expose the actual disk size for them and cap the quota by |
338 // the available disk space. | 333 // the available disk space. |
339 if (is_unlimited || can_query_disk_size) { | 334 if (is_unlimited || can_query_disk_size) { |
340 callback.Run( | 335 callback.Run( |
341 status, usage, | 336 status, host_usage, |
342 CalculateQuotaWithDiskSpace( | 337 CalculateQuotaWithDiskSpace( |
343 usage_and_quota.available_disk_space, | 338 usage_and_quota.available_disk_space, |
344 usage, quota)); | 339 host_usage, host_quota)); |
345 return; | 340 return; |
346 } | 341 } |
347 | 342 |
348 callback.Run(status, usage, quota); | 343 callback.Run(status, host_usage, host_quota); |
349 } | 344 } |
350 | 345 |
351 } // namespace | 346 } // namespace |
352 | 347 |
353 UsageAndQuota::UsageAndQuota() | 348 UsageAndQuota::UsageAndQuota() |
354 : usage(0), | 349 : usage(0), |
355 global_limited_usage(0), | 350 global_limited_usage(0), |
356 quota(0), | 351 quota(0), |
357 available_disk_space(0) { | 352 available_disk_space(0) { |
358 } | 353 } |
(...skipping 559 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
918 bool unlimited = IsStorageUnlimited(origin, type); | 913 bool unlimited = IsStorageUnlimited(origin, type); |
919 bool can_query_disk_size = CanQueryDiskSize(origin); | 914 bool can_query_disk_size = CanQueryDiskSize(origin); |
920 | 915 |
921 UsageAndQuotaCallbackDispatcher* dispatcher = | 916 UsageAndQuotaCallbackDispatcher* dispatcher = |
922 new UsageAndQuotaCallbackDispatcher(this); | 917 new UsageAndQuotaCallbackDispatcher(this); |
923 | 918 |
924 if (unlimited) { | 919 if (unlimited) { |
925 dispatcher->set_quota(kNoLimit); | 920 dispatcher->set_quota(kNoLimit); |
926 } else { | 921 } else { |
927 if (type == kStorageTypeTemporary) { | 922 if (type == kStorageTypeTemporary) { |
928 GetUsageTracker(type)->GetGlobalLimitedUsage( | |
929 dispatcher->GetGlobalLimitedUsageCallback()); | |
930 GetTemporaryGlobalQuota(dispatcher->GetQuotaCallback()); | 923 GetTemporaryGlobalQuota(dispatcher->GetQuotaCallback()); |
931 } else if (type == kStorageTypePersistent) { | 924 } else if (type == kStorageTypePersistent) { |
932 GetPersistentHostQuota(net::GetHostOrSpecFromURL(origin), | 925 GetPersistentHostQuota(net::GetHostOrSpecFromURL(origin), |
933 dispatcher->GetQuotaCallback()); | 926 dispatcher->GetQuotaCallback()); |
934 } else { | 927 } else { |
935 dispatcher->set_quota(kSyncableStorageDefaultHostQuota); | 928 dispatcher->set_quota(kSyncableStorageDefaultHostQuota); |
936 } | 929 } |
937 } | 930 } |
938 | 931 |
939 DCHECK(GetUsageTracker(type)); | 932 DCHECK(GetUsageTracker(type)); |
940 GetUsageTracker(type)->GetHostUsage(net::GetHostOrSpecFromURL(origin), | 933 GetUsageTracker(type)->GetHostUsage(net::GetHostOrSpecFromURL(origin), |
941 dispatcher->GetHostUsageCallback()); | 934 dispatcher->GetHostUsageCallback()); |
942 | 935 |
943 if (!is_incognito_ && (unlimited || can_query_disk_size)) | 936 GetAvailableSpace(dispatcher->GetAvailableSpaceCallback()); |
jsbell
2016/03/23 18:08:29
Can we avoid this if is_incognito_ like we used to
michaeln
2016/04/07 00:32:34
that's a good question, the system is currently us
| |
944 GetAvailableSpace(dispatcher->GetAvailableSpaceCallback()); | |
945 | 937 |
946 dispatcher->WaitForResults(base::Bind( | 938 dispatcher->WaitForResults(base::Bind( |
947 &DispatchUsageAndQuotaForWebApps, | 939 &DispatchUsageAndQuotaForWebApps, |
948 type, is_incognito_, unlimited, can_query_disk_size, | 940 type, is_incognito_, unlimited, can_query_disk_size, |
949 callback)); | 941 callback)); |
950 } | 942 } |
951 | 943 |
952 void QuotaManager::GetUsageAndQuota( | 944 void QuotaManager::GetUsageAndQuota( |
953 const GURL& origin, StorageType type, | 945 const GURL& origin, StorageType type, |
954 const GetUsageAndQuotaCallback& callback) { | 946 const GetUsageAndQuotaCallback& callback) { |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1050 &QuotaManager::GetTemporaryGlobalQuota, | 1042 &QuotaManager::GetTemporaryGlobalQuota, |
1051 weak_factory_.GetWeakPtr(), callback)); | 1043 weak_factory_.GetWeakPtr(), callback)); |
1052 return; | 1044 return; |
1053 } | 1045 } |
1054 | 1046 |
1055 if (temporary_quota_override_ > 0) { | 1047 if (temporary_quota_override_ > 0) { |
1056 callback.Run(kQuotaStatusOk, temporary_quota_override_); | 1048 callback.Run(kQuotaStatusOk, temporary_quota_override_); |
1057 return; | 1049 return; |
1058 } | 1050 } |
1059 | 1051 |
1060 UsageAndQuotaCallbackDispatcher* dispatcher = | 1052 db_thread_->PostTask( |
1061 new UsageAndQuotaCallbackDispatcher(this); | 1053 FROM_HERE, |
1062 GetUsageTracker(kStorageTypeTemporary)-> | 1054 base::Bind(&QuotaManager::CalculateTemporaryPoolSize, |
1063 GetGlobalLimitedUsage(dispatcher->GetGlobalLimitedUsageCallback()); | 1055 get_volume_info_fn_, profile_path_, callback, |
1064 GetAvailableSpace(dispatcher->GetAvailableSpaceCallback()); | 1056 base::ThreadTaskRunnerHandle::Get())); |
1065 dispatcher->WaitForResults( | |
1066 base::Bind(&DispatchTemporaryGlobalQuotaCallback, callback)); | |
1067 } | 1057 } |
1068 | 1058 |
1069 void QuotaManager::SetTemporaryGlobalOverrideQuota( | 1059 void QuotaManager::SetTemporaryGlobalOverrideQuota( |
1070 int64_t new_quota, | 1060 int64_t new_quota, |
1071 const QuotaCallback& callback) { | 1061 const QuotaCallback& callback) { |
1072 LazyInitialize(); | 1062 LazyInitialize(); |
1073 | 1063 |
1074 if (new_quota < 0) { | 1064 if (new_quota < 0) { |
1075 if (!callback.is_null()) | 1065 if (!callback.is_null()) |
1076 callback.Run(kQuotaErrorInvalidModification, -1); | 1066 callback.Run(kQuotaErrorInvalidModification, -1); |
(...skipping 712 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1789 // |database_|, therefore we can be sure that database_ is alive when this | 1779 // |database_|, therefore we can be sure that database_ is alive when this |
1790 // task runs. | 1780 // task runs. |
1791 base::PostTaskAndReplyWithResult( | 1781 base::PostTaskAndReplyWithResult( |
1792 db_thread_.get(), | 1782 db_thread_.get(), |
1793 from_here, | 1783 from_here, |
1794 base::Bind(task, base::Unretained(database_.get())), | 1784 base::Bind(task, base::Unretained(database_.get())), |
1795 reply); | 1785 reply); |
1796 } | 1786 } |
1797 | 1787 |
1798 // static | 1788 // static |
1789 void QuotaManager::CalculateTemporaryPoolSize( | |
1790 GetVolumeInfoFn get_volume_info_fn, | |
1791 const base::FilePath& profile_path, | |
1792 const QuotaCallback& callback, | |
1793 const scoped_refptr<base::TaskRunner>& reply_runner) { | |
1794 QuotaStatusCode status_code = kQuotaStatusUnknown; | |
1795 int64_t pool_size = 0; | |
1796 uint64_t available, total; | |
1797 if (get_volume_info_fn(profile_path, &available, &total)) { | |
1798 status_code = kQuotaStatusOk; | |
1799 if (total > kOsAccomodation) | |
1800 total -= kOsAccomodation; // todo: what if total is super small??? | |
jsbell
2016/03/23 18:08:28
Maybe we want a threshold, e.g. os_reserve = min(k
michaeln
2016/04/07 00:32:34
Done
| |
1801 pool_size = static_cast<int64_t>(total * kTemporaryQuotaRatio); | |
1802 UMA_HISTOGRAM_MBYTES("Quota.GlobalTemporaryPoolSize", pool_size); | |
1803 } | |
1804 reply_runner->PostTask( | |
1805 FROM_HERE, base::Bind(callback, status_code, pool_size)); | |
1806 } | |
1807 | |
1808 // static | |
1799 int64_t QuotaManager::CallGetAmountOfFreeDiskSpace( | 1809 int64_t QuotaManager::CallGetAmountOfFreeDiskSpace( |
1800 GetVolumeInfoFn get_volume_info_fn, | 1810 GetVolumeInfoFn get_volume_info_fn, |
1801 const base::FilePath& profile_path) { | 1811 const base::FilePath& profile_path) { |
1802 // crbug.com/349708 | 1812 // crbug.com/349708 |
1803 TRACE_EVENT0("io", "CallSystemGetAmountOfFreeDiskSpace"); | 1813 TRACE_EVENT0("io", "CallSystemGetAmountOfFreeDiskSpace"); |
1804 if (!base::CreateDirectory(profile_path)) { | 1814 if (!base::CreateDirectory(profile_path)) { |
1805 LOG(WARNING) << "Create directory failed for path" << profile_path.value(); | 1815 LOG(WARNING) << "Create directory failed for path" << profile_path.value(); |
1806 return 0; | 1816 return 0; |
1807 } | 1817 } |
1808 uint64_t available, total; | 1818 uint64_t available, total; |
(...skipping 23 matching lines...) Expand all Loading... | |
1832 return false; | 1842 return false; |
1833 *available_space = static_cast<uint64_t>(stats.f_bavail) * stats.f_frsize; | 1843 *available_space = static_cast<uint64_t>(stats.f_bavail) * stats.f_frsize; |
1834 *total_size = static_cast<uint64_t>(stats.f_blocks) * stats.f_frsize; | 1844 *total_size = static_cast<uint64_t>(stats.f_blocks) * stats.f_frsize; |
1835 #else | 1845 #else |
1836 #error Not implemented | 1846 #error Not implemented |
1837 #endif | 1847 #endif |
1838 return true; | 1848 return true; |
1839 } | 1849 } |
1840 | 1850 |
1841 } // namespace storage | 1851 } // namespace storage |
OLD | NEW |