varnish-cache/lib/libvmod_directors/vdir.c
1
/*-
2
 * Copyright (c) 2013-2015 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@FreeBSD.org>
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 * 1. Redistributions of source code must retain the above copyright
11
 *    notice, this list of conditions and the following disclaimer.
12
 * 2. Redistributions in binary form must reproduce the above copyright
13
 *    notice, this list of conditions and the following disclaimer in the
14
 *    documentation and/or other materials provided with the distribution.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
20
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
 * SUCH DAMAGE.
27
 */
28
29
#include "config.h"
30
31
#include <stdlib.h>
32
33
#include "cache/cache.h"
34
35
#include "vbm.h"
36
37
#include "vdir.h"
38
39
static void
40 48
vdir_expand(struct vdir *vd, unsigned n)
41
{
42 48
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
43
44 48
        vd->backend = realloc(vd->backend, n * sizeof *vd->backend);
45 48
        AN(vd->backend);
46 48
        vd->weight = realloc(vd->weight, n * sizeof *vd->weight);
47 48
        AN(vd->weight);
48 48
        vd->l_backend = n;
49 48
}
50
51
void
52 54
vdir_new(VRT_CTX, struct vdir **vdp, const char *vcl_name,
53
    const struct vdi_methods *m, void *priv)
54
{
55
        struct vdir *vd;
56
57 54
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
58 54
        CHECK_OBJ_NOTNULL(m, VDI_METHODS_MAGIC);
59 54
        AN(vcl_name);
60 54
        AN(vdp);
61 54
        AZ(*vdp);
62 54
        ALLOC_OBJ(vd, VDIR_MAGIC);
63 54
        AN(vd);
64 54
        *vdp = vd;
65 54
        AZ(pthread_rwlock_init(&vd->mtx, NULL));
66 54
        vd->dir = VRT_AddDirector(ctx, m, priv, "%s", vcl_name);
67 54
        vd->vbm = vbit_new(8);
68 54
        AN(vd->vbm);
69 54
}
70
71
void
72 10
vdir_delete(struct vdir **vdp)
73
{
74
        struct vdir *vd;
75
76 10
        TAKE_OBJ_NOTNULL(vd, vdp, VDIR_MAGIC);
77
78 10
        AZ(vd->dir);
79 10
        free(vd->backend);
80 10
        free(vd->weight);
81 10
        AZ(pthread_rwlock_destroy(&vd->mtx));
82 10
        vbit_destroy(vd->vbm);
83 10
        FREE_OBJ(vd);
84 10
}
85
86
void
87 140
vdir_rdlock(struct vdir *vd)
88
{
89 140
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
90 140
        AZ(pthread_rwlock_rdlock(&vd->mtx));
91 140
}
92
93
void
94 280
vdir_wrlock(struct vdir *vd)
95
{
96 280
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
97 280
        AZ(pthread_rwlock_wrlock(&vd->mtx));
98 280
}
99
100
void
101 420
vdir_unlock(struct vdir *vd)
102
{
103 420
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
104 420
        AZ(pthread_rwlock_unlock(&vd->mtx));
105 420
}
106
107
108
void
109 122
vdir_add_backend(VRT_CTX, struct vdir *vd, VCL_BACKEND be, double weight)
110
{
111
        unsigned u;
112
113 122
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
114 122
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
115 122
        if (be == NULL) {
116 6
                VRT_fail(ctx, "%s: NULL backend cannot be added",
117
                    VRT_BACKEND_string(vd->dir));
118 6
                return;
119
        }
120 116
        AN(be);
121 116
        vdir_wrlock(vd);
122 116
        if (vd->n_backend >= vd->l_backend)
123 48
                vdir_expand(vd, vd->l_backend + 16);
124 116
        assert(vd->n_backend < vd->l_backend);
125 116
        u = vd->n_backend++;
126 116
        vd->backend[u] = be;
127 116
        vd->weight[u] = weight;
128 116
        vd->total_weight += weight;
129 116
        vdir_unlock(vd);
130
}
131
132
void
133 24
vdir_remove_backend(VRT_CTX, struct vdir *vd, VCL_BACKEND be, unsigned *cur)
134
{
135
        unsigned u, n;
136
137 24
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
138 24
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
139 24
        if (be == NULL) {
140 0
                VRT_fail(ctx, "%s: NULL backend cannot be removed",
141
                    VRT_BACKEND_string(vd->dir));
142 0
                return;
143
        }
144 24
        CHECK_OBJ(be, DIRECTOR_MAGIC);
145 24
        vdir_wrlock(vd);
146 34
        for (u = 0; u < vd->n_backend; u++)
147 34
                if (vd->backend[u] == be)
148 24
                        break;
149 24
        if (u == vd->n_backend) {
150 0
                vdir_unlock(vd);
151 0
                return;
152
        }
153 24
        vd->total_weight -= vd->weight[u];
154 24
        n = (vd->n_backend - u) - 1;
155 24
        memmove(&vd->backend[u], &vd->backend[u+1], n * sizeof(vd->backend[0]));
156 24
        memmove(&vd->weight[u], &vd->weight[u+1], n * sizeof(vd->weight[0]));
157 24
        vd->n_backend--;
158
159 24
        if (cur) {
160 8
                assert(*cur <= vd->n_backend);
161 8
                if (u < *cur)
162 2
                        (*cur)--;
163 6
                else if (*cur == vd->n_backend)
164 2
                        *cur = 0;
165
        }
166 24
        vdir_unlock(vd);
167
}
168
169
VCL_BOOL
170 60
vdir_any_healthy(VRT_CTX, struct vdir *vd, VCL_TIME *changed)
171
{
172 60
        unsigned retval = 0;
173
        VCL_BACKEND be;
174
        unsigned u;
175
        double c;
176
177 60
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
178 60
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
179 60
        vdir_rdlock(vd);
180 60
        if (changed != NULL)
181 0
                *changed = 0;
182 80
        for (u = 0; u < vd->n_backend; u++) {
183 72
                be = vd->backend[u];
184 72
                CHECK_OBJ_NOTNULL(be, DIRECTOR_MAGIC);
185 72
                retval = VRT_Healthy(ctx, be, &c);
186 72
                if (changed != NULL && c > *changed)
187 0
                        *changed = c;
188 72
                if (retval)
189 52
                        break;
190
        }
191 60
        vdir_unlock(vd);
192 60
        return (retval);
193
}
194
195
static unsigned
196 92
vdir_pick_by_weight(const struct vdir *vd, double w,
197
    const struct vbitmap *blacklist)
