1
2/*
3 * Copyright (C) Ruslan Ermilov
4 * Copyright (C) Nginx, Inc.
5 */
6
7
8#include <ngx_config.h>
9#include <ngx_core.h>
10#include <ngx_http.h>
11
12
13static char *ngx_http_upstream_zone(ngx_conf_t *cf, ngx_command_t *cmd,
14    void *conf);
15static ngx_int_t ngx_http_upstream_init_zone(ngx_shm_zone_t *shm_zone,
16    void *data);
17static ngx_http_upstream_rr_peers_t *ngx_http_upstream_zone_copy_peers(
18    ngx_slab_pool_t *shpool, ngx_http_upstream_srv_conf_t *uscf);
19
20
21static ngx_command_t  ngx_http_upstream_zone_commands[] = {
22
23    { ngx_string("zone"),
24      NGX_HTTP_UPS_CONF|NGX_CONF_TAKE12,
25      ngx_http_upstream_zone,
26      0,
27      0,
28      NULL },
29
30      ngx_null_command
31};
32
33
34static ngx_http_module_t  ngx_http_upstream_zone_module_ctx = {
35    NULL,                                  /* preconfiguration */
36    NULL,                                  /* postconfiguration */
37
38    NULL,                                  /* create main configuration */
39    NULL,                                  /* init main configuration */
40
41    NULL,                                  /* create server configuration */
42    NULL,                                  /* merge server configuration */
43
44    NULL,                                  /* create location configuration */
45    NULL                                   /* merge location configuration */
46};
47
48
49ngx_module_t  ngx_http_upstream_zone_module = {
50    NGX_MODULE_V1,
51    &ngx_http_upstream_zone_module_ctx,    /* module context */
52    ngx_http_upstream_zone_commands,       /* module directives */
53    NGX_HTTP_MODULE,                       /* module type */
54    NULL,                                  /* init master */
55    NULL,                                  /* init module */
56    NULL,                                  /* init process */
57    NULL,                                  /* init thread */
58    NULL,                                  /* exit thread */
59    NULL,                                  /* exit process */
60    NULL,                                  /* exit master */
61    NGX_MODULE_V1_PADDING
62};
63
64
65static char *
66ngx_http_upstream_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
67{
68    ssize_t                         size;
69    ngx_str_t                      *value;
70    ngx_http_upstream_srv_conf_t   *uscf;
71    ngx_http_upstream_main_conf_t  *umcf;
72
73    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
74    umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module);
75
76    value = cf->args->elts;
77
78    if (!value[1].len) {
79        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
80                           "invalid zone name \"%V\"", &value[1]);
81        return NGX_CONF_ERROR;
82    }
83
84    if (cf->args->nelts == 3) {
85        size = ngx_parse_size(&value[2]);
86
87        if (size == NGX_ERROR) {
88            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
89                               "invalid zone size \"%V\"", &value[2]);
90            return NGX_CONF_ERROR;
91        }
92
93        if (size < (ssize_t) (8 * ngx_pagesize)) {
94            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
95                               "zone \"%V\" is too small", &value[1]);
96            return NGX_CONF_ERROR;
97        }
98
99    } else {
100        size = 0;
101    }
102
103    uscf->shm_zone = ngx_shared_memory_add(cf, &value[1], size,
104                                           &ngx_http_upstream_module);
105    if (uscf->shm_zone == NULL) {
106        return NGX_CONF_ERROR;
107    }
108
109    uscf->shm_zone->init = ngx_http_upstream_init_zone;
110    uscf->shm_zone->data = umcf;
111
112    uscf->shm_zone->noreuse = 1;
113
114    return NGX_CONF_OK;
115}
116
117
118static ngx_int_t
119ngx_http_upstream_init_zone(ngx_shm_zone_t *shm_zone, void *data)
120{
121    size_t                          len;
122    ngx_uint_t                      i;
123    ngx_slab_pool_t                *shpool;
124    ngx_http_upstream_rr_peers_t   *peers, **peersp;
125    ngx_http_upstream_srv_conf_t   *uscf, **uscfp;
126    ngx_http_upstream_main_conf_t  *umcf;
127
128    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
129    umcf = shm_zone->data;
130    uscfp = umcf->upstreams.elts;
131
132    if (shm_zone->shm.exists) {
133        peers = shpool->data;
134
135        for (i = 0; i < umcf->upstreams.nelts; i++) {
136            uscf = uscfp[i];
137
138            if (uscf->shm_zone != shm_zone) {
139                continue;
140            }
141
142            uscf->peer.data = peers;
143            peers = peers->zone_next;
144        }
145
146        return NGX_OK;
147    }
148
149    len = sizeof(" in upstream zone \"\"") + shm_zone->shm.name.len;
150
151    shpool->log_ctx = ngx_slab_alloc(shpool, len);
152    if (shpool->log_ctx == NULL) {
153        return NGX_ERROR;
154    }
155
156    ngx_sprintf(shpool->log_ctx, " in upstream zone \"%V\"%Z",
157                &shm_zone->shm.name);
158
159
160    /* copy peers to shared memory */
161
162    peersp = (ngx_http_upstream_rr_peers_t **) (void *) &shpool->data;
163
164    for (i = 0; i < umcf->upstreams.nelts; i++) {
165        uscf = uscfp[i];
166
167        if (uscf->shm_zone != shm_zone) {
168            continue;
169        }
170
171        peers = ngx_http_upstream_zone_copy_peers(shpool, uscf);
172        if (peers == NULL) {
173            return NGX_ERROR;
174        }
175
176        *peersp = peers;
177        peersp = &peers->zone_next;
178    }
179
180    return NGX_OK;
181}
182
183
184static ngx_http_upstream_rr_peers_t *
185ngx_http_upstream_zone_copy_peers(ngx_slab_pool_t *shpool,
186    ngx_http_upstream_srv_conf_t *uscf)
187{
188    ngx_http_upstream_rr_peer_t   *peer, **peerp;
189    ngx_http_upstream_rr_peers_t  *peers, *backup;
190
191    peers = ngx_slab_alloc(shpool, sizeof(ngx_http_upstream_rr_peers_t));
192    if (peers == NULL) {
193        return NULL;
194    }
195
196    ngx_memcpy(peers, uscf->peer.data, sizeof(ngx_http_upstream_rr_peers_t));
197
198    peers->shpool = shpool;
199
200    for (peerp = &peers->peer; *peerp; peerp = &peer->next) {
201        /* pool is unlocked */
202        peer = ngx_slab_calloc_locked(shpool,
203                                      sizeof(ngx_http_upstream_rr_peer_t));
204        if (peer == NULL) {
205            return NULL;
206        }
207
208        ngx_memcpy(peer, *peerp, sizeof(ngx_http_upstream_rr_peer_t));
209
210        *peerp = peer;
211    }
212
213    if (peers->next == NULL) {
214        goto done;
215    }
216
217    backup = ngx_slab_alloc(shpool, sizeof(ngx_http_upstream_rr_peers_t));
218    if (backup == NULL) {
219        return NULL;
220    }
221
222    ngx_memcpy(backup, peers->next, sizeof(ngx_http_upstream_rr_peers_t));
223
224    backup->shpool = shpool;
225
226    for (peerp = &backup->peer; *peerp; peerp = &peer->next) {
227        /* pool is unlocked */
228        peer = ngx_slab_calloc_locked(shpool,
229                                      sizeof(ngx_http_upstream_rr_peer_t));
230        if (peer == NULL) {
231            return NULL;
232        }
233
234        ngx_memcpy(peer, *peerp, sizeof(ngx_http_upstream_rr_peer_t));
235
236        *peerp = peer;
237    }
238
239    peers->next = backup;
240
241done:
242
243    uscf->peer.data = peers;
244
245    return peers;
246}
247