xref: /unit/src/nxt_port_memory.c (revision 1546)
180Smax.romanov@nginx.com 
280Smax.romanov@nginx.com /*
380Smax.romanov@nginx.com  * Copyright (C) Max Romanov
480Smax.romanov@nginx.com  * Copyright (C) NGINX, Inc.
580Smax.romanov@nginx.com  */
642Smax.romanov@nginx.com 
742Smax.romanov@nginx.com #include <nxt_main.h>
842Smax.romanov@nginx.com 
942Smax.romanov@nginx.com #if (NXT_HAVE_MEMFD_CREATE)
1042Smax.romanov@nginx.com 
1142Smax.romanov@nginx.com #include <linux/memfd.h>
1242Smax.romanov@nginx.com #include <unistd.h>
1342Smax.romanov@nginx.com #include <sys/syscall.h>
1442Smax.romanov@nginx.com 
1542Smax.romanov@nginx.com #endif
1642Smax.romanov@nginx.com 
1780Smax.romanov@nginx.com #include <nxt_port_memory_int.h>
1842Smax.romanov@nginx.com 
19365Smax.romanov@nginx.com 
20364Smax.romanov@nginx.com nxt_inline void
21365Smax.romanov@nginx.com nxt_port_mmap_handler_use(nxt_port_mmap_handler_t *mmap_handler, int i)
2242Smax.romanov@nginx.com {
23613Svbart@nginx.com     int  c;
24365Smax.romanov@nginx.com 
25365Smax.romanov@nginx.com     c = nxt_atomic_fetch_add(&mmap_handler->use_count, i);
26365Smax.romanov@nginx.com 
27365Smax.romanov@nginx.com     if (i < 0 && c == -i) {
28365Smax.romanov@nginx.com         if (mmap_handler->hdr != NULL) {
29365Smax.romanov@nginx.com             nxt_mem_munmap(mmap_handler->hdr, PORT_MMAP_SIZE);
30365Smax.romanov@nginx.com             mmap_handler->hdr = NULL;
31365Smax.romanov@nginx.com         }
32365Smax.romanov@nginx.com 
33365Smax.romanov@nginx.com         nxt_free(mmap_handler);
3442Smax.romanov@nginx.com     }
3542Smax.romanov@nginx.com }
3642Smax.romanov@nginx.com 
3742Smax.romanov@nginx.com 
38364Smax.romanov@nginx.com static nxt_port_mmap_t *
39364Smax.romanov@nginx.com nxt_port_mmap_at(nxt_port_mmaps_t *port_mmaps, uint32_t i)
40141Smax.romanov@nginx.com {
41364Smax.romanov@nginx.com     uint32_t  cap;
42141Smax.romanov@nginx.com 
43364Smax.romanov@nginx.com     cap = port_mmaps->cap;
44141Smax.romanov@nginx.com 
45364Smax.romanov@nginx.com     if (cap == 0) {
46364Smax.romanov@nginx.com         cap = i + 1;
47141Smax.romanov@nginx.com     }
48141Smax.romanov@nginx.com 
49364Smax.romanov@nginx.com     while (i + 1 > cap) {
50364Smax.romanov@nginx.com 
51364Smax.romanov@nginx.com         if (cap < 16) {
521008Szelenkov@nginx.com             cap = cap * 2;
53141Smax.romanov@nginx.com 
54364Smax.romanov@nginx.com         } else {
551008Szelenkov@nginx.com             cap = cap + cap / 2;
56364Smax.romanov@nginx.com         }
57364Smax.romanov@nginx.com     }
58364Smax.romanov@nginx.com 
59364Smax.romanov@nginx.com     if (cap != port_mmaps->cap) {
60141Smax.romanov@nginx.com 
61364Smax.romanov@nginx.com         port_mmaps->elts = nxt_realloc(port_mmaps->elts,
62364Smax.romanov@nginx.com                                        cap * sizeof(nxt_port_mmap_t));
63364Smax.romanov@nginx.com         if (nxt_slow_path(port_mmaps->elts == NULL)) {
64364Smax.romanov@nginx.com             return NULL;
65364Smax.romanov@nginx.com         }
66364Smax.romanov@nginx.com 
67364Smax.romanov@nginx.com         nxt_memzero(port_mmaps->elts + port_mmaps->cap,
68364Smax.romanov@nginx.com                     sizeof(nxt_port_mmap_t) * (cap - port_mmaps->cap));
69141Smax.romanov@nginx.com 
70364Smax.romanov@nginx.com         port_mmaps->cap = cap;
71364Smax.romanov@nginx.com     }
72364Smax.romanov@nginx.com 
73364Smax.romanov@nginx.com     if (i + 1 > port_mmaps->size) {
74364Smax.romanov@nginx.com         port_mmaps->size = i + 1;
75364Smax.romanov@nginx.com     }
76364Smax.romanov@nginx.com 
77364Smax.romanov@nginx.com     return port_mmaps->elts + i;
78141Smax.romanov@nginx.com }
79141Smax.romanov@nginx.com 
80141Smax.romanov@nginx.com 
81141Smax.romanov@nginx.com void
82365Smax.romanov@nginx.com nxt_port_mmaps_destroy(nxt_port_mmaps_t *port_mmaps, nxt_bool_t free_elts)
83141Smax.romanov@nginx.com {
84141Smax.romanov@nginx.com     uint32_t         i;
85141Smax.romanov@nginx.com     nxt_port_mmap_t  *port_mmap;
86141Smax.romanov@nginx.com 
87141Smax.romanov@nginx.com     if (port_mmaps == NULL) {
88141Smax.romanov@nginx.com         return;
89141Smax.romanov@nginx.com     }
90141Smax.romanov@nginx.com 
91141Smax.romanov@nginx.com     port_mmap = port_mmaps->elts;
92141Smax.romanov@nginx.com 
93364Smax.romanov@nginx.com     for (i = 0; i < port_mmaps->size; i++) {
94365Smax.romanov@nginx.com         nxt_port_mmap_handler_use(port_mmap[i].mmap_handler, -1);
95141Smax.romanov@nginx.com     }
96141Smax.romanov@nginx.com 
97364Smax.romanov@nginx.com     port_mmaps->size = 0;
98141Smax.romanov@nginx.com 
99365Smax.romanov@nginx.com     if (free_elts != 0) {
100364Smax.romanov@nginx.com         nxt_free(port_mmaps->elts);
101141Smax.romanov@nginx.com     }
102141Smax.romanov@nginx.com }
103141Smax.romanov@nginx.com 
104141Smax.romanov@nginx.com 
105195Smax.romanov@nginx.com #define nxt_port_mmap_free_junk(p, size)                                      \
106195Smax.romanov@nginx.com     memset((p), 0xA5, size)
107195Smax.romanov@nginx.com 
108195Smax.romanov@nginx.com 
10942Smax.romanov@nginx.com static void
11042Smax.romanov@nginx.com nxt_port_mmap_buf_completion(nxt_task_t *task, void *obj, void *data)
11142Smax.romanov@nginx.com {
112365Smax.romanov@nginx.com     u_char                   *p;
113365Smax.romanov@nginx.com     nxt_mp_t                 *mp;
1141455Smax.romanov@nginx.com     nxt_buf_t                *b, *next;
1151321Smax.romanov@nginx.com     nxt_port_t               *port;
1161321Smax.romanov@nginx.com     nxt_process_t            *process;
117365Smax.romanov@nginx.com     nxt_chunk_id_t           c;
118365Smax.romanov@nginx.com     nxt_port_mmap_header_t   *hdr;
119365Smax.romanov@nginx.com     nxt_port_mmap_handler_t  *mmap_handler;
12042Smax.romanov@nginx.com 
121122Smax.romanov@nginx.com     if (nxt_buf_ts_handle(task, obj, data)) {
122122Smax.romanov@nginx.com         return;
123122Smax.romanov@nginx.com     }
124122Smax.romanov@nginx.com 
12542Smax.romanov@nginx.com     b = obj;
12642Smax.romanov@nginx.com 
127564Svbart@nginx.com     nxt_assert(data == b->parent);
12879Smax.romanov@nginx.com 
129365Smax.romanov@nginx.com     mmap_handler = data;
1301455Smax.romanov@nginx.com 
1311455Smax.romanov@nginx.com complete_buf:
1321455Smax.romanov@nginx.com 
133365Smax.romanov@nginx.com     hdr = mmap_handler->hdr;
13442Smax.romanov@nginx.com 
135363Smax.romanov@nginx.com     if (nxt_slow_path(hdr->src_pid != nxt_pid && hdr->dst_pid != nxt_pid)) {
136363Smax.romanov@nginx.com         nxt_debug(task, "mmap buf completion: mmap for other process pair "
137363Smax.romanov@nginx.com                   "%PI->%PI", hdr->src_pid, hdr->dst_pid);
138363Smax.romanov@nginx.com 
139363Smax.romanov@nginx.com         goto release_buf;
140363Smax.romanov@nginx.com     }
141363Smax.romanov@nginx.com 
14242Smax.romanov@nginx.com     if (b->is_port_mmap_sent && b->mem.pos > b->mem.start) {
14342Smax.romanov@nginx.com         /*
14442Smax.romanov@nginx.com          * Chunks until b->mem.pos has been sent to other side,
14542Smax.romanov@nginx.com          * let's release rest (if any).
14642Smax.romanov@nginx.com          */
14742Smax.romanov@nginx.com         p = b->mem.pos - 1;
14880Smax.romanov@nginx.com         c = nxt_port_mmap_chunk_id(hdr, p) + 1;
14980Smax.romanov@nginx.com         p = nxt_port_mmap_chunk_start(hdr, c);
150141Smax.romanov@nginx.com 
15142Smax.romanov@nginx.com     } else {
15242Smax.romanov@nginx.com         p = b->mem.start;
15380Smax.romanov@nginx.com         c = nxt_port_mmap_chunk_id(hdr, p);
15442Smax.romanov@nginx.com     }
15542Smax.romanov@nginx.com 
156195Smax.romanov@nginx.com     nxt_port_mmap_free_junk(p, b->mem.end - p);
157195Smax.romanov@nginx.com 
158494Spluknet@nginx.com     nxt_debug(task, "mmap buf completion: %p [%p,%uz] (sent=%d), "
159363Smax.romanov@nginx.com               "%PI->%PI,%d,%d", b, b->mem.start, b->mem.end - b->mem.start,
160363Smax.romanov@nginx.com               b->is_port_mmap_sent, hdr->src_pid, hdr->dst_pid, hdr->id, c);
161206Smax.romanov@nginx.com 
16242Smax.romanov@nginx.com     while (p < b->mem.end) {
163423Smax.romanov@nginx.com         nxt_port_mmap_set_chunk_free(hdr->free_map, c);
16442Smax.romanov@nginx.com 
16542Smax.romanov@nginx.com         p += PORT_MMAP_CHUNK_SIZE;
16642Smax.romanov@nginx.com         c++;
16742Smax.romanov@nginx.com     }
16842Smax.romanov@nginx.com 
1691321Smax.romanov@nginx.com     if (hdr->dst_pid == nxt_pid
1701321Smax.romanov@nginx.com         && nxt_atomic_cmp_set(&hdr->oosm, 1, 0))
1711321Smax.romanov@nginx.com     {
1721321Smax.romanov@nginx.com         process = nxt_runtime_process_find(task->thread->runtime, hdr->src_pid);
1731321Smax.romanov@nginx.com 
1741321Smax.romanov@nginx.com         if (process != NULL && !nxt_queue_is_empty(&process->ports)) {
1751321Smax.romanov@nginx.com             port = nxt_process_port_first(process);
1761321Smax.romanov@nginx.com 
1771488St.nateldemoura@f5.com             if (port->type == NXT_PROCESS_APP) {
1781321Smax.romanov@nginx.com                 (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_SHM_ACK,
1791321Smax.romanov@nginx.com                                              -1, 0, 0, NULL);
1801321Smax.romanov@nginx.com             }
1811321Smax.romanov@nginx.com         }
1821321Smax.romanov@nginx.com     }
1831321Smax.romanov@nginx.com 
184363Smax.romanov@nginx.com release_buf:
185363Smax.romanov@nginx.com 
186365Smax.romanov@nginx.com     nxt_port_mmap_handler_use(mmap_handler, -1);
187365Smax.romanov@nginx.com 
1881455Smax.romanov@nginx.com     next = b->next;
1891455Smax.romanov@nginx.com     mp = b->data;
1901455Smax.romanov@nginx.com 
191430Sigor@sysoev.ru     nxt_mp_free(mp, b);
192430Sigor@sysoev.ru     nxt_mp_release(mp);
1931455Smax.romanov@nginx.com 
1941455Smax.romanov@nginx.com     if (next != NULL) {
1951455Smax.romanov@nginx.com         b = next;
1961455Smax.romanov@nginx.com         mmap_handler = b->parent;
1971455Smax.romanov@nginx.com 
1981455Smax.romanov@nginx.com         goto complete_buf;
1991455Smax.romanov@nginx.com     }
20042Smax.romanov@nginx.com }
20142Smax.romanov@nginx.com 
20242Smax.romanov@nginx.com 
203365Smax.romanov@nginx.com nxt_port_mmap_handler_t *
20442Smax.romanov@nginx.com nxt_port_incoming_port_mmap(nxt_task_t *task, nxt_process_t *process,
20542Smax.romanov@nginx.com     nxt_fd_t fd)
20642Smax.romanov@nginx.com {
207365Smax.romanov@nginx.com     void                     *mem;
208365Smax.romanov@nginx.com     struct stat              mmap_stat;
209365Smax.romanov@nginx.com     nxt_port_mmap_t          *port_mmap;
210365Smax.romanov@nginx.com     nxt_port_mmap_header_t   *hdr;
211365Smax.romanov@nginx.com     nxt_port_mmap_handler_t  *mmap_handler;
21242Smax.romanov@nginx.com 
21342Smax.romanov@nginx.com     nxt_debug(task, "got new mmap fd #%FD from process %PI",
21442Smax.romanov@nginx.com               fd, process->pid);
21542Smax.romanov@nginx.com 
21680Smax.romanov@nginx.com     port_mmap = NULL;
21780Smax.romanov@nginx.com 
21842Smax.romanov@nginx.com     if (fstat(fd, &mmap_stat) == -1) {
21942Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN, "fstat(%FD) failed %E", fd, nxt_errno);
22042Smax.romanov@nginx.com 
22142Smax.romanov@nginx.com         return NULL;
22242Smax.romanov@nginx.com     }
22342Smax.romanov@nginx.com 
22480Smax.romanov@nginx.com     mem = nxt_mem_mmap(NULL, mmap_stat.st_size,
22580Smax.romanov@nginx.com                        PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
22642Smax.romanov@nginx.com 
22780Smax.romanov@nginx.com     if (nxt_slow_path(mem == MAP_FAILED)) {
22842Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN, "mmap() failed %E", nxt_errno);
22942Smax.romanov@nginx.com 
230364Smax.romanov@nginx.com         return NULL;
23142Smax.romanov@nginx.com     }
23242Smax.romanov@nginx.com 
233364Smax.romanov@nginx.com     hdr = mem;
234364Smax.romanov@nginx.com 
235365Smax.romanov@nginx.com     mmap_handler = nxt_zalloc(sizeof(nxt_port_mmap_handler_t));
236365Smax.romanov@nginx.com     if (nxt_slow_path(mmap_handler == NULL)) {
237365Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN, "failed to allocate mmap_handler");
238365Smax.romanov@nginx.com 
239382Smax.romanov@nginx.com         nxt_mem_munmap(mem, PORT_MMAP_SIZE);
240382Smax.romanov@nginx.com 
241365Smax.romanov@nginx.com         return NULL;
242365Smax.romanov@nginx.com     }
243365Smax.romanov@nginx.com 
244365Smax.romanov@nginx.com     mmap_handler->hdr = hdr;
245365Smax.romanov@nginx.com 
246551Smax.romanov@nginx.com     if (nxt_slow_path(hdr->src_pid != process->pid
247551Smax.romanov@nginx.com                       || hdr->dst_pid != nxt_pid))
248551Smax.romanov@nginx.com     {
249551Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN, "unexpected pid in mmap header detected: "
250551Smax.romanov@nginx.com                 "%PI != %PI or %PI != %PI", hdr->src_pid, process->pid,
251551Smax.romanov@nginx.com                 hdr->dst_pid, nxt_pid);
252551Smax.romanov@nginx.com 
253551Smax.romanov@nginx.com         return NULL;
254551Smax.romanov@nginx.com     }
255551Smax.romanov@nginx.com 
256364Smax.romanov@nginx.com     nxt_thread_mutex_lock(&process->incoming.mutex);
25780Smax.romanov@nginx.com 
258364Smax.romanov@nginx.com     port_mmap = nxt_port_mmap_at(&process->incoming, hdr->id);
259364Smax.romanov@nginx.com     if (nxt_slow_path(port_mmap == NULL)) {
260364Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN, "failed to add mmap to incoming array");
261364Smax.romanov@nginx.com 
262364Smax.romanov@nginx.com         nxt_mem_munmap(mem, PORT_MMAP_SIZE);
263364Smax.romanov@nginx.com         hdr = NULL;
264364Smax.romanov@nginx.com 
265365Smax.romanov@nginx.com         nxt_free(mmap_handler);
266365Smax.romanov@nginx.com         mmap_handler = NULL;
267365Smax.romanov@nginx.com 
268364Smax.romanov@nginx.com         goto fail;
26980Smax.romanov@nginx.com     }
27080Smax.romanov@nginx.com 
271365Smax.romanov@nginx.com     port_mmap->mmap_handler = mmap_handler;
272365Smax.romanov@nginx.com     nxt_port_mmap_handler_use(mmap_handler, 1);
273364Smax.romanov@nginx.com 
274323Smax.romanov@nginx.com     hdr->sent_over = 0xFFFFu;
275323Smax.romanov@nginx.com 
27680Smax.romanov@nginx.com fail:
27780Smax.romanov@nginx.com 
278364Smax.romanov@nginx.com     nxt_thread_mutex_unlock(&process->incoming.mutex);
27990Smax.romanov@nginx.com 
280365Smax.romanov@nginx.com     return mmap_handler;
28142Smax.romanov@nginx.com }
28242Smax.romanov@nginx.com 
28342Smax.romanov@nginx.com 
284365Smax.romanov@nginx.com static nxt_port_mmap_handler_t *
285*1546Smax.romanov@nginx.com nxt_port_new_port_mmap(nxt_task_t *task, nxt_port_mmaps_t *mmaps,
286*1546Smax.romanov@nginx.com     nxt_bool_t tracking, nxt_int_t n)
28742Smax.romanov@nginx.com {
288365Smax.romanov@nginx.com     void                     *mem;
289365Smax.romanov@nginx.com     nxt_fd_t                 fd;
290723Smax.romanov@nginx.com     nxt_int_t                i;
291423Smax.romanov@nginx.com     nxt_free_map_t           *free_map;
292365Smax.romanov@nginx.com     nxt_port_mmap_t          *port_mmap;
293365Smax.romanov@nginx.com     nxt_port_mmap_header_t   *hdr;
294365Smax.romanov@nginx.com     nxt_port_mmap_handler_t  *mmap_handler;
295365Smax.romanov@nginx.com 
296365Smax.romanov@nginx.com     mmap_handler = nxt_zalloc(sizeof(nxt_port_mmap_handler_t));
297365Smax.romanov@nginx.com     if (nxt_slow_path(mmap_handler == NULL)) {
298*1546Smax.romanov@nginx.com         nxt_alert(task, "failed to allocate mmap_handler");
299365Smax.romanov@nginx.com 
300365Smax.romanov@nginx.com         return NULL;
301365Smax.romanov@nginx.com     }
30242Smax.romanov@nginx.com 
303*1546Smax.romanov@nginx.com     port_mmap = nxt_port_mmap_at(mmaps, mmaps->size);
30442Smax.romanov@nginx.com     if (nxt_slow_path(port_mmap == NULL)) {
305*1546Smax.romanov@nginx.com         nxt_alert(task, "failed to add port mmap to mmaps array");
30642Smax.romanov@nginx.com 
307365Smax.romanov@nginx.com         nxt_free(mmap_handler);
30842Smax.romanov@nginx.com         return NULL;
30942Smax.romanov@nginx.com     }
31042Smax.romanov@nginx.com 
3111526Smax.romanov@nginx.com     fd = nxt_shm_open(task, PORT_MMAP_SIZE);
312566Spluknet@nginx.com     if (nxt_slow_path(fd == -1)) {
31342Smax.romanov@nginx.com         goto remove_fail;
31442Smax.romanov@nginx.com     }
31542Smax.romanov@nginx.com 
316277Sigor@sysoev.ru     mem = nxt_mem_mmap(NULL, PORT_MMAP_SIZE, PROT_READ | PROT_WRITE,
317277Sigor@sysoev.ru                        MAP_SHARED, fd, 0);
31842Smax.romanov@nginx.com 
31980Smax.romanov@nginx.com     if (nxt_slow_path(mem == MAP_FAILED)) {
32042Smax.romanov@nginx.com         goto remove_fail;
32142Smax.romanov@nginx.com     }
32242Smax.romanov@nginx.com 
323365Smax.romanov@nginx.com     mmap_handler->hdr = mem;
324*1546Smax.romanov@nginx.com     mmap_handler->fd = fd;
325365Smax.romanov@nginx.com     port_mmap->mmap_handler = mmap_handler;
326365Smax.romanov@nginx.com     nxt_port_mmap_handler_use(mmap_handler, 1);
32780Smax.romanov@nginx.com 
32842Smax.romanov@nginx.com     /* Init segment header. */
329365Smax.romanov@nginx.com     hdr = mmap_handler->hdr;
33042Smax.romanov@nginx.com 
33142Smax.romanov@nginx.com     nxt_memset(hdr->free_map, 0xFFU, sizeof(hdr->free_map));
332423Smax.romanov@nginx.com     nxt_memset(hdr->free_tracking_map, 0xFFU, sizeof(hdr->free_tracking_map));
33342Smax.romanov@nginx.com 
334*1546Smax.romanov@nginx.com     hdr->id = mmaps->size - 1;
335363Smax.romanov@nginx.com     hdr->src_pid = nxt_pid;
336*1546Smax.romanov@nginx.com     hdr->sent_over = 0xFFFFu;
33780Smax.romanov@nginx.com 
33880Smax.romanov@nginx.com     /* Mark first chunk as busy */
339423Smax.romanov@nginx.com     free_map = tracking ? hdr->free_tracking_map : hdr->free_map;
340423Smax.romanov@nginx.com 
341723Smax.romanov@nginx.com     for (i = 0; i < n; i++) {
342723Smax.romanov@nginx.com         nxt_port_mmap_set_chunk_busy(free_map, i);
343723Smax.romanov@nginx.com     }
34480Smax.romanov@nginx.com 
34542Smax.romanov@nginx.com     /* Mark as busy chunk followed the last available chunk. */
346423Smax.romanov@nginx.com     nxt_port_mmap_set_chunk_busy(hdr->free_map, PORT_MMAP_CHUNK_COUNT);
347423Smax.romanov@nginx.com     nxt_port_mmap_set_chunk_busy(hdr->free_tracking_map, PORT_MMAP_CHUNK_COUNT);
34842Smax.romanov@nginx.com 
349*1546Smax.romanov@nginx.com     nxt_log(task, NXT_LOG_DEBUG, "new mmap #%D created for %PI -> ...",
350*1546Smax.romanov@nginx.com             hdr->id, nxt_pid);
35142Smax.romanov@nginx.com 
352365Smax.romanov@nginx.com     return mmap_handler;
35342Smax.romanov@nginx.com 
35442Smax.romanov@nginx.com remove_fail:
35542Smax.romanov@nginx.com 
356365Smax.romanov@nginx.com     nxt_free(mmap_handler);
357365Smax.romanov@nginx.com 
358*1546Smax.romanov@nginx.com     mmaps->size--;
35942Smax.romanov@nginx.com 
36042Smax.romanov@nginx.com     return NULL;
36142Smax.romanov@nginx.com }
36242Smax.romanov@nginx.com 
36342Smax.romanov@nginx.com 
3641526Smax.romanov@nginx.com nxt_int_t
3651526Smax.romanov@nginx.com nxt_shm_open(nxt_task_t *task, size_t size)
3661526Smax.romanov@nginx.com {
3671526Smax.romanov@nginx.com     nxt_fd_t  fd;
3681526Smax.romanov@nginx.com 
3691526Smax.romanov@nginx.com #if (NXT_HAVE_MEMFD_CREATE || NXT_HAVE_SHM_OPEN)
3701526Smax.romanov@nginx.com 
3711526Smax.romanov@nginx.com     u_char    *p, name[64];
3721526Smax.romanov@nginx.com 
3731526Smax.romanov@nginx.com     p = nxt_sprintf(name, name + sizeof(name), NXT_SHM_PREFIX "unit.%PI.%uxD",
3741526Smax.romanov@nginx.com                     nxt_pid, nxt_random(&task->thread->random));
3751526Smax.romanov@nginx.com     *p = '\0';
3761526Smax.romanov@nginx.com 
3771526Smax.romanov@nginx.com #endif
3781526Smax.romanov@nginx.com 
3791526Smax.romanov@nginx.com #if (NXT_HAVE_MEMFD_CREATE)
3801526Smax.romanov@nginx.com 
3811526Smax.romanov@nginx.com     fd = syscall(SYS_memfd_create, name, MFD_CLOEXEC);
3821526Smax.romanov@nginx.com 
3831526Smax.romanov@nginx.com     if (nxt_slow_path(fd == -1)) {
3841526Smax.romanov@nginx.com         nxt_alert(task, "memfd_create(%s) failed %E", name, nxt_errno);
3851526Smax.romanov@nginx.com 
3861526Smax.romanov@nginx.com         return -1;
3871526Smax.romanov@nginx.com     }
3881526Smax.romanov@nginx.com 
3891526Smax.romanov@nginx.com     nxt_debug(task, "memfd_create(%s): %FD", name, fd);
3901526Smax.romanov@nginx.com 
3911526Smax.romanov@nginx.com #elif (NXT_HAVE_SHM_OPEN_ANON)
3921526Smax.romanov@nginx.com 
3931526Smax.romanov@nginx.com     fd = shm_open(SHM_ANON, O_RDWR, S_IRUSR | S_IWUSR);
3941526Smax.romanov@nginx.com 
3951526Smax.romanov@nginx.com     if (nxt_slow_path(fd == -1)) {
3961526Smax.romanov@nginx.com         nxt_alert(task, "shm_open(SHM_ANON) failed %E", nxt_errno);
3971526Smax.romanov@nginx.com 
3981526Smax.romanov@nginx.com         return -1;
3991526Smax.romanov@nginx.com     }
4001526Smax.romanov@nginx.com 
4011526Smax.romanov@nginx.com     nxt_debug(task, "shm_open(SHM_ANON): %FD", fd);
4021526Smax.romanov@nginx.com 
4031526Smax.romanov@nginx.com #elif (NXT_HAVE_SHM_OPEN)
4041526Smax.romanov@nginx.com 
4051526Smax.romanov@nginx.com     /* Just in case. */
4061526Smax.romanov@nginx.com     shm_unlink((char *) name);
4071526Smax.romanov@nginx.com 
4081526Smax.romanov@nginx.com     fd = shm_open((char *) name, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
4091526Smax.romanov@nginx.com 
4101526Smax.romanov@nginx.com     if (nxt_slow_path(fd == -1)) {
4111526Smax.romanov@nginx.com         nxt_alert(task, "shm_open(%s) failed %E", name, nxt_errno);
4121526Smax.romanov@nginx.com 
4131526Smax.romanov@nginx.com         return -1;
4141526Smax.romanov@nginx.com     }
4151526Smax.romanov@nginx.com 
4161526Smax.romanov@nginx.com     nxt_debug(task, "shm_open(%s): %FD", name, fd);
4171526Smax.romanov@nginx.com 
4181526Smax.romanov@nginx.com     if (nxt_slow_path(shm_unlink((char *) name) == -1)) {
4191526Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN, "shm_unlink(%s) failed %E", name,
4201526Smax.romanov@nginx.com                 nxt_errno);
4211526Smax.romanov@nginx.com     }
4221526Smax.romanov@nginx.com 
4231526Smax.romanov@nginx.com #else
4241526Smax.romanov@nginx.com 
4251526Smax.romanov@nginx.com #error No working shared memory implementation.
4261526Smax.romanov@nginx.com 
4271526Smax.romanov@nginx.com #endif
4281526Smax.romanov@nginx.com 
4291526Smax.romanov@nginx.com     if (nxt_slow_path(ftruncate(fd, size) == -1)) {
4301526Smax.romanov@nginx.com         nxt_alert(task, "ftruncate() failed %E", nxt_errno);
4311526Smax.romanov@nginx.com 
4321526Smax.romanov@nginx.com         nxt_fd_close(fd);
4331526Smax.romanov@nginx.com 
4341526Smax.romanov@nginx.com         return -1;
4351526Smax.romanov@nginx.com     }
4361526Smax.romanov@nginx.com 
4371526Smax.romanov@nginx.com     return fd;
4381526Smax.romanov@nginx.com }
4391526Smax.romanov@nginx.com 
4401526Smax.romanov@nginx.com 
441365Smax.romanov@nginx.com static nxt_port_mmap_handler_t *
442*1546Smax.romanov@nginx.com nxt_port_mmap_get(nxt_task_t *task, nxt_port_mmaps_t *mmaps, nxt_chunk_id_t *c,
443699Smax.romanov@nginx.com     nxt_int_t n, nxt_bool_t tracking)
44442Smax.romanov@nginx.com {
445699Smax.romanov@nginx.com     nxt_int_t                i, res, nchunks;
446423Smax.romanov@nginx.com     nxt_free_map_t           *free_map;
447365Smax.romanov@nginx.com     nxt_port_mmap_t          *port_mmap;
448365Smax.romanov@nginx.com     nxt_port_mmap_t          *end_port_mmap;
449365Smax.romanov@nginx.com     nxt_port_mmap_header_t   *hdr;
450365Smax.romanov@nginx.com     nxt_port_mmap_handler_t  *mmap_handler;
45142Smax.romanov@nginx.com 
452*1546Smax.romanov@nginx.com     nxt_thread_mutex_lock(&mmaps->mutex);
45342Smax.romanov@nginx.com 
454*1546Smax.romanov@nginx.com     end_port_mmap = mmaps->elts + mmaps->size;
45580Smax.romanov@nginx.com 
456*1546Smax.romanov@nginx.com     for (port_mmap = mmaps->elts;
457365Smax.romanov@nginx.com          port_mmap < end_port_mmap;
458365Smax.romanov@nginx.com          port_mmap++)
459365Smax.romanov@nginx.com     {
460365Smax.romanov@nginx.com         mmap_handler = port_mmap->mmap_handler;
461365Smax.romanov@nginx.com         hdr = mmap_handler->hdr;
46280Smax.romanov@nginx.com 
463*1546Smax.romanov@nginx.com         if (hdr->sent_over != 0xFFFFu) {
464365Smax.romanov@nginx.com             continue;
465365Smax.romanov@nginx.com         }
466365Smax.romanov@nginx.com 
467699Smax.romanov@nginx.com         *c = 0;
468699Smax.romanov@nginx.com 
469423Smax.romanov@nginx.com         free_map = tracking ? hdr->free_tracking_map : hdr->free_map;
470423Smax.romanov@nginx.com 
471699Smax.romanov@nginx.com         while (nxt_port_mmap_get_free_chunk(free_map, c)) {
472699Smax.romanov@nginx.com             nchunks = 1;
473699Smax.romanov@nginx.com 
474699Smax.romanov@nginx.com             while (nchunks < n) {
475699Smax.romanov@nginx.com                 res = nxt_port_mmap_chk_set_chunk_busy(free_map, *c + nchunks);
476699Smax.romanov@nginx.com 
477699Smax.romanov@nginx.com                 if (res == 0) {
478699Smax.romanov@nginx.com                     for (i = 0; i < nchunks; i++) {
479699Smax.romanov@nginx.com                         nxt_port_mmap_set_chunk_free(free_map, *c + i);
480699Smax.romanov@nginx.com                     }
481699Smax.romanov@nginx.com 
482699Smax.romanov@nginx.com                     *c += nchunks + 1;
483699Smax.romanov@nginx.com                     nchunks = 0;
484699Smax.romanov@nginx.com                     break;
485699Smax.romanov@nginx.com                 }
486699Smax.romanov@nginx.com 
487699Smax.romanov@nginx.com                 nchunks++;
488699Smax.romanov@nginx.com             }
489699Smax.romanov@nginx.com 
490699Smax.romanov@nginx.com             if (nchunks == n) {
491699Smax.romanov@nginx.com                 goto unlock_return;
492699Smax.romanov@nginx.com             }
49342Smax.romanov@nginx.com         }
4941321Smax.romanov@nginx.com 
4951321Smax.romanov@nginx.com         hdr->oosm = 1;
49642Smax.romanov@nginx.com     }
49742Smax.romanov@nginx.com 
49842Smax.romanov@nginx.com     /* TODO introduce port_mmap limit and release wait. */
49980Smax.romanov@nginx.com 
500699Smax.romanov@nginx.com     *c = 0;
501*1546Smax.romanov@nginx.com     mmap_handler = nxt_port_new_port_mmap(task, mmaps, tracking, n);
50280Smax.romanov@nginx.com 
50380Smax.romanov@nginx.com unlock_return:
50480Smax.romanov@nginx.com 
505*1546Smax.romanov@nginx.com     nxt_thread_mutex_unlock(&mmaps->mutex);
50690Smax.romanov@nginx.com 
507365Smax.romanov@nginx.com     return mmap_handler;
50842Smax.romanov@nginx.com }
50942Smax.romanov@nginx.com 
51042Smax.romanov@nginx.com 
511365Smax.romanov@nginx.com static nxt_port_mmap_handler_t *
51242Smax.romanov@nginx.com nxt_port_get_port_incoming_mmap(nxt_task_t *task, nxt_pid_t spid, uint32_t id)
51342Smax.romanov@nginx.com {
514365Smax.romanov@nginx.com     nxt_process_t            *process;
515365Smax.romanov@nginx.com     nxt_port_mmap_handler_t  *mmap_handler;
51642Smax.romanov@nginx.com 
517196Smax.romanov@nginx.com     process = nxt_runtime_process_find(task->thread->runtime, spid);
51842Smax.romanov@nginx.com     if (nxt_slow_path(process == NULL)) {
51942Smax.romanov@nginx.com         return NULL;
52042Smax.romanov@nginx.com     }
52142Smax.romanov@nginx.com 
522364Smax.romanov@nginx.com     nxt_thread_mutex_lock(&process->incoming.mutex);
52380Smax.romanov@nginx.com 
524364Smax.romanov@nginx.com     if (nxt_fast_path(process->incoming.size > id)) {
525365Smax.romanov@nginx.com         mmap_handler = process->incoming.elts[id].mmap_handler;
526423Smax.romanov@nginx.com 
527423Smax.romanov@nginx.com     } else {
528423Smax.romanov@nginx.com         mmap_handler = NULL;
529423Smax.romanov@nginx.com 
530423Smax.romanov@nginx.com         nxt_debug(task, "invalid incoming mmap id %uD for pid %PI", id, spid);
53142Smax.romanov@nginx.com     }
53242Smax.romanov@nginx.com 
533364Smax.romanov@nginx.com     nxt_thread_mutex_unlock(&process->incoming.mutex);
53490Smax.romanov@nginx.com 
535365Smax.romanov@nginx.com     return mmap_handler;
53642Smax.romanov@nginx.com }
53742Smax.romanov@nginx.com 
53842Smax.romanov@nginx.com 
539423Smax.romanov@nginx.com nxt_int_t
540*1546Smax.romanov@nginx.com nxt_port_mmap_get_tracking(nxt_task_t *task, nxt_port_mmaps_t *mmaps,
541423Smax.romanov@nginx.com     nxt_port_mmap_tracking_t *tracking, uint32_t stream)
542423Smax.romanov@nginx.com {
543423Smax.romanov@nginx.com     nxt_chunk_id_t           c;
544423Smax.romanov@nginx.com     nxt_port_mmap_header_t   *hdr;
545423Smax.romanov@nginx.com     nxt_port_mmap_handler_t  *mmap_handler;
546423Smax.romanov@nginx.com 
547423Smax.romanov@nginx.com     nxt_debug(task, "request tracking for stream #%uD", stream);
548423Smax.romanov@nginx.com 
549*1546Smax.romanov@nginx.com     mmap_handler = nxt_port_mmap_get(task, mmaps, &c, 1, 1);
550423Smax.romanov@nginx.com     if (nxt_slow_path(mmap_handler == NULL)) {
551423Smax.romanov@nginx.com         return NXT_ERROR;
552423Smax.romanov@nginx.com     }
553423Smax.romanov@nginx.com 
554423Smax.romanov@nginx.com     nxt_port_mmap_handler_use(mmap_handler, 1);
555423Smax.romanov@nginx.com 
556423Smax.romanov@nginx.com     hdr = mmap_handler->hdr;
557423Smax.romanov@nginx.com 
558423Smax.romanov@nginx.com     tracking->mmap_handler = mmap_handler;
559423Smax.romanov@nginx.com     tracking->tracking = hdr->tracking + c;
560423Smax.romanov@nginx.com 
561423Smax.romanov@nginx.com     *tracking->tracking = stream;
562423Smax.romanov@nginx.com 
563423Smax.romanov@nginx.com     nxt_debug(task, "outgoing tracking allocation: %PI->%PI,%d,%d",
564423Smax.romanov@nginx.com               hdr->src_pid, hdr->dst_pid, hdr->id, c);
565423Smax.romanov@nginx.com 
566423Smax.romanov@nginx.com     return NXT_OK;
567423Smax.romanov@nginx.com }
568423Smax.romanov@nginx.com 
569423Smax.romanov@nginx.com 
570423Smax.romanov@nginx.com nxt_bool_t
571423Smax.romanov@nginx.com nxt_port_mmap_tracking_cancel(nxt_task_t *task,
572423Smax.romanov@nginx.com     nxt_port_mmap_tracking_t *tracking, uint32_t stream)
573423Smax.romanov@nginx.com {
574423Smax.romanov@nginx.com     nxt_bool_t               res;
575423Smax.romanov@nginx.com     nxt_chunk_id_t           c;
576423Smax.romanov@nginx.com     nxt_port_mmap_header_t   *hdr;
577423Smax.romanov@nginx.com     nxt_port_mmap_handler_t  *mmap_handler;
578423Smax.romanov@nginx.com 
579423Smax.romanov@nginx.com     mmap_handler = tracking->mmap_handler;
580423Smax.romanov@nginx.com 
581423Smax.romanov@nginx.com     if (nxt_slow_path(mmap_handler == NULL)) {
582423Smax.romanov@nginx.com         return 0;
583423Smax.romanov@nginx.com     }
584423Smax.romanov@nginx.com 
585423Smax.romanov@nginx.com     hdr = mmap_handler->hdr;
586423Smax.romanov@nginx.com 
587423Smax.romanov@nginx.com     res = nxt_atomic_cmp_set(tracking->tracking, stream, 0);
588423Smax.romanov@nginx.com 
589423Smax.romanov@nginx.com     nxt_debug(task, "%s tracking for stream #%uD",
590423Smax.romanov@nginx.com               (res ? "cancelled" : "failed to cancel"), stream);
591423Smax.romanov@nginx.com 
592423Smax.romanov@nginx.com     if (!res) {
593423Smax.romanov@nginx.com         c = tracking->tracking - hdr->tracking;
594423Smax.romanov@nginx.com         nxt_port_mmap_set_chunk_free(hdr->free_tracking_map, c);
595423Smax.romanov@nginx.com     }
596423Smax.romanov@nginx.com 
597423Smax.romanov@nginx.com     nxt_port_mmap_handler_use(mmap_handler, -1);
598423Smax.romanov@nginx.com 
599423Smax.romanov@nginx.com     return res;
600423Smax.romanov@nginx.com }
601423Smax.romanov@nginx.com 
602423Smax.romanov@nginx.com 
603423Smax.romanov@nginx.com nxt_int_t
604423Smax.romanov@nginx.com nxt_port_mmap_tracking_write(uint32_t *buf, nxt_port_mmap_tracking_t *t)
605423Smax.romanov@nginx.com {
606423Smax.romanov@nginx.com     nxt_port_mmap_handler_t  *mmap_handler;
607423Smax.romanov@nginx.com 
608423Smax.romanov@nginx.com     mmap_handler = t->mmap_handler;
609538Svbart@nginx.com 
610538Svbart@nginx.com #if (NXT_DEBUG)
611538Svbart@nginx.com     {
612538Svbart@nginx.com     nxt_atomic_t  *tracking;
613538Svbart@nginx.com 
614423Smax.romanov@nginx.com     tracking = mmap_handler->hdr->tracking;
615423Smax.romanov@nginx.com 
616423Smax.romanov@nginx.com     nxt_assert(t->tracking >= tracking);
617423Smax.romanov@nginx.com     nxt_assert(t->tracking < tracking + PORT_MMAP_CHUNK_COUNT);
618538Svbart@nginx.com     }
619538Svbart@nginx.com #endif
620423Smax.romanov@nginx.com 
621423Smax.romanov@nginx.com     buf[0] = mmap_handler->hdr->id;
622423Smax.romanov@nginx.com     buf[1] = t->tracking - mmap_handler->hdr->tracking;
623423Smax.romanov@nginx.com 
624423Smax.romanov@nginx.com     return NXT_OK;
625423Smax.romanov@nginx.com }
626423Smax.romanov@nginx.com 
627423Smax.romanov@nginx.com nxt_bool_t
628423Smax.romanov@nginx.com nxt_port_mmap_tracking_read(nxt_task_t *task, nxt_port_recv_msg_t *msg)
629423Smax.romanov@nginx.com {
630423Smax.romanov@nginx.com     nxt_buf_t                     *b;
631423Smax.romanov@nginx.com     nxt_bool_t                    res;
632423Smax.romanov@nginx.com     nxt_chunk_id_t                c;
633423Smax.romanov@nginx.com     nxt_port_mmap_header_t        *hdr;
634423Smax.romanov@nginx.com     nxt_port_mmap_handler_t       *mmap_handler;
635423Smax.romanov@nginx.com     nxt_port_mmap_tracking_msg_t  *tracking_msg;
636423Smax.romanov@nginx.com 
637423Smax.romanov@nginx.com     b = msg->buf;
638423Smax.romanov@nginx.com 
639521Szelenkov@nginx.com     if (nxt_buf_used_size(b) < (int) sizeof(nxt_port_mmap_tracking_msg_t)) {
640494Spluknet@nginx.com         nxt_debug(task, "too small message %O", nxt_buf_used_size(b));
641423Smax.romanov@nginx.com         return 0;
642423Smax.romanov@nginx.com     }
643423Smax.romanov@nginx.com 
644423Smax.romanov@nginx.com     tracking_msg = (nxt_port_mmap_tracking_msg_t *) b->mem.pos;
645423Smax.romanov@nginx.com 
646423Smax.romanov@nginx.com     b->mem.pos += sizeof(nxt_port_mmap_tracking_msg_t);
647423Smax.romanov@nginx.com     mmap_handler = nxt_port_get_port_incoming_mmap(task, msg->port_msg.pid,
648423Smax.romanov@nginx.com                                                    tracking_msg->mmap_id);
649423Smax.romanov@nginx.com 
650423Smax.romanov@nginx.com     if (nxt_slow_path(mmap_handler == NULL)) {
651423Smax.romanov@nginx.com         return 0;
652423Smax.romanov@nginx.com     }
653423Smax.romanov@nginx.com 
654423Smax.romanov@nginx.com     hdr = mmap_handler->hdr;
655423Smax.romanov@nginx.com 
656423Smax.romanov@nginx.com     c = tracking_msg->tracking_id;
657423Smax.romanov@nginx.com     res = nxt_atomic_cmp_set(hdr->tracking + c, msg->port_msg.stream, 0);
658423Smax.romanov@nginx.com 
659423Smax.romanov@nginx.com     nxt_debug(task, "tracking for stream #%uD %s", msg->port_msg.stream,
660423Smax.romanov@nginx.com               (res ? "received" : "already cancelled"));
661423Smax.romanov@nginx.com 
662423Smax.romanov@nginx.com     if (!res) {
663423Smax.romanov@nginx.com         nxt_port_mmap_set_chunk_free(hdr->free_tracking_map, c);
664423Smax.romanov@nginx.com     }
665423Smax.romanov@nginx.com 
666423Smax.romanov@nginx.com     return res;
667423Smax.romanov@nginx.com }
668423Smax.romanov@nginx.com 
669423Smax.romanov@nginx.com 
67042Smax.romanov@nginx.com nxt_buf_t *
671*1546Smax.romanov@nginx.com nxt_port_mmap_get_buf(nxt_task_t *task, nxt_port_mmaps_t *mmaps, size_t size)
67242Smax.romanov@nginx.com {
673430Sigor@sysoev.ru     nxt_mp_t                 *mp;
674365Smax.romanov@nginx.com     nxt_buf_t                *b;
675699Smax.romanov@nginx.com     nxt_int_t                nchunks;
676365Smax.romanov@nginx.com     nxt_chunk_id_t           c;
677365Smax.romanov@nginx.com     nxt_port_mmap_header_t   *hdr;
678365Smax.romanov@nginx.com     nxt_port_mmap_handler_t  *mmap_handler;
67942Smax.romanov@nginx.com 
68042Smax.romanov@nginx.com     nxt_debug(task, "request %z bytes shm buffer", size);
68142Smax.romanov@nginx.com 
682699Smax.romanov@nginx.com     nchunks = (size + PORT_MMAP_CHUNK_SIZE - 1) / PORT_MMAP_CHUNK_SIZE;
683699Smax.romanov@nginx.com 
684699Smax.romanov@nginx.com     if (nxt_slow_path(nchunks > PORT_MMAP_CHUNK_COUNT)) {
685699Smax.romanov@nginx.com         nxt_alert(task, "requested buffer (%z) too big", size);
686699Smax.romanov@nginx.com 
687699Smax.romanov@nginx.com         return NULL;
688699Smax.romanov@nginx.com     }
689699Smax.romanov@nginx.com 
690342Smax.romanov@nginx.com     b = nxt_buf_mem_ts_alloc(task, task->thread->engine->mem_pool, 0);
69142Smax.romanov@nginx.com     if (nxt_slow_path(b == NULL)) {
69242Smax.romanov@nginx.com         return NULL;
69342Smax.romanov@nginx.com     }
69442Smax.romanov@nginx.com 
69542Smax.romanov@nginx.com     b->completion_handler = nxt_port_mmap_buf_completion;
69642Smax.romanov@nginx.com     nxt_buf_set_port_mmap(b);
69742Smax.romanov@nginx.com 
698*1546Smax.romanov@nginx.com     mmap_handler = nxt_port_mmap_get(task, mmaps, &c, nchunks, 0);
699365Smax.romanov@nginx.com     if (nxt_slow_path(mmap_handler == NULL)) {
700430Sigor@sysoev.ru         mp = task->thread->engine->mem_pool;
701430Sigor@sysoev.ru         nxt_mp_free(mp, b);
702430Sigor@sysoev.ru         nxt_mp_release(mp);
70342Smax.romanov@nginx.com         return NULL;
70442Smax.romanov@nginx.com     }
70542Smax.romanov@nginx.com 
706365Smax.romanov@nginx.com     b->parent = mmap_handler;
707365Smax.romanov@nginx.com 
708365Smax.romanov@nginx.com     nxt_port_mmap_handler_use(mmap_handler, 1);
709365Smax.romanov@nginx.com 
710365Smax.romanov@nginx.com     hdr = mmap_handler->hdr;
711122Smax.romanov@nginx.com 
71280Smax.romanov@nginx.com     b->mem.start = nxt_port_mmap_chunk_start(hdr, c);
71342Smax.romanov@nginx.com     b->mem.pos = b->mem.start;
71442Smax.romanov@nginx.com     b->mem.free = b->mem.start;
715699Smax.romanov@nginx.com     b->mem.end = b->mem.start + nchunks * PORT_MMAP_CHUNK_SIZE;
71642Smax.romanov@nginx.com 
717494Spluknet@nginx.com     nxt_debug(task, "outgoing mmap buf allocation: %p [%p,%uz] %PI->%PI,%d,%d",
718363Smax.romanov@nginx.com               b, b->mem.start, b->mem.end - b->mem.start,
719363Smax.romanov@nginx.com               hdr->src_pid, hdr->dst_pid, hdr->id, c);
720206Smax.romanov@nginx.com 
72142Smax.romanov@nginx.com     return b;
72242Smax.romanov@nginx.com }
72342Smax.romanov@nginx.com 
72442Smax.romanov@nginx.com 
72580Smax.romanov@nginx.com nxt_int_t
726206Smax.romanov@nginx.com nxt_port_mmap_increase_buf(nxt_task_t *task, nxt_buf_t *b, size_t size,
727206Smax.romanov@nginx.com     size_t min_size)
72880Smax.romanov@nginx.com {
729365Smax.romanov@nginx.com     size_t                   nchunks, free_size;
730365Smax.romanov@nginx.com     nxt_chunk_id_t           c, start;
731365Smax.romanov@nginx.com     nxt_port_mmap_header_t   *hdr;
732365Smax.romanov@nginx.com     nxt_port_mmap_handler_t  *mmap_handler;
73380Smax.romanov@nginx.com 
73480Smax.romanov@nginx.com     nxt_debug(task, "request increase %z bytes shm buffer", size);
73580Smax.romanov@nginx.com 
73680Smax.romanov@nginx.com     if (nxt_slow_path(nxt_buf_is_port_mmap(b) == 0)) {
73780Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN,
73880Smax.romanov@nginx.com                 "failed to increase, not a mmap buffer");
73980Smax.romanov@nginx.com         return NXT_ERROR;
74080Smax.romanov@nginx.com     }
74180Smax.romanov@nginx.com 
742206Smax.romanov@nginx.com     free_size = nxt_buf_mem_free_size(&b->mem);
743206Smax.romanov@nginx.com 
744206Smax.romanov@nginx.com     if (nxt_slow_path(size <= free_size)) {
74580Smax.romanov@nginx.com         return NXT_OK;
74680Smax.romanov@nginx.com     }
74780Smax.romanov@nginx.com 
748365Smax.romanov@nginx.com     mmap_handler = b->parent;
749365Smax.romanov@nginx.com     hdr = mmap_handler->hdr;
75080Smax.romanov@nginx.com 
75180Smax.romanov@nginx.com     start = nxt_port_mmap_chunk_id(hdr, b->mem.end);
75280Smax.romanov@nginx.com 
753206Smax.romanov@nginx.com     size -= free_size;
75480Smax.romanov@nginx.com 
755723Smax.romanov@nginx.com     nchunks = (size + PORT_MMAP_CHUNK_SIZE - 1) / PORT_MMAP_CHUNK_SIZE;
75680Smax.romanov@nginx.com 
75780Smax.romanov@nginx.com     c = start;
75880Smax.romanov@nginx.com 
75980Smax.romanov@nginx.com     /* Try to acquire as much chunks as required. */
76080Smax.romanov@nginx.com     while (nchunks > 0) {
76180Smax.romanov@nginx.com 
762423Smax.romanov@nginx.com         if (nxt_port_mmap_chk_set_chunk_busy(hdr->free_map, c) == 0) {
76380Smax.romanov@nginx.com             break;
76480Smax.romanov@nginx.com         }
76580Smax.romanov@nginx.com 
76680Smax.romanov@nginx.com         c++;
76780Smax.romanov@nginx.com         nchunks--;
76880Smax.romanov@nginx.com     }
76980Smax.romanov@nginx.com 
770277Sigor@sysoev.ru     if (nchunks != 0
771277Sigor@sysoev.ru         && min_size > free_size + PORT_MMAP_CHUNK_SIZE * (c - start))
772277Sigor@sysoev.ru     {
77380Smax.romanov@nginx.com         c--;
77480Smax.romanov@nginx.com         while (c >= start) {
775423Smax.romanov@nginx.com             nxt_port_mmap_set_chunk_free(hdr->free_map, c);
77680Smax.romanov@nginx.com             c--;
77780Smax.romanov@nginx.com         }
77880Smax.romanov@nginx.com 
779494Spluknet@nginx.com         nxt_debug(task, "failed to increase, %uz chunks busy", nchunks);
78080Smax.romanov@nginx.com 
78180Smax.romanov@nginx.com         return NXT_ERROR;
782277Sigor@sysoev.ru 
78380Smax.romanov@nginx.com     } else {
78480Smax.romanov@nginx.com         b->mem.end += PORT_MMAP_CHUNK_SIZE * (c - start);
78580Smax.romanov@nginx.com 
78680Smax.romanov@nginx.com         return NXT_OK;
78780Smax.romanov@nginx.com     }
78880Smax.romanov@nginx.com }
78980Smax.romanov@nginx.com 
79080Smax.romanov@nginx.com 
79142Smax.romanov@nginx.com static nxt_buf_t *
79242Smax.romanov@nginx.com nxt_port_mmap_get_incoming_buf(nxt_task_t *task, nxt_port_t *port,
79342Smax.romanov@nginx.com     nxt_pid_t spid, nxt_port_mmap_msg_t *mmap_msg)
79442Smax.romanov@nginx.com {
795365Smax.romanov@nginx.com     size_t                   nchunks;
796365Smax.romanov@nginx.com     nxt_buf_t                *b;
797365Smax.romanov@nginx.com     nxt_port_mmap_header_t   *hdr;
798365Smax.romanov@nginx.com     nxt_port_mmap_handler_t  *mmap_handler;
79980Smax.romanov@nginx.com 
800365Smax.romanov@nginx.com     mmap_handler = nxt_port_get_port_incoming_mmap(task, spid,
801365Smax.romanov@nginx.com                                                    mmap_msg->mmap_id);
802365Smax.romanov@nginx.com     if (nxt_slow_path(mmap_handler == NULL)) {
80380Smax.romanov@nginx.com         return NULL;
80480Smax.romanov@nginx.com     }
80542Smax.romanov@nginx.com 
806122Smax.romanov@nginx.com     b = nxt_buf_mem_ts_alloc(task, port->mem_pool, 0);
80742Smax.romanov@nginx.com     if (nxt_slow_path(b == NULL)) {
80842Smax.romanov@nginx.com         return NULL;
80942Smax.romanov@nginx.com     }
81042Smax.romanov@nginx.com 
81142Smax.romanov@nginx.com     b->completion_handler = nxt_port_mmap_buf_completion;
81242Smax.romanov@nginx.com 
81342Smax.romanov@nginx.com     nxt_buf_set_port_mmap(b);
81442Smax.romanov@nginx.com 
81542Smax.romanov@nginx.com     nchunks = mmap_msg->size / PORT_MMAP_CHUNK_SIZE;
81642Smax.romanov@nginx.com     if ((mmap_msg->size % PORT_MMAP_CHUNK_SIZE) != 0) {
81742Smax.romanov@nginx.com         nchunks++;
81842Smax.romanov@nginx.com     }
81942Smax.romanov@nginx.com 
820365Smax.romanov@nginx.com     hdr = mmap_handler->hdr;
821365Smax.romanov@nginx.com 
82280Smax.romanov@nginx.com     b->mem.start = nxt_port_mmap_chunk_start(hdr, mmap_msg->chunk_id);
82342Smax.romanov@nginx.com     b->mem.pos = b->mem.start;
82442Smax.romanov@nginx.com     b->mem.free = b->mem.start + mmap_msg->size;
82542Smax.romanov@nginx.com     b->mem.end = b->mem.start + nchunks * PORT_MMAP_CHUNK_SIZE;
82642Smax.romanov@nginx.com 
827365Smax.romanov@nginx.com     b->parent = mmap_handler;
828365Smax.romanov@nginx.com     nxt_port_mmap_handler_use(mmap_handler, 1);
82942Smax.romanov@nginx.com 
830494Spluknet@nginx.com     nxt_debug(task, "incoming mmap buf allocation: %p [%p,%uz] %PI->%PI,%d,%d",
831277Sigor@sysoev.ru               b, b->mem.start, b->mem.end - b->mem.start,
832363Smax.romanov@nginx.com               hdr->src_pid, hdr->dst_pid, hdr->id, mmap_msg->chunk_id);
833206Smax.romanov@nginx.com 
83442Smax.romanov@nginx.com     return b;
83542Smax.romanov@nginx.com }
83642Smax.romanov@nginx.com 
83742Smax.romanov@nginx.com 
83842Smax.romanov@nginx.com void
83942Smax.romanov@nginx.com nxt_port_mmap_write(nxt_task_t *task, nxt_port_t *port,
8401125Smax.romanov@nginx.com     nxt_port_send_msg_t *msg, nxt_sendbuf_coalesce_t *sb, void *mmsg_buf)
84142Smax.romanov@nginx.com {
842365Smax.romanov@nginx.com     size_t                   bsize;
843365Smax.romanov@nginx.com     nxt_buf_t                *bmem;
844365Smax.romanov@nginx.com     nxt_uint_t               i;
845365Smax.romanov@nginx.com     nxt_port_mmap_msg_t      *mmap_msg;
846365Smax.romanov@nginx.com     nxt_port_mmap_header_t   *hdr;
847365Smax.romanov@nginx.com     nxt_port_mmap_handler_t  *mmap_handler;
84842Smax.romanov@nginx.com 
84942Smax.romanov@nginx.com     nxt_debug(task, "prepare %z bytes message for transfer to process %PI "
850277Sigor@sysoev.ru                     "via shared memory", sb->size, port->pid);
85142Smax.romanov@nginx.com 
85242Smax.romanov@nginx.com     bsize = sb->niov * sizeof(nxt_port_mmap_msg_t);
8531125Smax.romanov@nginx.com     mmap_msg = mmsg_buf;
85442Smax.romanov@nginx.com 
85542Smax.romanov@nginx.com     bmem = msg->buf;
85642Smax.romanov@nginx.com 
85742Smax.romanov@nginx.com     for (i = 0; i < sb->niov; i++, mmap_msg++) {
85842Smax.romanov@nginx.com 
85942Smax.romanov@nginx.com         /* Lookup buffer which starts current iov_base. */
86042Smax.romanov@nginx.com         while (bmem && sb->iobuf[i].iov_base != bmem->mem.pos) {
86142Smax.romanov@nginx.com             bmem = bmem->next;
86242Smax.romanov@nginx.com         }
86342Smax.romanov@nginx.com 
86442Smax.romanov@nginx.com         if (nxt_slow_path(bmem == NULL)) {
865277Sigor@sysoev.ru             nxt_log_error(NXT_LOG_ERR, task->log,
866277Sigor@sysoev.ru                           "failed to find buf for iobuf[%d]", i);
86742Smax.romanov@nginx.com             return;
86842Smax.romanov@nginx.com             /* TODO clear b and exit */
86942Smax.romanov@nginx.com         }
87042Smax.romanov@nginx.com 
871365Smax.romanov@nginx.com         mmap_handler = bmem->parent;
872365Smax.romanov@nginx.com         hdr = mmap_handler->hdr;
87342Smax.romanov@nginx.com 
87480Smax.romanov@nginx.com         mmap_msg->mmap_id = hdr->id;
87580Smax.romanov@nginx.com         mmap_msg->chunk_id = nxt_port_mmap_chunk_id(hdr, bmem->mem.pos);
87642Smax.romanov@nginx.com         mmap_msg->size = sb->iobuf[i].iov_len;
87742Smax.romanov@nginx.com 
87842Smax.romanov@nginx.com         nxt_debug(task, "mmap_msg={%D, %D, %D} to %PI",
87942Smax.romanov@nginx.com                   mmap_msg->mmap_id, mmap_msg->chunk_id, mmap_msg->size,
88042Smax.romanov@nginx.com                   port->pid);
88142Smax.romanov@nginx.com     }
88242Smax.romanov@nginx.com 
8831125Smax.romanov@nginx.com     sb->iobuf[0].iov_base = mmsg_buf;
88442Smax.romanov@nginx.com     sb->iobuf[0].iov_len = bsize;
88542Smax.romanov@nginx.com     sb->niov = 1;
88642Smax.romanov@nginx.com     sb->size = bsize;
88742Smax.romanov@nginx.com 
88842Smax.romanov@nginx.com     msg->port_msg.mmap = 1;
88942Smax.romanov@nginx.com }
89042Smax.romanov@nginx.com 
89142Smax.romanov@nginx.com 
89242Smax.romanov@nginx.com void
893423Smax.romanov@nginx.com nxt_port_mmap_read(nxt_task_t *task, nxt_port_recv_msg_t *msg)
89442Smax.romanov@nginx.com {
89542Smax.romanov@nginx.com     nxt_buf_t            *b, **pb;
89642Smax.romanov@nginx.com     nxt_port_mmap_msg_t  *end, *mmap_msg;
89742Smax.romanov@nginx.com 
89842Smax.romanov@nginx.com     pb = &msg->buf;
89982Smax.romanov@nginx.com     msg->size = 0;
90042Smax.romanov@nginx.com 
901423Smax.romanov@nginx.com     for (b = msg->buf; b != NULL; b = b->next) {
902423Smax.romanov@nginx.com 
903423Smax.romanov@nginx.com         mmap_msg = (nxt_port_mmap_msg_t *) b->mem.pos;
904423Smax.romanov@nginx.com         end = (nxt_port_mmap_msg_t *) b->mem.free;
90542Smax.romanov@nginx.com 
906423Smax.romanov@nginx.com         while (mmap_msg < end) {
907423Smax.romanov@nginx.com             nxt_debug(task, "mmap_msg={%D, %D, %D} from %PI",
908423Smax.romanov@nginx.com                       mmap_msg->mmap_id, mmap_msg->chunk_id, mmap_msg->size,
909423Smax.romanov@nginx.com                       msg->port_msg.pid);
91042Smax.romanov@nginx.com 
911423Smax.romanov@nginx.com             *pb = nxt_port_mmap_get_incoming_buf(task, msg->port,
912423Smax.romanov@nginx.com                                                  msg->port_msg.pid, mmap_msg);
913423Smax.romanov@nginx.com             if (nxt_slow_path(*pb == NULL)) {
914423Smax.romanov@nginx.com                 nxt_log_error(NXT_LOG_ERR, task->log,
915423Smax.romanov@nginx.com                               "failed to get mmap buffer");
916423Smax.romanov@nginx.com 
917423Smax.romanov@nginx.com                 break;
918423Smax.romanov@nginx.com             }
91942Smax.romanov@nginx.com 
920423Smax.romanov@nginx.com             msg->size += mmap_msg->size;
921423Smax.romanov@nginx.com             pb = &(*pb)->next;
922423Smax.romanov@nginx.com             mmap_msg++;
923423Smax.romanov@nginx.com 
924423Smax.romanov@nginx.com             /* Mark original buf as complete. */
925423Smax.romanov@nginx.com             b->mem.pos += sizeof(nxt_port_mmap_msg_t);
926423Smax.romanov@nginx.com         }
92742Smax.romanov@nginx.com     }
92842Smax.romanov@nginx.com }
92942Smax.romanov@nginx.com 
93042Smax.romanov@nginx.com 
93142Smax.romanov@nginx.com nxt_port_method_t
93242Smax.romanov@nginx.com nxt_port_mmap_get_method(nxt_task_t *task, nxt_port_t *port, nxt_buf_t *b)
93342Smax.romanov@nginx.com {
934*1546Smax.romanov@nginx.com     nxt_port_method_t  m;
93542Smax.romanov@nginx.com 
93642Smax.romanov@nginx.com     m = NXT_PORT_METHOD_ANY;
93742Smax.romanov@nginx.com 
938613Svbart@nginx.com     for (/* void */; b != NULL; b = b->next) {
93942Smax.romanov@nginx.com         if (nxt_buf_used_size(b) == 0) {
94042Smax.romanov@nginx.com             /* empty buffers does not affect method */
94142Smax.romanov@nginx.com             continue;
94242Smax.romanov@nginx.com         }
94342Smax.romanov@nginx.com 
94442Smax.romanov@nginx.com         if (nxt_buf_is_port_mmap(b)) {
94542Smax.romanov@nginx.com             if (m == NXT_PORT_METHOD_PLAIN) {
94642Smax.romanov@nginx.com                 nxt_log_error(NXT_LOG_ERR, task->log,
94742Smax.romanov@nginx.com                               "mixing plain and mmap buffers, "
94842Smax.romanov@nginx.com                               "using plain mode");
94942Smax.romanov@nginx.com 
95042Smax.romanov@nginx.com                 break;
95142Smax.romanov@nginx.com             }
95242Smax.romanov@nginx.com 
95342Smax.romanov@nginx.com             if (m == NXT_PORT_METHOD_ANY) {
95442Smax.romanov@nginx.com                 nxt_debug(task, "using mmap mode");
95542Smax.romanov@nginx.com 
95642Smax.romanov@nginx.com                 m = NXT_PORT_METHOD_MMAP;
95742Smax.romanov@nginx.com             }
95842Smax.romanov@nginx.com         } else {
95942Smax.romanov@nginx.com             if (m == NXT_PORT_METHOD_MMAP) {
96042Smax.romanov@nginx.com                 nxt_log_error(NXT_LOG_ERR, task->log,
96142Smax.romanov@nginx.com                               "mixing mmap and plain buffers, "
96242Smax.romanov@nginx.com                               "switching to plain mode");
96342Smax.romanov@nginx.com 
96442Smax.romanov@nginx.com                 m = NXT_PORT_METHOD_PLAIN;
96542Smax.romanov@nginx.com 
96642Smax.romanov@nginx.com                 break;
96742Smax.romanov@nginx.com             }
96842Smax.romanov@nginx.com 
96942Smax.romanov@nginx.com             if (m == NXT_PORT_METHOD_ANY) {
97042Smax.romanov@nginx.com                 nxt_debug(task, "using plain mode");
97142Smax.romanov@nginx.com 
97242Smax.romanov@nginx.com                 m = NXT_PORT_METHOD_PLAIN;
97342Smax.romanov@nginx.com             }
97442Smax.romanov@nginx.com         }
97542Smax.romanov@nginx.com     }
97642Smax.romanov@nginx.com 
97742Smax.romanov@nginx.com     return m;
97842Smax.romanov@nginx.com }
979