]>
Commit | Line | Data |
---|---|---|
595c7973 | 1 | /* |
262a0e14 | 2 | * $Id$ |
595c7973 | 3 | * |
1b52df9a | 4 | * DEBUG: section 43 Windows AIOPS |
595c7973 | 5 | * AUTHOR: Stewart Forster <slf@connect.com.au> |
1b52df9a | 6 | * AUTHOR: Robert Collins <robertc@squid-cache.org> |
7 | * AUTHOR: Guido Serassio <serassio@squid-cache.org> | |
595c7973 | 8 | * |
9 | * SQUID Web Proxy Cache http://www.squid-cache.org/ | |
10 | * ---------------------------------------------------------- | |
11 | * | |
12 | * Squid is the result of efforts by numerous individuals from | |
13 | * the Internet community; see the CONTRIBUTORS file for full | |
14 | * details. Many organizations have provided support for Squid's | |
15 | * development; see the SPONSORS file for full details. Squid is | |
16 | * Copyrighted (C) 2001 by the Regents of the University of | |
17 | * California; see the COPYRIGHT file for full details. Squid | |
18 | * incorporates software developed and/or copyrighted by other | |
19 | * sources; see the CREDITS file for full details. | |
20 | * | |
21 | * This program is free software; you can redistribute it and/or modify | |
22 | * it under the terms of the GNU General Public License as published by | |
23 | * the Free Software Foundation; either version 2 of the License, or | |
24 | * (at your option) any later version. | |
26ac0430 | 25 | * |
595c7973 | 26 | * This program is distributed in the hope that it will be useful, |
27 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
28 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
29 | * GNU General Public License for more details. | |
26ac0430 | 30 | * |
595c7973 | 31 | * You should have received a copy of the GNU General Public License |
32 | * along with this program; if not, write to the Free Software | |
33 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
34 | * | |
35 | */ | |
36 | ||
37 | #include "squid.h" | |
38 | #include "squid_windows.h" | |
489520a9 | 39 | #include "CommIO.h" |
595c7973 | 40 | #include "DiskThreads.h" |
489520a9 AJ |
41 | #include "SquidTime.h" |
42 | #include "Store.h" | |
595c7973 | 43 | |
44 | #include <stdio.h> | |
595c7973 | 45 | #include <sys/stat.h> |
46 | #include <fcntl.h> | |
47 | #include <errno.h> | |
48 | #include <dirent.h> | |
49 | #include <signal.h> | |
595c7973 | 50 | |
51 | #define RIDICULOUS_LENGTH 4096 | |
52 | ||
53 | enum _squidaio_thread_status { | |
54 | _THREAD_STARTING = 0, | |
55 | _THREAD_WAITING, | |
56 | _THREAD_BUSY, | |
57 | _THREAD_FAILED, | |
58 | _THREAD_DONE | |
59 | }; | |
60 | typedef enum _squidaio_thread_status squidaio_thread_status; | |
61 | ||
26ac0430 | 62 | typedef struct squidaio_request_t { |
595c7973 | 63 | |
64 | struct squidaio_request_t *next; | |
65 | squidaio_request_type request_type; | |
66 | int cancelled; | |
67 | char *path; | |
68 | int oflag; | |
69 | mode_t mode; | |
70 | int fd; | |
71 | char *bufferp; | |
72 | char *tmpbufp; | |
ee139403 | 73 | size_t buflen; |
595c7973 | 74 | off_t offset; |
75 | int whence; | |
76 | int ret; | |
77 | int err; | |
78 | ||
79 | struct stat *tmpstatp; | |
80 | ||
81 | struct stat *statp; | |
82 | squidaio_result_t *resultp; | |
2fadd50d | 83 | } squidaio_request_t; |
595c7973 | 84 | |
26ac0430 | 85 | typedef struct squidaio_request_queue_t { |
595c7973 | 86 | HANDLE mutex; |
87 | HANDLE cond; /* See Event objects */ | |
88 | squidaio_request_t *volatile head; | |
89 | squidaio_request_t *volatile *volatile tailp; | |
90 | unsigned long requests; | |
91 | unsigned long blocked; /* main failed to lock the queue */ | |
2fadd50d | 92 | } squidaio_request_queue_t; |
595c7973 | 93 | |
94 | typedef struct squidaio_thread_t squidaio_thread_t; | |
95 | ||
26ac0430 | 96 | struct squidaio_thread_t { |
595c7973 | 97 | squidaio_thread_t *next; |
98 | HANDLE thread; | |
99 | DWORD dwThreadId; /* thread ID */ | |
100 | squidaio_thread_status status; | |
101 | ||
102 | struct squidaio_request_t *current_req; | |
103 | unsigned long requests; | |
104 | int volatile exit; | |
105 | }; | |
106 | ||
107 | static void squidaio_queue_request(squidaio_request_t *); | |
108 | static void squidaio_cleanup_request(squidaio_request_t *); | |
109 | static DWORD WINAPI squidaio_thread_loop( LPVOID lpParam ); | |
110 | static void squidaio_do_open(squidaio_request_t *); | |
111 | static void squidaio_do_read(squidaio_request_t *); | |
112 | static void squidaio_do_write(squidaio_request_t *); | |
113 | static void squidaio_do_close(squidaio_request_t *); | |
114 | static void squidaio_do_stat(squidaio_request_t *); | |
595c7973 | 115 | static void squidaio_do_unlink(squidaio_request_t *); |
595c7973 | 116 | #if AIO_OPENDIR |
117 | static void *squidaio_do_opendir(squidaio_request_t *); | |
118 | #endif | |
119 | static void squidaio_debug(squidaio_request_t *); | |
120 | static void squidaio_poll_queues(void); | |
121 | ||
122 | static squidaio_thread_t *threads = NULL; | |
123 | static int squidaio_initialised = 0; | |
124 | ||
125 | ||
126 | #define AIO_LARGE_BUFS 16384 | |
127 | #define AIO_MEDIUM_BUFS AIO_LARGE_BUFS >> 1 | |
128 | #define AIO_SMALL_BUFS AIO_LARGE_BUFS >> 2 | |
129 | #define AIO_TINY_BUFS AIO_LARGE_BUFS >> 3 | |
130 | #define AIO_MICRO_BUFS 128 | |
131 | ||
132 | static MemAllocator *squidaio_large_bufs = NULL; /* 16K */ | |
133 | static MemAllocator *squidaio_medium_bufs = NULL; /* 8K */ | |
134 | static MemAllocator *squidaio_small_bufs = NULL; /* 4K */ | |
135 | static MemAllocator *squidaio_tiny_bufs = NULL; /* 2K */ | |
136 | static MemAllocator *squidaio_micro_bufs = NULL; /* 128K */ | |
137 | ||
138 | static int request_queue_len = 0; | |
139 | static MemAllocator *squidaio_request_pool = NULL; | |
140 | static MemAllocator *squidaio_thread_pool = NULL; | |
141 | static squidaio_request_queue_t request_queue; | |
142 | ||
26ac0430 | 143 | static struct { |
595c7973 | 144 | squidaio_request_t *head, **tailp; |
145 | } | |
146 | ||
147 | request_queue2 = { | |
148 | ||
26ac0430 AJ |
149 | NULL, &request_queue2.head |
150 | }; | |
595c7973 | 151 | static squidaio_request_queue_t done_queue; |
152 | ||
26ac0430 | 153 | static struct { |
595c7973 | 154 | squidaio_request_t *head, **tailp; |
155 | } | |
156 | ||
157 | done_requests = { | |
158 | ||
26ac0430 AJ |
159 | NULL, &done_requests.head |
160 | }; | |
595c7973 | 161 | |
162 | static HANDLE main_thread; | |
163 | ||
164 | static MemAllocator * | |
165 | squidaio_get_pool(int size) | |
166 | { | |
167 | if (size <= AIO_LARGE_BUFS) { | |
168 | if (size <= AIO_MICRO_BUFS) | |
169 | return squidaio_micro_bufs; | |
170 | else if (size <= AIO_TINY_BUFS) | |
171 | return squidaio_tiny_bufs; | |
172 | else if (size <= AIO_SMALL_BUFS) | |
173 | return squidaio_small_bufs; | |
174 | else if (size <= AIO_MEDIUM_BUFS) | |
175 | return squidaio_medium_bufs; | |
176 | else | |
177 | return squidaio_large_bufs; | |
178 | } | |
179 | ||
180 | return NULL; | |
181 | } | |
182 | ||
183 | void * | |
184 | squidaio_xmalloc(int size) | |
185 | { | |
186 | void *p; | |
187 | MemAllocator *pool; | |
188 | ||
189 | if ((pool = squidaio_get_pool(size)) != NULL) { | |
190 | p = pool->alloc(); | |
191 | } else | |
192 | p = xmalloc(size); | |
193 | ||
194 | return p; | |
195 | } | |
196 | ||
197 | static char * | |
198 | squidaio_xstrdup(const char *str) | |
199 | { | |
200 | char *p; | |
201 | int len = strlen(str) + 1; | |
202 | ||
203 | p = (char *)squidaio_xmalloc(len); | |
204 | strncpy(p, str, len); | |
205 | ||
206 | return p; | |
207 | } | |
208 | ||
209 | void | |
210 | squidaio_xfree(void *p, int size) | |
211 | { | |
212 | MemAllocator *pool; | |
213 | ||
214 | if ((pool = squidaio_get_pool(size)) != NULL) { | |
215 | pool->free(p); | |
216 | } else | |
217 | xfree(p); | |
218 | } | |
219 | ||
220 | static void | |
221 | squidaio_xstrfree(char *str) | |
222 | { | |
223 | MemAllocator *pool; | |
224 | int len = strlen(str) + 1; | |
225 | ||
226 | if ((pool = squidaio_get_pool(len)) != NULL) { | |
227 | pool->free(str); | |
228 | } else | |
229 | xfree(str); | |
230 | } | |
231 | ||
232 | void | |
233 | squidaio_init(void) | |
234 | { | |
235 | int i; | |
236 | squidaio_thread_t *threadp; | |
237 | ||
238 | if (squidaio_initialised) | |
239 | return; | |
240 | ||
241 | if (!DuplicateHandle(GetCurrentProcess(), /* pseudo handle, don't close */ | |
242 | GetCurrentThread(), /* pseudo handle to copy */ | |
243 | GetCurrentProcess(), /* pseudo handle, don't close */ | |
244 | &main_thread, | |
245 | 0, /* required access */ | |
246 | FALSE, /* child process's don't inherit the handle */ | |
247 | DUPLICATE_SAME_ACCESS)) { | |
248 | /* spit errors */ | |
249 | fatal("Couldn't get current thread handle"); | |
250 | } | |
251 | ||
252 | /* Initialize request queue */ | |
253 | if ((request_queue.mutex = CreateMutex(NULL, /* no inheritance */ | |
254 | FALSE, /* start unowned (as per mutex_init) */ | |
255 | NULL) /* no name */ | |
256 | ) == NULL) { | |
257 | fatal("Failed to create mutex"); | |
258 | } | |
259 | ||
260 | if ((request_queue.cond = CreateEvent(NULL, /* no inheritance */ | |
261 | FALSE, /* auto signal reset - which I think is pthreads like ? */ | |
262 | FALSE, /* start non signaled */ | |
263 | NULL) /* no name */ | |
264 | ) == NULL) { | |
265 | fatal("Failed to create condition variable"); | |
266 | } | |
267 | ||
268 | request_queue.head = NULL; | |
269 | ||
270 | request_queue.tailp = &request_queue.head; | |
271 | ||
272 | request_queue.requests = 0; | |
273 | ||
274 | request_queue.blocked = 0; | |
275 | ||
276 | /* Initialize done queue */ | |
277 | ||
278 | if ((done_queue.mutex = CreateMutex(NULL, /* no inheritance */ | |
279 | FALSE, /* start unowned (as per mutex_init) */ | |
280 | NULL) /* no name */ | |
281 | ) == NULL) { | |
282 | fatal("Failed to create mutex"); | |
283 | } | |
284 | ||
285 | if ((done_queue.cond = CreateEvent(NULL, /* no inheritance */ | |
286 | TRUE, /* manually signaled - which I think is pthreads like ? */ | |
287 | FALSE, /* start non signaled */ | |
288 | NULL) /* no name */ | |
289 | ) == NULL) { | |
290 | fatal("Failed to create condition variable"); | |
291 | } | |
292 | ||
293 | done_queue.head = NULL; | |
294 | ||
295 | done_queue.tailp = &done_queue.head; | |
296 | ||
297 | done_queue.requests = 0; | |
298 | ||
299 | done_queue.blocked = 0; | |
300 | ||
301 | CommIO::NotifyIOCompleted(); | |
302 | ||
303 | /* Create threads and get them to sit in their wait loop */ | |
304 | squidaio_thread_pool = memPoolCreate("aio_thread", sizeof(squidaio_thread_t)); | |
305 | ||
306 | assert(NUMTHREADS); | |
307 | ||
308 | for (i = 0; i < NUMTHREADS; i++) { | |
309 | threadp = (squidaio_thread_t *)squidaio_thread_pool->alloc(); | |
310 | threadp->status = _THREAD_STARTING; | |
311 | threadp->current_req = NULL; | |
312 | threadp->requests = 0; | |
313 | threadp->next = threads; | |
314 | threads = threadp; | |
315 | ||
316 | if ((threadp->thread = CreateThread(NULL, /* no security attributes */ | |
317 | 0, /* use default stack size */ | |
318 | squidaio_thread_loop, /* thread function */ | |
319 | threadp, /* argument to thread function */ | |
320 | 0, /* use default creation flags */ | |
321 | &(threadp->dwThreadId)) /* returns the thread identifier */ | |
322 | ) == NULL) { | |
323 | fprintf(stderr, "Thread creation failed\n"); | |
324 | threadp->status = _THREAD_FAILED; | |
325 | continue; | |
326 | } | |
327 | ||
328 | /* Set the new thread priority above parent process */ | |
329 | SetThreadPriority(threadp->thread,THREAD_PRIORITY_ABOVE_NORMAL); | |
330 | } | |
331 | ||
332 | /* Create request pool */ | |
333 | squidaio_request_pool = memPoolCreate("aio_request", sizeof(squidaio_request_t)); | |
334 | ||
335 | squidaio_large_bufs = memPoolCreate("squidaio_large_bufs", AIO_LARGE_BUFS); | |
336 | ||
337 | squidaio_medium_bufs = memPoolCreate("squidaio_medium_bufs", AIO_MEDIUM_BUFS); | |
338 | ||
339 | squidaio_small_bufs = memPoolCreate("squidaio_small_bufs", AIO_SMALL_BUFS); | |
340 | ||
341 | squidaio_tiny_bufs = memPoolCreate("squidaio_tiny_bufs", AIO_TINY_BUFS); | |
342 | ||
343 | squidaio_micro_bufs = memPoolCreate("squidaio_micro_bufs", AIO_MICRO_BUFS); | |
344 | ||
345 | squidaio_initialised = 1; | |
346 | } | |
347 | ||
348 | void | |
349 | squidaio_shutdown(void) | |
350 | { | |
351 | squidaio_thread_t *threadp; | |
352 | int i; | |
353 | HANDLE * hthreads; | |
354 | ||
355 | if (!squidaio_initialised) | |
356 | return; | |
357 | ||
358 | /* This is the same as in squidaio_sync */ | |
359 | do { | |
360 | squidaio_poll_queues(); | |
361 | } while (request_queue_len > 0); | |
362 | ||
363 | hthreads = (HANDLE *) xcalloc (NUMTHREADS, sizeof (HANDLE)); | |
364 | ||
365 | threadp = threads; | |
366 | ||
367 | for (i = 0; i < NUMTHREADS; i++) { | |
368 | threadp->exit = 1; | |
369 | hthreads[i] = threadp->thread; | |
370 | threadp = threadp->next; | |
371 | } | |
372 | ||
373 | ReleaseMutex(request_queue.mutex); | |
374 | ResetEvent(request_queue.cond); | |
375 | ReleaseMutex(done_queue.mutex); | |
376 | ResetEvent(done_queue.cond); | |
377 | Sleep(0); | |
378 | ||
379 | WaitForMultipleObjects(NUMTHREADS, hthreads, TRUE, 2000); | |
380 | ||
381 | for (i = 0; i < NUMTHREADS; i++) { | |
382 | CloseHandle(hthreads[i]); | |
383 | } | |
384 | ||
385 | CloseHandle(main_thread); | |
386 | CommIO::NotifyIOClose(); | |
387 | ||
388 | squidaio_initialised = 0; | |
389 | xfree(hthreads); | |
390 | } | |
391 | ||
392 | static DWORD WINAPI | |
393 | squidaio_thread_loop(LPVOID lpParam) | |
394 | { | |
395 | squidaio_thread_t *threadp = (squidaio_thread_t *)lpParam; | |
396 | squidaio_request_t *request; | |
397 | HANDLE cond; /* local copy of the event queue because win32 event handles | |
398 | * don't atomically release the mutex as cond variables do. */ | |
399 | ||
400 | /* lock the thread info */ | |
401 | ||
402 | if (WAIT_FAILED == WaitForSingleObject(request_queue.mutex, INFINITE)) { | |
403 | fatal("Can't get ownership of mutex\n"); | |
404 | } | |
405 | ||
406 | /* duplicate the handle */ | |
407 | if (!DuplicateHandle(GetCurrentProcess(), /* pseudo handle, don't close */ | |
408 | request_queue.cond, /* handle to copy */ | |
409 | GetCurrentProcess(), /* pseudo handle, don't close */ | |
410 | &cond, | |
411 | 0, /* required access */ | |
412 | FALSE, /* child process's don't inherit the handle */ | |
413 | DUPLICATE_SAME_ACCESS)) | |
414 | fatal("Can't duplicate mutex handle\n"); | |
415 | ||
416 | if (!ReleaseMutex(request_queue.mutex)) { | |
417 | CloseHandle(cond); | |
418 | fatal("Can't release mutex\n"); | |
419 | } | |
420 | ||
421 | Sleep(0); | |
422 | ||
423 | while (1) { | |
424 | DWORD rv; | |
425 | threadp->current_req = request = NULL; | |
426 | request = NULL; | |
427 | /* Get a request to process */ | |
428 | threadp->status = _THREAD_WAITING; | |
429 | ||
430 | if (threadp->exit) { | |
431 | CloseHandle(request_queue.mutex); | |
432 | CloseHandle(cond); | |
433 | return 0; | |
434 | } | |
435 | ||
436 | rv = WaitForSingleObject(request_queue.mutex, INFINITE); | |
437 | ||
438 | if (rv == WAIT_FAILED) { | |
439 | CloseHandle(cond); | |
440 | return 1; | |
441 | } | |
442 | ||
443 | while (!request_queue.head) { | |
444 | if (!ReleaseMutex(request_queue.mutex)) { | |
445 | CloseHandle(cond); | |
446 | threadp->status = _THREAD_FAILED; | |
447 | return 1; | |
448 | } | |
449 | ||
450 | Sleep(0); | |
451 | rv = WaitForSingleObject(cond, INFINITE); | |
452 | ||
453 | if (rv == WAIT_FAILED) { | |
454 | CloseHandle(cond); | |
455 | return 1; | |
456 | } | |
457 | ||
458 | rv = WaitForSingleObject(request_queue.mutex, INFINITE); | |
459 | ||
460 | if (rv == WAIT_FAILED) { | |
461 | CloseHandle(cond); | |
462 | return 1; | |
463 | } | |
464 | } | |
465 | ||
466 | request = request_queue.head; | |
467 | ||
468 | if (request) | |
469 | request_queue.head = request->next; | |
470 | ||
471 | if (!request_queue.head) | |
472 | request_queue.tailp = &request_queue.head; | |
473 | ||
474 | if (!ReleaseMutex(request_queue.mutex)) { | |
475 | CloseHandle(cond); | |
476 | return 1; | |
477 | } | |
478 | ||
479 | Sleep(0); | |
480 | ||
481 | /* process the request */ | |
482 | threadp->status = _THREAD_BUSY; | |
483 | ||
484 | request->next = NULL; | |
485 | ||
486 | threadp->current_req = request; | |
487 | ||
488 | errno = 0; | |
489 | ||
490 | if (!request->cancelled) { | |
491 | switch (request->request_type) { | |
492 | ||
493 | case _AIO_OP_OPEN: | |
494 | squidaio_do_open(request); | |
495 | break; | |
496 | ||
497 | case _AIO_OP_READ: | |
498 | squidaio_do_read(request); | |
499 | break; | |
500 | ||
501 | case _AIO_OP_WRITE: | |
502 | squidaio_do_write(request); | |
503 | break; | |
504 | ||
505 | case _AIO_OP_CLOSE: | |
506 | squidaio_do_close(request); | |
507 | break; | |
508 | ||
595c7973 | 509 | case _AIO_OP_UNLINK: |
510 | squidaio_do_unlink(request); | |
511 | break; | |
512 | ||
595c7973 | 513 | #if AIO_OPENDIR /* Opendir not implemented yet */ |
514 | ||
515 | case _AIO_OP_OPENDIR: | |
516 | squidaio_do_opendir(request); | |
517 | break; | |
518 | #endif | |
519 | ||
520 | case _AIO_OP_STAT: | |
521 | squidaio_do_stat(request); | |
522 | break; | |
523 | ||
524 | default: | |
525 | request->ret = -1; | |
526 | request->err = EINVAL; | |
527 | break; | |
528 | } | |
529 | } else { /* cancelled */ | |
530 | request->ret = -1; | |
531 | request->err = EINTR; | |
532 | } | |
533 | ||
534 | threadp->status = _THREAD_DONE; | |
535 | /* put the request in the done queue */ | |
536 | rv = WaitForSingleObject(done_queue.mutex, INFINITE); | |
537 | ||
538 | if (rv == WAIT_FAILED) { | |
539 | CloseHandle(cond); | |
540 | return 1; | |
541 | } | |
542 | ||
543 | *done_queue.tailp = request; | |
544 | done_queue.tailp = &request->next; | |
545 | ||
546 | if (!ReleaseMutex(done_queue.mutex)) { | |
547 | CloseHandle(cond); | |
548 | return 1; | |
549 | } | |
550 | ||
551 | CommIO::NotifyIOCompleted(); | |
552 | Sleep(0); | |
553 | threadp->requests++; | |
554 | } /* while forever */ | |
555 | ||
556 | CloseHandle(cond); | |
557 | ||
558 | return 0; | |
559 | } /* squidaio_thread_loop */ | |
560 | ||
561 | static void | |
562 | squidaio_queue_request(squidaio_request_t * request) | |
563 | { | |
564 | static int high_start = 0; | |
bf8fe701 | 565 | debugs(43, 9, "squidaio_queue_request: " << request << " type=" << request->request_type << " result=" << request->resultp); |
595c7973 | 566 | /* Mark it as not executed (failing result, no error) */ |
567 | request->ret = -1; | |
568 | request->err = 0; | |
569 | /* Internal housekeeping */ | |
570 | request_queue_len += 1; | |
571 | request->resultp->_data = request; | |
572 | /* Play some tricks with the request_queue2 queue */ | |
573 | request->next = NULL; | |
574 | ||
575 | if (WaitForSingleObject(request_queue.mutex, 0) == WAIT_OBJECT_0) { | |
576 | if (request_queue2.head) { | |
577 | /* Grab blocked requests */ | |
578 | *request_queue.tailp = request_queue2.head; | |
579 | request_queue.tailp = request_queue2.tailp; | |
580 | } | |
581 | ||
582 | /* Enqueue request */ | |
583 | *request_queue.tailp = request; | |
584 | ||
585 | request_queue.tailp = &request->next; | |
586 | ||
587 | if (!SetEvent(request_queue.cond)) | |
588 | fatal("Couldn't push queue"); | |
589 | ||
590 | if (!ReleaseMutex(request_queue.mutex)) { | |
591 | /* unexpected error */ | |
592 | fatal("Couldn't push queue"); | |
593 | } | |
594 | ||
595 | Sleep(0); | |
596 | ||
597 | if (request_queue2.head) { | |
598 | /* Clear queue of blocked requests */ | |
599 | request_queue2.head = NULL; | |
600 | request_queue2.tailp = &request_queue2.head; | |
601 | } | |
602 | } else { | |
603 | /* Oops, the request queue is blocked, use request_queue2 */ | |
604 | *request_queue2.tailp = request; | |
605 | request_queue2.tailp = &request->next; | |
606 | } | |
607 | ||
608 | if (request_queue2.head) { | |
609 | static int filter = 0; | |
610 | static int filter_limit = 8; | |
611 | ||
612 | if (++filter >= filter_limit) { | |
613 | filter_limit += filter; | |
614 | filter = 0; | |
bf8fe701 | 615 | debugs(43, 1, "squidaio_queue_request: WARNING - Queue congestion"); |
595c7973 | 616 | } |
617 | } | |
618 | ||
619 | /* Warn if out of threads */ | |
620 | if (request_queue_len > MAGIC1) { | |
621 | static int last_warn = 0; | |
622 | static int queue_high, queue_low; | |
623 | ||
624 | if (high_start == 0) { | |
625 | high_start = (int)squid_curtime; | |
626 | queue_high = request_queue_len; | |
627 | queue_low = request_queue_len; | |
628 | } | |
629 | ||
630 | if (request_queue_len > queue_high) | |
631 | queue_high = request_queue_len; | |
632 | ||
633 | if (request_queue_len < queue_low) | |
634 | queue_low = request_queue_len; | |
635 | ||
636 | if (squid_curtime >= (last_warn + 15) && | |
637 | squid_curtime >= (high_start + 5)) { | |
bf8fe701 | 638 | debugs(43, 1, "squidaio_queue_request: WARNING - Disk I/O overloading"); |
595c7973 | 639 | |
640 | if (squid_curtime >= (high_start + 15)) | |
bf8fe701 | 641 | debugs(43, 1, "squidaio_queue_request: Queue Length: current=" << |
642 | request_queue_len << ", high=" << queue_high << | |
643 | ", low=" << queue_low << ", duration=" << | |
644 | (long int) (squid_curtime - high_start)); | |
595c7973 | 645 | |
646 | last_warn = (int)squid_curtime; | |
647 | } | |
648 | } else { | |
649 | high_start = 0; | |
650 | } | |
651 | ||
652 | /* Warn if seriously overloaded */ | |
653 | if (request_queue_len > RIDICULOUS_LENGTH) { | |
bf8fe701 | 654 | debugs(43, 0, "squidaio_queue_request: Async request queue growing uncontrollably!"); |
655 | debugs(43, 0, "squidaio_queue_request: Syncing pending I/O operations.. (blocking)"); | |
595c7973 | 656 | squidaio_sync(); |
bf8fe701 | 657 | debugs(43, 0, "squidaio_queue_request: Synced"); |
595c7973 | 658 | } |
659 | } /* squidaio_queue_request */ | |
660 | ||
661 | static void | |
662 | squidaio_cleanup_request(squidaio_request_t * requestp) | |
663 | { | |
664 | squidaio_result_t *resultp = requestp->resultp; | |
665 | int cancelled = requestp->cancelled; | |
666 | ||
667 | /* Free allocated structures and copy data back to user space if the */ | |
668 | /* request hasn't been cancelled */ | |
669 | ||
670 | switch (requestp->request_type) { | |
671 | ||
672 | case _AIO_OP_STAT: | |
673 | ||
674 | if (!cancelled && requestp->ret == 0) | |
41d00cd3 | 675 | memcpy(requestp->statp, requestp->tmpstatp, sizeof(struct stat)); |
595c7973 | 676 | |
677 | squidaio_xfree(requestp->tmpstatp, sizeof(struct stat)); | |
678 | ||
679 | squidaio_xstrfree(requestp->path); | |
680 | ||
681 | break; | |
682 | ||
683 | case _AIO_OP_OPEN: | |
684 | if (cancelled && requestp->ret >= 0) | |
685 | /* The open() was cancelled but completed */ | |
686 | close(requestp->ret); | |
687 | ||
688 | squidaio_xstrfree(requestp->path); | |
689 | ||
690 | break; | |
691 | ||
692 | case _AIO_OP_CLOSE: | |
693 | if (cancelled && requestp->ret < 0) | |
694 | /* The close() was cancelled and never got executed */ | |
695 | close(requestp->fd); | |
696 | ||
697 | break; | |
698 | ||
699 | case _AIO_OP_UNLINK: | |
700 | ||
595c7973 | 701 | case _AIO_OP_OPENDIR: |
702 | squidaio_xstrfree(requestp->path); | |
703 | ||
704 | break; | |
705 | ||
706 | case _AIO_OP_READ: | |
707 | break; | |
708 | ||
709 | case _AIO_OP_WRITE: | |
710 | break; | |
711 | ||
712 | default: | |
713 | break; | |
714 | } | |
715 | ||
716 | if (resultp != NULL && !cancelled) { | |
717 | resultp->aio_return = requestp->ret; | |
718 | resultp->aio_errno = requestp->err; | |
719 | } | |
720 | ||
721 | squidaio_request_pool->free(requestp); | |
722 | } /* squidaio_cleanup_request */ | |
723 | ||
724 | ||
725 | int | |
726 | squidaio_cancel(squidaio_result_t * resultp) | |
727 | { | |
728 | squidaio_request_t *request = (squidaio_request_t *)resultp->_data; | |
729 | ||
730 | if (request && request->resultp == resultp) { | |
bf8fe701 | 731 | debugs(43, 9, "squidaio_cancel: " << request << " type=" << request->request_type << " result=" << request->resultp); |
595c7973 | 732 | request->cancelled = 1; |
733 | request->resultp = NULL; | |
734 | resultp->_data = NULL; | |
735 | resultp->result_type = _AIO_OP_NONE; | |
736 | return 0; | |
737 | } | |
738 | ||
739 | return 1; | |
740 | } /* squidaio_cancel */ | |
741 | ||
742 | ||
743 | int | |
744 | squidaio_open(const char *path, int oflag, mode_t mode, squidaio_result_t * resultp) | |
745 | { | |
746 | squidaio_init(); | |
747 | squidaio_request_t *requestp; | |
748 | ||
749 | requestp = (squidaio_request_t *)squidaio_request_pool->alloc(); | |
750 | ||
751 | requestp->path = (char *) squidaio_xstrdup(path); | |
752 | ||
753 | requestp->oflag = oflag; | |
754 | ||
755 | requestp->mode = mode; | |
756 | ||
757 | requestp->resultp = resultp; | |
758 | ||
759 | requestp->request_type = _AIO_OP_OPEN; | |
760 | ||
761 | requestp->cancelled = 0; | |
762 | ||
763 | resultp->result_type = _AIO_OP_OPEN; | |
764 | ||
765 | squidaio_queue_request(requestp); | |
766 | ||
767 | return 0; | |
768 | } | |
769 | ||
770 | ||
771 | static void | |
772 | squidaio_do_open(squidaio_request_t * requestp) | |
773 | { | |
774 | requestp->ret = open(requestp->path, requestp->oflag, requestp->mode); | |
775 | requestp->err = errno; | |
776 | } | |
777 | ||
778 | ||
779 | int | |
ee139403 | 780 | squidaio_read(int fd, char *bufp, size_t bufs, off_t offset, int whence, squidaio_result_t * resultp) |
595c7973 | 781 | { |
782 | squidaio_request_t *requestp; | |
783 | ||
784 | requestp = (squidaio_request_t *)squidaio_request_pool->alloc(); | |
785 | ||
786 | requestp->fd = fd; | |
787 | ||
788 | requestp->bufferp = bufp; | |
789 | ||
790 | requestp->buflen = bufs; | |
791 | ||
792 | requestp->offset = offset; | |
793 | ||
794 | requestp->whence = whence; | |
795 | ||
796 | requestp->resultp = resultp; | |
797 | ||
798 | requestp->request_type = _AIO_OP_READ; | |
799 | ||
800 | requestp->cancelled = 0; | |
801 | ||
802 | resultp->result_type = _AIO_OP_READ; | |
803 | ||
804 | squidaio_queue_request(requestp); | |
805 | ||
806 | return 0; | |
807 | } | |
808 | ||
809 | ||
810 | static void | |
811 | squidaio_do_read(squidaio_request_t * requestp) | |
812 | { | |
813 | lseek(requestp->fd, requestp->offset, requestp->whence); | |
814 | ||
815 | if (!ReadFile((HANDLE)_get_osfhandle(requestp->fd), requestp->bufferp, | |
816 | requestp->buflen, (LPDWORD)&requestp->ret, NULL)) { | |
817 | WIN32_maperror(GetLastError()); | |
818 | requestp->ret = -1; | |
819 | } | |
820 | ||
821 | requestp->err = errno; | |
822 | } | |
823 | ||
824 | ||
825 | int | |
ee139403 | 826 | squidaio_write(int fd, char *bufp, size_t bufs, off_t offset, int whence, squidaio_result_t * resultp) |
595c7973 | 827 | { |
828 | squidaio_request_t *requestp; | |
829 | ||
830 | requestp = (squidaio_request_t *)squidaio_request_pool->alloc(); | |
831 | ||
832 | requestp->fd = fd; | |
833 | ||
834 | requestp->bufferp = bufp; | |
835 | ||
836 | requestp->buflen = bufs; | |
837 | ||
838 | requestp->offset = offset; | |
839 | ||
840 | requestp->whence = whence; | |
841 | ||
842 | requestp->resultp = resultp; | |
843 | ||
844 | requestp->request_type = _AIO_OP_WRITE; | |
845 | ||
846 | requestp->cancelled = 0; | |
847 | ||
848 | resultp->result_type = _AIO_OP_WRITE; | |
849 | ||
850 | squidaio_queue_request(requestp); | |
851 | ||
852 | return 0; | |
853 | } | |
854 | ||
855 | ||
856 | static void | |
857 | squidaio_do_write(squidaio_request_t * requestp) | |
858 | { | |
859 | if (!WriteFile((HANDLE)_get_osfhandle(requestp->fd), requestp->bufferp, | |
860 | requestp->buflen, (LPDWORD)&requestp->ret, NULL)) { | |
861 | WIN32_maperror(GetLastError()); | |
862 | requestp->ret = -1; | |
863 | } | |
864 | ||
865 | requestp->err = errno; | |
866 | } | |
867 | ||
868 | ||
869 | int | |
870 | squidaio_close(int fd, squidaio_result_t * resultp) | |
871 | { | |
872 | squidaio_request_t *requestp; | |
873 | ||
874 | requestp = (squidaio_request_t *)squidaio_request_pool->alloc(); | |
875 | ||
876 | requestp->fd = fd; | |
877 | ||
878 | requestp->resultp = resultp; | |
879 | ||
880 | requestp->request_type = _AIO_OP_CLOSE; | |
881 | ||
882 | requestp->cancelled = 0; | |
883 | ||
884 | resultp->result_type = _AIO_OP_CLOSE; | |
885 | ||
886 | squidaio_queue_request(requestp); | |
887 | ||
888 | return 0; | |
889 | } | |
890 | ||
891 | ||
892 | static void | |
893 | squidaio_do_close(squidaio_request_t * requestp) | |
894 | { | |
26ac0430 | 895 | if ((requestp->ret = close(requestp->fd)) < 0) { |
bf8fe701 | 896 | debugs(43, 0, "squidaio_do_close: FD " << requestp->fd << ", errno " << errno); |
595c7973 | 897 | close(requestp->fd); |
898 | } | |
899 | ||
900 | requestp->err = errno; | |
901 | } | |
902 | ||
903 | ||
904 | int | |
905 | ||
906 | squidaio_stat(const char *path, struct stat *sb, squidaio_result_t * resultp) | |
907 | { | |
908 | squidaio_init(); | |
909 | squidaio_request_t *requestp; | |
910 | ||
911 | requestp = (squidaio_request_t *)squidaio_request_pool->alloc(); | |
912 | ||
913 | requestp->path = (char *) squidaio_xstrdup(path); | |
914 | ||
915 | requestp->statp = sb; | |
916 | ||
917 | requestp->tmpstatp = (struct stat *) squidaio_xmalloc(sizeof(struct stat)); | |
918 | ||
919 | requestp->resultp = resultp; | |
920 | ||
921 | requestp->request_type = _AIO_OP_STAT; | |
922 | ||
923 | requestp->cancelled = 0; | |
924 | ||
925 | resultp->result_type = _AIO_OP_STAT; | |
926 | ||
927 | squidaio_queue_request(requestp); | |
928 | ||
929 | return 0; | |
930 | } | |
931 | ||
932 | ||
933 | static void | |
934 | squidaio_do_stat(squidaio_request_t * requestp) | |
935 | { | |
936 | requestp->ret = stat(requestp->path, requestp->tmpstatp); | |
937 | requestp->err = errno; | |
938 | } | |
939 | ||
940 | ||
595c7973 | 941 | int |
942 | squidaio_unlink(const char *path, squidaio_result_t * resultp) | |
943 | { | |
944 | squidaio_init(); | |
945 | squidaio_request_t *requestp; | |
946 | ||
947 | requestp = (squidaio_request_t *)squidaio_request_pool->alloc(); | |
948 | ||
949 | requestp->path = squidaio_xstrdup(path); | |
950 | ||
951 | requestp->resultp = resultp; | |
952 | ||
953 | requestp->request_type = _AIO_OP_UNLINK; | |
954 | ||
955 | requestp->cancelled = 0; | |
956 | ||
957 | resultp->result_type = _AIO_OP_UNLINK; | |
958 | ||
959 | squidaio_queue_request(requestp); | |
960 | ||
961 | return 0; | |
962 | } | |
963 | ||
964 | ||
965 | static void | |
966 | squidaio_do_unlink(squidaio_request_t * requestp) | |
967 | { | |
968 | requestp->ret = unlink(requestp->path); | |
969 | requestp->err = errno; | |
970 | } | |
971 | ||
595c7973 | 972 | #if AIO_OPENDIR |
973 | /* XXX squidaio_opendir NOT implemented yet.. */ | |
974 | ||
975 | int | |
976 | squidaio_opendir(const char *path, squidaio_result_t * resultp) | |
977 | { | |
978 | squidaio_request_t *requestp; | |
979 | int len; | |
980 | ||
981 | requestp = squidaio_request_pool->alloc(); | |
982 | ||
983 | resultp->result_type = _AIO_OP_OPENDIR; | |
984 | ||
985 | return -1; | |
986 | } | |
987 | ||
988 | static void | |
989 | squidaio_do_opendir(squidaio_request_t * requestp) | |
990 | { | |
991 | /* NOT IMPLEMENTED */ | |
992 | } | |
993 | ||
994 | #endif | |
995 | ||
996 | static void | |
997 | squidaio_poll_queues(void) | |
998 | { | |
999 | /* kick "overflow" request queue */ | |
1000 | ||
1001 | if (request_queue2.head && | |
1002 | (WaitForSingleObject(request_queue.mutex, 0 )== WAIT_OBJECT_0)) { | |
1003 | *request_queue.tailp = request_queue2.head; | |
1004 | request_queue.tailp = request_queue2.tailp; | |
1005 | ||
1006 | if (!SetEvent(request_queue.cond)) | |
1007 | fatal("couldn't push queue\n"); | |
1008 | ||
1009 | if (!ReleaseMutex(request_queue.mutex)) { | |
1010 | /* unexpected error */ | |
1011 | } | |
1012 | ||
1013 | Sleep(0); | |
1014 | request_queue2.head = NULL; | |
1015 | request_queue2.tailp = &request_queue2.head; | |
1016 | } | |
1017 | ||
1018 | /* poll done queue */ | |
1019 | if (done_queue.head && | |
1020 | (WaitForSingleObject(done_queue.mutex, 0)==WAIT_OBJECT_0)) { | |
1021 | ||
1022 | struct squidaio_request_t *requests = done_queue.head; | |
1023 | done_queue.head = NULL; | |
1024 | done_queue.tailp = &done_queue.head; | |
1025 | ||
1026 | if (!ReleaseMutex(done_queue.mutex)) { | |
1027 | /* unexpected error */ | |
1028 | } | |
1029 | ||
1030 | Sleep(0); | |
1031 | *done_requests.tailp = requests; | |
1032 | request_queue_len -= 1; | |
1033 | ||
1034 | while (requests->next) { | |
1035 | requests = requests->next; | |
1036 | request_queue_len -= 1; | |
1037 | } | |
1038 | ||
1039 | done_requests.tailp = &requests->next; | |
1040 | } | |
1041 | } | |
1042 | ||
1043 | squidaio_result_t * | |
1044 | squidaio_poll_done(void) | |
1045 | { | |
1046 | squidaio_request_t *request; | |
1047 | squidaio_result_t *resultp; | |
1048 | int cancelled; | |
1049 | int polled = 0; | |
1050 | ||
1051 | AIO_REPOLL: | |
1052 | request = done_requests.head; | |
1053 | ||
1054 | if (request == NULL && !polled) { | |
1055 | CommIO::ResetNotifications(); | |
1056 | squidaio_poll_queues(); | |
1057 | polled = 1; | |
1058 | request = done_requests.head; | |
1059 | } | |
1060 | ||
1061 | if (!request) { | |
1062 | return NULL; | |
1063 | } | |
1064 | ||
bf8fe701 | 1065 | debugs(43, 9, "squidaio_poll_done: " << request << " type=" << request->request_type << " result=" << request->resultp); |
595c7973 | 1066 | done_requests.head = request->next; |
1067 | ||
1068 | if (!done_requests.head) | |
1069 | done_requests.tailp = &done_requests.head; | |
1070 | ||
1071 | resultp = request->resultp; | |
1072 | ||
1073 | cancelled = request->cancelled; | |
1074 | ||
1075 | squidaio_debug(request); | |
1076 | ||
bf8fe701 | 1077 | debugs(43, 5, "DONE: " << request->ret << " -> " << request->err); |
595c7973 | 1078 | |
1079 | squidaio_cleanup_request(request); | |
1080 | ||
1081 | if (cancelled) | |
1082 | goto AIO_REPOLL; | |
1083 | ||
1084 | return resultp; | |
1085 | } /* squidaio_poll_done */ | |
1086 | ||
1087 | int | |
1088 | squidaio_operations_pending(void) | |
1089 | { | |
1090 | return request_queue_len + (done_requests.head ? 1 : 0); | |
1091 | } | |
1092 | ||
1093 | int | |
1094 | squidaio_sync(void) | |
1095 | { | |
1096 | /* XXX This might take a while if the queue is large.. */ | |
1097 | ||
1098 | do { | |
1099 | squidaio_poll_queues(); | |
1100 | } while (request_queue_len > 0); | |
1101 | ||
1102 | return squidaio_operations_pending(); | |
1103 | } | |
1104 | ||
1105 | int | |
1106 | squidaio_get_queue_len(void) | |
1107 | { | |
1108 | return request_queue_len; | |
1109 | } | |
1110 | ||
1111 | static void | |
1112 | squidaio_debug(squidaio_request_t * request) | |
1113 | { | |
1114 | switch (request->request_type) { | |
1115 | ||
1116 | case _AIO_OP_OPEN: | |
bf8fe701 | 1117 | debugs(43, 5, "OPEN of " << request->path << " to FD " << request->ret); |
595c7973 | 1118 | break; |
1119 | ||
1120 | case _AIO_OP_READ: | |
bf8fe701 | 1121 | debugs(43, 5, "READ on fd: " << request->fd); |
595c7973 | 1122 | break; |
1123 | ||
1124 | case _AIO_OP_WRITE: | |
bf8fe701 | 1125 | debugs(43, 5, "WRITE on fd: " << request->fd); |
595c7973 | 1126 | break; |
1127 | ||
1128 | case _AIO_OP_CLOSE: | |
bf8fe701 | 1129 | debugs(43, 5, "CLOSE of fd: " << request->fd); |
595c7973 | 1130 | break; |
1131 | ||
1132 | case _AIO_OP_UNLINK: | |
bf8fe701 | 1133 | debugs(43, 5, "UNLINK of " << request->path); |
595c7973 | 1134 | break; |
1135 | ||
595c7973 | 1136 | default: |
1137 | break; | |
1138 | } | |
1139 | } | |
1140 | ||
1141 | void | |
1142 | squidaio_stats(StoreEntry * sentry) | |
1143 | { | |
1144 | squidaio_thread_t *threadp; | |
1145 | int i; | |
1146 | ||
1147 | if (!squidaio_initialised) | |
1148 | return; | |
1149 | ||
1150 | storeAppendPrintf(sentry, "\n\nThreads Status:\n"); | |
1151 | ||
1152 | storeAppendPrintf(sentry, "#\tID\t# Requests\n"); | |
1153 | ||
1154 | threadp = threads; | |
1155 | ||
1156 | for (i = 0; i < NUMTHREADS; i++) { | |
1157 | storeAppendPrintf(sentry, "%i\t0x%lx\t%ld\n", i + 1, threadp->dwThreadId, threadp->requests); | |
1158 | threadp = threadp->next; | |
1159 | } | |
1160 | } |