Apache HTTPD
mod_socache_shmcb.c
Go to the documentation of this file.
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "httpd.h"
18#include "http_log.h"
19#include "http_request.h"
20#include "http_protocol.h"
21#include "http_config.h"
22#include "mod_status.h"
23
24#include "apr.h"
25#include "apr_strings.h"
26#include "apr_time.h"
27#include "apr_shm.h"
28#define APR_WANT_STRFUNC
29#include "apr_want.h"
30#include "apr_general.h"
31
32#if APR_HAVE_LIMITS_H
33#include <limits.h>
34#endif
35
36#include "ap_socache.h"
37
38/* XXX Unfortunately, there are still many unsigned ints in use here, so we
39 * XXX cannot allow more than UINT_MAX. Since some of the ints are exposed in
40 * XXX public interfaces, a simple search and replace is not enough.
41 * XXX It should be possible to extend that so that the total cache size can
42 * XXX be APR_SIZE_MAX and only the object size needs to be smaller than
43 * XXX UINT_MAX.
44 */
45#define SHMCB_MAX_SIZE (UINT_MAX<APR_SIZE_MAX ? UINT_MAX : APR_SIZE_MAX)
46
47#define DEFAULT_SHMCB_PREFIX "socache-shmcb-"
48
49#define DEFAULT_SHMCB_SUFFIX ".cache"
50
51#define ALIGNED_HEADER_SIZE APR_ALIGN_DEFAULT(sizeof(SHMCBHeader))
52#define ALIGNED_SUBCACHE_SIZE APR_ALIGN_DEFAULT(sizeof(SHMCBSubcache))
53#define ALIGNED_INDEX_SIZE APR_ALIGN_DEFAULT(sizeof(SHMCBIndex))
54
55/*
56 * Header structure - the start of the shared-mem segment
57 */
58typedef struct {
59 /* Stats for cache operations */
60 unsigned long stat_stores;
61 unsigned long stat_replaced;
62 unsigned long stat_expiries;
63 unsigned long stat_scrolled;
64 unsigned long stat_retrieves_hit;
65 unsigned long stat_retrieves_miss;
66 unsigned long stat_removes_hit;
67 unsigned long stat_removes_miss;
68 /* Number of subcaches */
69 unsigned int subcache_num;
70 /* How many indexes each subcache's queue has */
71 unsigned int index_num;
72 /* How large each subcache is, including the queue and data */
73 unsigned int subcache_size;
74 /* How far into each subcache the data area is (optimisation) */
76 /* How large the data area in each subcache is (optimisation) */
77 unsigned int subcache_data_size;
79
80/*
81 * Subcache structure - the start of each subcache, followed by
82 * indexes then data
83 */
84typedef struct {
85 /* The start position and length of the cyclic buffer of indexes */
86 unsigned int idx_pos, idx_used;
87 /* Same for the data area */
88 unsigned int data_pos, data_used;
90
91/*
92 * Index structure - each subcache has an array of these
93 */
94typedef struct {
95 /* absolute time this entry expires */
97 /* location within the subcache's data area */
98 unsigned int data_pos;
99 /* size (most logic ignores this, we keep it only to minimise memcpy) */
100 unsigned int data_used;
101 /* length of the used data which contains the id */
102 unsigned int id_len;
103 /* Used to mark explicitly-removed socache entries */
104 unsigned char removed;
105} SHMCBIndex;
106
109 const char *data_file;
113};
114
115/* The SHM data segment is of fixed size and stores data as follows.
116 *
117 * [ SHMCBHeader | Subcaches ]
118 *
119 * The SHMCBHeader header structure stores metadata concerning the
120 * cache and the contained subcaches.
121 *
122 * Subcaches is a hash table of header->subcache_num SHMCBSubcache
123 * structures. The hash table is indexed by SHMCB_MASK(id). Each
124 * SHMCBSubcache structure has a fixed size (header->subcache_size),
125 * which is determined at creation time, and looks like the following:
126 *
127 * [ SHMCBSubcache | Indexes | Data ]
128 *
129 * Each subcache is prefixed by the SHMCBSubcache structure.
130 *
131 * The subcache's "Data" segment is a single cyclic data buffer, of
132 * total size header->subcache_data_size; data inside is referenced
133 * using byte offsets. The offset marking the beginning of the cyclic
134 * buffer is subcache->data_pos; the buffer's length is
135 * subcache->data_used.
136 *
137 * "Indexes" is an array of header->index_num SHMCBIndex structures,
138 * which is used as a cyclic queue; subcache->idx_pos gives the array
139 * index of the first in use, subcache->idx_used gives the number in
140 * use. Both ->idx_* values have a range of [0, header->index_num)
141 *
142 * Each in-use SHMCBIndex structure represents a single cached object.
143 * The ID and data segment are stored consecutively in the subcache's
144 * cyclic data buffer. The "Data" segment can thus be seen to
145 * look like this, for example
146 *
147 * offset: [ 0 1 2 3 4 5 6 ...
148 * contents:[ ID1 Data1 ID2 Data2 ID3 ...
149 *
150 * where the corresponding indices would look like:
151 *
152 * idx1 = { data_pos = 0, data_used = 3, id_len = 1, ...}
153 * idx2 = { data_pos = 3, data_used = 3, id_len = 1, ...}
154 * ...
155 */
156
157/* This macro takes a pointer to the header and a zero-based index and returns
158 * a pointer to the corresponding subcache. */
159#define SHMCB_SUBCACHE(pHeader, num) \
160 (SHMCBSubcache *)(((unsigned char *)(pHeader)) + \
161 ALIGNED_HEADER_SIZE + \
162 (num) * ((pHeader)->subcache_size))
163
164/* This macro takes a pointer to the header and an id and returns a
165 * pointer to the corresponding subcache. */
166#define SHMCB_MASK(pHeader, id) \
167 SHMCB_SUBCACHE((pHeader), *(id) & ((pHeader)->subcache_num - 1))
168
169/* This macro takes the same params as the last, generating two outputs for use
170 * in ap_log_error(...). */
171#define SHMCB_MASK_DBG(pHeader, id) \
172 *(id), (*(id) & ((pHeader)->subcache_num - 1))
173
174/* This macro takes a pointer to a subcache and a zero-based index and returns
175 * a pointer to the corresponding SHMCBIndex. */
176#define SHMCB_INDEX(pSubcache, num) \
177 (SHMCBIndex *)(((unsigned char *)pSubcache) + \
178 ALIGNED_SUBCACHE_SIZE + \
179 (num) * ALIGNED_INDEX_SIZE)
180
181/* This macro takes a pointer to the header and a subcache and returns a
182 * pointer to the corresponding data area. */
183#define SHMCB_DATA(pHeader, pSubcache) \
184 ((unsigned char *)(pSubcache) + (pHeader)->subcache_data_offset)
185
186/*
187 * Cyclic functions - assists in "wrap-around"/modulo logic
188 */
189
190/* Addition modulo 'mod' */
191#define SHMCB_CYCLIC_INCREMENT(val,inc,mod) \
192 (((val) + (inc)) % (mod))
193
194/* Subtraction (or "distance between") modulo 'mod' */
195#define SHMCB_CYCLIC_SPACE(val1,val2,mod) \
196 ((val2) >= (val1) ? ((val2) - (val1)) : \
197 ((val2) + (mod) - (val1)))
198
199/* A "normal-to-cyclic" memcpy. */
200static void shmcb_cyclic_ntoc_memcpy(unsigned int buf_size, unsigned char *data,
201 unsigned int dest_offset, const unsigned char *src,
202 unsigned int src_len)
203{
205 /* It be copied all in one go */
207 else {
208 /* Copy the two splits */
212 }
213}
214
215/* A "cyclic-to-normal" memcpy. */
216static void shmcb_cyclic_cton_memcpy(unsigned int buf_size, unsigned char *dest,
217 const unsigned char *data, unsigned int src_offset,
218 unsigned int src_len)
219{
221 /* It be copied all in one go */
223 else {
224 /* Copy the two splits */
228 }
229}
230
231/* A memcmp against a cyclic data buffer. Compares SRC of length
232 * SRC_LEN against the contents of cyclic buffer DATA (which is of
233 * size BUF_SIZE), starting at offset DEST_OFFSET. Got that? Good. */
234static int shmcb_cyclic_memcmp(unsigned int buf_size, unsigned char *data,
235 unsigned int dest_offset,
236 const unsigned char *src,
237 unsigned int src_len)
238{
240 /* It be compared all in one go */
241 return memcmp(data + dest_offset, src, src_len);
242 else {
243 /* Compare the two splits */
244 int diff;
245
247 if (diff) {
248 return diff;
249 }
250 return memcmp(data, src + buf_size - dest_offset,
252 }
253}
254
255
256/* Prototypes for low-level subcache operations */
258 apr_time_t);
259/* Returns zero on success, non-zero on failure. */
260static int shmcb_subcache_store(server_rec *s, SHMCBHeader *header,
262 unsigned char *data, unsigned int data_len,
263 const unsigned char *id, unsigned int id_len,
264 apr_time_t expiry);
265/* Returns zero on success, non-zero on failure. */
267 const unsigned char *id, unsigned int idlen,
268 unsigned char *data, unsigned int *datalen);
269/* Returns zero on success, non-zero on failure. */
271 const unsigned char *, unsigned int);
272
273/* Returns result of the (iterator)() call, zero is success (continue) */
275 server_rec *s,
276 void *userctx,
277 SHMCBHeader *header,
279 ap_socache_iterator_t *iterator,
280 unsigned char **buf,
284
285/*
286 * High-Level "handlers" as per ssl_scache.c
287 * subcache internals are deferred to shmcb_subcache_*** functions lower down
288 */
289
291 const char *arg,
292 apr_pool_t *tmp, apr_pool_t *p)
293{
295 char *path, *cp, *cp2;
296
297 /* Allocate the context. */
298 *context = ctx = apr_pcalloc(p, sizeof *ctx);
299 ctx->pool = p;
300
301 ctx->shm_size = 1024*512; /* 512KB */
302
303 if (!arg || *arg == '\0') {
304 /* Use defaults. */
305 return NULL;
306 }
307
308 ctx->data_file = path = ap_server_root_relative(p, arg);
309
310 cp = strrchr(path, '(');
311 cp2 = path + strlen(path) - 1;
312 if (cp) {
313 char *endptr;
314 if (*cp2 != ')') {
315 return "Invalid argument: no closing parenthesis or cache size "
316 "missing after pathname with parenthesis";
317 }
318 *cp++ = '\0';
319 *cp2 = '\0';
320
321
322 ctx->shm_size = strtol(cp, &endptr, 10);
323 if (endptr != cp2) {
324 return "Invalid argument: cache size not numerical";
325 }
326
327 if (ctx->shm_size < 8192) {
328 return "Invalid argument: size has to be >= 8192 bytes";
329
330 }
331
332 if (ctx->shm_size >= SHMCB_MAX_SIZE) {
333 return apr_psprintf(tmp, "Invalid argument: size has "
334 "to be < %" APR_SIZE_T_FMT " bytes on this platform",
336 }
337 }
338 else if (cp2 >= path && *cp2 == ')') {
339 return "Invalid argument: no opening parenthesis";
340 }
341
342 return NULL;
343}
344
346{
348 if (ctx->shm) {
349 apr_shm_destroy(ctx->shm);
350 ctx->shm = NULL;
351 }
352 return APR_SUCCESS;
353}
354
356 const char *namespace,
357 const struct ap_socache_hints *hints,
359{
360 void *shm_segment;
362 apr_status_t rv;
363 SHMCBHeader *header;
364 unsigned int num_subcache, num_idx, loop;
365 apr_size_t avg_obj_size, avg_id_len;
366
367 /* Create shared memory segment */
368 if (ctx->data_file == NULL) {
369 const char *path = apr_pstrcat(p, DEFAULT_SHMCB_PREFIX, namespace,
371
372 ctx->data_file = ap_runtime_dir_relative(p, path);
373 }
374
375 /* Use anonymous shm by default, fall back on name-based. */
376 rv = apr_shm_create(&ctx->shm, ctx->shm_size, NULL, p);
377 if (APR_STATUS_IS_ENOTIMPL(rv)) {
378 /* If anon shm isn't supported, fail if no named file was
379 * configured successfully; the ap_server_root_relative call
380 * above will return NULL for invalid paths. */
381 if (ctx->data_file == NULL) {
383 "Could not use anonymous shm for '%s' cache",
384 namespace);
385 ctx->shm = NULL;
386 return APR_EINVAL;
387 }
388
389 /* For a name-based segment, remove it first in case of a
390 * previous unclean shutdown. */
391 apr_shm_remove(ctx->data_file, p);
392
393 rv = apr_shm_create(&ctx->shm, ctx->shm_size, ctx->data_file, p);
394 }
395
396 if (rv != APR_SUCCESS) {
398 "Could not allocate shared memory segment for shmcb "
399 "socache");
400 ctx->shm = NULL;
401 return rv;
402 }
405
408 if (shm_segsize < (5 * ALIGNED_HEADER_SIZE)) {
409 /* the segment is ridiculously small, bail out */
411 "shared memory segment too small");
412 return APR_ENOSPC;
413 }
415 "shmcb_init allocated %" APR_SIZE_T_FMT
416 " bytes of shared memory",
418 /* Discount the header */
420 /* Select index size based on average object size hints, if given. */
421 avg_obj_size = hints && hints->avg_obj_size ? hints->avg_obj_size : 150;
422 avg_id_len = hints && hints->avg_id_len ? hints->avg_id_len : 30;
423 num_idx = (shm_segsize) / (avg_obj_size + avg_id_len);
424 num_subcache = 256;
425 while ((num_idx / num_subcache) < (2 * num_subcache))
426 num_subcache /= 2;
429 "for %" APR_SIZE_T_FMT " bytes (%" APR_SIZE_T_FMT
430 " including header), recommending %u subcaches, "
431 "%u indexes each", shm_segsize,
434 if (num_idx < 5) {
435 /* we're still too small, bail out */
437 "shared memory segment too small");
438 return APR_ENOSPC;
439 }
440 /* OK, we're sorted */
441 ctx->header = header = shm_segment;
442 header->stat_stores = 0;
443 header->stat_replaced = 0;
444 header->stat_expiries = 0;
445 header->stat_scrolled = 0;
446 header->stat_retrieves_hit = 0;
447 header->stat_retrieves_miss = 0;
448 header->stat_removes_hit = 0;
449 header->stat_removes_miss = 0;
450 header->subcache_num = num_subcache;
451 /* Convert the subcache size (in bytes) to a value that is suitable for
452 * structure alignment on the host platform, by rounding down if necessary. */
454 if (header->subcache_size != APR_ALIGN_DEFAULT(header->subcache_size)) {
457 }
460 header->subcache_data_size = header->subcache_size -
461 header->subcache_data_offset;
462 header->index_num = num_idx;
463
464 /* Output trace info */
466 "shmcb_init_memory choices follow");
468 "subcache_num = %u", header->subcache_num);
470 "subcache_size = %u", header->subcache_size);
472 "subcache_data_offset = %u", header->subcache_data_offset);
474 "subcache_data_size = %u", header->subcache_data_size);
476 "index_num = %u", header->index_num);
477 /* The header is done, make the caches empty */
478 for (loop = 0; loop < header->subcache_num; loop++) {
480 subcache->idx_pos = subcache->idx_used = 0;
481 subcache->data_pos = subcache->data_used = 0;
482 }
484 "Shared memory socache initialised");
485 /* Success ... */
486
487 return APR_SUCCESS;
488}
489
496
498 server_rec *s, const unsigned char *id,
499 unsigned int idlen, apr_time_t expiry,
500 unsigned char *encoded,
501 unsigned int len_encoded,
502 apr_pool_t *p)
503{
504 SHMCBHeader *header = ctx->header;
505 SHMCBSubcache *subcache = SHMCB_MASK(header, id);
506 int tryreplace;
507
509 "socache_shmcb_store (0x%02x -> subcache %d)",
510 SHMCB_MASK_DBG(header, id));
511 /* XXX: Says who? Why shouldn't this be acceptable, or padded if not? */
512 if (idlen < 4) {
513 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00832) "unusably short id provided "
514 "(%u bytes)", idlen);
515 return APR_EINVAL;
516 }
518 if (shmcb_subcache_store(s, header, subcache, encoded,
519 len_encoded, id, idlen, expiry)) {
521 "can't store an socache entry!");
522 return APR_ENOSPC;
523 }
524 if (tryreplace == 0) {
525 header->stat_replaced++;
526 }
527 else {
528 header->stat_stores++;
529 }
531 "leaving socache_shmcb_store successfully");
532 return APR_SUCCESS;
533}
534
536 server_rec *s,
537 const unsigned char *id, unsigned int idlen,
538 unsigned char *dest, unsigned int *destlen,
539 apr_pool_t *p)
540{
541 SHMCBHeader *header = ctx->header;
542 SHMCBSubcache *subcache = SHMCB_MASK(header, id);
543 int rv;
544
546 "socache_shmcb_retrieve (0x%02x -> subcache %d)",
547 SHMCB_MASK_DBG(header, id));
548
549 /* Get the entry corresponding to the id, if it exists. */
550 rv = shmcb_subcache_retrieve(s, header, subcache, id, idlen,
551 dest, destlen);
552 if (rv == 0)
553 header->stat_retrieves_hit++;
554 else
555 header->stat_retrieves_miss++;
557 "leaving socache_shmcb_retrieve successfully");
558
559 return rv == 0 ? APR_SUCCESS : APR_NOTFOUND;
560}
561
563 server_rec *s, const unsigned char *id,
564 unsigned int idlen, apr_pool_t *p)
565{
566 SHMCBHeader *header = ctx->header;
567 SHMCBSubcache *subcache = SHMCB_MASK(header, id);
568 apr_status_t rv;
569
571 "socache_shmcb_remove (0x%02x -> subcache %d)",
572 SHMCB_MASK_DBG(header, id));
573 if (idlen < 4) {
574 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00838) "unusably short id provided "
575 "(%u bytes)", idlen);
576 return APR_EINVAL;
577 }
578 if (shmcb_subcache_remove(s, header, subcache, id, idlen) == 0) {
579 header->stat_removes_hit++;
580 rv = APR_SUCCESS;
581 } else {
582 header->stat_removes_miss++;
583 rv = APR_NOTFOUND;
584 }
586 "leaving socache_shmcb_remove successfully");
587
588 return rv;
589}
590
592 request_rec *r, int flags)
593{
594 server_rec *s = r->server;
595 SHMCBHeader *header = ctx->header;
596 unsigned int loop, total = 0, cache_total = 0, non_empty_subcaches = 0;
599 double expiry_total = 0;
600 int index_pct, cache_pct;
601
602 AP_DEBUG_ASSERT(header->subcache_num > 0);
603 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00840) "inside shmcb_status");
604 /* Perform the iteration inside the mutex to avoid corruption or invalid
605 * pointer arithmetic. The rest of our logic uses read-only header data so
606 * doesn't need the lock. */
607 /* Iterate over the subcaches */
608 for (loop = 0; loop < header->subcache_num; loop++) {
611 total += subcache->idx_used;
612 cache_total += subcache->data_used;
613 if (subcache->idx_used) {
616 idx_expiry = idx->expires;
619 if (!min_expiry)
621 else
623 }
624 }
625 index_pct = (100 * total) / (header->index_num *
626 header->subcache_num);
627 cache_pct = (100 * cache_total) / (header->subcache_data_size *
628 header->subcache_num);
629 /* Generate Output */
630 if (!(flags & AP_STATUS_SHORT)) {
631 ap_rprintf(r, "cache type: <b>SHMCB</b>, shared memory: <b>%" APR_SIZE_T_FMT "</b> "
632 "bytes, current entries: <b>%d</b><br>",
633 ctx->shm_size, total);
634 ap_rprintf(r, "subcaches: <b>%d</b>, indexes per subcache: <b>%d</b><br>",
635 header->subcache_num, header->index_num);
638 ap_rprintf(r, "time left on oldest entries' objects: ");
639 if (now < average_expiry)
640 ap_rprintf(r, "avg: <b>%d</b> seconds, (range: %d...%d)<br>",
643 (int)apr_time_sec(max_expiry - now));
644 else
645 ap_rprintf(r, "expiry_threshold: <b>Calculation error!</b><br>");
646 }
647
648 ap_rprintf(r, "index usage: <b>%d%%</b>, cache usage: <b>%d%%</b><br>",
650 ap_rprintf(r, "total entries stored since starting: <b>%lu</b><br>",
651 header->stat_stores);
652 ap_rprintf(r, "total entries replaced since starting: <b>%lu</b><br>",
653 header->stat_replaced);
654 ap_rprintf(r, "total entries expired since starting: <b>%lu</b><br>",
655 header->stat_expiries);
656 ap_rprintf(r, "total (pre-expiry) entries scrolled out of the cache: "
657 "<b>%lu</b><br>", header->stat_scrolled);
658 ap_rprintf(r, "total retrieves since starting: <b>%lu</b> hit, "
659 "<b>%lu</b> miss<br>", header->stat_retrieves_hit,
660 header->stat_retrieves_miss);
661 ap_rprintf(r, "total removes since starting: <b>%lu</b> hit, "
662 "<b>%lu</b> miss<br>", header->stat_removes_hit,
663 header->stat_removes_miss);
664 }
665 else {
666 ap_rputs("CacheType: SHMCB\n", r);
667 ap_rprintf(r, "CacheSharedMemory: %" APR_SIZE_T_FMT "\n",
668 ctx->shm_size);
669 ap_rprintf(r, "CacheCurrentEntries: %d\n", total);
670 ap_rprintf(r, "CacheSubcaches: %d\n", header->subcache_num);
671 ap_rprintf(r, "CacheIndexesPerSubcaches: %d\n", header->index_num);
674 if (now < average_expiry) {
675 ap_rprintf(r, "CacheTimeLeftOldestAvg: %d\n", (int)apr_time_sec(average_expiry - now));
676 ap_rprintf(r, "CacheTimeLeftOldestMin: %d\n", (int)apr_time_sec(min_expiry - now));
677 ap_rprintf(r, "CacheTimeLeftOldestMax: %d\n", (int)apr_time_sec(max_expiry - now));
678 }
679 }
680
681 ap_rprintf(r, "CacheIndexUsage: %d%%\n", index_pct);
682 ap_rprintf(r, "CacheUsage: %d%%\n", cache_pct);
683 ap_rprintf(r, "CacheStoreCount: %lu\n", header->stat_stores);
684 ap_rprintf(r, "CacheReplaceCount: %lu\n", header->stat_replaced);
685 ap_rprintf(r, "CacheExpireCount: %lu\n", header->stat_expiries);
686 ap_rprintf(r, "CacheDiscardCount: %lu\n", header->stat_scrolled);
687 ap_rprintf(r, "CacheRetrieveHitCount: %lu\n", header->stat_retrieves_hit);
688 ap_rprintf(r, "CacheRetrieveMissCount: %lu\n", header->stat_retrieves_miss);
689 ap_rprintf(r, "CacheRemoveHitCount: %lu\n", header->stat_removes_hit);
690 ap_rprintf(r, "CacheRemoveMissCount: %lu\n", header->stat_removes_miss);
691 }
692 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00841) "leaving shmcb_status");
693}
694
696 server_rec *s, void *userctx,
697 ap_socache_iterator_t *iterator,
699{
700 SHMCBHeader *header = instance->header;
701 unsigned int loop;
704 apr_size_t buflen = 0;
705 unsigned char *buf = NULL;
706
707 /* Perform the iteration inside the mutex to avoid corruption or invalid
708 * pointer arithmetic. The rest of our logic uses read-only header data so
709 * doesn't need the lock. */
710 /* Iterate over the subcaches */
711 for (loop = 0; loop < header->subcache_num && rv == APR_SUCCESS; loop++) {
714 iterator, &buf, &buflen, pool, now);
715 }
716 return rv;
717}
718
719/*
720 * Subcache-level cache operations
721 */
722
725{
726 unsigned int loop = 0, freed = 0, expired = 0;
727 unsigned int new_idx_pos = subcache->idx_pos;
729
730 while (loop < subcache->idx_used) {
732 if (idx->removed)
733 freed++;
734 else if (idx->expires <= now)
735 expired++;
736 else
737 /* not removed and not expired yet, we're done iterating */
738 break;
739 loop++;
741 }
742 if (!loop)
743 /* Nothing to do */
744 return;
746 "expiring %u and reclaiming %u removed socache entries",
747 expired, freed);
748 if (loop == subcache->idx_used) {
749 /* We're expiring everything, piece of cake */
750 subcache->idx_used = 0;
751 subcache->data_used = 0;
752 } else {
753 /* There remain other indexes, so we can use idx to adjust 'data' */
754 unsigned int diff = SHMCB_CYCLIC_SPACE(subcache->data_pos,
755 idx->data_pos,
756 header->subcache_data_size);
757 /* Adjust the indexes */
758 subcache->idx_used -= loop;
759 subcache->idx_pos = new_idx_pos;
760 /* Adjust the data area */
761 subcache->data_used -= diff;
762 subcache->data_pos = idx->data_pos;
763 }
764 header->stat_expiries += expired;
766 "we now have %u socache entries", subcache->idx_used);
767}
768
771 unsigned char *data, unsigned int data_len,
772 const unsigned char *id, unsigned int id_len,
773 apr_time_t expiry)
774{
775 unsigned int data_offset, new_idx, id_offset;
777 unsigned int total_len = id_len + data_len;
778
779 /* Sanity check the input */
780 if (total_len > header->subcache_data_size) {
782 "inserting socache entry larger (%d) than subcache data area (%d)",
784 return -1;
785 }
786
787 /* First reclaim space from removed and expired records. */
789
790 /* Loop until there is enough space to insert
791 * XXX: This should first compress out-of-order expiries and
792 * removed records, and then force-remove oldest-first
793 */
794 if (header->subcache_data_size - subcache->data_used < total_len
795 || subcache->idx_used == header->index_num) {
796
797 idx = SHMCB_INDEX(subcache, subcache->idx_pos);
799 "about to force-expire, subcache: idx_used=%d, "
800 "data_used=%d", subcache->idx_used, subcache->data_used);
801 do {
803
804 /* Adjust the indexes by one */
805 subcache->idx_pos = SHMCB_CYCLIC_INCREMENT(subcache->idx_pos, 1,
806 header->index_num);
807 subcache->idx_used--;
808 if (!subcache->idx_used) {
809 /* There's nothing left */
810 subcache->data_used = 0;
811 break;
812 }
813 /* Adjust the data */
814 idx2 = SHMCB_INDEX(subcache, subcache->idx_pos);
815 subcache->data_used -= SHMCB_CYCLIC_SPACE(idx->data_pos, idx2->data_pos,
816 header->subcache_data_size);
817 subcache->data_pos = idx2->data_pos;
818 /* Stats */
819 header->stat_scrolled++;
820 /* Loop admin */
821 idx = idx2;
822 } while (header->subcache_data_size - subcache->data_used < total_len);
823
825 "finished force-expire, subcache: idx_used=%d, "
826 "data_used=%d", subcache->idx_used, subcache->data_used);
827 }
828
829 /* HERE WE ASSUME THAT THE NEW ENTRY SHOULD GO ON THE END! I'M NOT
830 * CHECKING WHETHER IT SHOULD BE GENUINELY "INSERTED" SOMEWHERE.
831 *
832 * We aught to fix that. httpd (never mind third party modules)
833 * does not promise to perform any processing in date order
834 * (c.f. FAQ "My log entries are not in date order!")
835 */
836 /* Insert the id */
837 id_offset = SHMCB_CYCLIC_INCREMENT(subcache->data_pos, subcache->data_used,
838 header->subcache_data_size);
840 SHMCB_DATA(header, subcache), id_offset,
841 id, id_len);
842 subcache->data_used += id_len;
843 /* Insert the data */
844 data_offset = SHMCB_CYCLIC_INCREMENT(subcache->data_pos, subcache->data_used,
845 header->subcache_data_size);
848 data, data_len);
849 subcache->data_used += data_len;
850 /* Insert the index */
851 new_idx = SHMCB_CYCLIC_INCREMENT(subcache->idx_pos, subcache->idx_used,
852 header->index_num);
854 idx->expires = expiry;
855 idx->data_pos = id_offset;
856 idx->data_used = total_len;
857 idx->id_len = id_len;
858 idx->removed = 0;
859 subcache->idx_used++;
861 "insert happened at idx=%d, data=(%u:%u)", new_idx,
864 "finished insert, subcache: idx_pos/idx_used=%d/%d, "
865 "data_pos/data_used=%d/%d",
866 subcache->idx_pos, subcache->idx_used,
867 subcache->data_pos, subcache->data_used);
868 return 0;
869}
870
873 const unsigned char *id, unsigned int idlen,
874 unsigned char *dest, unsigned int *destlen)
875{
876 unsigned int pos;
877 unsigned int loop = 0;
879
880 pos = subcache->idx_pos;
881
882 while (loop < subcache->idx_used) {
884
885 /* Only consider 'idx' if the id matches, and the "removed"
886 * flag isn't set, and the record is not expired.
887 * Check the data length too to avoid a buffer overflow
888 * in case of corruption, which should be impossible,
889 * but it's cheap to be safe. */
890 if (!idx->removed
891 && idx->id_len == idlen
892 && (idx->data_used - idx->id_len) <= *destlen
894 SHMCB_DATA(header, subcache),
895 idx->data_pos, id, idx->id_len) == 0) {
897 "match at idx=%d, data=%d", pos, idx->data_pos);
898 if (idx->expires > now) {
899 unsigned int data_offset;
900
901 /* Find the offset of the data segment, after the id */
903 idx->id_len,
904 header->subcache_data_size);
905
906 *destlen = idx->data_used - idx->id_len;
907
908 /* Copy out the data */
910 dest, SHMCB_DATA(header, subcache),
912
913 return 0;
914 }
915 else {
916 /* Already stale, quietly remove and treat as not-found */
917 idx->removed = 1;
918 header->stat_expiries++;
920 "shmcb_subcache_retrieve discarding expired entry");
921 return -1;
922 }
923 }
924 /* Increment */
925 loop++;
926 pos = SHMCB_CYCLIC_INCREMENT(pos, 1, header->index_num);
927 }
928
930 "shmcb_subcache_retrieve found no match");
931 return -1;
932}
933
936 const unsigned char *id,
937 unsigned int idlen)
938{
939 unsigned int pos;
940 unsigned int loop = 0;
941
942 pos = subcache->idx_pos;
943 while (loop < subcache->idx_used) {
945
946 /* Only consider 'idx' if the id matches, and the "removed"
947 * flag isn't set. */
948 if (!idx->removed && idx->id_len == idlen
950 SHMCB_DATA(header, subcache),
951 idx->data_pos, id, idx->id_len) == 0) {
953 "possible match at idx=%d, data=%d", pos, idx->data_pos);
954
955 /* Found the matching entry, remove it quietly. */
956 idx->removed = 1;
958 "shmcb_subcache_remove removing matching entry");
959 return 0;
960 }
961 /* Increment */
962 loop++;
963 pos = SHMCB_CYCLIC_INCREMENT(pos, 1, header->index_num);
964 }
965
966 return -1; /* failure */
967}
968
969
971 server_rec *s,
972 void *userctx,
973 SHMCBHeader *header,
975 ap_socache_iterator_t *iterator,
976 unsigned char **buf,
980{
981 unsigned int pos;
982 unsigned int loop = 0;
983 apr_status_t rv;
984
985 pos = subcache->idx_pos;
986 while (loop < subcache->idx_used) {
988
989 /* Only consider 'idx' if the "removed" flag isn't set. */
990 if (!idx->removed) {
991
993 "iterating idx=%d, data=%d", pos, idx->data_pos);
994 if (idx->expires > now) {
995 unsigned char *id = *buf;
996 unsigned char *dest;
997 unsigned int data_offset, dest_len;
999
1000 /* Find the offset of the data segment, after the id */
1002 idx->id_len,
1003 header->subcache_data_size);
1004
1005 dest_len = idx->data_used - idx->id_len;
1006
1007 buf_req = APR_ALIGN_DEFAULT(idx->id_len + 1)
1009
1010 if (buf_req > *buf_len) {
1011 /* Grow to ~150% of this buffer requirement on resize
1012 * always using APR_ALIGN_DEFAULT sized pages
1013 */
1015 *buf = apr_palloc(pool, *buf_len);
1016 id = *buf;
1017 }
1018
1019 dest = *buf + APR_ALIGN_DEFAULT(idx->id_len + 1);
1020
1021 /* Copy out the data, because it's potentially cyclic */
1023 SHMCB_DATA(header, subcache),
1024 idx->data_pos, idx->id_len);
1025 id[idx->id_len] = '\0';
1026
1028 SHMCB_DATA(header, subcache),
1030 dest[dest_len] = '\0';
1031
1032 rv = iterator(instance, s, userctx, id, idx->id_len,
1033 dest, dest_len, pool);
1035 "shmcb entry iterated");
1036 if (rv != APR_SUCCESS)
1037 return rv;
1038 }
1039 else {
1040 /* Already stale, quietly remove and treat as not-found */
1041 idx->removed = 1;
1042 header->stat_expiries++;
1044 "shmcb_subcache_iterate discarding expired entry");
1045 }
1046 }
1047 /* Increment */
1048 loop++;
1049 pos = SHMCB_CYCLIC_INCREMENT(pos, 1, header->index_num);
1050 }
1051
1052 return APR_SUCCESS;
1053}
1054
1067
1069{
1072 &socache_shmcb);
1073
1074 /* Also register shmcb under the default provider name. */
1078 &socache_shmcb);
1079}
1080
1083 NULL, NULL, NULL, NULL, NULL,
1085};
Small object cache provider interface.
APR Miscellaneous library routines.
APR Shared Memory Routines.
APR Strings library.
APR Time Library.
APR Standard Headers Support.
char * ap_runtime_dir_relative(apr_pool_t *p, const char *fname)
Definition config.c:1610
#define AP_DECLARE_MODULE(foo)
char * ap_server_root_relative(apr_pool_t *p, const char *fname)
Definition config.c:1594
request_rec int int apr_table_t const char * path
request_rec * r
#define APLOGNO(n)
Definition http_log.h:117
#define APLOG_INFO
Definition http_log.h:70
#define ap_log_rerror
Definition http_log.h:454
#define APLOG_ERR
Definition http_log.h:67
#define ap_log_error
Definition http_log.h:370
#define APLOG_MARK
Definition http_log.h:283
#define APLOG_DEBUG
Definition http_log.h:71
const unsigned char * buf
Definition util_md5.h:50
apr_md5_ctx_t * context
Definition util_md5.h:58
int ap_rprintf(request_rec *r, const char *fmt,...) __attribute__((format(printf
static APR_INLINE int ap_rputs(const char *str, request_rec *r)
apr_status_t ap_register_provider(apr_pool_t *pool, const char *provider_group, const char *provider_name, const char *provider_version, const void *provider)
Definition provider.c:35
void const char * arg
Definition http_vhost.h:63
#define APR_ENOSPC
Definition apr_errno.h:676
#define APR_NOTFOUND
Definition apr_errno.h:463
#define APR_EINVAL
Definition apr_errno.h:711
#define APR_STATUS_IS_ENOTIMPL(s)
Definition apr_errno.h:615
apr_brigade_flush void * ctx
const char * src
Definition apr_encode.h:167
const char apr_ssize_t int flags
Definition apr_encode.h:168
const char const apr_size_t data_len
#define AP_SOCACHE_DEFAULT_PROVIDER
Definition ap_socache.h:223
apr_status_t() ap_socache_iterator_t(ap_socache_instance_t *instance, server_rec *s, void *userctx, const unsigned char *id, unsigned int idlen, const unsigned char *data, unsigned int datalen, apr_pool_t *pool)
Definition ap_socache.h:77
#define AP_SOCACHE_PROVIDER_GROUP
Definition ap_socache.h:218
#define AP_SOCACHE_FLAG_NOTMPSAFE
Definition ap_socache.h:46
#define AP_SOCACHE_PROVIDER_VERSION
Definition ap_socache.h:220
#define AP_STATUS_SHORT
Definition mod_status.h:32
#define STANDARD20_MODULE_STUFF
#define AP_DEBUG_ASSERT(exp)
Definition httpd.h:2283
apr_size_t size
const char int apr_pool_t * pool
Definition apr_cstr.h:84
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
void * data
#define APR_ALIGN_DEFAULT(size)
apr_size_t buflen
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
const char * s
Definition apr_strings.h:95
apr_int64_t apr_time_t
Definition apr_time.h:45
#define apr_time_sec(time)
Definition apr_time.h:63
Apache Configuration.
Apache Logging library.
HTTP protocol handling.
Apache Request library.
HTTP Daemon routines.
apr_pool_t * p
Definition md_event.c:32
return NULL
Definition mod_so.c:359
static apr_status_t socache_shmcb_store(ap_socache_instance_t *ctx, server_rec *s, const unsigned char *id, unsigned int idlen, apr_time_t expiry, unsigned char *encoded, unsigned int len_encoded, apr_pool_t *p)
static void socache_shmcb_destroy(ap_socache_instance_t *ctx, server_rec *s)
#define ALIGNED_HEADER_SIZE
#define SHMCB_MASK_DBG(pHeader, id)
static const char * socache_shmcb_create(ap_socache_instance_t **context, const char *arg, apr_pool_t *tmp, apr_pool_t *p)
static apr_status_t socache_shmcb_iterate(ap_socache_instance_t *instance, server_rec *s, void *userctx, ap_socache_iterator_t *iterator, apr_pool_t *pool)
static int shmcb_subcache_retrieve(server_rec *, SHMCBHeader *, SHMCBSubcache *, const unsigned char *id, unsigned int idlen, unsigned char *data, unsigned int *datalen)
#define DEFAULT_SHMCB_SUFFIX
#define SHMCB_INDEX(pSubcache, num)
static const ap_socache_provider_t socache_shmcb
static void shmcb_cyclic_ntoc_memcpy(unsigned int buf_size, unsigned char *data, unsigned int dest_offset, const unsigned char *src, unsigned int src_len)
#define SHMCB_CYCLIC_INCREMENT(val, inc, mod)
#define SHMCB_SUBCACHE(pHeader, num)
static apr_status_t socache_shmcb_init(ap_socache_instance_t *ctx, const char *namespace, const struct ap_socache_hints *hints, server_rec *s, apr_pool_t *p)
static void register_hooks(apr_pool_t *p)
#define SHMCB_MAX_SIZE
static int shmcb_subcache_remove(server_rec *, SHMCBHeader *, SHMCBSubcache *, const unsigned char *, unsigned int)
static apr_status_t socache_shmcb_remove(ap_socache_instance_t *ctx, server_rec *s, const unsigned char *id, unsigned int idlen, apr_pool_t *p)
static void socache_shmcb_status(ap_socache_instance_t *ctx, request_rec *r, int flags)
static apr_status_t socache_shmcb_cleanup(void *arg)
static int shmcb_cyclic_memcmp(unsigned int buf_size, unsigned char *data, unsigned int dest_offset, const unsigned char *src, unsigned int src_len)
#define SHMCB_CYCLIC_SPACE(val1, val2, mod)
static int shmcb_subcache_store(server_rec *s, SHMCBHeader *header, SHMCBSubcache *subcache, unsigned char *data, unsigned int data_len, const unsigned char *id, unsigned int id_len, apr_time_t expiry)
#define ALIGNED_SUBCACHE_SIZE
static void shmcb_subcache_expire(server_rec *, SHMCBHeader *, SHMCBSubcache *, apr_time_t)
#define SHMCB_MASK(pHeader, id)
#define ALIGNED_INDEX_SIZE
#define SHMCB_DATA(pHeader, pSubcache)
static apr_status_t socache_shmcb_retrieve(ap_socache_instance_t *ctx, server_rec *s, const unsigned char *id, unsigned int idlen, unsigned char *dest, unsigned int *destlen, apr_pool_t *p)
static apr_status_t shmcb_subcache_iterate(ap_socache_instance_t *instance, server_rec *s, void *userctx, SHMCBHeader *header, SHMCBSubcache *subcache, ap_socache_iterator_t *iterator, unsigned char **buf, apr_size_t *buf_len, apr_pool_t *pool, apr_time_t now)
#define DEFAULT_SHMCB_PREFIX
static void shmcb_cyclic_cton_memcpy(unsigned int buf_size, unsigned char *dest, const unsigned char *data, unsigned int src_offset, unsigned int src_len)
Status Report Extension Module to Apache.
unsigned int index_num
unsigned int subcache_data_offset
unsigned int subcache_data_size
unsigned long stat_replaced
unsigned long stat_expiries
unsigned int subcache_size
unsigned long stat_retrieves_hit
unsigned long stat_removes_miss
unsigned long stat_stores
unsigned long stat_retrieves_miss
unsigned long stat_scrolled
unsigned long stat_removes_hit
unsigned int subcache_num
apr_time_t expires
unsigned int id_len
unsigned char removed
unsigned int data_used
unsigned int data_pos
unsigned int data_pos
unsigned int idx_pos
A structure that represents the current request.
Definition httpd.h:845
server_rec * server
Definition httpd.h:851
A structure to store information for each virtual server.
Definition httpd.h:1322
static apr_time_t now
Definition testtime.c:33