198
{
199 92
        double a = 0.0;
200 92
        VCL_BACKEND be = NULL;
201
        unsigned u;
202
203 92
        AN(blacklist);
204 144
        for (u = 0; u < vd->n_backend; u++) {
205 144
                be = vd->backend[u];
206 144
                CHECK_OBJ_NOTNULL(be, DIRECTOR_MAGIC);
207 144
                if (vbit_test(blacklist, u))
208 8
                        continue;
209 136
                a += vd->weight[u];
210 136
                if (w < a)
211 184
                        return (u);
212
        }
213 0
        WRONG("");
214
}
215
216
VCL_BACKEND
217 94
vdir_pick_be(VRT_CTX, struct vdir *vd, double w)
218
{
219
        unsigned u;
220 94
        double tw = 0.0;
221 94
        VCL_BACKEND be = NULL;
222
223 94
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
224 94
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
225 94
        vdir_wrlock(vd);
226 278
        for (u = 0; u < vd->n_backend; u++) {
227 184
                if (VRT_Healthy(ctx, vd->backend[u], NULL)) {
228 170
                        vbit_clr(vd->vbm, u);
229 170
                        tw += vd->weight[u];
230
                } else
231 14
                        vbit_set(vd->vbm, u);
232
        }
233 94
        if (tw > 0.0) {
234 92
                u = vdir_pick_by_weight(vd, w * tw, vd->vbm);
235 92
                assert(u < vd->n_backend);
236 92
                be = vd->backend[u];
237 92
                CHECK_OBJ_NOTNULL(be, DIRECTOR_MAGIC);
238
        }
239 94
        vdir_unlock(vd);
240 94
        return (be);
241
}