xref: /unit/src/nxt_port_memory.c (revision 364)
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 
19*364Smax.romanov@nginx.com nxt_inline void
2042Smax.romanov@nginx.com nxt_port_mmap_destroy(nxt_port_mmap_t *port_mmap)
2142Smax.romanov@nginx.com {
2280Smax.romanov@nginx.com     if (port_mmap->hdr != NULL) {
2380Smax.romanov@nginx.com         nxt_mem_munmap(port_mmap->hdr, PORT_MMAP_SIZE);
2480Smax.romanov@nginx.com         port_mmap->hdr = NULL;
2542Smax.romanov@nginx.com     }
2642Smax.romanov@nginx.com }
2742Smax.romanov@nginx.com 
2842Smax.romanov@nginx.com 
29*364Smax.romanov@nginx.com static nxt_port_mmap_t *
30*364Smax.romanov@nginx.com nxt_port_mmap_at(nxt_port_mmaps_t *port_mmaps, uint32_t i)
31141Smax.romanov@nginx.com {
32*364Smax.romanov@nginx.com     uint32_t  cap;
33141Smax.romanov@nginx.com 
34*364Smax.romanov@nginx.com     cap = port_mmaps->cap;
35141Smax.romanov@nginx.com 
36*364Smax.romanov@nginx.com     if (cap == 0) {
37*364Smax.romanov@nginx.com         cap = i + 1;
38141Smax.romanov@nginx.com     }
39141Smax.romanov@nginx.com 
40*364Smax.romanov@nginx.com     while (i + 1 > cap) {
41*364Smax.romanov@nginx.com 
42*364Smax.romanov@nginx.com         if (cap < 16) {
43*364Smax.romanov@nginx.com           cap = cap * 2;
44141Smax.romanov@nginx.com 
45*364Smax.romanov@nginx.com         } else {
46*364Smax.romanov@nginx.com           cap = cap + cap / 2;
47*364Smax.romanov@nginx.com         }
48*364Smax.romanov@nginx.com     }
49*364Smax.romanov@nginx.com 
50*364Smax.romanov@nginx.com     if (cap != port_mmaps->cap) {
51141Smax.romanov@nginx.com 
52*364Smax.romanov@nginx.com         port_mmaps->elts = nxt_realloc(port_mmaps->elts,
53*364Smax.romanov@nginx.com                                        cap * sizeof(nxt_port_mmap_t));
54*364Smax.romanov@nginx.com         if (nxt_slow_path(port_mmaps->elts == NULL)) {
55*364Smax.romanov@nginx.com             return NULL;
56*364Smax.romanov@nginx.com         }
57*364Smax.romanov@nginx.com 
58*364Smax.romanov@nginx.com         nxt_memzero(port_mmaps->elts + port_mmaps->cap,
59*364Smax.romanov@nginx.com                     sizeof(nxt_port_mmap_t) * (cap - port_mmaps->cap));
60141Smax.romanov@nginx.com 
61*364Smax.romanov@nginx.com         port_mmaps->cap = cap;
62*364Smax.romanov@nginx.com     }
63*364Smax.romanov@nginx.com 
64*364Smax.romanov@nginx.com     if (i + 1 > port_mmaps->size) {
65*364Smax.romanov@nginx.com         port_mmaps->size = i + 1;
66*364Smax.romanov@nginx.com     }
67*364Smax.romanov@nginx.com 
68*364Smax.romanov@nginx.com     return port_mmaps->elts + i;
69141Smax.romanov@nginx.com }
70141Smax.romanov@nginx.com 
71141Smax.romanov@nginx.com 
72141Smax.romanov@nginx.com void
73*364Smax.romanov@nginx.com nxt_port_mmaps_destroy(nxt_port_mmaps_t *port_mmaps, nxt_bool_t free)
74141Smax.romanov@nginx.com {
75141Smax.romanov@nginx.com     uint32_t         i;
76141Smax.romanov@nginx.com     nxt_port_mmap_t  *port_mmap;
77141Smax.romanov@nginx.com 
78141Smax.romanov@nginx.com     if (port_mmaps == NULL) {
79141Smax.romanov@nginx.com         return;
80141Smax.romanov@nginx.com     }
81141Smax.romanov@nginx.com 
82141Smax.romanov@nginx.com     port_mmap = port_mmaps->elts;
83141Smax.romanov@nginx.com 
84*364Smax.romanov@nginx.com     for (i = 0; i < port_mmaps->size; i++) {
85*364Smax.romanov@nginx.com         nxt_port_mmap_destroy(port_mmap + i);
86141Smax.romanov@nginx.com     }
87141Smax.romanov@nginx.com 
88*364Smax.romanov@nginx.com     port_mmaps->size = 0;
89141Smax.romanov@nginx.com 
90*364Smax.romanov@nginx.com     if (free != 0) {
91*364Smax.romanov@nginx.com         nxt_free(port_mmaps->elts);
92141Smax.romanov@nginx.com     }
93141Smax.romanov@nginx.com }
94141Smax.romanov@nginx.com 
95141Smax.romanov@nginx.com 
96195Smax.romanov@nginx.com #define nxt_port_mmap_free_junk(p, size)                                      \
97195Smax.romanov@nginx.com     memset((p), 0xA5, size)
98195Smax.romanov@nginx.com 
99195Smax.romanov@nginx.com 
10042Smax.romanov@nginx.com static void
10142Smax.romanov@nginx.com nxt_port_mmap_buf_completion(nxt_task_t *task, void *obj, void *data)
10242Smax.romanov@nginx.com {
10342Smax.romanov@nginx.com     u_char                  *p;
10465Sigor@sysoev.ru     nxt_mp_t                *mp;
10542Smax.romanov@nginx.com     nxt_buf_t               *b;
10642Smax.romanov@nginx.com     nxt_chunk_id_t          c;
10742Smax.romanov@nginx.com     nxt_port_mmap_header_t  *hdr;
10842Smax.romanov@nginx.com 
109122Smax.romanov@nginx.com     if (nxt_buf_ts_handle(task, obj, data)) {
110122Smax.romanov@nginx.com         return;
111122Smax.romanov@nginx.com     }
112122Smax.romanov@nginx.com 
11342Smax.romanov@nginx.com     b = obj;
11442Smax.romanov@nginx.com 
11542Smax.romanov@nginx.com     mp = b->data;
11642Smax.romanov@nginx.com 
11779Smax.romanov@nginx.com #if (NXT_DEBUG)
11879Smax.romanov@nginx.com     if (nxt_slow_path(data != b->parent)) {
11979Smax.romanov@nginx.com         nxt_log_alert(task->log, "completion data (%p) != b->parent (%p)",
12079Smax.romanov@nginx.com                       data, b->parent);
12179Smax.romanov@nginx.com         nxt_abort();
12279Smax.romanov@nginx.com     }
12379Smax.romanov@nginx.com #endif
12479Smax.romanov@nginx.com 
12580Smax.romanov@nginx.com     hdr = data;
12642Smax.romanov@nginx.com 
127363Smax.romanov@nginx.com     if (nxt_slow_path(hdr->src_pid != nxt_pid && hdr->dst_pid != nxt_pid)) {
128363Smax.romanov@nginx.com         nxt_debug(task, "mmap buf completion: mmap for other process pair "
129363Smax.romanov@nginx.com                   "%PI->%PI", hdr->src_pid, hdr->dst_pid);
130363Smax.romanov@nginx.com 
131363Smax.romanov@nginx.com         goto release_buf;
132363Smax.romanov@nginx.com     }
133363Smax.romanov@nginx.com 
13442Smax.romanov@nginx.com     if (b->is_port_mmap_sent && b->mem.pos > b->mem.start) {
13542Smax.romanov@nginx.com         /*
13642Smax.romanov@nginx.com          * Chunks until b->mem.pos has been sent to other side,
13742Smax.romanov@nginx.com          * let's release rest (if any).
13842Smax.romanov@nginx.com          */
13942Smax.romanov@nginx.com         p = b->mem.pos - 1;
14080Smax.romanov@nginx.com         c = nxt_port_mmap_chunk_id(hdr, p) + 1;
14180Smax.romanov@nginx.com         p = nxt_port_mmap_chunk_start(hdr, c);
142141Smax.romanov@nginx.com 
14342Smax.romanov@nginx.com     } else {
14442Smax.romanov@nginx.com         p = b->mem.start;
14580Smax.romanov@nginx.com         c = nxt_port_mmap_chunk_id(hdr, p);
14642Smax.romanov@nginx.com     }
14742Smax.romanov@nginx.com 
148195Smax.romanov@nginx.com     nxt_port_mmap_free_junk(p, b->mem.end - p);
149195Smax.romanov@nginx.com 
150363Smax.romanov@nginx.com     nxt_debug(task, "mmap buf completion: %p [%p,%d] (sent=%d), "
151363Smax.romanov@nginx.com               "%PI->%PI,%d,%d", b, b->mem.start, b->mem.end - b->mem.start,
152363Smax.romanov@nginx.com               b->is_port_mmap_sent, hdr->src_pid, hdr->dst_pid, hdr->id, c);
153206Smax.romanov@nginx.com 
15442Smax.romanov@nginx.com     while (p < b->mem.end) {
15542Smax.romanov@nginx.com         nxt_port_mmap_set_chunk_free(hdr, c);
15642Smax.romanov@nginx.com 
15742Smax.romanov@nginx.com         p += PORT_MMAP_CHUNK_SIZE;
15842Smax.romanov@nginx.com         c++;
15942Smax.romanov@nginx.com     }
16042Smax.romanov@nginx.com 
161363Smax.romanov@nginx.com release_buf:
162363Smax.romanov@nginx.com 
163122Smax.romanov@nginx.com     nxt_mp_release(mp, b);
16442Smax.romanov@nginx.com }
16542Smax.romanov@nginx.com 
16642Smax.romanov@nginx.com 
16780Smax.romanov@nginx.com nxt_port_mmap_header_t *
16842Smax.romanov@nginx.com nxt_port_incoming_port_mmap(nxt_task_t *task, nxt_process_t *process,
16942Smax.romanov@nginx.com     nxt_fd_t fd)
17042Smax.romanov@nginx.com {
17180Smax.romanov@nginx.com     void                    *mem;
17280Smax.romanov@nginx.com     struct stat             mmap_stat;
17380Smax.romanov@nginx.com     nxt_port_mmap_t         *port_mmap;
17480Smax.romanov@nginx.com     nxt_port_mmap_header_t  *hdr;
17542Smax.romanov@nginx.com 
17642Smax.romanov@nginx.com     nxt_debug(task, "got new mmap fd #%FD from process %PI",
17742Smax.romanov@nginx.com               fd, process->pid);
17842Smax.romanov@nginx.com 
17980Smax.romanov@nginx.com     port_mmap = NULL;
18080Smax.romanov@nginx.com     hdr = NULL;
18180Smax.romanov@nginx.com 
18242Smax.romanov@nginx.com     if (fstat(fd, &mmap_stat) == -1) {
18342Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN, "fstat(%FD) failed %E", fd, nxt_errno);
18442Smax.romanov@nginx.com 
18542Smax.romanov@nginx.com         return NULL;
18642Smax.romanov@nginx.com     }
18742Smax.romanov@nginx.com 
18880Smax.romanov@nginx.com     mem = nxt_mem_mmap(NULL, mmap_stat.st_size,
18980Smax.romanov@nginx.com                        PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
19042Smax.romanov@nginx.com 
19180Smax.romanov@nginx.com     if (nxt_slow_path(mem == MAP_FAILED)) {
19242Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN, "mmap() failed %E", nxt_errno);
19342Smax.romanov@nginx.com 
194*364Smax.romanov@nginx.com         return NULL;
19542Smax.romanov@nginx.com     }
19642Smax.romanov@nginx.com 
197*364Smax.romanov@nginx.com     hdr = mem;
198*364Smax.romanov@nginx.com 
199*364Smax.romanov@nginx.com     nxt_thread_mutex_lock(&process->incoming.mutex);
20080Smax.romanov@nginx.com 
201*364Smax.romanov@nginx.com     port_mmap = nxt_port_mmap_at(&process->incoming, hdr->id);
202*364Smax.romanov@nginx.com     if (nxt_slow_path(port_mmap == NULL)) {
203*364Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN, "failed to add mmap to incoming array");
204*364Smax.romanov@nginx.com 
205*364Smax.romanov@nginx.com         nxt_mem_munmap(mem, PORT_MMAP_SIZE);
206*364Smax.romanov@nginx.com         hdr = NULL;
207*364Smax.romanov@nginx.com 
208*364Smax.romanov@nginx.com         goto fail;
20980Smax.romanov@nginx.com     }
21080Smax.romanov@nginx.com 
211363Smax.romanov@nginx.com     nxt_assert(hdr->src_pid == process->pid);
212363Smax.romanov@nginx.com     nxt_assert(hdr->dst_pid == nxt_pid);
213363Smax.romanov@nginx.com 
214*364Smax.romanov@nginx.com     port_mmap->hdr = hdr;
215*364Smax.romanov@nginx.com 
216323Smax.romanov@nginx.com     hdr->sent_over = 0xFFFFu;
217323Smax.romanov@nginx.com 
21880Smax.romanov@nginx.com fail:
21980Smax.romanov@nginx.com 
220*364Smax.romanov@nginx.com     nxt_thread_mutex_unlock(&process->incoming.mutex);
22190Smax.romanov@nginx.com 
22280Smax.romanov@nginx.com     return hdr;
22342Smax.romanov@nginx.com }
22442Smax.romanov@nginx.com 
22542Smax.romanov@nginx.com 
22680Smax.romanov@nginx.com static nxt_port_mmap_header_t *
22742Smax.romanov@nginx.com nxt_port_new_port_mmap(nxt_task_t *task, nxt_process_t *process,
22842Smax.romanov@nginx.com     nxt_port_t *port)
22942Smax.romanov@nginx.com {
23080Smax.romanov@nginx.com     void                    *mem;
23142Smax.romanov@nginx.com     u_char                  *p, name[64];
23280Smax.romanov@nginx.com     nxt_fd_t                fd;
23342Smax.romanov@nginx.com     nxt_port_mmap_t         *port_mmap;
23442Smax.romanov@nginx.com     nxt_port_mmap_header_t  *hdr;
23542Smax.romanov@nginx.com 
236*364Smax.romanov@nginx.com     port_mmap = nxt_port_mmap_at(&process->outgoing, process->outgoing.size);
23742Smax.romanov@nginx.com     if (nxt_slow_path(port_mmap == NULL)) {
23842Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN,
23942Smax.romanov@nginx.com                 "failed to add port mmap to outgoing array");
24042Smax.romanov@nginx.com 
24142Smax.romanov@nginx.com         return NULL;
24242Smax.romanov@nginx.com     }
24342Smax.romanov@nginx.com 
244259Sigor@sysoev.ru     p = nxt_sprintf(name, name + sizeof(name), "/unit.%PI.%uxD",
245138Sigor@sysoev.ru                     nxt_pid, nxt_random(&task->thread->random));
24642Smax.romanov@nginx.com     *p = '\0';
24742Smax.romanov@nginx.com 
24842Smax.romanov@nginx.com #if (NXT_HAVE_MEMFD_CREATE)
249277Sigor@sysoev.ru 
25080Smax.romanov@nginx.com     fd = syscall(SYS_memfd_create, name, MFD_CLOEXEC);
25142Smax.romanov@nginx.com 
25280Smax.romanov@nginx.com     if (nxt_slow_path(fd == -1)) {
25342Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_CRIT, "memfd_create(%s) failed %E",
25442Smax.romanov@nginx.com                 name, nxt_errno);
25542Smax.romanov@nginx.com 
25642Smax.romanov@nginx.com         goto remove_fail;
25742Smax.romanov@nginx.com     }
25842Smax.romanov@nginx.com 
25980Smax.romanov@nginx.com     nxt_debug(task, "memfd_create(%s): %FD", name, fd);
26042Smax.romanov@nginx.com 
26142Smax.romanov@nginx.com #elif (NXT_HAVE_SHM_OPEN)
262277Sigor@sysoev.ru 
263277Sigor@sysoev.ru     /* Just in case. */
264277Sigor@sysoev.ru     shm_unlink((char *) name);
26542Smax.romanov@nginx.com 
26680Smax.romanov@nginx.com     fd = shm_open((char *) name, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
26742Smax.romanov@nginx.com 
26880Smax.romanov@nginx.com     nxt_debug(task, "shm_open(%s): %FD", name, fd);
26942Smax.romanov@nginx.com 
27080Smax.romanov@nginx.com     if (nxt_slow_path(fd == -1)) {
27142Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_CRIT, "shm_open(%s) failed %E", name, nxt_errno);
27242Smax.romanov@nginx.com 
27342Smax.romanov@nginx.com         goto remove_fail;
27442Smax.romanov@nginx.com     }
27542Smax.romanov@nginx.com 
27642Smax.romanov@nginx.com     if (nxt_slow_path(shm_unlink((char *) name) == -1)) {
27742Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN, "shm_unlink(%s) failed %E", name,
27842Smax.romanov@nginx.com                 nxt_errno);
27942Smax.romanov@nginx.com     }
280277Sigor@sysoev.ru 
28142Smax.romanov@nginx.com #endif
28242Smax.romanov@nginx.com 
28380Smax.romanov@nginx.com     if (nxt_slow_path(ftruncate(fd, PORT_MMAP_SIZE) == -1)) {
28442Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN, "ftruncate() failed %E", nxt_errno);
28542Smax.romanov@nginx.com 
28642Smax.romanov@nginx.com         goto remove_fail;
28742Smax.romanov@nginx.com     }
28842Smax.romanov@nginx.com 
289277Sigor@sysoev.ru     mem = nxt_mem_mmap(NULL, PORT_MMAP_SIZE, PROT_READ | PROT_WRITE,
290277Sigor@sysoev.ru                        MAP_SHARED, fd, 0);
29142Smax.romanov@nginx.com 
29280Smax.romanov@nginx.com     if (nxt_slow_path(mem == MAP_FAILED)) {
29342Smax.romanov@nginx.com         goto remove_fail;
29442Smax.romanov@nginx.com     }
29542Smax.romanov@nginx.com 
29680Smax.romanov@nginx.com     port_mmap->hdr = mem;
29780Smax.romanov@nginx.com 
29842Smax.romanov@nginx.com     /* Init segment header. */
29980Smax.romanov@nginx.com     hdr = port_mmap->hdr;
30042Smax.romanov@nginx.com 
30142Smax.romanov@nginx.com     nxt_memset(hdr->free_map, 0xFFU, sizeof(hdr->free_map));
30242Smax.romanov@nginx.com 
303*364Smax.romanov@nginx.com     hdr->id = process->outgoing.size - 1;
304363Smax.romanov@nginx.com     hdr->src_pid = nxt_pid;
305363Smax.romanov@nginx.com     hdr->dst_pid = process->pid;
306323Smax.romanov@nginx.com     hdr->sent_over = port->id;
30780Smax.romanov@nginx.com 
30880Smax.romanov@nginx.com     /* Mark first chunk as busy */
30980Smax.romanov@nginx.com     nxt_port_mmap_set_chunk_busy(hdr, 0);
31080Smax.romanov@nginx.com 
31142Smax.romanov@nginx.com     /* Mark as busy chunk followed the last available chunk. */
31242Smax.romanov@nginx.com     nxt_port_mmap_set_chunk_busy(hdr, PORT_MMAP_CHUNK_COUNT);
31342Smax.romanov@nginx.com 
314363Smax.romanov@nginx.com     nxt_debug(task, "send mmap fd %FD to process %PI", fd, port->pid);
31542Smax.romanov@nginx.com 
31642Smax.romanov@nginx.com     /* TODO handle error */
317189Smax.romanov@nginx.com     (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_MMAP, fd, 0, 0, NULL);
31842Smax.romanov@nginx.com 
31942Smax.romanov@nginx.com     nxt_log(task, NXT_LOG_DEBUG, "new mmap #%D created for %PI -> %PI",
32080Smax.romanov@nginx.com             hdr->id, nxt_pid, process->pid);
32142Smax.romanov@nginx.com 
32280Smax.romanov@nginx.com     return hdr;
32342Smax.romanov@nginx.com 
32442Smax.romanov@nginx.com remove_fail:
32542Smax.romanov@nginx.com 
326*364Smax.romanov@nginx.com     process->outgoing.size--;
32742Smax.romanov@nginx.com 
32842Smax.romanov@nginx.com     return NULL;
32942Smax.romanov@nginx.com }
33042Smax.romanov@nginx.com 
33142Smax.romanov@nginx.com 
33280Smax.romanov@nginx.com static nxt_port_mmap_header_t *
33342Smax.romanov@nginx.com nxt_port_mmap_get(nxt_task_t *task, nxt_port_t *port, nxt_chunk_id_t *c,
33442Smax.romanov@nginx.com     size_t size)
33542Smax.romanov@nginx.com {
33680Smax.romanov@nginx.com     nxt_process_t           *process;
33780Smax.romanov@nginx.com     nxt_port_mmap_t         *port_mmap;
33880Smax.romanov@nginx.com     nxt_port_mmap_t         *end_port_mmap;
33980Smax.romanov@nginx.com     nxt_port_mmap_header_t  *hdr;
34042Smax.romanov@nginx.com 
34180Smax.romanov@nginx.com     process = port->process;
34242Smax.romanov@nginx.com     if (nxt_slow_path(process == NULL)) {
34342Smax.romanov@nginx.com         return NULL;
34442Smax.romanov@nginx.com     }
34542Smax.romanov@nginx.com 
34642Smax.romanov@nginx.com     *c = 0;
34780Smax.romanov@nginx.com     port_mmap = NULL;
34880Smax.romanov@nginx.com     hdr = NULL;
34942Smax.romanov@nginx.com 
350*364Smax.romanov@nginx.com     nxt_thread_mutex_lock(&process->outgoing.mutex);
35180Smax.romanov@nginx.com 
352*364Smax.romanov@nginx.com     port_mmap = process->outgoing.elts;
353*364Smax.romanov@nginx.com     end_port_mmap = port_mmap + process->outgoing.size;
35442Smax.romanov@nginx.com 
35542Smax.romanov@nginx.com     while (port_mmap < end_port_mmap) {
35642Smax.romanov@nginx.com 
357323Smax.romanov@nginx.com         if ( (port_mmap->hdr->sent_over == 0xFFFFu ||
358323Smax.romanov@nginx.com               port_mmap->hdr->sent_over == port->id) &&
359323Smax.romanov@nginx.com             nxt_port_mmap_get_free_chunk(port_mmap->hdr, c)) {
36080Smax.romanov@nginx.com             hdr = port_mmap->hdr;
36180Smax.romanov@nginx.com 
36280Smax.romanov@nginx.com             goto unlock_return;
36342Smax.romanov@nginx.com         }
36442Smax.romanov@nginx.com 
36542Smax.romanov@nginx.com         port_mmap++;
36642Smax.romanov@nginx.com     }
36742Smax.romanov@nginx.com 
36842Smax.romanov@nginx.com     /* TODO introduce port_mmap limit and release wait. */
36980Smax.romanov@nginx.com 
37080Smax.romanov@nginx.com     hdr = nxt_port_new_port_mmap(task, process, port);
37180Smax.romanov@nginx.com 
37280Smax.romanov@nginx.com unlock_return:
37380Smax.romanov@nginx.com 
374*364Smax.romanov@nginx.com     nxt_thread_mutex_unlock(&process->outgoing.mutex);
37590Smax.romanov@nginx.com 
37680Smax.romanov@nginx.com     return hdr;
37742Smax.romanov@nginx.com }
37842Smax.romanov@nginx.com 
37942Smax.romanov@nginx.com 
38080Smax.romanov@nginx.com static nxt_port_mmap_header_t *
38142Smax.romanov@nginx.com nxt_port_get_port_incoming_mmap(nxt_task_t *task, nxt_pid_t spid, uint32_t id)
38242Smax.romanov@nginx.com {
38380Smax.romanov@nginx.com     nxt_process_t           *process;
38480Smax.romanov@nginx.com     nxt_port_mmap_header_t  *hdr;
38542Smax.romanov@nginx.com 
386196Smax.romanov@nginx.com     process = nxt_runtime_process_find(task->thread->runtime, spid);
38742Smax.romanov@nginx.com     if (nxt_slow_path(process == NULL)) {
38842Smax.romanov@nginx.com         return NULL;
38942Smax.romanov@nginx.com     }
39042Smax.romanov@nginx.com 
39180Smax.romanov@nginx.com     hdr = NULL;
39280Smax.romanov@nginx.com 
393*364Smax.romanov@nginx.com     nxt_thread_mutex_lock(&process->incoming.mutex);
39480Smax.romanov@nginx.com 
395*364Smax.romanov@nginx.com     if (nxt_fast_path(process->incoming.size > id)) {
396*364Smax.romanov@nginx.com         hdr = process->incoming.elts[id].hdr;
397277Sigor@sysoev.ru 
39880Smax.romanov@nginx.com     } else {
39980Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN,
40080Smax.romanov@nginx.com                 "failed to get incoming mmap #%d for process %PI", id, spid);
40142Smax.romanov@nginx.com     }
40242Smax.romanov@nginx.com 
403*364Smax.romanov@nginx.com     nxt_thread_mutex_unlock(&process->incoming.mutex);
40490Smax.romanov@nginx.com 
40580Smax.romanov@nginx.com     return hdr;
40642Smax.romanov@nginx.com }
40742Smax.romanov@nginx.com 
40842Smax.romanov@nginx.com 
40942Smax.romanov@nginx.com nxt_buf_t *
41042Smax.romanov@nginx.com nxt_port_mmap_get_buf(nxt_task_t *task, nxt_port_t *port, size_t size)
41142Smax.romanov@nginx.com {
41242Smax.romanov@nginx.com     size_t                  nchunks;
41342Smax.romanov@nginx.com     nxt_buf_t               *b;
41442Smax.romanov@nginx.com     nxt_chunk_id_t          c;
41542Smax.romanov@nginx.com     nxt_port_mmap_header_t  *hdr;
41642Smax.romanov@nginx.com 
41742Smax.romanov@nginx.com     nxt_debug(task, "request %z bytes shm buffer", size);
41842Smax.romanov@nginx.com 
419342Smax.romanov@nginx.com     b = nxt_buf_mem_ts_alloc(task, task->thread->engine->mem_pool, 0);
42042Smax.romanov@nginx.com     if (nxt_slow_path(b == NULL)) {
42142Smax.romanov@nginx.com         return NULL;
42242Smax.romanov@nginx.com     }
42342Smax.romanov@nginx.com 
42442Smax.romanov@nginx.com     b->completion_handler = nxt_port_mmap_buf_completion;
42542Smax.romanov@nginx.com     nxt_buf_set_port_mmap(b);
42642Smax.romanov@nginx.com 
42780Smax.romanov@nginx.com     hdr = nxt_port_mmap_get(task, port, &c, size);
42880Smax.romanov@nginx.com     if (nxt_slow_path(hdr == NULL)) {
429342Smax.romanov@nginx.com         nxt_mp_release(task->thread->engine->mem_pool, b);
43042Smax.romanov@nginx.com         return NULL;
43142Smax.romanov@nginx.com     }
43242Smax.romanov@nginx.com 
43380Smax.romanov@nginx.com     b->parent = hdr;
434122Smax.romanov@nginx.com 
43580Smax.romanov@nginx.com     b->mem.start = nxt_port_mmap_chunk_start(hdr, c);
43642Smax.romanov@nginx.com     b->mem.pos = b->mem.start;
43742Smax.romanov@nginx.com     b->mem.free = b->mem.start;
43842Smax.romanov@nginx.com     b->mem.end = b->mem.start + PORT_MMAP_CHUNK_SIZE;
43942Smax.romanov@nginx.com 
44042Smax.romanov@nginx.com     nchunks = size / PORT_MMAP_CHUNK_SIZE;
44142Smax.romanov@nginx.com     if ((size % PORT_MMAP_CHUNK_SIZE) != 0 || nchunks == 0) {
44242Smax.romanov@nginx.com         nchunks++;
44342Smax.romanov@nginx.com     }
44442Smax.romanov@nginx.com 
445363Smax.romanov@nginx.com     nxt_debug(task, "outgoing mmap buf allocation: %p [%p,%d] %PI->%PI,%d,%d",
446363Smax.romanov@nginx.com               b, b->mem.start, b->mem.end - b->mem.start,
447363Smax.romanov@nginx.com               hdr->src_pid, hdr->dst_pid, hdr->id, c);
448206Smax.romanov@nginx.com 
44942Smax.romanov@nginx.com     c++;
45042Smax.romanov@nginx.com     nchunks--;
45142Smax.romanov@nginx.com 
45242Smax.romanov@nginx.com     /* Try to acquire as much chunks as required. */
45342Smax.romanov@nginx.com     while (nchunks > 0) {
45442Smax.romanov@nginx.com 
45580Smax.romanov@nginx.com         if (nxt_port_mmap_chk_set_chunk_busy(hdr, c) == 0) {
45642Smax.romanov@nginx.com             break;
45742Smax.romanov@nginx.com         }
45842Smax.romanov@nginx.com 
45942Smax.romanov@nginx.com         b->mem.end += PORT_MMAP_CHUNK_SIZE;
46042Smax.romanov@nginx.com         c++;
46142Smax.romanov@nginx.com         nchunks--;
46242Smax.romanov@nginx.com     }
46342Smax.romanov@nginx.com 
46442Smax.romanov@nginx.com     return b;
46542Smax.romanov@nginx.com }
46642Smax.romanov@nginx.com 
46742Smax.romanov@nginx.com 
46880Smax.romanov@nginx.com nxt_int_t
469206Smax.romanov@nginx.com nxt_port_mmap_increase_buf(nxt_task_t *task, nxt_buf_t *b, size_t size,
470206Smax.romanov@nginx.com     size_t min_size)
47180Smax.romanov@nginx.com {
472206Smax.romanov@nginx.com     size_t                  nchunks, free_size;
47380Smax.romanov@nginx.com     nxt_chunk_id_t          c, start;
47480Smax.romanov@nginx.com     nxt_port_mmap_header_t  *hdr;
47580Smax.romanov@nginx.com 
47680Smax.romanov@nginx.com     nxt_debug(task, "request increase %z bytes shm buffer", size);
47780Smax.romanov@nginx.com 
47880Smax.romanov@nginx.com     if (nxt_slow_path(nxt_buf_is_port_mmap(b) == 0)) {
47980Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN,
48080Smax.romanov@nginx.com                 "failed to increase, not a mmap buffer");
48180Smax.romanov@nginx.com         return NXT_ERROR;
48280Smax.romanov@nginx.com     }
48380Smax.romanov@nginx.com 
484206Smax.romanov@nginx.com     free_size = nxt_buf_mem_free_size(&b->mem);
485206Smax.romanov@nginx.com 
486206Smax.romanov@nginx.com     if (nxt_slow_path(size <= free_size)) {
48780Smax.romanov@nginx.com         return NXT_OK;
48880Smax.romanov@nginx.com     }
48980Smax.romanov@nginx.com 
49080Smax.romanov@nginx.com     hdr = b->parent;
49180Smax.romanov@nginx.com 
49280Smax.romanov@nginx.com     start = nxt_port_mmap_chunk_id(hdr, b->mem.end);
49380Smax.romanov@nginx.com 
494206Smax.romanov@nginx.com     size -= free_size;
49580Smax.romanov@nginx.com 
49680Smax.romanov@nginx.com     nchunks = size / PORT_MMAP_CHUNK_SIZE;
49780Smax.romanov@nginx.com     if ((size % PORT_MMAP_CHUNK_SIZE) != 0 || nchunks == 0) {
49880Smax.romanov@nginx.com         nchunks++;
49980Smax.romanov@nginx.com     }
50080Smax.romanov@nginx.com 
50180Smax.romanov@nginx.com     c = start;
50280Smax.romanov@nginx.com 
50380Smax.romanov@nginx.com     /* Try to acquire as much chunks as required. */
50480Smax.romanov@nginx.com     while (nchunks > 0) {
50580Smax.romanov@nginx.com 
50680Smax.romanov@nginx.com         if (nxt_port_mmap_chk_set_chunk_busy(hdr, c) == 0) {
50780Smax.romanov@nginx.com             break;
50880Smax.romanov@nginx.com         }
50980Smax.romanov@nginx.com 
51080Smax.romanov@nginx.com         c++;
51180Smax.romanov@nginx.com         nchunks--;
51280Smax.romanov@nginx.com     }
51380Smax.romanov@nginx.com 
514277Sigor@sysoev.ru     if (nchunks != 0
515277Sigor@sysoev.ru         && min_size > free_size + PORT_MMAP_CHUNK_SIZE * (c - start))
516277Sigor@sysoev.ru     {
51780Smax.romanov@nginx.com         c--;
51880Smax.romanov@nginx.com         while (c >= start) {
51980Smax.romanov@nginx.com             nxt_port_mmap_set_chunk_free(hdr, c);
52080Smax.romanov@nginx.com             c--;
52180Smax.romanov@nginx.com         }
52280Smax.romanov@nginx.com 
52380Smax.romanov@nginx.com         nxt_debug(task, "failed to increase, %d chunks busy", nchunks);
52480Smax.romanov@nginx.com 
52580Smax.romanov@nginx.com         return NXT_ERROR;
526277Sigor@sysoev.ru 
52780Smax.romanov@nginx.com     } else {
52880Smax.romanov@nginx.com         b->mem.end += PORT_MMAP_CHUNK_SIZE * (c - start);
52980Smax.romanov@nginx.com 
53080Smax.romanov@nginx.com         return NXT_OK;
53180Smax.romanov@nginx.com     }
53280Smax.romanov@nginx.com }
53380Smax.romanov@nginx.com 
53480Smax.romanov@nginx.com 
53542Smax.romanov@nginx.com static nxt_buf_t *
53642Smax.romanov@nginx.com nxt_port_mmap_get_incoming_buf(nxt_task_t *task, nxt_port_t *port,
53742Smax.romanov@nginx.com     nxt_pid_t spid, nxt_port_mmap_msg_t *mmap_msg)
53842Smax.romanov@nginx.com {
53942Smax.romanov@nginx.com     size_t                  nchunks;
54042Smax.romanov@nginx.com     nxt_buf_t               *b;
54180Smax.romanov@nginx.com     nxt_port_mmap_header_t  *hdr;
54280Smax.romanov@nginx.com 
54380Smax.romanov@nginx.com     hdr = nxt_port_get_port_incoming_mmap(task, spid, mmap_msg->mmap_id);
54480Smax.romanov@nginx.com     if (nxt_slow_path(hdr == NULL)) {
54580Smax.romanov@nginx.com         return NULL;
54680Smax.romanov@nginx.com     }
54742Smax.romanov@nginx.com 
548122Smax.romanov@nginx.com     b = nxt_buf_mem_ts_alloc(task, port->mem_pool, 0);
54942Smax.romanov@nginx.com     if (nxt_slow_path(b == NULL)) {
55042Smax.romanov@nginx.com         return NULL;
55142Smax.romanov@nginx.com     }
55242Smax.romanov@nginx.com 
55342Smax.romanov@nginx.com     b->completion_handler = nxt_port_mmap_buf_completion;
55442Smax.romanov@nginx.com 
55542Smax.romanov@nginx.com     nxt_buf_set_port_mmap(b);
55642Smax.romanov@nginx.com 
55742Smax.romanov@nginx.com     nchunks = mmap_msg->size / PORT_MMAP_CHUNK_SIZE;
55842Smax.romanov@nginx.com     if ((mmap_msg->size % PORT_MMAP_CHUNK_SIZE) != 0) {
55942Smax.romanov@nginx.com         nchunks++;
56042Smax.romanov@nginx.com     }
56142Smax.romanov@nginx.com 
56280Smax.romanov@nginx.com     b->mem.start = nxt_port_mmap_chunk_start(hdr, mmap_msg->chunk_id);
56342Smax.romanov@nginx.com     b->mem.pos = b->mem.start;
56442Smax.romanov@nginx.com     b->mem.free = b->mem.start + mmap_msg->size;
56542Smax.romanov@nginx.com     b->mem.end = b->mem.start + nchunks * PORT_MMAP_CHUNK_SIZE;
56642Smax.romanov@nginx.com 
56780Smax.romanov@nginx.com     b->parent = hdr;
56842Smax.romanov@nginx.com 
569363Smax.romanov@nginx.com     nxt_debug(task, "incoming mmap buf allocation: %p [%p,%d] %PI->%PI,%d,%d",
570277Sigor@sysoev.ru               b, b->mem.start, b->mem.end - b->mem.start,
571363Smax.romanov@nginx.com               hdr->src_pid, hdr->dst_pid, hdr->id, mmap_msg->chunk_id);
572206Smax.romanov@nginx.com 
57342Smax.romanov@nginx.com     return b;
57442Smax.romanov@nginx.com }
57542Smax.romanov@nginx.com 
57642Smax.romanov@nginx.com 
57742Smax.romanov@nginx.com void
57842Smax.romanov@nginx.com nxt_port_mmap_write(nxt_task_t *task, nxt_port_t *port,
57942Smax.romanov@nginx.com     nxt_port_send_msg_t *msg, nxt_sendbuf_coalesce_t *sb)
58042Smax.romanov@nginx.com {
58180Smax.romanov@nginx.com     size_t                  bsize;
582197Smax.romanov@nginx.com     nxt_buf_t               *bmem;
58380Smax.romanov@nginx.com     nxt_uint_t              i;
58480Smax.romanov@nginx.com     nxt_port_mmap_msg_t     *mmap_msg;
58580Smax.romanov@nginx.com     nxt_port_mmap_header_t  *hdr;
58642Smax.romanov@nginx.com 
58742Smax.romanov@nginx.com     nxt_debug(task, "prepare %z bytes message for transfer to process %PI "
588277Sigor@sysoev.ru                     "via shared memory", sb->size, port->pid);
58942Smax.romanov@nginx.com 
59042Smax.romanov@nginx.com     bsize = sb->niov * sizeof(nxt_port_mmap_msg_t);
591197Smax.romanov@nginx.com     mmap_msg = port->mmsg_buf;
59242Smax.romanov@nginx.com 
59342Smax.romanov@nginx.com     bmem = msg->buf;
59442Smax.romanov@nginx.com 
59542Smax.romanov@nginx.com     for (i = 0; i < sb->niov; i++, mmap_msg++) {
59642Smax.romanov@nginx.com 
59742Smax.romanov@nginx.com         /* Lookup buffer which starts current iov_base. */
59842Smax.romanov@nginx.com         while (bmem && sb->iobuf[i].iov_base != bmem->mem.pos) {
59942Smax.romanov@nginx.com             bmem = bmem->next;
60042Smax.romanov@nginx.com         }
60142Smax.romanov@nginx.com 
60242Smax.romanov@nginx.com         if (nxt_slow_path(bmem == NULL)) {
603277Sigor@sysoev.ru             nxt_log_error(NXT_LOG_ERR, task->log,
604277Sigor@sysoev.ru                           "failed to find buf for iobuf[%d]", i);
60542Smax.romanov@nginx.com             return;
60642Smax.romanov@nginx.com             /* TODO clear b and exit */
60742Smax.romanov@nginx.com         }
60842Smax.romanov@nginx.com 
60980Smax.romanov@nginx.com         hdr = bmem->parent;
61042Smax.romanov@nginx.com 
61180Smax.romanov@nginx.com         mmap_msg->mmap_id = hdr->id;
61280Smax.romanov@nginx.com         mmap_msg->chunk_id = nxt_port_mmap_chunk_id(hdr, bmem->mem.pos);
61342Smax.romanov@nginx.com         mmap_msg->size = sb->iobuf[i].iov_len;
61442Smax.romanov@nginx.com 
61542Smax.romanov@nginx.com         nxt_debug(task, "mmap_msg={%D, %D, %D} to %PI",
61642Smax.romanov@nginx.com                   mmap_msg->mmap_id, mmap_msg->chunk_id, mmap_msg->size,
61742Smax.romanov@nginx.com                   port->pid);
61842Smax.romanov@nginx.com     }
61942Smax.romanov@nginx.com 
620197Smax.romanov@nginx.com     sb->iobuf[0].iov_base = port->mmsg_buf;
62142Smax.romanov@nginx.com     sb->iobuf[0].iov_len = bsize;
62242Smax.romanov@nginx.com     sb->niov = 1;
62342Smax.romanov@nginx.com     sb->size = bsize;
62442Smax.romanov@nginx.com 
62542Smax.romanov@nginx.com     msg->port_msg.mmap = 1;
62642Smax.romanov@nginx.com }
62742Smax.romanov@nginx.com 
62842Smax.romanov@nginx.com 
62942Smax.romanov@nginx.com void
63042Smax.romanov@nginx.com nxt_port_mmap_read(nxt_task_t *task, nxt_port_t *port,
63182Smax.romanov@nginx.com     nxt_port_recv_msg_t *msg)
63242Smax.romanov@nginx.com {
63342Smax.romanov@nginx.com     nxt_buf_t            *b, **pb;
63442Smax.romanov@nginx.com     nxt_port_mmap_msg_t  *end, *mmap_msg;
63542Smax.romanov@nginx.com 
63642Smax.romanov@nginx.com     b = msg->buf;
63742Smax.romanov@nginx.com 
63842Smax.romanov@nginx.com     mmap_msg = (nxt_port_mmap_msg_t *) b->mem.pos;
63942Smax.romanov@nginx.com     end = (nxt_port_mmap_msg_t *) b->mem.free;
64042Smax.romanov@nginx.com 
64142Smax.romanov@nginx.com     pb = &msg->buf;
64282Smax.romanov@nginx.com     msg->size = 0;
64342Smax.romanov@nginx.com 
64442Smax.romanov@nginx.com     while (mmap_msg < end) {
64542Smax.romanov@nginx.com         nxt_debug(task, "mmap_msg={%D, %D, %D} from %PI",
64642Smax.romanov@nginx.com                   mmap_msg->mmap_id, mmap_msg->chunk_id, mmap_msg->size,
64742Smax.romanov@nginx.com                   msg->port_msg.pid);
64842Smax.romanov@nginx.com 
64942Smax.romanov@nginx.com         *pb = nxt_port_mmap_get_incoming_buf(task, port, msg->port_msg.pid,
65042Smax.romanov@nginx.com                                              mmap_msg);
65142Smax.romanov@nginx.com         if (nxt_slow_path(*pb == NULL)) {
65242Smax.romanov@nginx.com             nxt_log_error(NXT_LOG_ERR, task->log, "failed to get mmap buffer");
65342Smax.romanov@nginx.com 
65442Smax.romanov@nginx.com             break;
65542Smax.romanov@nginx.com         }
65642Smax.romanov@nginx.com 
65782Smax.romanov@nginx.com         msg->size += mmap_msg->size;
65842Smax.romanov@nginx.com         pb = &(*pb)->next;
65942Smax.romanov@nginx.com         mmap_msg++;
66042Smax.romanov@nginx.com     }
66142Smax.romanov@nginx.com 
66242Smax.romanov@nginx.com     /* Mark original buf as complete. */
66342Smax.romanov@nginx.com     b->mem.pos += nxt_buf_used_size(b);
66442Smax.romanov@nginx.com }
66542Smax.romanov@nginx.com 
66642Smax.romanov@nginx.com 
66742Smax.romanov@nginx.com nxt_port_method_t
66842Smax.romanov@nginx.com nxt_port_mmap_get_method(nxt_task_t *task, nxt_port_t *port, nxt_buf_t *b)
66942Smax.romanov@nginx.com {
67080Smax.romanov@nginx.com     nxt_port_method_t       m;
67180Smax.romanov@nginx.com     nxt_port_mmap_header_t  *hdr;
67242Smax.romanov@nginx.com 
67342Smax.romanov@nginx.com     m = NXT_PORT_METHOD_ANY;
67442Smax.romanov@nginx.com 
67542Smax.romanov@nginx.com     for (; b != NULL; b = b->next) {
67642Smax.romanov@nginx.com         if (nxt_buf_used_size(b) == 0) {
67742Smax.romanov@nginx.com             /* empty buffers does not affect method */
67842Smax.romanov@nginx.com             continue;
67942Smax.romanov@nginx.com         }
68042Smax.romanov@nginx.com 
68142Smax.romanov@nginx.com         if (nxt_buf_is_port_mmap(b)) {
68280Smax.romanov@nginx.com             hdr = b->parent;
68342Smax.romanov@nginx.com 
68442Smax.romanov@nginx.com             if (m == NXT_PORT_METHOD_PLAIN) {
68542Smax.romanov@nginx.com                 nxt_log_error(NXT_LOG_ERR, task->log,
68642Smax.romanov@nginx.com                               "mixing plain and mmap buffers, "
68742Smax.romanov@nginx.com                               "using plain mode");
68842Smax.romanov@nginx.com 
68942Smax.romanov@nginx.com                 break;
69042Smax.romanov@nginx.com             }
69142Smax.romanov@nginx.com 
692363Smax.romanov@nginx.com             if (port->pid != hdr->dst_pid) {
69342Smax.romanov@nginx.com                 nxt_log_error(NXT_LOG_ERR, task->log,
69442Smax.romanov@nginx.com                               "send mmap buffer for %PI to %PI, "
695363Smax.romanov@nginx.com                               "using plain mode", hdr->dst_pid, port->pid);
69642Smax.romanov@nginx.com 
69742Smax.romanov@nginx.com                 m = NXT_PORT_METHOD_PLAIN;
69842Smax.romanov@nginx.com 
69942Smax.romanov@nginx.com                 break;
70042Smax.romanov@nginx.com             }
70142Smax.romanov@nginx.com 
70242Smax.romanov@nginx.com             if (m == NXT_PORT_METHOD_ANY) {
70342Smax.romanov@nginx.com                 nxt_debug(task, "using mmap mode");
70442Smax.romanov@nginx.com 
70542Smax.romanov@nginx.com                 m = NXT_PORT_METHOD_MMAP;
70642Smax.romanov@nginx.com             }
70742Smax.romanov@nginx.com         } else {
70842Smax.romanov@nginx.com             if (m == NXT_PORT_METHOD_MMAP) {
70942Smax.romanov@nginx.com                 nxt_log_error(NXT_LOG_ERR, task->log,
71042Smax.romanov@nginx.com                               "mixing mmap and plain buffers, "
71142Smax.romanov@nginx.com                               "switching to plain mode");
71242Smax.romanov@nginx.com 
71342Smax.romanov@nginx.com                 m = NXT_PORT_METHOD_PLAIN;
71442Smax.romanov@nginx.com 
71542Smax.romanov@nginx.com                 break;
71642Smax.romanov@nginx.com             }
71742Smax.romanov@nginx.com 
71842Smax.romanov@nginx.com             if (m == NXT_PORT_METHOD_ANY) {
71942Smax.romanov@nginx.com                 nxt_debug(task, "using plain mode");
72042Smax.romanov@nginx.com 
72142Smax.romanov@nginx.com                 m = NXT_PORT_METHOD_PLAIN;
72242Smax.romanov@nginx.com             }
72342Smax.romanov@nginx.com         }
72442Smax.romanov@nginx.com     }
72542Smax.romanov@nginx.com 
72642Smax.romanov@nginx.com     return m;
72742Smax.romanov@nginx.com }
728