~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Nginx/core/ngx_open_file_cache.c

Version: ~ [ nginx-0.6.26 ] ~ [ nginx-0.5.35 ] ~ [ nginx-0.5.20 ] ~ [ nginx-0.5.19 ] ~

  1 
  2 /*
  3  * Copyright (C) Igor Sysoev
  4  */
  5 
  6 
  7 #include <ngx_config.h>
  8 #include <ngx_core.h>
  9 #include <ngx_event.h>
 10 
 11 
 12 /*
 13  * open file cache caches
 14  *    open file handles with stat() info;
 15  *    directories stat() info;
 16  *    files and directories errors: not found, access denied, etc.
 17  */
 18 
 19 
 20 static void ngx_open_file_cache_cleanup(void *data);
 21 static ngx_int_t ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of,
 22     ngx_log_t *log);
 23 static void ngx_open_file_add_event(ngx_open_file_cache_t *cache,
 24     ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log);
 25 static void ngx_open_file_cleanup(void *data);
 26 static void ngx_close_cached_file(ngx_open_file_cache_t *cache,
 27     ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log);
 28 static void ngx_open_file_del_event(ngx_cached_open_file_t *file);
 29 static void ngx_expire_old_cached_files(ngx_open_file_cache_t *cache,
 30     ngx_uint_t n, ngx_log_t *log);
 31 static void ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
 32     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
 33 static ngx_cached_open_file_t *
 34     ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name,
 35     uint32_t hash);
 36 static void ngx_open_file_cache_remove(ngx_event_t *ev);
 37 
 38 
 39 ngx_open_file_cache_t *
 40 ngx_open_file_cache_init(ngx_pool_t *pool, ngx_uint_t max, time_t inactive)
 41 {
 42     ngx_pool_cleanup_t     *cln;
 43     ngx_open_file_cache_t  *cache;
 44 
 45     cache = ngx_palloc(pool, sizeof(ngx_open_file_cache_t));
 46     if (cache == NULL) {
 47         return NULL;
 48     }
 49 
 50     ngx_rbtree_init(&cache->rbtree, &cache->sentinel,
 51                     ngx_open_file_cache_rbtree_insert_value);
 52 
 53     ngx_queue_init(&cache->expire_queue);
 54 
 55     cache->current = 0;
 56     cache->max = max;
 57     cache->inactive = inactive;
 58 
 59     cln = ngx_pool_cleanup_add(pool, 0);
 60     if (cln == NULL) {
 61         return NULL;
 62     }
 63 
 64     cln->handler = ngx_open_file_cache_cleanup;
 65     cln->data = cache;
 66 
 67     return cache;
 68 }
 69 
 70 
 71 static void
 72 ngx_open_file_cache_cleanup(void *data)
 73 {
 74     ngx_open_file_cache_t  *cache = data;
 75 
 76     ngx_queue_t             *q;
 77     ngx_cached_open_file_t  *file;
 78 
 79     ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
 80                    "open file cache cleanup");
 81 
 82     for ( ;; ) {
 83 
 84         if (ngx_queue_empty(&cache->expire_queue)) {
 85             break;
 86         }
 87 
 88         q = ngx_queue_last(&cache->expire_queue);
 89 
 90         file = ngx_queue_data(q, ngx_cached_open_file_t, queue);
 91 
 92         ngx_queue_remove(q);
 93 
 94         ngx_rbtree_delete(&cache->rbtree, &file->node);
 95 
 96         cache->current--;
 97 
 98         ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
 99                        "delete cached open file: %s", file->name);
