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
#include "cache/cache_director.h"
35
36
#include "vbm.h"
37
38
#include "vdir.h"
39
40
static void
41 22
vdir_expand(struct vdir *vd, unsigned n)
42
{
43 22
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
44
45 22
        vd->backend = realloc(vd->backend, n * sizeof *vd->backend);
46 22
        AN(vd->backend);
47 22
        vd->weight = realloc(vd->weight, n * sizeof *vd->weight);
48 22
        AN(vd->weight);
49 22
        vd->l_backend = n;
50 22
}
51
52
void
53 23
vdir_new(struct vdir **vdp, const char *name, const char *vcl_name,
54
    vdi_healthy_f *healthy, vdi_resolve_f *resolve, void *priv)
55
{
56
        struct vdir *vd;
57
58 23
        AN(name);
59 23
        AN(vcl_name);
60 23
        AN(vdp);
61 23
        AZ(*vdp);
62 23
        ALLOC_OBJ(vd, VDIR_MAGIC);
63 23
        AN(vd);
64 23
        *vdp = vd;
65 23
        AZ(pthread_rwlock_init(&vd->mtx, NULL));
66
67 23
        ALLOC_OBJ(vd->dir, DIRECTOR_MAGIC);
68 23
        AN(vd->dir);
69 23
        vd->dir->name = name;
70 23
        REPLACE(vd->dir->vcl_name, vcl_name);
71 23
        vd->dir->priv = priv;
72 23
        vd->dir->healthy = healthy;
73 23
        vd->dir->resolve = resolve;
74 23
        vd->vbm = vbit_new(8);
75 23
        AN(vd->vbm);
76 23
}
77
78
void
79 3
vdir_delete(struct vdir **vdp)
80
{
81
        struct vdir *vd;
82
83 3
        TAKE_OBJ_NOTNULL(vd, vdp, VDIR_MAGIC);
84
85 3
        free(vd->backend);
86 3
        free(vd->weight);
87 3
        AZ(pthread_rwlock_destroy(&vd->mtx));
88 3
        free(vd->dir->vcl_name);
89 3
        FREE_OBJ(vd->dir);
90 3
        vbit_destroy(vd->vbm);
91 3
        FREE_OBJ(vd);
92 3
}
93
94
void
95 50
vdir_rdlock(struct vdir *vd)
96
{
97 50
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
98 50
        AZ(pthread_rwlock_rdlock(&vd->mtx));
99 50
}
100
101
void
102 132
vdir_wrlock(struct vdir *vd)
103
{
104 132
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
105 132
        AZ(pthread_rwlock_wrlock(&vd->mtx));
106 132
}
107
108
void
109 182
vdir_unlock(struct vdir *vd)
110
{
111 182
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
112 182
        AZ(pthread_rwlock_unlock(&vd->mtx));
113 182
}
114
115
116
void
117 54
vdir_add_backend(VRT_CTX, struct vdir *vd, VCL_BACKEND be, double weight)
118
{
119
        unsigned u;
120
121 54
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
122 54
        if (be == NULL) {
123 1
                VRT_fail(ctx, "NULL backend cannot be added");
124 55
                return;
125
        }
126 53
        AN(be);
127 53
        vdir_wrlock(vd);
128 53
        if (vd->n_backend >= vd->l_backend)
129 22
                vdir_expand(vd, vd->l_backend + 16);
130 53
        assert(vd->n_backend < vd->l_backend);
131 53
        u = vd->n_backend++;
132 53
        vd->backend[u] = be;
133 53
        vd->weight[u] = weight;
134 53
        vd->total_weight += weight;
135 53
        vdir_unlock(vd);
136
}
137
138
void
139 9
vdir_remove_backend(VRT_CTX, struct vdir *vd, VCL_BACKEND be, unsigned *cur)
140
{
141
        unsigned u, n;
142
143 9
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
144 9
        if (be == NULL) {
145 0
                VRT_fail(ctx, "NULL backend cannot be removed");
146 0
                return;
147
        }
148 9
        CHECK_OBJ(be, DIRECTOR_MAGIC);
149 9
        vdir_wrlock(vd);
150 14
        for (u = 0; u < vd->n_backend; u++)
151 14
                if (vd->backend[u] == be)
152 9
                        break;
153 9
        if (u == vd->n_backend) {
154 0
                vdir_unlock(vd);
155 0
                return;
156
        }
157 9
        vd->total_weight -= vd->weight[u];
158 9
        n = (vd->n_backend - u) - 1;
159 9
        memmove(&vd->backend[u], &vd->backend[u+1], n * sizeof(vd->backend[0]));
160 9
        memmove(&vd->weight[u], &vd->weight[u+1], n * sizeof(vd->weight[0]));
161 9
        vd->n_backend--;
162
163 9
        if (cur) {
164 4
                assert(*cur <= vd->n_backend);
165 4
                if (u < *cur)
166 1
                        (*cur)--;
167 3
                else if (*cur == vd->n_backend)
168 1
                        *cur = 0;
169
        }
170 9
        vdir_unlock(vd);
171
}
172
173
unsigned
174 20
vdir_any_healthy(struct vdir *vd, const struct busyobj *bo, double *changed)
175
{
176 20
        unsigned retval = 0;
177
        VCL_BACKEND be;
178
        unsigned u;
179
        double c;
180
181 20
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
182 20
        CHECK_OBJ_ORNULL(bo, BUSYOBJ_MAGIC);
183 20
        vdir_rdlock(vd);
184 20
        if (changed != NULL)
185 0
                *changed = 0;
186 30
        for (u = 0; u < vd->n_backend; u++) {
187 26
                be = vd->backend[u];
188 26
                CHECK_OBJ_NOTNULL(be, DIRECTOR_MAGIC);
189 26
                retval = be->healthy(be, bo, &c);
190 26
                if (changed != NULL && c > *changed)
191 0
                        *changed = c;
192 26
                if (retval)
193 16
                        break;
194
        }
195 20
        vdir_unlock(vd);
196 20
        return (retval);
197
}
198
199
static unsigned
200 46
vdir_pick_by_weight(const struct vdir *vd, double w,
201
    const struct vbitmap *blacklist)
