| | varnish-cache/bin/varnishd/hash/hash_classic.c |
0 |
|
/*- |
1 |
|
* Copyright (c) 2006 Verdens Gang AS |
2 |
|
* Copyright (c) 2006-2011 Varnish Software AS |
3 |
|
* All rights reserved. |
4 |
|
* |
5 |
|
* Author: Poul-Henning Kamp <phk@phk.freebsd.dk> |
6 |
|
* |
7 |
|
* SPDX-License-Identifier: BSD-2-Clause |
8 |
|
* |
9 |
|
* Redistribution and use in source and binary forms, with or without |
10 |
|
* modification, are permitted provided that the following conditions |
11 |
|
* are met: |
12 |
|
* 1. Redistributions of source code must retain the above copyright |
13 |
|
* notice, this list of conditions and the following disclaimer. |
14 |
|
* 2. Redistributions in binary form must reproduce the above copyright |
15 |
|
* notice, this list of conditions and the following disclaimer in the |
16 |
|
* documentation and/or other materials provided with the distribution. |
17 |
|
* |
18 |
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
19 |
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
20 |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
21 |
|
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE |
22 |
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
23 |
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
24 |
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
25 |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
26 |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
27 |
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
28 |
|
* SUCH DAMAGE. |
29 |
|
* |
30 |
|
* A classic bucketed hash |
31 |
|
*/ |
32 |
|
|
33 |
|
#include "config.h" |
34 |
|
|
35 |
|
#include <stdio.h> |
36 |
|
#include <stdlib.h> |
37 |
|
|
38 |
|
#include "cache/cache_varnishd.h" |
39 |
|
#include "cache/cache_objhead.h" |
40 |
|
#include "common/heritage.h" |
41 |
|
|
42 |
|
#include "hash/hash_slinger.h" |
43 |
|
|
44 |
|
static struct VSC_lck *lck_hcl; |
45 |
|
|
46 |
|
/*--------------------------------------------------------------------*/ |
47 |
|
|
48 |
|
struct hcl_hd { |
49 |
|
unsigned magic; |
50 |
|
#define HCL_HEAD_MAGIC 0x0f327016 |
51 |
|
VTAILQ_HEAD(, objhead) head; |
52 |
|
struct lock mtx; |
53 |
|
}; |
54 |
|
|
55 |
|
static unsigned hcl_nhash = 16383; |
56 |
|
static struct hcl_hd *hcl_head; |
57 |
|
|
58 |
|
/*-------------------------------------------------------------------- |
59 |
|
* The ->init method allows the management process to pass arguments |
60 |
|
*/ |
61 |
|
|
62 |
|
static void v_matchproto_(hash_init_f) |
63 |
40 |
hcl_init(int ac, char * const *av) |
64 |
|
{ |
65 |
|
int i; |
66 |
|
unsigned u; |
67 |
|
|
68 |
40 |
if (ac == 0) |
69 |
0 |
return; |
70 |
40 |
if (ac > 1) |
71 |
0 |
ARGV_ERR("(-hclassic) too many arguments\n"); |
72 |
40 |
i = sscanf(av[0], "%u", &u); |
73 |
40 |
if (i <= 0 || u == 0) |
74 |
0 |
return; |
75 |
40 |
if (u > 2 && !(u & (u - 1))) { |
76 |
0 |
fprintf(stderr, |
77 |
|
"NOTE:\n" |
78 |
|
"\tA power of two number of hash buckets is " |
79 |
|
"marginally less efficient\n" |
80 |
|
"\twith systematic URLs. Reducing by one" |
81 |
|
" hash bucket.\n"); |
82 |
0 |
u--; |
83 |
0 |
} |
84 |
40 |
hcl_nhash = u; |
85 |
40 |
fprintf(stderr, "Classic hash: %u buckets\n", hcl_nhash); |
86 |
40 |
} |
87 |
|
|
88 |
|
/*-------------------------------------------------------------------- |
89 |
|
* The ->start method is called during cache process start and allows |
90 |
|
* initialization to happen before the first lookup. |
91 |
|
*/ |
92 |
|
|
93 |
|
static void v_matchproto_(hash_start_f) |
94 |
40 |
hcl_start(void) |
95 |
|
{ |
96 |
|
unsigned u; |
97 |
|
|
98 |
40 |
lck_hcl = Lck_CreateClass(NULL, "hcl"); |
99 |
40 |
hcl_head = calloc(hcl_nhash, sizeof *hcl_head); |
100 |
40 |
XXXAN(hcl_head); |
101 |
|
|
102 |
480 |
for (u = 0; u < hcl_nhash; u++) { |
103 |
440 |
VTAILQ_INIT(&hcl_head[u].head); |
104 |
440 |
Lck_New(&hcl_head[u].mtx, lck_hcl); |
105 |
440 |
hcl_head[u].magic = HCL_HEAD_MAGIC; |
106 |
440 |
} |
107 |
40 |
} |
108 |
|
|
109 |
|
/*-------------------------------------------------------------------- |
110 |
|
* Lookup and possibly insert element. |
111 |
|
* If nobj != NULL and the lookup does not find key, nobj is inserted. |
112 |
|
* If nobj == NULL and the lookup does not find key, NULL is returned. |
113 |
|
* A reference to the returned object is held. |
114 |
|
* We use a two-pass algorithm to handle inserts as they are quite |
115 |
|
* rare and collisions even rarer. |
116 |
|
*/ |
117 |
|
|
118 |
|
static struct objhead * v_matchproto_(hash_lookup_f) |
119 |
160 |
hcl_lookup(struct worker *wrk, const void *digest, struct objhead **noh) |
120 |
|
{ |
121 |
|
struct objhead *oh; |
122 |
|
struct hcl_hd *hp; |
123 |
|
unsigned u1, hdigest; |
124 |
|
int i; |
125 |
|
|
126 |
160 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
127 |
160 |
AN(digest); |
128 |
160 |
if (noh != NULL) |
129 |
160 |
CHECK_OBJ_NOTNULL(*noh, OBJHEAD_MAGIC); |
130 |
|
|
131 |
160 |
assert(sizeof oh->digest >= sizeof hdigest); |
132 |
160 |
memcpy(&hdigest, digest, sizeof hdigest); |
133 |
160 |
u1 = hdigest % hcl_nhash; |
134 |
160 |
hp = &hcl_head[u1]; |
135 |
|
|
136 |
160 |
Lck_Lock(&hp->mtx); |
137 |
160 |
VTAILQ_FOREACH(oh, &hp->head, hoh_list) { |
138 |
80 |
CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC); |
139 |
80 |
i = memcmp(oh->digest, digest, sizeof oh->digest); |
140 |
80 |
if (i < 0) |
141 |
0 |
continue; |
142 |
80 |
if (i > 0) |
143 |
0 |
break; |
144 |
80 |
oh->refcnt++; |
145 |
80 |
Lck_Unlock(&hp->mtx); |
146 |
80 |
Lck_Lock(&oh->mtx); |
147 |
80 |
return (oh); |
148 |
|
} |
149 |
|
|
150 |
80 |
if (noh == NULL) { |
151 |
0 |
Lck_Unlock(&hp->mtx); |
152 |
0 |
return (NULL); |
153 |
|
} |
154 |
|
|
155 |
80 |
if (oh != NULL) |
156 |
0 |
VTAILQ_INSERT_BEFORE(oh, *noh, hoh_list); |
157 |
|
else |
158 |
80 |
VTAILQ_INSERT_TAIL(&hp->head, *noh, hoh_list); |
159 |
|
|
160 |
80 |
oh = *noh; |
161 |
80 |
*noh = NULL; |
162 |
80 |
memcpy(oh->digest, digest, sizeof oh->digest); |
163 |
|
|
164 |
80 |
oh->hoh_head = hp; |
165 |
|
|
166 |
80 |
Lck_Unlock(&hp->mtx); |
167 |
80 |
Lck_Lock(&oh->mtx); |
168 |
80 |
return (oh); |
169 |
160 |
} |
170 |
|
|
171 |
|
/*-------------------------------------------------------------------- |
172 |
|
* Dereference and if no references are left, free. |
173 |
|
*/ |
174 |
|
|
175 |
|
static int v_matchproto_(hash_deref_f) |
176 |
80 |
hcl_deref(struct worker *wrk, struct objhead *oh) |
177 |
|
{ |
178 |
|
struct hcl_hd *hp; |
179 |
|
int ret; |
180 |
|
|
181 |
80 |
CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC); |
182 |
80 |
Lck_AssertHeld(&oh->mtx); |
183 |
80 |
Lck_Unlock(&oh->mtx); |
184 |
|
|
185 |
80 |
CAST_OBJ_NOTNULL(hp, oh->hoh_head, HCL_HEAD_MAGIC); |
186 |
80 |
assert(oh->refcnt > 0); |
187 |
80 |
Lck_Lock(&hp->mtx); |
188 |
80 |
if (--oh->refcnt == 0) { |
189 |
0 |
VTAILQ_REMOVE(&hp->head, oh, hoh_list); |
190 |
0 |
ret = 0; |
191 |
0 |
} else |
192 |
80 |
ret = 1; |
193 |
80 |
Lck_Unlock(&hp->mtx); |
194 |
80 |
if (!ret) |
195 |
0 |
HSH_DeleteObjHead(wrk, oh); |
196 |
80 |
return (ret); |
197 |
|
} |
198 |
|
|
199 |
|
/*--------------------------------------------------------------------*/ |
200 |
|
|
201 |
|
const struct hash_slinger hcl_slinger = { |
202 |
|
.magic = SLINGER_MAGIC, |
203 |
|
.name = "classic", |
204 |
|
.init = hcl_init, |
205 |
|
.start = hcl_start, |
206 |
|
.lookup = hcl_lookup, |
207 |
|
.deref = hcl_deref, |
208 |
|
}; |