]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/disk.cc
2 * DEBUG: section 06 Disk I/O Routines
3 * AUTHOR: Harvest Derived
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
34 #include "comm/Loops.h"
41 #include "profiler/Profiler.h"
42 #include "StatCounters.h"
48 static PF diskHandleRead
;
49 static PF diskHandleWrite
;
51 #if _SQUID_WINDOWS_ || _SQUID_OS2_
53 diskWriteIsComplete(int fd
)
55 return fd_table
[fd
].disk
.write_q
? 0 : 1;
66 /* hack needed on SunStudio to avoid linkage convention mismatch */
67 static void cxx_xfree(void *ptr
)
73 * opens a disk file specified by 'path'. This function always
74 * blocks! There is no callback.
77 file_open(const char *path
, int mode
)
80 PROF_start(file_open
);
82 if (FILE_MODE(mode
) == O_WRONLY
)
87 fd
= open(path
, mode
, 0644);
89 ++ statCounter
.syscalls
.disk
.opens
;
92 debugs(50, 3, "file_open: error opening file " << path
<< ": " << xstrerror());
95 debugs(6, 5, "file_open: FD " << fd
);
96 commSetCloseOnExec(fd
);
97 fd_open(fd
, FD_FILE
, path
);
100 PROF_stop(file_open
);
104 /* close a disk file. */
108 fde
*F
= &fd_table
[fd
];
110 PROF_start(file_close
);
112 assert(F
->flags
.open
);
114 if ((read_callback
= F
->read_handler
)) {
115 F
->read_handler
= NULL
;
116 read_callback(-1, F
->read_data
);
119 if (F
->flags
.write_daemon
) {
120 #if _SQUID_WINDOWS_ || _SQUID_OS2_
122 * on some operating systems, you can not delete or rename
123 * open files, so we won't allow delayed close.
125 while (!diskWriteIsComplete(fd
))
126 diskHandleWrite(fd
, NULL
);
128 F
->flags
.close_request
= true;
129 debugs(6, 2, "file_close: FD " << fd
<< ", delaying close");
130 PROF_stop(file_close
);
137 * Assert there is no write callback. Otherwise we might be
138 * leaking write state data by closing the descriptor
140 assert(F
->write_handler
== NULL
);
142 #if CALL_FSYNC_BEFORE_CLOSE
150 debugs(6, F
->flags
.close_request
? 2 : 5, "file_close: FD " << fd
<< " really closing\n");
154 ++ statCounter
.syscalls
.disk
.closes
;
156 PROF_stop(file_close
);
160 * This function has the purpose of combining multiple writes. This is
161 * to facilitate the ASYNC_IO option since it can only guarantee 1
162 * write to a file per trip around the comm.c select() loop. That's bad
163 * because more than 1 write can be made to the access.log file per
164 * trip, and so this code is purely designed to help batch multiple
165 * sequential writes to the access.log file. Squid will never issue
166 * multiple writes for any other file type during 1 trip around the
167 * select() loop. --SLF
170 diskCombineWrites(_fde_disk
*fdd
)
173 * We need to combine multiple write requests on an FD's write
174 * queue But only if we don't need to seek() in between them, ugh!
175 * XXX This currently ignores any seeks (file_offset)
178 if (fdd
->write_q
!= NULL
&& fdd
->write_q
->next
!= NULL
) {
181 for (dwrite_q
*q
= fdd
->write_q
; q
!= NULL
; q
= q
->next
)
182 len
+= q
->len
- q
->buf_offset
;
184 dwrite_q
*wq
= (dwrite_q
*)memAllocate(MEM_DWRITE_Q
);
186 wq
->buf
= (char *)xmalloc(len
);
194 wq
->free_func
= cxx_xfree
;
196 while (fdd
->write_q
!= NULL
) {
197 dwrite_q
*q
= fdd
->write_q
;
199 len
= q
->len
- q
->buf_offset
;
200 memcpy(wq
->buf
+ wq
->len
, q
->buf
+ q
->buf_offset
, len
);
202 fdd
->write_q
= q
->next
;
205 q
->free_func(q
->buf
);
207 memFree(q
, MEM_DWRITE_Q
);
210 fdd
->write_q_tail
= wq
;
218 diskHandleWrite(int fd
, void *notused
)
221 fde
*F
= &fd_table
[fd
];
223 _fde_disk
*fdd
= &F
->disk
;
224 dwrite_q
*q
= fdd
->write_q
;
225 int status
= DISK_OK
;
231 PROF_start(diskHandleWrite
);
233 debugs(6, 3, "diskHandleWrite: FD " << fd
);
235 F
->flags
.write_daemon
= false;
237 assert(fdd
->write_q
!= NULL
);
239 assert(fdd
->write_q
->len
> fdd
->write_q
->buf_offset
);
241 debugs(6, 3, "diskHandleWrite: FD " << fd
<< " writing " <<
242 (fdd
->write_q
->len
- fdd
->write_q
->buf_offset
) << " bytes at " <<
243 fdd
->write_q
->file_offset
);
247 if (fdd
->write_q
->file_offset
!= -1)
248 lseek(fd
, fdd
->write_q
->file_offset
, SEEK_SET
); /* XXX ignore return? */
250 len
= FD_WRITE_METHOD(fd
,
251 fdd
->write_q
->buf
+ fdd
->write_q
->buf_offset
,
252 fdd
->write_q
->len
- fdd
->write_q
->buf_offset
);
254 debugs(6, 3, "diskHandleWrite: FD " << fd
<< " len = " << len
);
256 ++ statCounter
.syscalls
.disk
.writes
;
258 fd_bytes(fd
, len
, FD_WRITE
);
261 if (!ignoreErrno(errno
)) {
262 status
= errno
== ENOSPC
? DISK_NO_SPACE_LEFT
: DISK_ERROR
;
263 debugs(50, DBG_IMPORTANT
, "diskHandleWrite: FD " << fd
<< ": disk write error: " << xstrerror());
266 * If there is no write callback, then this file is
267 * most likely something important like a log file, or
268 * an interprocess pipe. Its not a swapfile. We feel
269 * that a write failure on a log file is rather important,
270 * and Squid doesn't otherwise deal with this condition.
271 * So to get the administrators attention, we exit with
275 if (fdd
->wrt_handle
== NULL
)
276 fatal("Write failure -- check your disk space and cache.log");
279 * If there is a write failure, then we notify the
280 * upper layer via the callback, at the end of this
281 * function. Meanwhile, flush all pending buffers
282 * here. Let the upper layer decide how to handle the
283 * failure. This will prevent experiencing multiple,
284 * repeated write failures for the same FD because of
288 fdd
->write_q
= q
->next
;
291 q
->free_func(q
->buf
);
294 memFree(q
, MEM_DWRITE_Q
);
297 } while ((q
= fdd
->write_q
));
304 /* q might become NULL from write failure above */
305 q
->buf_offset
+= len
;
307 if (q
->buf_offset
> q
->len
)
308 debugs(50, DBG_IMPORTANT
, "diskHandleWriteComplete: q->buf_offset > q->len (" <<
309 q
<< "," << (int) q
->buf_offset
<< ", " << q
->len
<< ", " <<
310 len
<< " FD " << fd
<< ")");
312 assert(q
->buf_offset
<= q
->len
);
314 if (q
->buf_offset
== q
->len
) {
316 fdd
->write_q
= q
->next
;
319 q
->free_func(q
->buf
);
322 memFree(q
, MEM_DWRITE_Q
);
328 if (fdd
->write_q
== NULL
) {
330 fdd
->write_q_tail
= NULL
;
332 /* another block is queued */
333 diskCombineWrites(fdd
);
334 Comm::SetSelect(fd
, COMM_SELECT_WRITE
, diskHandleWrite
, NULL
, 0);
335 F
->flags
.write_daemon
= true;
338 do_close
= F
->flags
.close_request
;
340 if (fdd
->wrt_handle
) {
341 DWCB
*callback
= fdd
->wrt_handle
;
343 fdd
->wrt_handle
= NULL
;
345 if (cbdataReferenceValidDone(fdd
->wrt_handle_data
, &cbdata
)) {
346 callback(fd
, status
, len
, cbdata
);
348 * NOTE, this callback can close the FD, so we must
349 * not touch 'F', 'fdd', etc. after this.
351 PROF_stop(diskHandleWrite
);
353 /* XXX But what about close_request??? */
360 PROF_stop(diskHandleWrite
);
363 /* write block to a file */
364 /* write back queue. Only one writer at a time. */
365 /* call a handle when writing is complete. */
369 void const *ptr_to_buf
,
376 fde
*F
= &fd_table
[fd
];
377 PROF_start(file_write
);
379 assert(F
->flags
.open
);
380 /* if we got here. Caller is eligible to write. */
381 wq
= (dwrite_q
*)memAllocate(MEM_DWRITE_Q
);
382 wq
->file_offset
= file_offset
;
383 wq
->buf
= (char *)ptr_to_buf
;
387 wq
->free_func
= free_func
;
389 if (!F
->disk
.wrt_handle_data
) {
390 F
->disk
.wrt_handle
= handle
;
391 F
->disk
.wrt_handle_data
= cbdataReference(handle_data
);
393 /* Detect if there is multiple concurrent users of this fd.. we only support one callback */
394 assert(F
->disk
.wrt_handle_data
== handle_data
&& F
->disk
.wrt_handle
== handle
);
398 if (F
->disk
.write_q
== NULL
) {
400 F
->disk
.write_q
= F
->disk
.write_q_tail
= wq
;
402 F
->disk
.write_q_tail
->next
= wq
;
403 F
->disk
.write_q_tail
= wq
;
406 if (!F
->flags
.write_daemon
) {
407 diskHandleWrite(fd
, NULL
);
410 PROF_stop(file_write
);
414 * a wrapper around file_write to allow for MemBuf to be file_written
418 file_write_mbuf(int fd
, off_t off
, MemBuf mb
, DWCB
* handler
, void *handler_data
)
420 file_write(fd
, off
, mb
.buf
, mb
.size
, handler
, handler_data
, mb
.freeFunc());
425 diskHandleRead(int fd
, void *data
)
427 dread_ctrl
*ctrl_dat
= (dread_ctrl
*)data
;
428 fde
*F
= &fd_table
[fd
];
432 * FD < 0 indicates premature close; we just have to free
437 memFree(ctrl_dat
, MEM_DREAD_CTRL
);
441 PROF_start(diskHandleRead
);
443 #if WRITES_MAINTAIN_DISK_OFFSET
444 if (F
->disk
.offset
!= ctrl_dat
->offset
) {
448 debugs(6, 3, "diskHandleRead: FD " << fd
<< " seeking to offset " << ctrl_dat
->offset
);
449 lseek(fd
, ctrl_dat
->offset
, SEEK_SET
); /* XXX ignore return? */
450 ++ statCounter
.syscalls
.disk
.seeks
;
451 F
->disk
.offset
= ctrl_dat
->offset
;
455 len
= FD_READ_METHOD(fd
, ctrl_dat
->buf
, ctrl_dat
->req_len
);
458 F
->disk
.offset
+= len
;
460 ++ statCounter
.syscalls
.disk
.reads
;
462 fd_bytes(fd
, len
, FD_READ
);
465 if (ignoreErrno(errno
)) {
466 Comm::SetSelect(fd
, COMM_SELECT_READ
, diskHandleRead
, ctrl_dat
, 0);
467 PROF_stop(diskHandleRead
);
471 debugs(50, DBG_IMPORTANT
, "diskHandleRead: FD " << fd
<< ": " << xstrerror());
474 } else if (len
== 0) {
478 if (cbdataReferenceValid(ctrl_dat
->client_data
))
479 ctrl_dat
->handler(fd
, ctrl_dat
->buf
, len
, rc
, ctrl_dat
->client_data
);
481 cbdataReferenceDone(ctrl_dat
->client_data
);
483 memFree(ctrl_dat
, MEM_DREAD_CTRL
);
485 PROF_stop(diskHandleRead
);
488 /* start read operation */
489 /* buffer must be allocated from the caller.
490 * It must have at least req_len space in there.
491 * call handler when a reading is complete. */
493 file_read(int fd
, char *buf
, int req_len
, off_t offset
, DRCB
* handler
, void *client_data
)
495 dread_ctrl
*ctrl_dat
;
496 PROF_start(file_read
);
498 ctrl_dat
= (dread_ctrl
*)memAllocate(MEM_DREAD_CTRL
);
500 ctrl_dat
->offset
= offset
;
501 ctrl_dat
->req_len
= req_len
;
503 ctrl_dat
->end_of_file
= 0;
504 ctrl_dat
->handler
= handler
;
505 ctrl_dat
->client_data
= cbdataReference(client_data
);
506 diskHandleRead(fd
, ctrl_dat
);
507 PROF_stop(file_read
);
511 safeunlink(const char *s
, int quiet
)
513 ++ statCounter
.syscalls
.disk
.unlinks
;
515 if (unlink(s
) < 0 && !quiet
)
516 debugs(50, DBG_IMPORTANT
, "safeunlink: Couldn't delete " << s
<< ": " << xstrerror());
520 * Same as rename(2) but complains if something goes wrong;
521 * the caller is responsible for handing and explaining the
522 * consequences of errors.
525 xrename(const char *from
, const char *to
)
527 debugs(21, 2, "xrename: renaming " << from
<< " to " << to
);
528 #if _SQUID_OS2_ || _SQUID_WINDOWS_
532 if (0 == rename(from
, to
))
535 debugs(21, errno
== ENOENT
? 2 : 1, "xrename: Cannot rename " << from
<< " to " << to
<< ": " << xstrerror());