diff options
author | Scott Anderson <saa@android.com> | 2012-04-24 16:35:57 -0700 |
---|---|---|
committer | Scott Anderson <saa@android.com> | 2012-04-26 12:05:50 -0700 |
commit | 613ee1f783d25f83aeb9b5ad25c3ceb3da49b163 (patch) | |
tree | bf2902d1a7cc273d4f151bbe1dbb037edea78d08 | |
parent | af3e2eba53c86b352f49634dcffd23775b3de6be (diff) | |
download | stressapptest-613ee1f783d25f83aeb9b5ad25c3ceb3da49b163.tar.gz |
Work around O_DIRECT being invalid on Android
If a file is opened with O_DIRECT on Android, an EINVAL will be
triggered. To try to accomplish the same effect as O_DIRECT,
this change adds calls to flush the page cache at key points for
the file and disk threads. These calls will sync and write a "1"
to /proc/sys/vm/drop_caches if O_DIRECT caused an EINVAL, but
will be a NOP otherwise.
Change-Id: If0c7ca455384f9d60d4587127e96979a3c7f1169
Signed-off-by: Scott Anderson <saa@android.com>
-rw-r--r-- | src/os.cc | 42 | ||||
-rw-r--r-- | src/os.h | 10 | ||||
-rw-r--r-- | src/worker.cc | 28 |
3 files changed, 73 insertions, 7 deletions
@@ -77,6 +77,8 @@ OsLayer::OsLayer() { has_clflush_ = false; has_sse2_ = false; + + use_flush_page_cache_ = false; } // OsLayer cleanup. @@ -169,6 +171,46 @@ void OsLayer::GetFeatures() { } +// Enable FlushPageCache to be functional instead of a NOP. +void OsLayer::ActivateFlushPageCache(void) { + logprintf(9, "Log: page cache will be flushed as needed\n"); + use_flush_page_cache_ = true; +} + +// Flush the page cache to ensure reads come from the disk. +bool OsLayer::FlushPageCache(void) { + if (!use_flush_page_cache_) + return true; + + // First, ask the kernel to write the cache to the disk. + sync(); + + // Second, ask the kernel to empty the cache by writing "1" to + // "/proc/sys/vm/drop_caches". + static const char *drop_caches_file = "/proc/sys/vm/drop_caches"; + int dcfile = open(drop_caches_file, O_WRONLY); + if (dcfile < 0) { + int err = errno; + string errtxt = ErrorString(err); + logprintf(3, "Log: failed to open %s - err %d (%s)\n", + drop_caches_file, err, errtxt.c_str()); + return false; + } + + ssize_t bytes_written = write(dcfile, "1", 1); + close(dcfile); + + if (bytes_written != 1) { + int err = errno; + string errtxt = ErrorString(err); + logprintf(3, "Log: failed to write %s - err %d (%s)\n", + drop_caches_file, err, errtxt.c_str()); + return false; + } + return true; +} + + // We need to flush the cacheline here. void OsLayer::Flush(void *vaddr) { // Use the generic flush. This function is just so we can override @@ -101,6 +101,15 @@ class OsLayer { // This will output a machine readable line regarding the error. virtual bool ErrorReport(const char *part, const char *symptom, int count); + // Flushes page cache. Used to circumvent the page cache when doing disk + // I/O. This will be a NOP until ActivateFlushPageCache() is called, which + // is typically done when opening a file with O_DIRECT fails. + // Returns false on error, true on success or NOP. + // Subclasses may implement this in machine specific ways.. + virtual bool FlushPageCache(void); + // Enable FlushPageCache() to actually do the flush instead of being a NOP. + virtual void ActivateFlushPageCache(void); + // Flushes cacheline. Used to distinguish read or write errors. // Subclasses may implement this in machine specific ways.. // Takes a pointer, and flushed the cacheline containing that pointer. @@ -260,6 +269,7 @@ class OsLayer { int address_mode_; // Are we running 32 or 64 bit? bool has_sse2_; // Do we have sse2 instructions? bool has_clflush_; // Do we have clflush instructions? + bool use_flush_page_cache_; // Do we need to flush the page cache? time_t time_initialized_; // Start time of test. diff --git a/src/worker.cc b/src/worker.cc index 2dae464..dcf4dcb 100644 --- a/src/worker.cc +++ b/src/worker.cc @@ -1600,15 +1600,21 @@ void FileThread::SetFile(const char *filename_init) { // Open the file for access. bool FileThread::OpenFile(int *pfile) { - int fd = open(filename_.c_str(), - O_RDWR | O_CREAT | O_SYNC | O_DIRECT, - 0644); + bool no_O_DIRECT = false; + int flags = O_RDWR | O_CREAT | O_SYNC; + int fd = open(filename_.c_str(), flags | O_DIRECT, 0644); + if (O_DIRECT != 0 && fd < 0 && errno == EINVAL) { + no_O_DIRECT = true; + fd = open(filename_.c_str(), flags, 0644); // Try without O_DIRECT + } if (fd < 0) { logprintf(0, "Process Error: Failed to create file %s!!\n", filename_.c_str()); pages_copied_ = 0; return false; } + if (no_O_DIRECT) + os_->ActivateFlushPageCache(); // Not using O_DIRECT fixed EINVAL *pfile = fd; return true; } @@ -1679,7 +1685,7 @@ bool FileThread::WritePages(int fd) { if (!result) return false; } - return true; + return os_->FlushPageCache(); // If O_DIRECT worked, this will be a NOP. } // Copy data from file into memory block. @@ -2691,14 +2697,20 @@ bool DiskThread::SetParameters(int read_block_size, // Open a device, return false on failure. bool DiskThread::OpenDevice(int *pfile) { - int fd = open(device_name_.c_str(), - O_RDWR | O_SYNC | O_DIRECT | O_LARGEFILE, - 0); + bool no_O_DIRECT = false; + int flags = O_RDWR | O_SYNC | O_LARGEFILE; + int fd = open(device_name_.c_str(), flags | O_DIRECT, 0); + if (O_DIRECT != 0 && fd < 0 && errno == EINVAL) { + no_O_DIRECT = true; + fd = open(device_name_.c_str(), flags, 0); // Try without O_DIRECT + } if (fd < 0) { logprintf(0, "Process Error: Failed to open device %s (thread %d)!!\n", device_name_.c_str(), thread_num_); return false; } + if (no_O_DIRECT) + os_->ActivateFlushPageCache(); *pfile = fd; return GetDiskSize(fd); @@ -2858,6 +2870,8 @@ bool DiskThread::DoWork(int fd) { in_flight_sectors_.push(block); } + if (!os_->FlushPageCache()) // If O_DIRECT worked, this will be a NOP. + return false; // Verify blocks on disk. logprintf(20, "Log: Read phase for disk %s (thread %d).\n", |