UFSStoreState.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 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"
14 #include "DiskIO/DiskIOStrategy.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 
26 void
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 
62 void
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 
87 void
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  */
114 void
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 
123 void
124 Fs::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  */
157 bool
158 Fs::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  */
191 void
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 
228 void
229 Fs::Ufs::UFSStoreState::readCompleted(const char *buf, int len, int, RefCount<ReadRequest> result)
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 
271 void
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 
297 void
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 
327 Fs::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 
352 void
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 
364 bool
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  */
392 void
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  */
428 void
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 
#define INDEXSD(i)
Definition: SquidConfig.h:74
#define DBG_CRITICAL
Definition: Stream.h:37
#define cbdataReferenceValidDone(var, ptr)
Definition: cbdata.h:239
void FREE(void *)
Definition: forward.h:37
manages a single cache_dir
Definition: Disk.h:21
void read_(char *buf, size_t size, off_t offset, STRCB *callback, void *callback_data) override
void readCompleted(const char *buf, int len, int errflag, RefCount< ReadRequest >) override
void close(int how) override
finish or abort swapping per CloseHow
Definition: cbdata.cc:37
int64_t maxObjectSize() const override
the maximum size of a storable object; -1 if unlimited
Definition: Disk.cc:103
RefCount< DiskFile > theFile
Definition: UFSStoreState.h:36
C * getRaw() const
Definition: RefCount.h:89
#define cbdataReference(var)
Definition: cbdata.h:348
#define DISK_OK
Definition: defines.h:27
struct Fs::Ufs::UFSStoreState::@58 flags
void closeCompleted() override
int size
Definition: ModDevPoll.cc:69
AsHex< Integer > asHex(const Integer n)
a helper to ease AsHex object creation
Definition: IoManip.h:169
virtual bool error() const =0
#define assert(EX)
Definition: assert.h:17
CBDATA_NAMESPACED_CLASS_INIT(Fs::Ufs, UFSStoreState)
#define FILE_MODE(x)
Definition: defines.h:143
UFSStoreState(SwapDir *SD, StoreEntry *anEntry, STIOCB *callback_, void *callback_data_)
#define DBG_IMPORTANT
Definition: Stream.h:38
StoreEntry * e
Definition: StoreIOState.h:73
int index
Definition: Disk.h:103
#define MYNAME
Definition: Stream.h:219
sdirno swap_dirn
Definition: StoreIOState.h:71
#define DISK_ERROR
Definition: defines.h:28
sfileno swap_filen
Definition: StoreIOState.h:72
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
void ioCompletedNotification() override
bool write(char const *buf, size_t size, off_t offset, FREE *free_func) override
void writeCompleted(int errflag, size_t len, RefCount< WriteRequest >) override
virtual void doCloseCallback(int errflag)
sfileno swap_filen
unique ID inside a cache_dir for swapped out entries; -1 for others
Definition: Store.h:235

 

Introduction

Documentation

Support

Miscellaneous