1 /*
2  * Copyright (C) 1996-2021 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.
7  */
8 
9 /* DEBUG: section 79    Squid-side DISKD I/O functions. */
10 
11 #include "squid.h"
12 #include "ConfigOption.h"
13 #include "diomsg.h"
14 #include "DiskdFile.h"
15 #include "DiskdIOStrategy.h"
16 #include "DiskIO/IORequestor.h"
17 #include "DiskIO/ReadRequest.h"
18 #include "DiskIO/WriteRequest.h"
19 #include "StatCounters.h"
20 
21 #if HAVE_SYS_IPC_H
22 #include <sys/ipc.h>
23 #endif
24 #if HAVE_SYS_MSG_H
25 #include <sys/msg.h>
26 #endif
27 #if HAVE_SYS_SHM_H
28 #include <sys/shm.h>
29 #endif
30 
31 CBDATA_CLASS_INIT(DiskdFile);
32 
DiskdFile(char const * aPath,DiskdIOStrategy * anIO)33 DiskdFile::DiskdFile(char const *aPath, DiskdIOStrategy *anIO) :
34     errorOccured(false),
35     IO(anIO),
36     mode(0),
37     inProgressIOs(0)
38 {
39     assert(aPath);
40     debugs(79, 3, "DiskdFile::DiskdFile: " << aPath);
41     path_ = xstrdup(aPath);
42     id = diskd_stats.sio_id;
43     ++diskd_stats.sio_id;
44 }
45 
~DiskdFile()46 DiskdFile::~DiskdFile()
47 {
48     assert(inProgressIOs == 0);
49     safe_free (path_);
50 }
51 
52 void
open(int flags,mode_t,RefCount<IORequestor> callback)53 DiskdFile::open(int flags, mode_t, RefCount<IORequestor> callback)
54 {
55     debugs(79, 3, "DiskdFile::open: " << this << " opening for " << callback.getRaw());
56     assert(ioRequestor.getRaw() == NULL);
57     ioRequestor = callback;
58     assert(callback.getRaw());
59     mode = flags;
60     ssize_t shm_offset;
61     char *buf = (char *)IO->shm.get(&shm_offset);
62     xstrncpy(buf, path_, SHMBUF_BLKSZ);
63     ioAway();
64     int x = IO->send(_MQD_OPEN,
65                      id,
66                      this,
67                      strlen(buf) + 1,
68                      mode,
69                      shm_offset,
70                      NULL);
71 
72     if (x < 0) {
73         ioCompleted();
74         errorOccured = true;
75         //        IO->shm.put (shm_offset);
76         ioRequestor->ioCompletedNotification();
77         ioRequestor = NULL;
78     }
79 
80     ++diskd_stats.open.ops;
81 }
82 
83 void
create(int flags,mode_t,RefCount<IORequestor> callback)84 DiskdFile::create(int flags, mode_t, RefCount<IORequestor> callback)
85 {
86     debugs(79, 3, "DiskdFile::create: " << this << " creating for " << callback.getRaw());
87     assert (ioRequestor.getRaw() == NULL);
88     ioRequestor = callback;
89     assert (callback.getRaw());
90     mode = flags;
91     ssize_t shm_offset;
92     char *buf = (char *)IO->shm.get(&shm_offset);
93     xstrncpy(buf, path_, SHMBUF_BLKSZ);
94     ioAway();
95     int x = IO->send(_MQD_CREATE,
96                      id,
97                      this,
98                      strlen(buf) + 1,
99                      mode,
100                      shm_offset,
101                      NULL);
102 
103     if (x < 0) {
104         int xerrno = errno;
105         ioCompleted();
106         errorOccured = true;
107         //        IO->shm.put (shm_offset);
108         debugs(79, DBG_IMPORTANT, "storeDiskdSend CREATE: " << xstrerr(xerrno));
109         notifyClient();
110         ioRequestor = NULL;
111         return;
112     }
113 
114     ++diskd_stats.create.ops;
115 }
116 
117 void
read(ReadRequest * aRead)118 DiskdFile::read(ReadRequest *aRead)
119 {
120     assert (ioRequestor.getRaw() != NULL);
121     ssize_t shm_offset;
122     char *rbuf = (char *)IO->shm.get(&shm_offset);
123     assert(rbuf);
124     ioAway();
125     int x = IO->send(_MQD_READ,
126                      id,
127                      this,
128                      aRead->len,
129                      aRead->offset,
130                      shm_offset,
131                      aRead);
132 
133     if (x < 0) {
134         int xerrno = errno;
135         ioCompleted();
136         errorOccured = true;
137         //        IO->shm.put (shm_offset);
138         debugs(79, DBG_IMPORTANT, "storeDiskdSend READ: " << xstrerr(xerrno));
139         notifyClient();
140         ioRequestor = NULL;
141         return;
142     }
143 
144     ++diskd_stats.read.ops;
145 }
146 
147 void
close()148 DiskdFile::close()
149 {
150     debugs(79, 3, "DiskdFile::close: " << this << " closing for " << ioRequestor.getRaw());
151     assert (ioRequestor.getRaw());
152     ioAway();
153     int x = IO->send(_MQD_CLOSE,
154                      id,
155                      this,
156                      0,
157                      0,
158                      -1,
159                      NULL);
160 
161     if (x < 0) {
162         int xerrno = errno;
163         ioCompleted();
164         errorOccured = true;
165         debugs(79, DBG_IMPORTANT, "storeDiskdSend CLOSE: " << xstrerr(xerrno));
166         notifyClient();
167         ioRequestor = NULL;
168         return;
169     }
170 
171     ++diskd_stats.close.ops;
172 }
173 
174 bool
error() const175 DiskdFile::error() const
176 {
177     return errorOccured;
178 }
179 
180 bool
canRead() const181 DiskdFile::canRead() const
182 {
183     return !error();
184 }
185 
186 bool
canNotifyClient() const187 DiskdFile::canNotifyClient() const
188 {
189     if (!ioRequestor.getRaw()) {
190         debugs(79, 3, "DiskdFile::canNotifyClient: No ioRequestor to notify");
191         return false;
192     }
193 
194     return true;
195 }
196 
197 void
notifyClient()198 DiskdFile::notifyClient()
199 {
200     if (!canNotifyClient()) {
201         return;
202     }
203 
204     ioRequestor->ioCompletedNotification();
205 }
206 
207 void
completed(diomsg * M)208 DiskdFile::completed(diomsg *M)
209 {
210     assert (M->newstyle);
211 
212     switch (M->mtype) {
213 
214     case _MQD_OPEN:
215         openDone(M);
216         break;
217 
218     case _MQD_CREATE:
219         createDone(M);
220         break;
221 
222     case _MQD_CLOSE:
223         closeDone(M);
224         break;
225 
226     case _MQD_READ:
227         readDone(M);
228         break;
229 
230     case _MQD_WRITE:
231         writeDone(M);
232         break;
233 
234     case _MQD_UNLINK:
235         assert (0);
236         break;
237 
238     default:
239         assert(0);
240         break;
241     }
242 }
243 
244 void
openDone(diomsg * M)245 DiskdFile::openDone(diomsg *M)
246 {
247     ++statCounter.syscalls.disk.opens;
248     debugs(79, 3, "storeDiskdOpenDone: status " << M->status);
249 
250     if (M->status < 0) {
251         ++diskd_stats.open.fail;
252         errorOccured = true;
253     } else {
254         ++diskd_stats.open.success;
255     }
256 
257     ioCompleted();
258     notifyClient();
259 }
260 
261 void
createDone(diomsg * M)262 DiskdFile::createDone(diomsg *M)
263 {
264     ++statCounter.syscalls.disk.opens;
265     debugs(79, 3, "storeDiskdCreateDone: status " << M->status);
266 
267     if (M->status < 0) {
268         ++diskd_stats.create.fail;
269         errorOccured = true;
270     } else {
271         ++diskd_stats.create.success;
272     }
273 
274     ioCompleted();
275     notifyClient();
276 }
277 
278 void
write(WriteRequest * aRequest)279 DiskdFile::write(WriteRequest *aRequest)
280 {
281     debugs(79, 3, "DiskdFile::write: this " << (void *)this << ", buf " << (void *)aRequest->buf << ", off " << aRequest->offset << ", len " << aRequest->len);
282     ssize_t shm_offset;
283     char *sbuf = (char *)IO->shm.get(&shm_offset);
284     memcpy(sbuf, aRequest->buf, aRequest->len);
285 
286     if (aRequest->free_func)
287         aRequest->free_func(const_cast<char *>(aRequest->buf));
288 
289     ioAway();
290 
291     int x = IO->send(_MQD_WRITE,
292                      id,
293                      this,
294                      aRequest->len,
295                      aRequest->offset,
296                      shm_offset,
297                      aRequest);
298 
299     if (x < 0) {
300         int xerrno = errno;
301         ioCompleted();
302         errorOccured = true;
303         debugs(79, DBG_IMPORTANT, "storeDiskdSend WRITE: " << xstrerr(xerrno));
304         //        IO->shm.put (shm_offset);
305         notifyClient();
306         ioRequestor = NULL;
307         return;
308     }
309 
310     ++diskd_stats.write.ops;
311 }
312 
313 void
ioAway()314 DiskdFile::ioAway()
315 {
316     ++inProgressIOs;
317 }
318 
319 void
ioCompleted()320 DiskdFile::ioCompleted()
321 {
322     --inProgressIOs;
323 }
324 
325 void
closeDone(diomsg * M)326 DiskdFile::closeDone(diomsg * M)
327 {
328     ++statCounter.syscalls.disk.closes;
329     debugs(79, 3, "DiskdFile::closeDone: status " << M->status);
330 
331     if (M->status < 0) {
332         ++diskd_stats.close.fail;
333         errorOccured = true;
334     } else {
335         ++diskd_stats.close.success;
336     }
337 
338     ioCompleted();
339 
340     if (canNotifyClient())
341         ioRequestor->closeCompleted();
342 
343     ioRequestor = NULL;
344 }
345 
346 void
readDone(diomsg * M)347 DiskdFile::readDone(diomsg * M)
348 {
349     ++statCounter.syscalls.disk.reads;
350     debugs(79, 3, "DiskdFile::readDone: status " << M->status);
351     assert (M->requestor);
352     ReadRequest::Pointer readRequest = dynamic_cast<ReadRequest *>(M->requestor);
353 
354     /* remove the free protection */
355     if (readRequest != NULL) {
356         const uint32_t lcount = readRequest->unlock();
357         if (lcount == 0)
358             debugs(79, DBG_IMPORTANT, "invariant check failed: readRequest reference count is 0");
359     }
360 
361     if (M->status < 0) {
362         ++diskd_stats.read.fail;
363         ioCompleted();
364         errorOccured = true;
365         ioRequestor->readCompleted(NULL, -1, DISK_ERROR, readRequest);
366         return;
367     }
368 
369     ++diskd_stats.read.success;
370 
371     ioCompleted();
372     ioRequestor->readCompleted (IO->shm.buf + M->shm_offset,  M->status, DISK_OK, readRequest);
373 }
374 
375 void
writeDone(diomsg * M)376 DiskdFile::writeDone(diomsg *M)
377 {
378     ++statCounter.syscalls.disk.writes;
379     debugs(79, 3, "storeDiskdWriteDone: status " << M->status);
380     assert (M->requestor);
381     WriteRequest::Pointer writeRequest = dynamic_cast<WriteRequest *>(M->requestor);
382 
383     /* remove the free protection */
384     if (writeRequest != NULL) {
385         const uint32_t lcount = writeRequest->unlock();
386         if (lcount == 0)
387             debugs(79, DBG_IMPORTANT, "invariant check failed: writeRequest reference count is 0");
388     }
389 
390     if (M->status < 0) {
391         errorOccured = true;
392         ++diskd_stats.write.fail;
393         ioCompleted();
394         ioRequestor->writeCompleted (DISK_ERROR,0, writeRequest);
395         return;
396     }
397 
398     ++diskd_stats.write.success;
399     ioCompleted();
400     ioRequestor->writeCompleted (DISK_OK,M->status, writeRequest);
401 }
402 
403 bool
ioInProgress() const404 DiskdFile::ioInProgress()const
405 {
406     return inProgressIOs != 0;
407 }
408 
409