Implement ZERO and DISCARD operations in update_engine.
ZERO and DISCARD operations are implemented using the specific ioctl()
that allow SSD to do a faster operation when writing zeros. In case of
failure, such as when they are not supported, the operation will fall
back to write the zeros directly.
Bug: 23180264
Test: Added a unittest.
Change-Id: I0f12ab9864620b50250157c1de17b7fc58c7ea1c
diff --git a/file_descriptor.cc b/file_descriptor.cc
index 8b8fa72..4718528 100644
--- a/file_descriptor.cc
+++ b/file_descriptor.cc
@@ -17,6 +17,8 @@
#include "update_engine/file_descriptor.h"
#include <fcntl.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -63,6 +65,42 @@
return lseek64(fd_, offset, whence);
}
+bool EintrSafeFileDescriptor::BlkIoctl(int request,
+ uint64_t start,
+ uint64_t length,
+ int* result) {
+ DCHECK(request == BLKDISCARD || request == BLKZEROOUT ||
+ request == BLKSECDISCARD);
+ // On some devices, the BLKDISCARD will actually read back as zeros, instead
+ // of "undefined" data. The BLKDISCARDZEROES ioctl tells whether that's the
+ // case, so we issue a BLKDISCARD in those cases to speed up the writes.
+ unsigned int arg;
+ if (request == BLKZEROOUT && ioctl(fd_, BLKDISCARDZEROES, &arg) == 0 && arg)
+ request = BLKDISCARD;
+
+ // Ensure the |fd_| is in O_DIRECT mode during this operation, so the write
+ // cache for this region is invalidated. This is required since otherwise
+ // reading back this region could consume stale data from the cache.
+ int flags = fcntl(fd_, F_GETFL, 0);
+ if (flags == -1) {
+ PLOG(WARNING) << "Couldn't get flags on fd " << fd_;
+ return false;
+ }
+ if ((flags & O_DIRECT) == 0 && fcntl(fd_, F_SETFL, flags | O_DIRECT) == -1) {
+ PLOG(WARNING) << "Couldn't set O_DIRECT on fd " << fd_;
+ return false;
+ }
+
+ uint64_t range[2] = {start, length};
+ *result = ioctl(fd_, request, range);
+
+ if ((flags & O_DIRECT) == 0 && fcntl(fd_, F_SETFL, flags) == -1) {
+ PLOG(WARNING) << "Couldn't remove O_DIRECT on fd " << fd_;
+ return false;
+ }
+ return true;
+}
+
bool EintrSafeFileDescriptor::Close() {
CHECK_GE(fd_, 0);
if (IGNORE_EINTR(close(fd_)))