Fix: erroneous specifier used with ERR_FMT macro
[lttng-tools.git] / src / common / io-hint.cpp
1 /*
2 * Copyright (C) 2023 Michael Jeanson <mjeanson@efficios.com>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-only
5 *
6 */
7
8 #include <common/error.hpp>
9 #include <common/io-hint.hpp>
10 #include <common/scope-exit.hpp>
11
12 #include <cinttypes>
13 #include <fcntl.h>
14 #include <sys/mman.h>
15 #include <unistd.h>
16
17 /*
18 * Use sync_file_range when available.
19 */
20 #ifdef HAVE_SYNC_FILE_RANGE
21
22 #include <fcntl.h>
23
24 namespace {
25 int flush_range(int fd, off_t offset, off_t nbytes, unsigned int flags)
26 {
27 int ret;
28
29 ret = sync_file_range(fd, offset, nbytes, flags);
30 if (ret) {
31 PERROR("Failed to sync file range: fd=%i, offset=%" PRIu64 ", nbytes=%" PRIu64
32 ", flags=%i",
33 fd,
34 static_cast<uint64_t>(offset),
35 static_cast<uint64_t>(nbytes),
36 flags);
37 }
38
39 return ret;
40 }
41
42 int flush_range_sync(int fd, off_t offset, off_t nbytes)
43 {
44 return flush_range(fd,
45 offset,
46 nbytes,
47 SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE |
48 SYNC_FILE_RANGE_WAIT_AFTER);
49 }
50
51 int flush_range_async(int fd, off_t offset, off_t nbytes)
52 {
53 return flush_range(fd, offset, nbytes, SYNC_FILE_RANGE_WRITE);
54 }
55 } /* namespace */
56
57 #else /* HAVE_SYNC_FILE_RANGE */
58
59 namespace {
60 /*
61 * Use a memory mapping with msync() to emulate sync_file_range().
62 */
63 int flush_range(int fd, off_t offset, off_t nbytes, int flags)
64 {
65 void *mapped_region = mmap(NULL, nbytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset);
66 if (mapped_region == MAP_FAILED) {
67 PERROR("Failed to mmap region to flush range: fd=%i, offset=%" PRIu64
68 ", nbytes=%" PRIu64 ", flags=%i",
69 fd,
70 static_cast<uint64_t>(offset),
71 static_cast<uint64_t>(nbytes),
72 flags);
73 return -1;
74 }
75
76 const auto munmap_on_exit = lttng::make_scope_exit([&]() noexcept {
77 const auto munmap_ret = munmap(mapped_region, nbytes);
78 if (munmap_ret) {
79 PERROR("Failed to munmap region while flushing range: fd=%i, offset=%" PRIu64
80 ", nbytes=%" PRIu64 ", flags=%i",
81 fd,
82 static_cast<uint64_t>(offset),
83 static_cast<uint64_t>(nbytes),
84 flags);
85 }
86 });
87
88 const auto msync_ret = msync(mapped_region, nbytes, flags);
89 if (msync_ret) {
90 PERROR("Failed to msync region while flushing range: fd=%i, offset=%" PRIu64
91 ", nbytes=%" PRIu64 ", flags=%i",
92 fd,
93 static_cast<uint64_t>(offset),
94 static_cast<uint64_t>(nbytes),
95 flags);
96 return -1;
97 }
98
99 return 0;
100 }
101
102 int flush_range_sync(int fd, off_t offset, off_t nbytes)
103 {
104 return flush_range(fd, offset, nbytes, MS_SYNC);
105 }
106
107 int flush_range_async(int fd, off_t offset, off_t nbytes)
108 {
109 return flush_range(fd, offset, nbytes, MS_ASYNC);
110 }
111 } /* namespace */
112 #endif /* !HAVE_SYNC_FILE_RANGE */
113
114 /*
115 * Use posix_fadvise when available.
116 */
117 #ifdef HAVE_POSIX_FADVISE
118 namespace {
119 int hint_dont_need(int fd, off_t offset, off_t nbytes)
120 {
121 const int ret = posix_fadvise(fd, offset, nbytes, POSIX_FADV_DONTNEED);
122 if (ret && ret != -ENOSYS) {
123 PERROR("Failed to mark region as DONTNEED with posix_fadvise: fd=%i, offset=%" PRIu64
124 ", nbytes=%" PRIu64,
125 fd,
126 static_cast<uint64_t>(offset),
127 static_cast<uint64_t>(nbytes));
128 errno = ret;
129 }
130
131 return ret;
132 }
133 } /* namespace */
134
135 #else /* HAVE_POSIX_FADVISE */
136
137 /*
138 * Generic noop compat for platforms wihtout posix_fadvise, this is acceptable
139 * since we are only giving a hint to the kernel.
140 */
141 namespace {
142 int hint_dont_need(int fd __attribute__((unused)),
143 off_t offset __attribute__((unused)),
144 off_t nbytes __attribute__((unused)))
145 {
146 return 0;
147 }
148 } /* namespace */
149 #endif /* !HAVE_POSIX_FADVISE */
150
151 /*
152 * Give a hint to the kernel that we won't need the data at the specified range
153 * so it can be dropped from the page cache and wait for it to be flushed to
154 * disk.
155 */
156 void lttng::io::hint_flush_range_dont_need_sync(int fd, off_t offset, off_t nbytes)
157 {
158 /* Waited for the page writeback to complete. */
159 flush_range_sync(fd, offset, nbytes);
160
161 /*
162 * Give hints to the kernel about how we access the file:
163 * POSIX_FADV_DONTNEED : we won't re-access data in a near future after
164 * we write it.
165 *
166 * We need to call fadvise again after the file grows because the
167 * kernel does not seem to apply fadvise to non-existing parts of the
168 * file.
169 *
170 * Call fadvise _after_ having waited for the page writeback to
171 * complete because the dirty page writeback semantic is not well
172 * defined. So it can be expected to lead to lower throughput in
173 * streaming.
174 */
175 hint_dont_need(fd, offset, nbytes);
176 }
177
178 /*
179 * Give a hint to the kernel that the data at the specified range should be
180 * flushed to disk and wait for it to complete.
181 */
182 void lttng::io::hint_flush_range_sync(int fd, off_t offset, off_t nbytes)
183 {
184 flush_range_sync(fd, offset, nbytes);
185 }
186
187 /*
188 * Give a hint to the kernel that the data at the specified range should be
189 * flushed to disk and return immediatly.
190 */
191 void lttng::io::hint_flush_range_async(int fd, off_t offset, off_t nbytes)
192 {
193 flush_range_async(fd, offset, nbytes);
194 }
This page took 0.034413 seconds and 4 git commands to generate.