]>
Commit | Line | Data |
---|---|---|
e2851fe7 | 1 | /* |
5b74111a | 2 | * Copyright (C) 1996-2018 The Squid Software Foundation and contributors |
bbc27441 AJ |
3 | * |
4 | * Squid software is distributed under GPLv2+ license and includes | |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
e2851fe7 AR |
7 | */ |
8 | ||
bbc27441 AJ |
9 | /* DEBUG: section 47 Store Directory Routines */ |
10 | ||
f7f3304a | 11 | #include "squid.h" |
582c2af2 | 12 | #include "Debug.h" |
e2851fe7 | 13 | #include "DiskIO/IORequestor.h" |
9199139f | 14 | #include "DiskIO/Mmapped/MmappedFile.h" |
e2851fe7 AR |
15 | #include "DiskIO/ReadRequest.h" |
16 | #include "DiskIO/WriteRequest.h" | |
b3f7fd88 | 17 | #include "fs_io.h" |
582c2af2 | 18 | #include "globals.h" |
582c2af2 | 19 | |
1a30fdf5 | 20 | #include <cerrno> |
582c2af2 | 21 | #if HAVE_SYS_MMAN_H |
9199139f | 22 | #include <sys/mman.h> |
582c2af2 FC |
23 | #endif |
24 | #if HAVE_SYS_STAT_H | |
25 | #include <sys/stat.h> | |
26 | #endif | |
e2851fe7 | 27 | |
def2dae1 AJ |
28 | // Some systems such as Hurd provide mmap() API but do not support MAP_NORESERVE |
29 | #ifndef MAP_NORESERVE | |
30 | #define MAP_NORESERVE 0 | |
31 | #endif | |
32 | ||
e2851fe7 AR |
33 | CBDATA_CLASS_INIT(MmappedFile); |
34 | ||
35 | // helper class to deal with mmap(2) offset alignment and other low-level specs | |
9199139f AR |
36 | class Mmapping |
37 | { | |
e2851fe7 AR |
38 | public: |
39 | Mmapping(int fd, size_t length, int prot, int flags, off_t offset); | |
40 | ~Mmapping(); | |
41 | ||
42 | void *map(); ///< calls mmap(2); returns usable buffer or nil on failure | |
43 | bool unmap(); ///< unmaps previously mapped buffer, if any | |
44 | ||
45 | private: | |
46 | const int fd; ///< descriptor of the mmapped file | |
47 | const size_t length; ///< user-requested data length, needed for munmap | |
48 | const int prot; ///< mmap(2) "protection" flags | |
49 | const int flags; ///< other mmap(2) flags | |
50 | const off_t offset; ///< user-requested data offset | |
51 | ||
52 | off_t delta; ///< mapped buffer increment to hit user offset | |
53 | void *buf; ///< buffer returned by mmap, needed for munmap | |
54 | }; | |
55 | ||
e2851fe7 | 56 | MmappedFile::MmappedFile(char const *aPath): fd(-1), |
f53969cc | 57 | minOffset(0), maxOffset(-1), error_(false) |
e2851fe7 AR |
58 | { |
59 | assert(aPath); | |
60 | path_ = xstrdup(aPath); | |
61 | debugs(79,5, HERE << this << ' ' << path_); | |
62 | } | |
63 | ||
64 | MmappedFile::~MmappedFile() | |
65 | { | |
66 | safe_free(path_); | |
67 | doClose(); | |
68 | } | |
69 | ||
70 | // XXX: almost a copy of BlockingFile::open | |
71 | void | |
ced8def3 | 72 | MmappedFile::open(int flags, mode_t, RefCount<IORequestor> callback) |
e2851fe7 AR |
73 | { |
74 | assert(fd < 0); | |
75 | ||
76 | /* Simulate async calls */ | |
9e167fa2 | 77 | fd = file_open(path_, flags); |
e2851fe7 AR |
78 | ioRequestor = callback; |
79 | ||
80 | if (fd < 0) { | |
b69e9ffa AJ |
81 | int xerrno = errno; |
82 | debugs(79,3, "open error: " << xstrerr(xerrno)); | |
e2851fe7 | 83 | error_ = true; |
9199139f | 84 | } else { |
cb4185f1 | 85 | ++store_open_disk_fd; |
b69e9ffa | 86 | debugs(79,3, "FD " << fd); |
e2851fe7 AR |
87 | |
88 | // setup mapping boundaries | |
89 | struct stat sb; | |
90 | if (fstat(fd, &sb) == 0) | |
91 | maxOffset = sb.st_size; // we do not expect it to change | |
9199139f | 92 | } |
e2851fe7 AR |
93 | |
94 | callback->ioCompletedNotification(); | |
95 | } | |
96 | ||
97 | /** | |
98 | * Alias for MmappedFile::open(...) | |
99 | \copydoc MmappedFile::open(int flags, mode_t mode, RefCount<IORequestor> callback) | |
100 | */ | |
101 | void | |
102 | MmappedFile::create(int flags, mode_t mode, RefCount<IORequestor> callback) | |
103 | { | |
104 | /* We use the same logic path for open */ | |
105 | open(flags, mode, callback); | |
106 | } | |
107 | ||
108 | void MmappedFile::doClose() | |
109 | { | |
110 | if (fd >= 0) { | |
111 | file_close(fd); | |
112 | fd = -1; | |
5e263176 | 113 | --store_open_disk_fd; |
9199139f | 114 | } |
e2851fe7 AR |
115 | } |
116 | ||
117 | void | |
118 | MmappedFile::close() | |
119 | { | |
120 | debugs(79, 3, HERE << this << " closing for " << ioRequestor); | |
121 | doClose(); | |
122 | assert(ioRequestor != NULL); | |
123 | ioRequestor->closeCompleted(); | |
124 | } | |
125 | ||
126 | bool | |
127 | MmappedFile::canRead() const | |
128 | { | |
129 | return fd >= 0; | |
130 | } | |
131 | ||
132 | bool | |
133 | MmappedFile::canWrite() const | |
134 | { | |
135 | return fd >= 0; | |
136 | } | |
137 | ||
138 | bool | |
139 | MmappedFile::error() const | |
140 | { | |
141 | return error_; | |
142 | } | |
143 | ||
144 | void | |
145 | MmappedFile::read(ReadRequest *aRequest) | |
146 | { | |
147 | debugs(79,3, HERE << "(FD " << fd << ", " << aRequest->len << ", " << | |
9199139f | 148 | aRequest->offset << ")"); |
e2851fe7 AR |
149 | |
150 | assert(fd >= 0); | |
151 | assert(ioRequestor != NULL); | |
e2851fe7 AR |
152 | assert(aRequest->len > 0); // TODO: work around mmap failures on zero-len? |
153 | assert(aRequest->offset >= 0); | |
154 | assert(!error_); // TODO: propagate instead? | |
155 | ||
156 | assert(minOffset < 0 || minOffset <= aRequest->offset); | |
db9958a9 | 157 | assert(maxOffset < 0 || static_cast<uint64_t>(aRequest->offset + aRequest->len) <= static_cast<uint64_t>(maxOffset)); |
e2851fe7 AR |
158 | |
159 | Mmapping mapping(fd, aRequest->len, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, | |
9199139f | 160 | aRequest->offset); |
e2851fe7 AR |
161 | |
162 | bool done = false; | |
9199139f | 163 | if (void *buf = mapping.map()) { |
e2851fe7 AR |
164 | memcpy(aRequest->buf, buf, aRequest->len); |
165 | done = mapping.unmap(); | |
9199139f | 166 | } |
e2851fe7 AR |
167 | error_ = !done; |
168 | ||
169 | const ssize_t rlen = error_ ? -1 : (ssize_t)aRequest->len; | |
9199139f | 170 | const int errflag = error_ ? DISK_ERROR :DISK_OK; |
e2851fe7 AR |
171 | ioRequestor->readCompleted(aRequest->buf, rlen, errflag, aRequest); |
172 | } | |
173 | ||
174 | void | |
175 | MmappedFile::write(WriteRequest *aRequest) | |
176 | { | |
177 | debugs(79,3, HERE << "(FD " << fd << ", " << aRequest->len << ", " << | |
9199139f | 178 | aRequest->offset << ")"); |
e2851fe7 AR |
179 | |
180 | assert(fd >= 0); | |
181 | assert(ioRequestor != NULL); | |
e2851fe7 AR |
182 | assert(aRequest->len > 0); // TODO: work around mmap failures on zero-len? |
183 | assert(aRequest->offset >= 0); | |
184 | assert(!error_); // TODO: propagate instead? | |
185 | ||
186 | assert(minOffset < 0 || minOffset <= aRequest->offset); | |
db9958a9 | 187 | assert(maxOffset < 0 || static_cast<uint64_t>(aRequest->offset + aRequest->len) <= static_cast<uint64_t>(maxOffset)); |
e2851fe7 AR |
188 | |
189 | const ssize_t written = | |
190 | pwrite(fd, aRequest->buf, aRequest->len, aRequest->offset); | |
191 | if (written < 0) { | |
e0236918 | 192 | debugs(79, DBG_IMPORTANT, HERE << "error: " << xstrerr(errno)); |
e2851fe7 | 193 | error_ = true; |
9199139f | 194 | } else if (static_cast<size_t>(written) != aRequest->len) { |
e0236918 | 195 | debugs(79, DBG_IMPORTANT, HERE << "problem: " << written << " < " << aRequest->len); |
e2851fe7 AR |
196 | error_ = true; |
197 | } | |
198 | ||
199 | if (aRequest->free_func) | |
200 | (aRequest->free_func)(const_cast<char*>(aRequest->buf)); // broken API? | |
201 | ||
202 | if (!error_) { | |
203 | debugs(79,5, HERE << "wrote " << aRequest->len << " to FD " << fd << " at " << aRequest->offset); | |
204 | } else { | |
205 | doClose(); | |
9199139f | 206 | } |
e2851fe7 AR |
207 | |
208 | const ssize_t rlen = error_ ? 0 : (ssize_t)aRequest->len; | |
9199139f | 209 | const int errflag = error_ ? DISK_ERROR :DISK_OK; |
e2851fe7 AR |
210 | ioRequestor->writeCompleted(errflag, rlen, aRequest); |
211 | } | |
212 | ||
213 | /// we only support blocking I/O | |
214 | bool | |
215 | MmappedFile::ioInProgress() const | |
216 | { | |
217 | return false; | |
218 | } | |
219 | ||
220 | Mmapping::Mmapping(int aFd, size_t aLength, int aProt, int aFlags, off_t anOffset): | |
f53969cc SM |
221 | fd(aFd), length(aLength), prot(aProt), flags(aFlags), offset(anOffset), |
222 | delta(-1), buf(NULL) | |
e2851fe7 AR |
223 | { |
224 | } | |
225 | ||
226 | Mmapping::~Mmapping() | |
227 | { | |
228 | if (buf) | |
229 | unmap(); | |
230 | } | |
231 | ||
232 | void * | |
233 | Mmapping::map() | |
234 | { | |
235 | // mmap(2) requires that offset is a multiple of the page size | |
236 | static const int pageSize = getpagesize(); | |
237 | delta = offset % pageSize; | |
238 | ||
239 | buf = mmap(NULL, length + delta, prot, flags, fd, offset - delta); | |
240 | ||
241 | if (buf == MAP_FAILED) { | |
242 | const int errNo = errno; | |
243 | debugs(79,3, HERE << "error FD " << fd << "mmap(" << length << '+' << | |
9199139f | 244 | delta << ", " << offset << '-' << delta << "): " << xstrerr(errNo)); |
e2851fe7 AR |
245 | buf = NULL; |
246 | return NULL; | |
247 | } | |
248 | ||
249 | return static_cast<char*>(buf) + delta; | |
250 | } | |
251 | ||
252 | bool | |
253 | Mmapping::unmap() | |
254 | { | |
255 | debugs(79,9, HERE << "FD " << fd << | |
9199139f | 256 | " munmap(" << buf << ", " << length << '+' << delta << ')'); |
e2851fe7 AR |
257 | |
258 | if (!buf) // forgot or failed to map | |
259 | return false; | |
260 | ||
261 | const bool error = munmap(buf, length + delta) != 0; | |
262 | if (error) { | |
263 | const int errNo = errno; | |
264 | debugs(79,3, HERE << "error FD " << fd << | |
9199139f AR |
265 | " munmap(" << buf << ", " << length << '+' << delta << "): " << |
266 | "): " << xstrerr(errNo)); | |
e2851fe7 AR |
267 | } |
268 | buf = NULL; | |
269 | return !error; | |
270 | } | |
271 | ||
272 | // TODO: check MAP_NORESERVE, consider MAP_POPULATE and MAP_FIXED | |
f53969cc | 273 |