202
{
203 46
        double a = 0.0;
204 46
        VCL_BACKEND be = NULL;
205
        unsigned u;
206
207 46
        AN(blacklist);
208 71
        for (u = 0; u < vd->n_backend; u++) {
209 71
                be = vd->backend[u];
210 71
                CHECK_OBJ_NOTNULL(be, DIRECTOR_MAGIC);
211 71
                if (vbit_test(blacklist, u))
212 4
                        continue;
213 67
                a += vd->weight[u];
214 67
                if (w < a)
215 92
                        return (u);
216
        }
217 0
        WRONG("");
218
}
219
220
VCL_BACKEND
221 47
vdir_pick_be(struct vdir *vd, double w, const struct busyobj *bo)
222
{
223
        unsigned u;
224 47
        double tw = 0.0;
225 47
        VCL_BACKEND be = NULL;
226
227 47
        vdir_wrlock(vd);
228 139
        for (u = 0; u < vd->n_backend; u++) {
229 92
                if (vd->backend[u]->healthy(vd->backend[u], bo, NULL)) {
230 85
                        vbit_clr(vd->vbm, u);
231 85
                        tw += vd->weight[u];
232
                } else
233 7
                        vbit_set(vd->vbm, u);
234
        }
235 47
        if (tw > 0.0) {
236 46
                u = vdir_pick_by_weight(vd, w * tw, vd->vbm);
237 46
                assert(u < vd->n_backend);
238 46
                be = vd->backend[u];
239 46
                CHECK_OBJ_NOTNULL(be, DIRECTOR_MAGIC);
240
        }
241 47
        vdir_unlock(vd);
242 47
        return (be);
243
}