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