]>
Commit | Line | Data |
---|---|---|
e2851fe7 | 1 | /* |
bbc27441 AJ |
2 | * Copyright (C) 1996-2014 The Squid Software Foundation and contributors |
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" |
602d9612 | 13 | #include "disk.h" |
e2851fe7 | 14 | #include "DiskIO/IORequestor.h" |
9199139f | 15 | #include "DiskIO/Mmapped/MmappedFile.h" |
e2851fe7 AR |
16 | #include "DiskIO/ReadRequest.h" |
17 | #include "DiskIO/WriteRequest.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), |
9199139f | 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 | |
72 | MmappedFile::open(int flags, mode_t mode, RefCount<IORequestor> callback) | |
73 | { | |
74 | assert(fd < 0); | |
75 | ||
76 | /* Simulate async calls */ | |
77 | fd = file_open(path_ , flags); | |
78 | ioRequestor = callback; | |
79 | ||
80 | if (fd < 0) { | |
81 | debugs(79,3, HERE << "open error: " << xstrerror()); | |
82 | error_ = true; | |
9199139f | 83 | } else { |
cb4185f1 | 84 | ++store_open_disk_fd; |
e2851fe7 AR |
85 | debugs(79,3, HERE << "FD " << fd); |
86 | ||
87 | // setup mapping boundaries | |
88 | struct stat sb; | |
89 | if (fstat(fd, &sb) == 0) | |
90 | maxOffset = sb.st_size; // we do not expect it to change | |
9199139f | 91 | } |
e2851fe7 AR |
92 | |
93 | callback->ioCompletedNotification(); | |
94 | } | |
95 | ||
96 | /** | |
97 | * Alias for MmappedFile::open(...) | |
98 | \copydoc MmappedFile::open(int flags, mode_t mode, RefCount<IORequestor> callback) | |
99 | */ | |
100 | void | |
101 | MmappedFile::create(int flags, mode_t mode, RefCount<IORequestor> callback) | |
102 | { | |
103 | /* We use the same logic path for open */ | |
104 | open(flags, mode, callback); | |
105 | } | |
106 | ||
107 | void MmappedFile::doClose() | |
108 | { | |
109 | if (fd >= 0) { | |
110 | file_close(fd); | |
111 | fd = -1; | |
5e263176 | 112 | --store_open_disk_fd; |
9199139f | 113 | } |
e2851fe7 AR |
114 | } |
115 | ||
116 | void | |
117 | MmappedFile::close() | |
118 | { | |
119 | debugs(79, 3, HERE << this << " closing for " << ioRequestor); | |
120 | doClose(); | |
121 | assert(ioRequestor != NULL); | |
122 | ioRequestor->closeCompleted(); | |
123 | } | |
124 | ||
125 | bool | |
126 | MmappedFile::canRead() const | |
127 | { | |
128 | return fd >= 0; | |
129 | } | |
130 | ||
131 | bool | |
132 | MmappedFile::canWrite() const | |
133 | { | |
134 | return fd >= 0; | |
135 | } | |
136 | ||
137 | bool | |
138 | MmappedFile::error() const | |
139 | { | |
140 | return error_; | |
141 | } | |
142 | ||
143 | void | |
144 | MmappedFile::read(ReadRequest *aRequest) | |
145 | { | |
146 | debugs(79,3, HERE << "(FD " << fd << ", " << aRequest->len << ", " << | |
9199139f | 147 | aRequest->offset << ")"); |
e2851fe7 AR |
148 | |
149 | assert(fd >= 0); | |
150 | assert(ioRequestor != NULL); | |
e2851fe7 AR |
151 | assert(aRequest->len > 0); // TODO: work around mmap failures on zero-len? |
152 | assert(aRequest->offset >= 0); | |
153 | assert(!error_); // TODO: propagate instead? | |
154 | ||
155 | assert(minOffset < 0 || minOffset <= aRequest->offset); | |
db9958a9 | 156 | assert(maxOffset < 0 || static_cast<uint64_t>(aRequest->offset + aRequest->len) <= static_cast<uint64_t>(maxOffset)); |
e2851fe7 AR |
157 | |
158 | Mmapping mapping(fd, aRequest->len, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, | |
9199139f | 159 | aRequest->offset); |
e2851fe7 AR |
160 | |
161 | bool done = false; | |
9199139f | 162 | if (void *buf = mapping.map()) { |
e2851fe7 AR |
163 | memcpy(aRequest->buf, buf, aRequest->len); |
164 | done = mapping.unmap(); | |
9199139f | 165 | } |
e2851fe7 AR |
166 | error_ = !done; |
167 | ||
168 | const ssize_t rlen = error_ ? -1 : (ssize_t)aRequest->len; | |
9199139f | 169 | const int errflag = error_ ? DISK_ERROR :DISK_OK; |
e2851fe7 AR |
170 | ioRequestor->readCompleted(aRequest->buf, rlen, errflag, aRequest); |
171 | } | |
172 | ||
173 | void | |
174 | MmappedFile::write(WriteRequest *aRequest) | |
175 | { | |
176 | debugs(79,3, HERE << "(FD " << fd << ", " << aRequest->len << ", " << | |
9199139f | 177 | aRequest->offset << ")"); |
e2851fe7 AR |
178 | |
179 | assert(fd >= 0); | |
180 | assert(ioRequestor != NULL); | |
e2851fe7 AR |
181 | assert(aRequest->len > 0); // TODO: work around mmap failures on zero-len? |
182 | assert(aRequest->offset >= 0); | |
183 | assert(!error_); // TODO: propagate instead? | |
184 | ||
185 | assert(minOffset < 0 || minOffset <= aRequest->offset); | |
db9958a9 | 186 | assert(maxOffset < 0 || static_cast<uint64_t>(aRequest->offset + aRequest->len) <= static_cast<uint64_t>(maxOffset)); |
e2851fe7 AR |
187 | |
188 | const ssize_t written = | |
189 | pwrite(fd, aRequest->buf, aRequest->len, aRequest->offset); | |
190 | if (written < 0) { | |
e0236918 | 191 | debugs(79, DBG_IMPORTANT, HERE << "error: " << xstrerr(errno)); |
e2851fe7 | 192 | error_ = true; |
9199139f | 193 | } else if (static_cast<size_t>(written) != aRequest->len) { |
e0236918 | 194 | debugs(79, DBG_IMPORTANT, HERE << "problem: " << written << " < " << aRequest->len); |
e2851fe7 AR |
195 | error_ = true; |
196 | } | |
197 | ||
198 | if (aRequest->free_func) | |
199 | (aRequest->free_func)(const_cast<char*>(aRequest->buf)); // broken API? | |
200 | ||
201 | if (!error_) { | |
202 | debugs(79,5, HERE << "wrote " << aRequest->len << " to FD " << fd << " at " << aRequest->offset); | |
203 | } else { | |
204 | doClose(); | |
9199139f | 205 | } |
e2851fe7 AR |
206 | |
207 | const ssize_t rlen = error_ ? 0 : (ssize_t)aRequest->len; | |
9199139f | 208 | const int errflag = error_ ? DISK_ERROR :DISK_OK; |
e2851fe7 AR |
209 | ioRequestor->writeCompleted(errflag, rlen, aRequest); |
210 | } | |
211 | ||
212 | /// we only support blocking I/O | |
213 | bool | |
214 | MmappedFile::ioInProgress() const | |
215 | { | |
216 | return false; | |
217 | } | |
218 | ||
219 | Mmapping::Mmapping(int aFd, size_t aLength, int aProt, int aFlags, off_t anOffset): | |
9199139f AR |
220 | fd(aFd), length(aLength), prot(aProt), flags(aFlags), offset(anOffset), |
221 | delta(-1), buf(NULL) | |
e2851fe7 AR |
222 | { |
223 | } | |
224 | ||
225 | Mmapping::~Mmapping() | |
226 | { | |
227 | if (buf) | |
228 | unmap(); | |
229 | } | |
230 | ||
231 | void * | |
232 | Mmapping::map() | |
233 | { | |
234 | // mmap(2) requires that offset is a multiple of the page size | |
235 | static const int pageSize = getpagesize(); | |
236 | delta = offset % pageSize; | |
237 | ||
238 | buf = mmap(NULL, length + delta, prot, flags, fd, offset - delta); | |
239 | ||
240 | if (buf == MAP_FAILED) { | |
241 | const int errNo = errno; | |
242 | debugs(79,3, HERE << "error FD " << fd << "mmap(" << length << '+' << | |
9199139f | 243 | delta << ", " << offset << '-' << delta << "): " << xstrerr(errNo)); |
e2851fe7 AR |
244 | buf = NULL; |
245 | return NULL; | |
246 | } | |
247 | ||
248 | return static_cast<char*>(buf) + delta; | |
249 | } | |
250 | ||
251 | bool | |
252 | Mmapping::unmap() | |
253 | { | |
254 | debugs(79,9, HERE << "FD " << fd << | |
9199139f | 255 | " munmap(" << buf << ", " << length << '+' << delta << ')'); |
e2851fe7 AR |
256 | |
257 | if (!buf) // forgot or failed to map | |
258 | return false; | |
259 | ||
260 | const bool error = munmap(buf, length + delta) != 0; | |
261 | if (error) { | |
262 | const int errNo = errno; | |
263 | debugs(79,3, HERE << "error FD " << fd << | |
9199139f AR |
264 | " munmap(" << buf << ", " << length << '+' << delta << "): " << |
265 | "): " << xstrerr(errNo)); | |
e2851fe7 AR |
266 | } |
267 | buf = NULL; | |
268 | return !error; | |
269 | } | |
270 | ||
271 | // TODO: check MAP_NORESERVE, consider MAP_POPULATE and MAP_FIXED |