100 
101         if (!file->err && !file->is_dir) {
102             file->close = 1;
103             file->count = 0;
104             ngx_close_cached_file(cache, file, 0, ngx_cycle->log);
105 
106         } else {
107             ngx_free(file->name);
108             ngx_free(file);
109         }
110     }
111 
112     if (cache->current) {
113         ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
114                       "%d items still leave in open file cache",
115                       cache->current);
116     }
117 
118     if (cache->rbtree.root != cache->rbtree.sentinel) {
119         ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
120                       "rbtree still is not empty in open file cache");
121 
122     }
123 }
124 
125 
126 ngx_int_t
127 ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name,
128     ngx_open_file_info_t *of, ngx_pool_t *pool)
129 {
130     time_t                          now;
131     uint32_t                        hash;
132     ngx_int_t                       rc;
133     ngx_pool_cleanup_t             *cln;
134     ngx_cached_open_file_t         *file;
135     ngx_pool_cleanup_file_t        *clnf;
136     ngx_open_file_cache_cleanup_t  *ofcln;
137 
138     of->err = 0;
139 
140     if (cache == NULL) {
141 
142         cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));
143         if (cln == NULL) {
144             return NGX_ERROR;
145         }
146 
147         rc = ngx_open_and_stat_file(name->data, of, pool->log);
148 
149         if (rc == NGX_OK && !of->is_dir) {
150             cln->handler = ngx_pool_cleanup_file;
151             clnf = cln->data;
152 
153             clnf->fd = of->fd;
154             clnf->name = name->data;
155             clnf->log = pool->log;
156         }
157 
158         return rc;
159     }
160 
161     cln = ngx_pool_cleanup_add(pool, sizeof(ngx_open_file_cache_cleanup_t));
162     if (cln == NULL) {
163         return NGX_ERROR;
164     }
165 
166     now = ngx_time();
167 
168     hash = ngx_crc32_long(name->data, name->len);
169 
170     file = ngx_open_file_lookup(cache, name, hash);
171 
172     if (file) {
173 
174         file->uses++;
175 
176         ngx_queue_remove(&file->queue);
177 
178         if (file->fd == NGX_INVALID_FILE && file->err == 0 && !file->is_dir) {
179 
180             /* file was not used often enough to keep open */
181 
182             rc = ngx_open_and_stat_file(name->data, of, pool->log);
183 
184             if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
185                 goto failed;
186             }
187 
188             goto add_event;
189         }
190 
191         if ((file->event && file->use_event)
192             || (file->event == NULL && now - file->created < of->valid))
193         {
194             if (file->err == 0) {
195 
196                 of->fd = file->fd;
197                 of->uniq = file->uniq;
198                 of->mtime = file->mtime;
199                 of->size = file->size;
200 
201                 of->is_dir = file->is_dir;
202                 of->is_file = file->is_file;
203                 of->is_link = file->is_link;
204                 of->is_exec = file->is_exec;
205 
206                 if (!file->is_dir) {
207                     file->count++;
208                     ngx_open_file_add_event(cache, file, of, pool->log);
209                 }
210 
211             } else {
212                 of->err = file->err;
213             }
214 
215             goto found;
216         }
217 
218         ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0,
219                        "retest open file: %s, fd:%d, c:%d, e:%d",
220                        file->name, file->fd, file->count, file->err);
221 
222         if (file->is_dir) {
223 
224             /*
225              * chances that directory became file are very small
226              * so test_dir flag allows to use a single syscall
227              * in ngx_file_info() instead of three syscalls
228              */
229 
230             of->test_dir = 1;
231         }
232 
233         rc = ngx_open_and_stat_file(name->data, of, pool->log);
234 
235         if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
236             goto failed;
237         }
238 
239         if (of->is_dir) {
240 
241             if (file->is_dir || file->err) {
242                 goto update;
243             }
244 
245             /* file became directory */
246 
247         } else if (of->err == 0) {  /* file */
248 
249             if (file->is_dir || file->err) {
250                 goto add_event;
251             }
252 
253             if (of->uniq == file->uniq
254                 && of->mtime == file->mtime
255                 && of->size == file->size)
256             {
257                 if (ngx_close_file(of->fd) == NGX_FILE_ERROR) {
258                     ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
259                                   ngx_close_file_n " \"%s\" failed",
260                                   name->data);
261                 }
262 
263                 of->fd = file->fd;
264                 file->count++;
265 
266                 if (file->event) {
267                     file->use_event = 1;
268                     goto renew;
269                 }
270 
271                 ngx_open_file_add_event(cache, file, of, pool->log);
272 
273                 goto renew;
274             }
275 
276             /* file was changed */
277 
278         } else { /* error to cache */
279 
280             if (file->err || file->is_dir) {
281                 goto update;
282             }
283 
284             /* file was removed, etc. */
285         }
286 
287         if (file->count == 0) {
288 
289             ngx_open_file_del_event(file);
290 
291             if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
292                 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
293                               ngx_close_file_n " \"%s\" failed",
294                               name->data);
295             }
296 
297             goto add_event;
298         }
299 
300         ngx_rbtree_delete(&cache->rbtree, &file->node);
301 
302         cache->current--;
303 
304         file->close = 1;
305 
306         goto create;
307     }
308 
309     /* not found */
310 
311     file = NULL;
312 
313     rc = ngx_open_and_stat_file(name->data, of, pool->log);
314 
315     if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
316         goto failed;
317     }
318 
319 create:
320 
321     if (cache->current >= cache->max) {
322         ngx_expire_old_cached_files(cache, 0, pool->log);
323     }
324 
325     file = ngx_alloc(sizeof(ngx_cached_open_file_t), pool->log);
326 
327     if (file == NULL) {
328         goto failed;
329     }
330 
331     file->name = ngx_alloc(name->len + 1, pool->log);
332 
333     if (file->name == NULL) {
334         ngx_free(file);
335         file = NULL;
336         goto failed;
337     }
338 
339     ngx_cpystrn(file->name, name->data, name->len + 1);
340 
341     file->node.key = hash;
342 
343     ngx_rbtree_insert(&cache->rbtree, &file->node);
344 
345     cache->current++;
346 
347     file->uses = 1;
348     file->count = 0;
349     file->use_event = 0;
350     file->event = NULL;
351 
352 add_event:
353 
354     ngx_open_file_add_event(cache, file, of, pool->log);
355 
356 update:
357 
358     file->fd = of->fd;
359     file->err = of->err;
360 
361     if (of->err == 0) {
362         file->uniq = of->uniq;
363         file->mtime = of->mtime;
364         file->size = of->size;
365 
366         file->close = 0;
367 
368         file->is_dir = of->is_dir;
369         file->is_file = of->is_file;
370         file->is_link = of->is_link;
371         file->is_exec = of->is_exec;
372 
373         if (!of->is_dir) {
374             file->count++;
375         }
376     }
377 
378 renew:
379 
380     file->created = now;
381 
382 found:
383 
384     file->accessed = now;
385 
386     ngx_queue_insert_head(&cache->expire_queue, &file->queue);
387 
388     ngx_log_debug5(NGX_LOG_DEBUG_CORE, pool->log, 0,
389                    "cached open file: %s, fd:%d, c:%d, e:%d, u:%d",
390                    file->name, file->fd, file->count, file->err, file->uses);
391 
392     if (of->err == 0) {
393 
394         if (!of->is_dir) {
395             cln->handler = ngx_open_file_cleanup;
396             ofcln = cln->data;
397 
398             ofcln->cache = cache;
399             ofcln->file = file;
400             ofcln->min_uses = of->min_uses;
401             ofcln->log = pool->log;
402         }
403 
404         return NGX_OK;
405     }
406 
407     return NGX_ERROR;
408 
409 failed:
410 
411     if (file && file->count == 0) {
412         ngx_rbtree_delete(&cache->rbtree, &file->node);
413 
414         cache->current--;
415 
416         if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
417             ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
418                           ngx_close_file_n " \"%s\" failed", file->name);
419         }
420 
421         ngx_free(file->name);
422         ngx_free(file);
423     }
424 
425     if (of->fd != NGX_INVALID_FILE) {
426         if (ngx_close_file(of->fd) == NGX_FILE_ERROR) {
427             ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
428                           ngx_close_file_n " \"%s\" failed", name->data);
429         }
430     }
431 
432     return NGX_ERROR;
433 }
434 
435 
436 static ngx_int_t
437 ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, ngx_log_t *log)
438 {
439     ngx_fd_t         fd;
440     ngx_file_info_t  fi;
441 
442     of->fd = NGX_INVALID_FILE;
443 
444     if (of->test_dir) {
445 
446         if (ngx_file_info(name, &fi) == -1) {
447             of->err = ngx_errno;
448 
449             return NGX_ERROR;
450         }
451 
452         of->uniq = ngx_file_uniq(&fi);
453         of->mtime = ngx_file_mtime(&fi);
454         of->size = ngx_file_size(&fi);
455         of->is_dir = ngx_is_dir(&fi);
456         of->is_file = ngx_is_file(&fi);
457         of->is_link = ngx_is_link(&fi);
458         of->is_exec = ngx_is_exec(&fi);
459 
460         if (of->is_dir) {
461             return NGX_OK;
462         }
463     }
464 
465     fd = ngx_open_file(name, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
466 
467     if (fd == NGX_INVALID_FILE) {
468         of->err = ngx_errno;
469         return NGX_ERROR;
470     }
471 
472     if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
473         ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,
474                       ngx_fd_info_n " \"%s\" failed", name);
475 
476         if (ngx_close_file(fd) == NGX_FILE_ERROR) {
477             ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
478                           ngx_close_file_n " \"%s\" failed", name);
479         }
480 
481         return NGX_ERROR;
482     }
483 
484     if (ngx_is_dir(&fi)) {
485         if (ngx_close_file(fd) == NGX_FILE_ERROR) {
486             ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
487                           ngx_close_file_n " \"%s\" failed", name);
488         }
489 
490         fd = NGX_INVALID_FILE;
491     }
492 
493     of->fd = fd;
494     of->uniq = ngx_file_uniq(&fi);
495     of->mtime = ngx_file_mtime(&fi);
496     of->size = ngx_file_size(&fi);
497     of->is_dir = ngx_is_dir(&fi);
498     of->is_file = ngx_is_file(&fi);
499     of->is_link = ngx_is_link(&fi);
500     of->is_exec = ngx_is_exec(&fi);
501 
502     return NGX_OK;
503 }
504 
505 
506 /*
507  * we ignore any possible event setting error and
508  * fallback to usual periodic file retests
509  */
510 
511 static void
512 ngx_open_file_add_event(ngx_open_file_cache_t *cache,
513     ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log)
514 {
515     ngx_open_file_cache_event_t  *fev;
516 
517     if (!(ngx_event_flags & NGX_USE_VNODE_EVENT)
518         || !of->events
519         || file->event
520         || of->fd == NGX_INVALID_FILE
521         || file->uses < of->min_uses)
522     {
523         return;
524     }
525 
526     file->event = ngx_calloc(sizeof(ngx_event_t), log);
527     if (file->event== NULL) {
528         return;
529     }
530 
531     fev = ngx_alloc(sizeof(ngx_open_file_cache_event_t), log);
532     if (fev == NULL) {
533         ngx_free(file->event);
534         file->event = NULL;
535         return;
536     }
537 
538     fev->fd = of->fd;
539     fev->file = file;
540     fev->cache = cache;
541 
542     file->event->handler = ngx_open_file_cache_remove;
543     file->event->data = fev;
544 
545     /*
546      * although vnode event may be called while ngx_cycle->poll
547      * destruction, however, cleanup procedures are run before any
548      * memory freeing and events will be canceled.
549      */
550 
551     file->event->log = ngx_cycle->log;
552 
553     if (ngx_add_event(file->event, NGX_VNODE_EVENT, NGX_ONESHOT_EVENT)
554         != NGX_OK)
555     {
556         ngx_free(file->event->data);
557         ngx_free(file->event);
558         file->event = NULL;
559         return;
560     }
561 
562     /*
563      * we do not file->use_event here because there may be a race
564      * condition between opening file and adding event, so we rely
565      * upon event notification only after first file revalidation
566      */
567 
568     return;
569 }
570 
571 
572 static void
573 ngx_open_file_cleanup(void *data)
574 {
575     ngx_open_file_cache_cleanup_t  *c = data;
576 
577     c->file->count--;
578 
579     ngx_close_cached_file(c->cache, c->file, c->min_uses, c->log);
580 
581     /* drop one or two expired open files */
582     ngx_expire_old_cached_files(c->cache, 1, c->log);
583 }
584 
585 
586 static void
587 ngx_close_cached_file(ngx_open_file_cache_t *cache,
588     ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log)
589 {
590     ngx_log_debug5(NGX_LOG_DEBUG_CORE, log, 0,
591                    "close cached open file: %s, fd:%d, c:%d, u:%d, %d",
592                    file->name, file->fd, file->count, file->uses, file->close);
593 
594     if (!file->close) {
595 
596         file->accessed = ngx_time();
597 
598         ngx_queue_remove(&file->queue);
599 
600         ngx_queue_insert_head(&cache->expire_queue, &file->queue);
601 
602         if (file->uses >= min_uses || file->count) {
603             return;
604         }
605     }
606 
607     ngx_open_file_del_event(file);
608 
609     if (file->count) {
610         return;
611     }
612 
613     if (file->fd != NGX_INVALID_FILE) {
614 
615         if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
616             ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
617                           ngx_close_file_n " \"%s\" failed", file->name);
618         }
619 
620         file->fd = NGX_INVALID_FILE;
621     }
622 
623     if (!file->close) {
624         return;
625     }
626 
627     ngx_free(file->name);
628     ngx_free(file);
629 }
630 
631 
632 static void
633 ngx_open_file_del_event(ngx_cached_open_file_t *file)
634 {
635     if (file->event == NULL) {
636         return;
637     }
638 
639     (void) ngx_del_event(file->event, NGX_VNODE_EVENT,
640                          file->count ? NGX_FLUSH_EVENT : NGX_CLOSE_EVENT);
641 
642     ngx_free(file->event->data);
643     ngx_free(file->event);
644     file->event = NULL;
645     file->use_event = 0;
646 }
647 
648 
649 static void
650 ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, ngx_uint_t n,
651     ngx_log_t *log)
652 {
653     time_t                   now;
654     ngx_queue_t             *q;
655     ngx_cached_open_file_t  *file;
656 
657     now = ngx_time();
658 
659     /*
660      * n == 1 deletes one or two inactive files
661      * n == 0 deletes least recently used file by force
662      *        and one or two inactive files
663      */
664 
665     while (n < 3) {
666 
667         if (ngx_queue_empty(&cache->expire_queue)) {
668             return;
669         }
670 
671         q = ngx_queue_last(&cache->expire_queue);
672 
673         file = ngx_queue_data(q, ngx_cached_open_file_t, queue);
674 
675         if (n++ != 0 && now - file->accessed <= cache->inactive) {
676             return;
677         }
678 
679         ngx_queue_remove(q);
680 
681         ngx_rbtree_delete(&cache->rbtree, &file->node);
682 
683         cache->current--;
684 
685         ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0,
686                        "expire cached open file: %s", file->name);
687 
688         if (!file->err && !file->is_dir) {
689             file->close = 1;
690             ngx_close_cached_file(cache, file, 0, log);
691 
692         } else {
693             ngx_free(file->name);
694             ngx_free(file);
695         }
696     }
697 }
698 
699 
700 static void
701 ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
702     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
703 {
704     ngx_rbtree_node_t       **p;
705     ngx_cached_open_file_t    *file, *file_temp;
706 
707     for ( ;; ) {
708 
709         if (node->key < temp->key) {
710 
711             p = &temp->left;
712 
713         } else if (node->key > temp->key) {
714 
715             p = &temp->right;
716 
717         } else { /* node->key == temp->key */
718 
719             file = (ngx_cached_open_file_t *) node;
720             file_temp = (ngx_cached_open_file_t *) temp;
721 
722             p = (ngx_strcmp(file->name, file_temp->name) < 0)
723                     ? &temp->left : &temp->right;
724         }
725 
726         if (*p == sentinel) {
727             break;
728         }
729 
730         temp = *p;
731     }
732 
733     *p = node;
734     node->parent = temp;
735     node->left = sentinel;
736     node->right = sentinel;
737     ngx_rbt_red(node);
738 }
739 
740 
741 static ngx_cached_open_file_t *
742 ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name,
743     uint32_t hash)
744 {
745     ngx_int_t                rc;
746     ngx_rbtree_node_t       *node, *sentinel;
747     ngx_cached_open_file_t  *file;
748 
749     node = cache->rbtree.root;
750     sentinel = cache->rbtree.sentinel;
751 
752     while (node != sentinel) {
753 
754         if (hash < node->key) {
755             node = node->left;
756             continue;
757         }
758 
759         if (hash > node->key) {
760             node = node->right;
761             continue;
762         }
763 
764         /* hash == node->key */
765 
766         do {
767             file = (ngx_cached_open_file_t *) node;
768 
769             rc = ngx_strcmp(name->data, file->name);
770 
771             if (rc == 0) {
772                 return file;
773             }
774 
775             node = (rc < 0) ? node->left : node->right;
776 
777         } while (node != sentinel && hash == node->key);
778 
779         break;
780     }
781 
782     return NULL;
783 }
784 
785 
786 static void
787 ngx_open_file_cache_remove(ngx_event_t *ev)
788 {
789     ngx_cached_open_file_t       *file;
790     ngx_open_file_cache_event_t  *fev;
791 
792     fev = ev->data;
793     file = fev->file;
794 
795     ngx_queue_remove(&file->queue);
796 
797     ngx_rbtree_delete(&fev->cache->rbtree, &file->node);
798 
799     fev->cache->current--;
800 
801     /* NGX_ONESHOT_EVENT was already deleted */
802     file->event = NULL;
803 
804     file->close = 1;
805 
806     ngx_close_cached_file(fev->cache, file, 0, ev->log);
807 
808     /* free memory only when fev->cache and fev->file are already not needed */
809 
810     ngx_free(ev->data);
811     ngx_free(ev);
812 }
813 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.