Squid Web Cache master
Loading...
Searching...
No Matches
UFSStoreState.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 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 Storage Manager UFS Interface */
10
11#include "squid.h"
12#include "base/IoManip.h"
13#include "DiskIO/DiskFile.h"
15#include "DiskIO/ReadRequest.h"
16#include "DiskIO/WriteRequest.h"
17#include "Generic.h"
18#include "SquidConfig.h"
19#include "Store.h"
20#include "store/Disk.h"
21#include "UFSStoreState.h"
22#include "UFSStrategy.h"
23
25
26void
28{
29 if (opening) {
30 opening = false;
31 debugs(79, 3, "opening: dirno " << swap_dirn <<
32 ", fileno " << asHex(swap_filen).minDigits(8) <<
33 " status " << theFile->error());
34
35 assert (FILE_MODE(mode) == O_RDONLY);
36 openDone();
37
38 return;
39 }
40
41 if (creating) {
42 creating = false;
43 debugs(79, 3, "creating: dirno " << swap_dirn <<
44 ", fileno " << asHex(swap_filen).minDigits(8) <<
45 " status " << theFile->error());
46
47 openDone();
48
49 return;
50 }
51
52 assert (!(closing ||opening));
53 debugs(79, 3, "error: dirno " << swap_dirn <<
54 ", fileno " << asHex(swap_filen).minDigits(8) <<
55 " status " << theFile->error());
56
57 /* Ok, notification past open means an error has occurred */
58 assert (theFile->error());
59 tryClosing();
60}
61
62void
64{
65 if (closing)
66 debugs(0, DBG_CRITICAL, "already closing in openDone()!?");
67
68 if (theFile->error()) {
69 tryClosing();
70 return;
71 }
72
73 if (FILE_MODE(mode) == O_WRONLY) {
74 drainWriteQueue();
75
76 } else if ((FILE_MODE(mode) == O_RDONLY) && !closing) {
77 if (kickReadQueue())
78 return;
79 }
80
81 if (flags.try_closing)
82 tryClosing();
83
84 debugs(79, 3, "UFSStoreState::openDone: exiting");
85}
86
87void
89{
90 assert (closing);
91 debugs(79, 3, "dirno " << swap_dirn <<
92 ", fileno " << asHex(swap_filen).minDigits(8) <<
93 " status " << theFile->error());
94
95 if (theFile->error()) {
96 debugs(79,3, "theFile->error() ret " << theFile->error());
97 doCloseCallback(DISK_ERROR);
98 } else {
99 doCloseCallback(DISK_OK);
100 }
101
102 closing = false;
103}
104
105/*
106 * DPW 2006-05-24
107 * This close function is called by the higher layer when it has finished
108 * reading/writing everything, or otherwise wants to close the swap
109 * file. In the case of writing and using aufs storage, close() might
110 * be called before any/all data is written, and even before the open
111 * callback occurs. Thus, we use our tryClosing() method, which knows
112 * when it is safe to actually signal the lower layer for closing.
113 */
114void
116{
117 // TODO: De-duplicate position printing Fs::Ufs code and fix upperCase() inconsistency.
118 debugs(79, 3, "dirno " << swap_dirn <<
119 ", fileno " << asHex(swap_filen).upperCase().minDigits(8));
120 tryClosing(); // UFS does not distinguish different closure types
121}
122
123void
124Fs::Ufs::UFSStoreState::read_(char *buf, size_t size, off_t aOffset, STRCB * aCallback, void *aCallbackData)
125{
126 assert(read.callback == nullptr);
127 assert(read.callback_data == nullptr);
128 assert(!reading);
129 assert(!closing);
130 assert (aCallback);
131
132 if (!theFile->canRead()) {
133 debugs(79, 3, "queueing read because theFile can't read");
134 assert(opening);
135 pending_reads.emplace(buf, size, aOffset, aCallback, aCallbackData);
136 return;
137 }
138
139 read.callback = aCallback;
140 read.callback_data = cbdataReference(aCallbackData);
141 debugs(79, 3, "dirno " << swap_dirn <<
142 ", fileno " << asHex(swap_filen).minDigits(8));
143 offset_ = aOffset;
144 read_buf = buf;
145 reading = true;
146 theFile->read(new ReadRequest(buf,aOffset,size));
147}
148
149/*
150 * DPW 2006-05-24
151 * This, the public write interface, places the write request at the end
152 * of the pending_writes queue to ensure correct ordering of writes.
153 * We could optimize things a little if there are no other pending
154 * writes and just do the write directly. But for now we'll keep the
155 * code simpler and always go through the pending_writes queue.
156 */
157bool
158Fs::Ufs::UFSStoreState::write(char const *buf, size_t size, off_t aOffset, FREE * free_func)
159{
160 debugs(79, 3, "dirno " << swap_dirn <<
161 ", fileno " << asHex(swap_filen).minDigits(8));
162
163 if (theFile->error()) {
164 debugs(79, DBG_IMPORTANT, "ERROR: avoid write on theFile with error");
165 debugs(79, DBG_IMPORTANT, "calling free_func for " << (void*) buf);
166 free_func((void*)buf);
167 return false;
168 }
169
170 const Store::Disk &dir = *INDEXSD(swap_dirn);
171 if (static_cast<uint64_t>(offset_ + size) > static_cast<uint64_t>(dir.maxObjectSize())) {
172 debugs(79, 2, "accepted unknown-size entry grew too big: " <<
173 (offset_ + size) << " > " << dir.maxObjectSize());
174 free_func((void*)buf);
175 tryClosing();
176 return false;
177 }
178
179 debugs(79, 3, (void*)this << " queueing write of size " << size);
180 pending_writes.emplace(buf, size, aOffset, free_func);
181 drainWriteQueue();
182 return true;
183}
184
185/*
186 * DPW 2006-05-24
187 * This, the private write method, calls the lower level write for the
188 * first write request in the pending_writes queue. doWrite() is only
189 * called by drainWriteQueue().
190 */
191void
193{
194 debugs(79, 3, (void*)this);
195
196 assert(theFile->canWrite());
197
198 if (pending_writes.empty()) {
199 debugs(79, 3, (void*)this << " write queue is empty");
200 return;
201 }
202
203 auto &q = pending_writes.front();
204
205 if (theFile->error()) {
206 debugs(79, DBG_IMPORTANT, "ERROR: " << MYNAME << "avoid write on theFile with error");
207 pending_writes.pop();
208 return;
209 }
210
211 /*
212 * DPW 2006-05-24
213 * UFSStoreState has a 'writing' flag that we used to set here,
214 * but it wasn't really used anywhere. In fact, some lower
215 * layers such as DISKD allow multiple outstanding writes, which
216 * makes the boolean writing flag meaningless. We would need
217 * a counter to keep track of writes going out and write callbacks
218 * coming in. For now let's just not use the writing flag at
219 * all.
220 */
221 debugs(79, 3, (void*)this << " calling theFile->write(" << q.size << ")");
222
223 theFile->write(new WriteRequest(q.buf, q.offset, q.size, q.free_func));
224 q.buf = nullptr; // prevent buf deletion on pop, its used by the above object
225 pending_writes.pop();
226}
227
228void
230{
231 assert (result.getRaw());
232 reading = false;
233 debugs(79, 3, "dirno " << swap_dirn <<
234 ", fileno " << asHex(swap_filen).minDigits(8) <<
235 " len " << len);
236
237 if (len > 0)
238 offset_ += len;
239
240 STRCB *callback_ = read.callback;
241
242 assert(callback_);
243
244 read.callback = nullptr;
245
246 void *cbdata;
247
248 /* A note:
249 * diskd IO queues closes via the diskd queue. So close callbacks
250 * occur strictly after reads and writes.
251 * ufs doesn't queue, it simply completes, so close callbacks occur
252 * strictly after reads and writes.
253 * aufs performs closes synchronously, so close events must be managed
254 * to force strict ordering.
255 * The below does this:
256 * closing is set when theFile->close() has been called, and close only triggers
257 * when no io's are pending.
258 * writeCompleted likewise.
259 */
260 if (!closing && cbdataReferenceValidDone(read.callback_data, &cbdata)) {
261 if (len > 0 && read_buf != buf)
262 memcpy(read_buf, buf, len);
263
264 callback_(cbdata, read_buf, len, this);
265 }
266
267 if (flags.try_closing || (theFile != nullptr && theFile->error()) )
268 tryClosing();
269}
270
271void
273{
274 debugs(79, 3, "dirno " << swap_dirn <<
275 ", fileno " << asHex(swap_filen).upperCase().minDigits(8) <<
276 ", len " << len);
277 /*
278 * DPW 2006-05-24
279 * See doWrites() for why we don't update UFSStoreState::writing
280 * here anymore.
281 */
282
283 offset_ += len;
284
285 if (theFile->error()) {
286 debugs(79,2, " detected an error, will try to close");
287 tryClosing();
288 }
289
290 /*
291 * HNO 2009-07-24
292 * Kick any pending write/close operations alive
293 */
294 drainWriteQueue();
295}
296
297void
299{
300 debugs(79, 3, "storeUfsIOCallback: errflag=" << errflag);
301 /*
302 * DPW 2006-05-24
303 * When we signal the higher layer with this callback, it might unlock
304 * the StoreEntry and its associated data. We must "free" any queued
305 * I/Os (especially writes) now, otherwise the StoreEntry's mem_node's
306 * will have their write_pending flag set, and we'll get an assertion.
307 */
308 freePending();
309 STIOCB *theCallback = callback;
310 callback = nullptr;
311
312 void *cbdata;
313
314 if (cbdataReferenceValidDone(callback_data, &cbdata) && theCallback)
315 theCallback(cbdata, errflag, this);
316
317 /*
318 * We are finished with theFile since the lower layer signalled
319 * us that the file has been closed. This must be the last line,
320 * as theFile may be the only object holding us in memory.
321 */
322 theFile = nullptr; // refcounted
323}
324
325/* ============= THE REAL UFS CODE ================ */
326
327Fs::Ufs::UFSStoreState::UFSStoreState(SwapDir * SD, StoreEntry * anEntry, STIOCB * cbIo, void *data) :
328 StoreIOState(cbIo, data),
329 opening(false),
330 creating(false),
331 closing(false),
332 reading(false),
333 writing(false),
334 read_buf(nullptr)
335{
336 // StoreIOState inherited members
337 swap_filen = anEntry->swap_filen;
338 swap_dirn = SD->index;
339 e = anEntry;
340
341 // our flags
342 flags.write_draining = false;
343 flags.try_closing = false;
344}
345
347{
348 assert(pending_reads.empty());
349 assert(pending_writes.empty());
350}
351
352void
354{
355 while (!pending_reads.empty())
356 pending_reads.pop();
357 debugs(79, 3, "freed pending reads");
358
359 while (!pending_writes.empty())
360 pending_writes.pop();
361 debugs(79, 3, "freed pending writes");
362}
363
364bool
366{
367 if (pending_reads.empty())
368 return false;
369
370 auto &q = pending_reads.front();
371
372 debugs(79, 3, "reading queued request of " << q.size << " bytes");
373
374 bool result = true;
375 void *cbdata;
376 if (cbdataReferenceValidDone(q.callback_data, &cbdata)) {
377 read_(q.buf, q.size, q.offset, q.callback, cbdata);
378 } else {
379 debugs(79, 2, "this=" << (void*)this << " cbdataReferenceValidDone returned false." <<
380 " closing: " << closing << " flags.try_closing: " << flags.try_closing);
381 result = false;
382 }
383
384 pending_reads.pop(); // erase the front object
385 return result;
386}
387
388/*
389 * DPW 2006-05-24
390 * drainWriteQueue() is a loop around doWrite().
391 */
392void
394{
395 /*
396 * DPW 2007-04-12
397 * We might find that flags.write_draining is already set
398 * because schemes like diskd can process I/O acks
399 * before sending another I/O request. e.g. the following
400 * sequence of events: open request -> write request ->
401 * drainWriteQueue() -> queue full -> callbacks -> openDone() ->
402 * drainWriteQueue().
403 */
404 if (flags.write_draining)
405 return;
406
407 if (!theFile || !theFile->canWrite())
408 return;
409
410 flags.write_draining = true;
411
412 while (!pending_writes.empty())
413 doWrite();
414
415 flags.write_draining = false;
416
417 if (flags.try_closing)
418 tryClosing();
419}
420
421/*
422 * DPW 2006-05-24
423 * This blows. DiskThreadsDiskFile::close() won't actually do the close
424 * if ioInProgress() is true. So we have to check it here. Maybe someday
425 * DiskThreadsDiskFile::close() will be modified to have a return value,
426 * or will remember to do the close for us.
427 */
428void
430{
431 debugs(79,3, this << " tryClosing()" <<
432 " closing = " << closing <<
433 " flags.try_closing = " << flags.try_closing <<
434 " ioInProgress = " << theFile->ioInProgress());
435
436 if (theFile->ioInProgress()) {
437 debugs(79, 3, this <<
438 " won't close since ioInProgress is true, bailing");
439 flags.try_closing = true;
440 return;
441 }
442
443 closing = true;
444 flags.try_closing = false;
445 theFile->close();
446}
447
AsHex< Integer > asHex(const Integer n)
a helper to ease AsHex object creation
Definition IoManip.h:169
int size
Definition ModDevPoll.cc:70
#define INDEXSD(i)
Definition SquidConfig.h:74
#define assert(EX)
Definition assert.h:17
#define cbdataReference(var)
Definition cbdata.h:348
#define cbdataReferenceValidDone(var, ptr)
Definition cbdata.h:239
#define CBDATA_NAMESPACED_CLASS_INIT(namespace, type)
Definition cbdata.h:333
virtual bool error() const =0
virtual void doCloseCallback(int errflag)
void closeCompleted() override
void read_(char *buf, size_t size, off_t offset, STRCB *callback, void *callback_data) override
void writeCompleted(int errflag, size_t len, RefCount< WriteRequest >) override
bool write(char const *buf, size_t size, off_t offset, FREE *free_func) override
UFSStoreState(SwapDir *SD, StoreEntry *anEntry, STIOCB *callback_, void *callback_data_)
struct Fs::Ufs::UFSStoreState::@51 flags
void close(int how) override
finish or abort swapping per CloseHow
void readCompleted(const char *buf, int len, int errflag, RefCount< ReadRequest >) override
RefCount< DiskFile > theFile
void ioCompletedNotification() override
C * getRaw() const
Definition RefCount.h:89
sfileno swap_filen
unique ID inside a cache_dir for swapped out entries; -1 for others
Definition Store.h:235
sfileno swap_filen
StoreEntry * e
sdirno swap_dirn
manages a single cache_dir
Definition Disk.h:22
int index
Definition Disk.h:103
int64_t maxObjectSize() const override
the maximum size of a storable object; -1 if unlimited
Definition Disk.cc:103
#define MYNAME
Definition Stream.h:219
#define DBG_IMPORTANT
Definition Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define DBG_CRITICAL
Definition Stream.h:37
#define FILE_MODE(x)
Definition defines.h:143
#define DISK_ERROR
Definition defines.h:28
#define DISK_OK
Definition defines.h:27
void FREE(void *)
Definition forward.h:37