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