xref: /unit/src/nxt_port_memory.c (revision 323)
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 
1942Smax.romanov@nginx.com 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 
29141Smax.romanov@nginx.com static nxt_array_t *
30141Smax.romanov@nginx.com nxt_port_mmaps_create()
31141Smax.romanov@nginx.com {
32141Smax.romanov@nginx.com     nxt_mp_t  *mp;
33141Smax.romanov@nginx.com 
34141Smax.romanov@nginx.com     mp = nxt_mp_create(1024, 128, 256, 32);
35141Smax.romanov@nginx.com 
36141Smax.romanov@nginx.com     if (nxt_slow_path(mp == NULL)) {
37141Smax.romanov@nginx.com         return NULL;
38141Smax.romanov@nginx.com     }
39141Smax.romanov@nginx.com 
40141Smax.romanov@nginx.com     return nxt_array_create(mp, 1, sizeof(nxt_port_mmap_t));
41141Smax.romanov@nginx.com }
42141Smax.romanov@nginx.com 
43141Smax.romanov@nginx.com 
44141Smax.romanov@nginx.com static nxt_port_mmap_t *
45141Smax.romanov@nginx.com nxt_port_mmap_add(nxt_array_t *port_mmaps)
46141Smax.romanov@nginx.com {
47141Smax.romanov@nginx.com     nxt_mp_thread_adopt(port_mmaps->mem_pool);
48141Smax.romanov@nginx.com 
49141Smax.romanov@nginx.com     return nxt_array_zero_add(port_mmaps);
50141Smax.romanov@nginx.com }
51141Smax.romanov@nginx.com 
52141Smax.romanov@nginx.com 
53141Smax.romanov@nginx.com void
54141Smax.romanov@nginx.com nxt_port_mmaps_destroy(nxt_array_t *port_mmaps, nxt_bool_t destroy_pool)
55141Smax.romanov@nginx.com {
56141Smax.romanov@nginx.com     uint32_t         i;
57141Smax.romanov@nginx.com     nxt_port_mmap_t  *port_mmap;
58141Smax.romanov@nginx.com 
59141Smax.romanov@nginx.com     if (port_mmaps == NULL) {
60141Smax.romanov@nginx.com         return;
61141Smax.romanov@nginx.com     }
62141Smax.romanov@nginx.com 
63141Smax.romanov@nginx.com     nxt_mp_thread_adopt(port_mmaps->mem_pool);
64141Smax.romanov@nginx.com 
65141Smax.romanov@nginx.com     port_mmap = port_mmaps->elts;
66141Smax.romanov@nginx.com 
67141Smax.romanov@nginx.com     for (i = 0; i < port_mmaps->nelts; i++) {
68141Smax.romanov@nginx.com         nxt_port_mmap_destroy(port_mmap);
69141Smax.romanov@nginx.com     }
70141Smax.romanov@nginx.com 
71141Smax.romanov@nginx.com     port_mmaps->nelts = 0;
72141Smax.romanov@nginx.com 
73141Smax.romanov@nginx.com     if (destroy_pool != 0) {
74141Smax.romanov@nginx.com         nxt_mp_destroy(port_mmaps->mem_pool);
75141Smax.romanov@nginx.com     }
76141Smax.romanov@nginx.com }
77141Smax.romanov@nginx.com 
78141Smax.romanov@nginx.com 
79195Smax.romanov@nginx.com #define nxt_port_mmap_free_junk(p, size)                                      \
80195Smax.romanov@nginx.com     memset((p), 0xA5, size)
81195Smax.romanov@nginx.com 
82195Smax.romanov@nginx.com 
8342Smax.romanov@nginx.com static void
8442Smax.romanov@nginx.com nxt_port_mmap_buf_completion(nxt_task_t *task, void *obj, void *data)
8542Smax.romanov@nginx.com {
8642Smax.romanov@nginx.com     u_char                  *p;
8765Sigor@sysoev.ru     nxt_mp_t                *mp;
8842Smax.romanov@nginx.com     nxt_buf_t               *b;
8942Smax.romanov@nginx.com     nxt_chunk_id_t          c;
9042Smax.romanov@nginx.com     nxt_port_mmap_header_t  *hdr;
9142Smax.romanov@nginx.com 
92122Smax.romanov@nginx.com     if (nxt_buf_ts_handle(task, obj, data)) {
93122Smax.romanov@nginx.com         return;
94122Smax.romanov@nginx.com     }
95122Smax.romanov@nginx.com 
9642Smax.romanov@nginx.com     b = obj;
9742Smax.romanov@nginx.com 
9842Smax.romanov@nginx.com     mp = b->data;
9942Smax.romanov@nginx.com 
10079Smax.romanov@nginx.com #if (NXT_DEBUG)
10179Smax.romanov@nginx.com     if (nxt_slow_path(data != b->parent)) {
10279Smax.romanov@nginx.com         nxt_log_alert(task->log, "completion data (%p) != b->parent (%p)",
10379Smax.romanov@nginx.com                       data, b->parent);
10479Smax.romanov@nginx.com         nxt_abort();
10579Smax.romanov@nginx.com     }
10679Smax.romanov@nginx.com #endif
10779Smax.romanov@nginx.com 
10880Smax.romanov@nginx.com     hdr = data;
10942Smax.romanov@nginx.com 
11042Smax.romanov@nginx.com     if (b->is_port_mmap_sent && b->mem.pos > b->mem.start) {
11142Smax.romanov@nginx.com         /*
11242Smax.romanov@nginx.com          * Chunks until b->mem.pos has been sent to other side,
11342Smax.romanov@nginx.com          * let's release rest (if any).
11442Smax.romanov@nginx.com          */
11542Smax.romanov@nginx.com         p = b->mem.pos - 1;
11680Smax.romanov@nginx.com         c = nxt_port_mmap_chunk_id(hdr, p) + 1;
11780Smax.romanov@nginx.com         p = nxt_port_mmap_chunk_start(hdr, c);
118141Smax.romanov@nginx.com 
11942Smax.romanov@nginx.com     } else {
12042Smax.romanov@nginx.com         p = b->mem.start;
12180Smax.romanov@nginx.com         c = nxt_port_mmap_chunk_id(hdr, p);
12242Smax.romanov@nginx.com     }
12342Smax.romanov@nginx.com 
124195Smax.romanov@nginx.com     nxt_port_mmap_free_junk(p, b->mem.end - p);
125195Smax.romanov@nginx.com 
126206Smax.romanov@nginx.com     nxt_debug(task, "mmap buf completion: %p [%p,%d] (sent=%d), %PI,%d,%d", b,
127206Smax.romanov@nginx.com               b->mem.start, b->mem.end - b->mem.start, b->is_port_mmap_sent,
128206Smax.romanov@nginx.com               hdr->pid, hdr->id, c);
129206Smax.romanov@nginx.com 
13042Smax.romanov@nginx.com     while (p < b->mem.end) {
13142Smax.romanov@nginx.com         nxt_port_mmap_set_chunk_free(hdr, c);
13242Smax.romanov@nginx.com 
13342Smax.romanov@nginx.com         p += PORT_MMAP_CHUNK_SIZE;
13442Smax.romanov@nginx.com         c++;
13542Smax.romanov@nginx.com     }
13642Smax.romanov@nginx.com 
137122Smax.romanov@nginx.com     nxt_mp_release(mp, b);
13842Smax.romanov@nginx.com }
13942Smax.romanov@nginx.com 
14042Smax.romanov@nginx.com 
14180Smax.romanov@nginx.com nxt_port_mmap_header_t *
14242Smax.romanov@nginx.com nxt_port_incoming_port_mmap(nxt_task_t *task, nxt_process_t *process,
14342Smax.romanov@nginx.com     nxt_fd_t fd)
14442Smax.romanov@nginx.com {
14580Smax.romanov@nginx.com     void                    *mem;
14680Smax.romanov@nginx.com     struct stat             mmap_stat;
14780Smax.romanov@nginx.com     nxt_port_mmap_t         *port_mmap;
14880Smax.romanov@nginx.com     nxt_port_mmap_header_t  *hdr;
14942Smax.romanov@nginx.com 
15042Smax.romanov@nginx.com     nxt_debug(task, "got new mmap fd #%FD from process %PI",
15142Smax.romanov@nginx.com               fd, process->pid);
15242Smax.romanov@nginx.com 
15380Smax.romanov@nginx.com     port_mmap = NULL;
15480Smax.romanov@nginx.com     hdr = NULL;
15580Smax.romanov@nginx.com 
15642Smax.romanov@nginx.com     if (fstat(fd, &mmap_stat) == -1) {
15742Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN, "fstat(%FD) failed %E", fd, nxt_errno);
15842Smax.romanov@nginx.com 
15942Smax.romanov@nginx.com         return NULL;
16042Smax.romanov@nginx.com     }
16142Smax.romanov@nginx.com 
16290Smax.romanov@nginx.com     nxt_thread_mutex_lock(&process->incoming_mutex);
16390Smax.romanov@nginx.com 
16442Smax.romanov@nginx.com     if (process->incoming == NULL) {
165141Smax.romanov@nginx.com         process->incoming = nxt_port_mmaps_create();
16642Smax.romanov@nginx.com     }
16742Smax.romanov@nginx.com 
16842Smax.romanov@nginx.com     if (nxt_slow_path(process->incoming == NULL)) {
16942Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN, "failed to allocate incoming array");
17042Smax.romanov@nginx.com 
17180Smax.romanov@nginx.com         goto fail;
17242Smax.romanov@nginx.com     }
17342Smax.romanov@nginx.com 
174141Smax.romanov@nginx.com     port_mmap = nxt_port_mmap_add(process->incoming);
17542Smax.romanov@nginx.com     if (nxt_slow_path(port_mmap == NULL)) {
17642Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN, "failed to add mmap to incoming array");
17742Smax.romanov@nginx.com 
17880Smax.romanov@nginx.com         goto fail;
17942Smax.romanov@nginx.com     }
18042Smax.romanov@nginx.com 
18180Smax.romanov@nginx.com     mem = nxt_mem_mmap(NULL, mmap_stat.st_size,
18280Smax.romanov@nginx.com                        PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
18342Smax.romanov@nginx.com 
18480Smax.romanov@nginx.com     if (nxt_slow_path(mem == MAP_FAILED)) {
18542Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN, "mmap() failed %E", nxt_errno);
18642Smax.romanov@nginx.com 
18780Smax.romanov@nginx.com         port_mmap = NULL;
18842Smax.romanov@nginx.com 
18980Smax.romanov@nginx.com         goto fail;
19042Smax.romanov@nginx.com     }
19142Smax.romanov@nginx.com 
19280Smax.romanov@nginx.com     port_mmap->hdr = mem;
19380Smax.romanov@nginx.com     hdr = port_mmap->hdr;
19480Smax.romanov@nginx.com 
19580Smax.romanov@nginx.com     if (nxt_slow_path(port_mmap->hdr->id != process->incoming->nelts - 1)) {
19680Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN, "port mmap id mismatch (%d != %d)",
19780Smax.romanov@nginx.com                 port_mmap->hdr->id, process->incoming->nelts - 1);
198141Smax.romanov@nginx.com         nxt_abort();
19980Smax.romanov@nginx.com     }
20080Smax.romanov@nginx.com 
201*323Smax.romanov@nginx.com     hdr->sent_over = 0xFFFFu;
202*323Smax.romanov@nginx.com 
20380Smax.romanov@nginx.com fail:
20480Smax.romanov@nginx.com 
20590Smax.romanov@nginx.com     nxt_thread_mutex_unlock(&process->incoming_mutex);
20690Smax.romanov@nginx.com 
20780Smax.romanov@nginx.com     return hdr;
20842Smax.romanov@nginx.com }
20942Smax.romanov@nginx.com 
21042Smax.romanov@nginx.com 
21180Smax.romanov@nginx.com static nxt_port_mmap_header_t *
21242Smax.romanov@nginx.com nxt_port_new_port_mmap(nxt_task_t *task, nxt_process_t *process,
21342Smax.romanov@nginx.com     nxt_port_t *port)
21442Smax.romanov@nginx.com {
21580Smax.romanov@nginx.com     void                    *mem;
21642Smax.romanov@nginx.com     u_char                  *p, name[64];
21780Smax.romanov@nginx.com     nxt_fd_t                fd;
21842Smax.romanov@nginx.com     nxt_port_mmap_t         *port_mmap;
21942Smax.romanov@nginx.com     nxt_port_mmap_header_t  *hdr;
22042Smax.romanov@nginx.com 
22180Smax.romanov@nginx.com     port_mmap = NULL;
22280Smax.romanov@nginx.com 
22342Smax.romanov@nginx.com     if (process->outgoing == NULL) {
224141Smax.romanov@nginx.com         process->outgoing = nxt_port_mmaps_create();
22542Smax.romanov@nginx.com     }
22642Smax.romanov@nginx.com 
22742Smax.romanov@nginx.com     if (nxt_slow_path(process->outgoing == NULL)) {
22842Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN, "failed to allocate outgoing array");
22942Smax.romanov@nginx.com 
23042Smax.romanov@nginx.com         return NULL;
23142Smax.romanov@nginx.com     }
23242Smax.romanov@nginx.com 
233141Smax.romanov@nginx.com     port_mmap = nxt_port_mmap_add(process->outgoing);
23442Smax.romanov@nginx.com     if (nxt_slow_path(port_mmap == NULL)) {
23542Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN,
23642Smax.romanov@nginx.com                 "failed to add port mmap to outgoing array");
23742Smax.romanov@nginx.com 
23842Smax.romanov@nginx.com         return NULL;
23942Smax.romanov@nginx.com     }
24042Smax.romanov@nginx.com 
241259Sigor@sysoev.ru     p = nxt_sprintf(name, name + sizeof(name), "/unit.%PI.%uxD",
242138Sigor@sysoev.ru                     nxt_pid, nxt_random(&task->thread->random));
24342Smax.romanov@nginx.com     *p = '\0';
24442Smax.romanov@nginx.com 
24542Smax.romanov@nginx.com #if (NXT_HAVE_MEMFD_CREATE)
246277Sigor@sysoev.ru 
24780Smax.romanov@nginx.com     fd = syscall(SYS_memfd_create, name, MFD_CLOEXEC);
24842Smax.romanov@nginx.com 
24980Smax.romanov@nginx.com     if (nxt_slow_path(fd == -1)) {
25042Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_CRIT, "memfd_create(%s) failed %E",
25142Smax.romanov@nginx.com                 name, nxt_errno);
25242Smax.romanov@nginx.com 
25342Smax.romanov@nginx.com         goto remove_fail;
25442Smax.romanov@nginx.com     }
25542Smax.romanov@nginx.com 
25680Smax.romanov@nginx.com     nxt_debug(task, "memfd_create(%s): %FD", name, fd);
25742Smax.romanov@nginx.com 
25842Smax.romanov@nginx.com #elif (NXT_HAVE_SHM_OPEN)
259277Sigor@sysoev.ru 
260277Sigor@sysoev.ru     /* Just in case. */
261277Sigor@sysoev.ru     shm_unlink((char *) name);
26242Smax.romanov@nginx.com 
26380Smax.romanov@nginx.com     fd = shm_open((char *) name, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
26442Smax.romanov@nginx.com 
26580Smax.romanov@nginx.com     nxt_debug(task, "shm_open(%s): %FD", name, fd);
26642Smax.romanov@nginx.com 
26780Smax.romanov@nginx.com     if (nxt_slow_path(fd == -1)) {
26842Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_CRIT, "shm_open(%s) failed %E", name, nxt_errno);
26942Smax.romanov@nginx.com 
27042Smax.romanov@nginx.com         goto remove_fail;
27142Smax.romanov@nginx.com     }
27242Smax.romanov@nginx.com 
27342Smax.romanov@nginx.com     if (nxt_slow_path(shm_unlink((char *) name) == -1)) {
27442Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN, "shm_unlink(%s) failed %E", name,
27542Smax.romanov@nginx.com                 nxt_errno);
27642Smax.romanov@nginx.com     }
277277Sigor@sysoev.ru 
27842Smax.romanov@nginx.com #endif
27942Smax.romanov@nginx.com 
28080Smax.romanov@nginx.com     if (nxt_slow_path(ftruncate(fd, PORT_MMAP_SIZE) == -1)) {
28142Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN, "ftruncate() failed %E", nxt_errno);
28242Smax.romanov@nginx.com 
28342Smax.romanov@nginx.com         goto remove_fail;
28442Smax.romanov@nginx.com     }
28542Smax.romanov@nginx.com 
286277Sigor@sysoev.ru     mem = nxt_mem_mmap(NULL, PORT_MMAP_SIZE, PROT_READ | PROT_WRITE,
287277Sigor@sysoev.ru                        MAP_SHARED, fd, 0);
28842Smax.romanov@nginx.com 
28980Smax.romanov@nginx.com     if (nxt_slow_path(mem == MAP_FAILED)) {
29042Smax.romanov@nginx.com         goto remove_fail;
29142Smax.romanov@nginx.com     }
29242Smax.romanov@nginx.com 
29380Smax.romanov@nginx.com     port_mmap->hdr = mem;
29480Smax.romanov@nginx.com 
29542Smax.romanov@nginx.com     /* Init segment header. */
29680Smax.romanov@nginx.com     hdr = port_mmap->hdr;
29742Smax.romanov@nginx.com 
29842Smax.romanov@nginx.com     nxt_memset(hdr->free_map, 0xFFU, sizeof(hdr->free_map));
29942Smax.romanov@nginx.com 
30080Smax.romanov@nginx.com     hdr->id = process->outgoing->nelts - 1;
30180Smax.romanov@nginx.com     hdr->pid = process->pid;
302*323Smax.romanov@nginx.com     hdr->sent_over = port->id;
30380Smax.romanov@nginx.com 
30480Smax.romanov@nginx.com     /* Mark first chunk as busy */
30580Smax.romanov@nginx.com     nxt_port_mmap_set_chunk_busy(hdr, 0);
30680Smax.romanov@nginx.com 
30742Smax.romanov@nginx.com     /* Mark as busy chunk followed the last available chunk. */
30842Smax.romanov@nginx.com     nxt_port_mmap_set_chunk_busy(hdr, PORT_MMAP_CHUNK_COUNT);
30942Smax.romanov@nginx.com 
31080Smax.romanov@nginx.com     nxt_debug(task, "send mmap fd %FD to process %PI", fd,
31142Smax.romanov@nginx.com               port->pid);
31242Smax.romanov@nginx.com 
31342Smax.romanov@nginx.com     /* TODO handle error */
314189Smax.romanov@nginx.com     (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_MMAP, fd, 0, 0, NULL);
31542Smax.romanov@nginx.com 
31642Smax.romanov@nginx.com     nxt_log(task, NXT_LOG_DEBUG, "new mmap #%D created for %PI -> %PI",
31780Smax.romanov@nginx.com             hdr->id, nxt_pid, process->pid);
31842Smax.romanov@nginx.com 
31980Smax.romanov@nginx.com     return hdr;
32042Smax.romanov@nginx.com 
32142Smax.romanov@nginx.com remove_fail:
32242Smax.romanov@nginx.com 
32342Smax.romanov@nginx.com     nxt_array_remove(process->outgoing, port_mmap);
32442Smax.romanov@nginx.com 
32542Smax.romanov@nginx.com     return NULL;
32642Smax.romanov@nginx.com }
32742Smax.romanov@nginx.com 
32842Smax.romanov@nginx.com 
32980Smax.romanov@nginx.com static nxt_port_mmap_header_t *
33042Smax.romanov@nginx.com nxt_port_mmap_get(nxt_task_t *task, nxt_port_t *port, nxt_chunk_id_t *c,
33142Smax.romanov@nginx.com     size_t size)
33242Smax.romanov@nginx.com {
33380Smax.romanov@nginx.com     nxt_array_t             *outgoing;
33480Smax.romanov@nginx.com     nxt_process_t           *process;
33580Smax.romanov@nginx.com     nxt_port_mmap_t         *port_mmap;
33680Smax.romanov@nginx.com     nxt_port_mmap_t         *end_port_mmap;
33780Smax.romanov@nginx.com     nxt_port_mmap_header_t  *hdr;
33842Smax.romanov@nginx.com 
33980Smax.romanov@nginx.com     process = port->process;
34042Smax.romanov@nginx.com     if (nxt_slow_path(process == NULL)) {
34142Smax.romanov@nginx.com         return NULL;
34242Smax.romanov@nginx.com     }
34342Smax.romanov@nginx.com 
34442Smax.romanov@nginx.com     *c = 0;
34580Smax.romanov@nginx.com     port_mmap = NULL;
34680Smax.romanov@nginx.com     hdr = NULL;
34742Smax.romanov@nginx.com 
34890Smax.romanov@nginx.com     nxt_thread_mutex_lock(&process->outgoing_mutex);
34990Smax.romanov@nginx.com 
35042Smax.romanov@nginx.com     if (process->outgoing == NULL) {
35180Smax.romanov@nginx.com         hdr = nxt_port_new_port_mmap(task, process, port);
35280Smax.romanov@nginx.com 
35380Smax.romanov@nginx.com         goto unlock_return;
35442Smax.romanov@nginx.com     }
35542Smax.romanov@nginx.com 
35642Smax.romanov@nginx.com     outgoing = process->outgoing;
35742Smax.romanov@nginx.com     port_mmap = outgoing->elts;
35842Smax.romanov@nginx.com     end_port_mmap = port_mmap + outgoing->nelts;
35942Smax.romanov@nginx.com 
36042Smax.romanov@nginx.com     while (port_mmap < end_port_mmap) {
36142Smax.romanov@nginx.com 
362*323Smax.romanov@nginx.com         if ( (port_mmap->hdr->sent_over == 0xFFFFu ||
363*323Smax.romanov@nginx.com               port_mmap->hdr->sent_over == port->id) &&
364*323Smax.romanov@nginx.com             nxt_port_mmap_get_free_chunk(port_mmap->hdr, c)) {
36580Smax.romanov@nginx.com             hdr = port_mmap->hdr;
36680Smax.romanov@nginx.com 
36780Smax.romanov@nginx.com             goto unlock_return;
36842Smax.romanov@nginx.com         }
36942Smax.romanov@nginx.com 
37042Smax.romanov@nginx.com         port_mmap++;
37142Smax.romanov@nginx.com     }
37242Smax.romanov@nginx.com 
37342Smax.romanov@nginx.com     /* TODO introduce port_mmap limit and release wait. */
37480Smax.romanov@nginx.com 
37580Smax.romanov@nginx.com     hdr = nxt_port_new_port_mmap(task, process, port);
37680Smax.romanov@nginx.com 
37780Smax.romanov@nginx.com unlock_return:
37880Smax.romanov@nginx.com 
37990Smax.romanov@nginx.com     nxt_thread_mutex_unlock(&process->outgoing_mutex);
38090Smax.romanov@nginx.com 
38180Smax.romanov@nginx.com     return hdr;
38242Smax.romanov@nginx.com }
38342Smax.romanov@nginx.com 
38442Smax.romanov@nginx.com 
38580Smax.romanov@nginx.com static nxt_port_mmap_header_t *
38642Smax.romanov@nginx.com nxt_port_get_port_incoming_mmap(nxt_task_t *task, nxt_pid_t spid, uint32_t id)
38742Smax.romanov@nginx.com {
38880Smax.romanov@nginx.com     nxt_array_t             *incoming;
38980Smax.romanov@nginx.com     nxt_process_t           *process;
39080Smax.romanov@nginx.com     nxt_port_mmap_t         *port_mmap;
39180Smax.romanov@nginx.com     nxt_port_mmap_header_t  *hdr;
39242Smax.romanov@nginx.com 
393196Smax.romanov@nginx.com     process = nxt_runtime_process_find(task->thread->runtime, spid);
39442Smax.romanov@nginx.com     if (nxt_slow_path(process == NULL)) {
39542Smax.romanov@nginx.com         return NULL;
39642Smax.romanov@nginx.com     }
39742Smax.romanov@nginx.com 
39880Smax.romanov@nginx.com     hdr = NULL;
39980Smax.romanov@nginx.com 
40090Smax.romanov@nginx.com     nxt_thread_mutex_lock(&process->incoming_mutex);
40190Smax.romanov@nginx.com 
40242Smax.romanov@nginx.com     incoming = process->incoming;
40380Smax.romanov@nginx.com 
40480Smax.romanov@nginx.com     if (nxt_fast_path(incoming != NULL && incoming->nelts > id)) {
40580Smax.romanov@nginx.com         port_mmap = incoming->elts;
40680Smax.romanov@nginx.com         hdr = port_mmap[id].hdr;
407277Sigor@sysoev.ru 
40880Smax.romanov@nginx.com     } else {
40980Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN,
41080Smax.romanov@nginx.com                 "failed to get incoming mmap #%d for process %PI", id, spid);
41142Smax.romanov@nginx.com     }
41242Smax.romanov@nginx.com 
41390Smax.romanov@nginx.com     nxt_thread_mutex_unlock(&process->incoming_mutex);
41490Smax.romanov@nginx.com 
41580Smax.romanov@nginx.com     return hdr;
41642Smax.romanov@nginx.com }
41742Smax.romanov@nginx.com 
41842Smax.romanov@nginx.com 
41942Smax.romanov@nginx.com nxt_buf_t *
42042Smax.romanov@nginx.com nxt_port_mmap_get_buf(nxt_task_t *task, nxt_port_t *port, size_t size)
42142Smax.romanov@nginx.com {
42242Smax.romanov@nginx.com     size_t                  nchunks;
42342Smax.romanov@nginx.com     nxt_buf_t               *b;
42442Smax.romanov@nginx.com     nxt_chunk_id_t          c;
42542Smax.romanov@nginx.com     nxt_port_mmap_header_t  *hdr;
42642Smax.romanov@nginx.com 
42742Smax.romanov@nginx.com     nxt_debug(task, "request %z bytes shm buffer", size);
42842Smax.romanov@nginx.com 
429122Smax.romanov@nginx.com     b = nxt_buf_mem_ts_alloc(task, port->mem_pool, 0);
43042Smax.romanov@nginx.com     if (nxt_slow_path(b == NULL)) {
43142Smax.romanov@nginx.com         return NULL;
43242Smax.romanov@nginx.com     }
43342Smax.romanov@nginx.com 
43442Smax.romanov@nginx.com     b->completion_handler = nxt_port_mmap_buf_completion;
43542Smax.romanov@nginx.com     nxt_buf_set_port_mmap(b);
43642Smax.romanov@nginx.com 
43780Smax.romanov@nginx.com     hdr = nxt_port_mmap_get(task, port, &c, size);
43880Smax.romanov@nginx.com     if (nxt_slow_path(hdr == NULL)) {
439122Smax.romanov@nginx.com         nxt_mp_release(port->mem_pool, b);
44042Smax.romanov@nginx.com         return NULL;
44142Smax.romanov@nginx.com     }
44242Smax.romanov@nginx.com 
44380Smax.romanov@nginx.com     b->parent = hdr;
444122Smax.romanov@nginx.com 
44580Smax.romanov@nginx.com     b->mem.start = nxt_port_mmap_chunk_start(hdr, c);
44642Smax.romanov@nginx.com     b->mem.pos = b->mem.start;
44742Smax.romanov@nginx.com     b->mem.free = b->mem.start;
44842Smax.romanov@nginx.com     b->mem.end = b->mem.start + PORT_MMAP_CHUNK_SIZE;
44942Smax.romanov@nginx.com 
45042Smax.romanov@nginx.com     nchunks = size / PORT_MMAP_CHUNK_SIZE;
45142Smax.romanov@nginx.com     if ((size % PORT_MMAP_CHUNK_SIZE) != 0 || nchunks == 0) {
45242Smax.romanov@nginx.com         nchunks++;
45342Smax.romanov@nginx.com     }
45442Smax.romanov@nginx.com 
455206Smax.romanov@nginx.com     nxt_debug(task, "outgoing mmap buf allocation: %p [%p,%d] %PI,%d,%d", b,
456206Smax.romanov@nginx.com               b->mem.start, b->mem.end - b->mem.start,
457206Smax.romanov@nginx.com               hdr->pid, hdr->id, c);
458206Smax.romanov@nginx.com 
45942Smax.romanov@nginx.com     c++;
46042Smax.romanov@nginx.com     nchunks--;
46142Smax.romanov@nginx.com 
46242Smax.romanov@nginx.com     /* Try to acquire as much chunks as required. */
46342Smax.romanov@nginx.com     while (nchunks > 0) {
46442Smax.romanov@nginx.com 
46580Smax.romanov@nginx.com         if (nxt_port_mmap_chk_set_chunk_busy(hdr, c) == 0) {
46642Smax.romanov@nginx.com             break;
46742Smax.romanov@nginx.com         }
46842Smax.romanov@nginx.com 
46942Smax.romanov@nginx.com         b->mem.end += PORT_MMAP_CHUNK_SIZE;
47042Smax.romanov@nginx.com         c++;
47142Smax.romanov@nginx.com         nchunks--;
47242Smax.romanov@nginx.com     }
47342Smax.romanov@nginx.com 
47442Smax.romanov@nginx.com     return b;
47542Smax.romanov@nginx.com }
47642Smax.romanov@nginx.com 
47742Smax.romanov@nginx.com 
47880Smax.romanov@nginx.com nxt_int_t
479206Smax.romanov@nginx.com nxt_port_mmap_increase_buf(nxt_task_t *task, nxt_buf_t *b, size_t size,
480206Smax.romanov@nginx.com     size_t min_size)
48180Smax.romanov@nginx.com {
482206Smax.romanov@nginx.com     size_t                  nchunks, free_size;
48380Smax.romanov@nginx.com     nxt_chunk_id_t          c, start;
48480Smax.romanov@nginx.com     nxt_port_mmap_header_t  *hdr;
48580Smax.romanov@nginx.com 
48680Smax.romanov@nginx.com     nxt_debug(task, "request increase %z bytes shm buffer", size);
48780Smax.romanov@nginx.com 
48880Smax.romanov@nginx.com     if (nxt_slow_path(nxt_buf_is_port_mmap(b) == 0)) {
48980Smax.romanov@nginx.com         nxt_log(task, NXT_LOG_WARN,
49080Smax.romanov@nginx.com                 "failed to increase, not a mmap buffer");
49180Smax.romanov@nginx.com         return NXT_ERROR;
49280Smax.romanov@nginx.com     }
49380Smax.romanov@nginx.com 
494206Smax.romanov@nginx.com     free_size = nxt_buf_mem_free_size(&b->mem);
495206Smax.romanov@nginx.com 
496206Smax.romanov@nginx.com     if (nxt_slow_path(size <= free_size)) {
49780Smax.romanov@nginx.com         return NXT_OK;
49880Smax.romanov@nginx.com     }
49980Smax.romanov@nginx.com 
50080Smax.romanov@nginx.com     hdr = b->parent;
50180Smax.romanov@nginx.com 
50280Smax.romanov@nginx.com     start = nxt_port_mmap_chunk_id(hdr, b->mem.end);
50380Smax.romanov@nginx.com 
504206Smax.romanov@nginx.com     size -= free_size;
50580Smax.romanov@nginx.com 
50680Smax.romanov@nginx.com     nchunks = size / PORT_MMAP_CHUNK_SIZE;
50780Smax.romanov@nginx.com     if ((size % PORT_MMAP_CHUNK_SIZE) != 0 || nchunks == 0) {
50880Smax.romanov@nginx.com         nchunks++;
50980Smax.romanov@nginx.com     }
51080Smax.romanov@nginx.com 
51180Smax.romanov@nginx.com     c = start;
51280Smax.romanov@nginx.com 
51380Smax.romanov@nginx.com     /* Try to acquire as much chunks as required. */
51480Smax.romanov@nginx.com     while (nchunks > 0) {
51580Smax.romanov@nginx.com 
51680Smax.romanov@nginx.com         if (nxt_port_mmap_chk_set_chunk_busy(hdr, c) == 0) {
51780Smax.romanov@nginx.com             break;
51880Smax.romanov@nginx.com         }
51980Smax.romanov@nginx.com 
52080Smax.romanov@nginx.com         c++;
52180Smax.romanov@nginx.com         nchunks--;
52280Smax.romanov@nginx.com     }
52380Smax.romanov@nginx.com 
524277Sigor@sysoev.ru     if (nchunks != 0
525277Sigor@sysoev.ru         && min_size > free_size + PORT_MMAP_CHUNK_SIZE * (c - start))
526277Sigor@sysoev.ru     {
52780Smax.romanov@nginx.com         c--;
52880Smax.romanov@nginx.com         while (c >= start) {
52980Smax.romanov@nginx.com             nxt_port_mmap_set_chunk_free(hdr, c);
53080Smax.romanov@nginx.com             c--;
53180Smax.romanov@nginx.com         }
53280Smax.romanov@nginx.com 
53380Smax.romanov@nginx.com         nxt_debug(task, "failed to increase, %d chunks busy", nchunks);
53480Smax.romanov@nginx.com 
53580Smax.romanov@nginx.com         return NXT_ERROR;
536277Sigor@sysoev.ru 
53780Smax.romanov@nginx.com     } else {
53880Smax.romanov@nginx.com         b->mem.end += PORT_MMAP_CHUNK_SIZE * (c - start);
53980Smax.romanov@nginx.com 
54080Smax.romanov@nginx.com         return NXT_OK;
54180Smax.romanov@nginx.com     }
54280Smax.romanov@nginx.com }
54380Smax.romanov@nginx.com 
54480Smax.romanov@nginx.com 
54542Smax.romanov@nginx.com static nxt_buf_t *
54642Smax.romanov@nginx.com nxt_port_mmap_get_incoming_buf(nxt_task_t *task, nxt_port_t *port,
54742Smax.romanov@nginx.com     nxt_pid_t spid, nxt_port_mmap_msg_t *mmap_msg)
54842Smax.romanov@nginx.com {
54942Smax.romanov@nginx.com     size_t                  nchunks;
55042Smax.romanov@nginx.com     nxt_buf_t               *b;
55180Smax.romanov@nginx.com     nxt_port_mmap_header_t  *hdr;
55280Smax.romanov@nginx.com 
55380Smax.romanov@nginx.com     hdr = nxt_port_get_port_incoming_mmap(task, spid, mmap_msg->mmap_id);
55480Smax.romanov@nginx.com     if (nxt_slow_path(hdr == NULL)) {
55580Smax.romanov@nginx.com         return NULL;
55680Smax.romanov@nginx.com     }
55742Smax.romanov@nginx.com 
558122Smax.romanov@nginx.com     b = nxt_buf_mem_ts_alloc(task, port->mem_pool, 0);
55942Smax.romanov@nginx.com     if (nxt_slow_path(b == NULL)) {
56042Smax.romanov@nginx.com         return NULL;
56142Smax.romanov@nginx.com     }
56242Smax.romanov@nginx.com 
56342Smax.romanov@nginx.com     b->completion_handler = nxt_port_mmap_buf_completion;
56442Smax.romanov@nginx.com 
56542Smax.romanov@nginx.com     nxt_buf_set_port_mmap(b);
56642Smax.romanov@nginx.com 
56742Smax.romanov@nginx.com     nchunks = mmap_msg->size / PORT_MMAP_CHUNK_SIZE;
56842Smax.romanov@nginx.com     if ((mmap_msg->size % PORT_MMAP_CHUNK_SIZE) != 0) {
56942Smax.romanov@nginx.com         nchunks++;
57042Smax.romanov@nginx.com     }
57142Smax.romanov@nginx.com 
57280Smax.romanov@nginx.com     b->mem.start = nxt_port_mmap_chunk_start(hdr, mmap_msg->chunk_id);
57342Smax.romanov@nginx.com     b->mem.pos = b->mem.start;
57442Smax.romanov@nginx.com     b->mem.free = b->mem.start + mmap_msg->size;
57542Smax.romanov@nginx.com     b->mem.end = b->mem.start + nchunks * PORT_MMAP_CHUNK_SIZE;
57642Smax.romanov@nginx.com 
57780Smax.romanov@nginx.com     b->parent = hdr;
57842Smax.romanov@nginx.com 
579277Sigor@sysoev.ru     nxt_debug(task, "incoming mmap buf allocation: %p [%p,%d] %PI,%d,%d",
580277Sigor@sysoev.ru               b, b->mem.start, b->mem.end - b->mem.start,
581206Smax.romanov@nginx.com               hdr->pid, hdr->id, mmap_msg->chunk_id);
582206Smax.romanov@nginx.com 
58342Smax.romanov@nginx.com     return b;
58442Smax.romanov@nginx.com }
58542Smax.romanov@nginx.com 
58642Smax.romanov@nginx.com 
58742Smax.romanov@nginx.com void
58842Smax.romanov@nginx.com nxt_port_mmap_write(nxt_task_t *task, nxt_port_t *port,
58942Smax.romanov@nginx.com     nxt_port_send_msg_t *msg, nxt_sendbuf_coalesce_t *sb)
59042Smax.romanov@nginx.com {
59180Smax.romanov@nginx.com     size_t                  bsize;
592197Smax.romanov@nginx.com     nxt_buf_t               *bmem;
59380Smax.romanov@nginx.com     nxt_uint_t              i;
59480Smax.romanov@nginx.com     nxt_port_mmap_msg_t     *mmap_msg;
59580Smax.romanov@nginx.com     nxt_port_mmap_header_t  *hdr;
59642Smax.romanov@nginx.com 
59742Smax.romanov@nginx.com     nxt_debug(task, "prepare %z bytes message for transfer to process %PI "
598277Sigor@sysoev.ru                     "via shared memory", sb->size, port->pid);
59942Smax.romanov@nginx.com 
60042Smax.romanov@nginx.com     bsize = sb->niov * sizeof(nxt_port_mmap_msg_t);
601197Smax.romanov@nginx.com     mmap_msg = port->mmsg_buf;
60242Smax.romanov@nginx.com 
60342Smax.romanov@nginx.com     bmem = msg->buf;
60442Smax.romanov@nginx.com 
60542Smax.romanov@nginx.com     for (i = 0; i < sb->niov; i++, mmap_msg++) {
60642Smax.romanov@nginx.com 
60742Smax.romanov@nginx.com         /* Lookup buffer which starts current iov_base. */
60842Smax.romanov@nginx.com         while (bmem && sb->iobuf[i].iov_base != bmem->mem.pos) {
60942Smax.romanov@nginx.com             bmem = bmem->next;
61042Smax.romanov@nginx.com         }
61142Smax.romanov@nginx.com 
61242Smax.romanov@nginx.com         if (nxt_slow_path(bmem == NULL)) {
613277Sigor@sysoev.ru             nxt_log_error(NXT_LOG_ERR, task->log,
614277Sigor@sysoev.ru                           "failed to find buf for iobuf[%d]", i);
61542Smax.romanov@nginx.com             return;
61642Smax.romanov@nginx.com             /* TODO clear b and exit */
61742Smax.romanov@nginx.com         }
61842Smax.romanov@nginx.com 
61980Smax.romanov@nginx.com         hdr = bmem->parent;
62042Smax.romanov@nginx.com 
62180Smax.romanov@nginx.com         mmap_msg->mmap_id = hdr->id;
62280Smax.romanov@nginx.com         mmap_msg->chunk_id = nxt_port_mmap_chunk_id(hdr, bmem->mem.pos);
62342Smax.romanov@nginx.com         mmap_msg->size = sb->iobuf[i].iov_len;
62442Smax.romanov@nginx.com 
62542Smax.romanov@nginx.com         nxt_debug(task, "mmap_msg={%D, %D, %D} to %PI",
62642Smax.romanov@nginx.com                   mmap_msg->mmap_id, mmap_msg->chunk_id, mmap_msg->size,
62742Smax.romanov@nginx.com                   port->pid);
62842Smax.romanov@nginx.com     }
62942Smax.romanov@nginx.com 
630197Smax.romanov@nginx.com     sb->iobuf[0].iov_base = port->mmsg_buf;
63142Smax.romanov@nginx.com     sb->iobuf[0].iov_len = bsize;
63242Smax.romanov@nginx.com     sb->niov = 1;
63342Smax.romanov@nginx.com     sb->size = bsize;
63442Smax.romanov@nginx.com 
63542Smax.romanov@nginx.com     msg->port_msg.mmap = 1;
63642Smax.romanov@nginx.com }
63742Smax.romanov@nginx.com 
63842Smax.romanov@nginx.com 
63942Smax.romanov@nginx.com void
64042Smax.romanov@nginx.com nxt_port_mmap_read(nxt_task_t *task, nxt_port_t *port,
64182Smax.romanov@nginx.com     nxt_port_recv_msg_t *msg)
64242Smax.romanov@nginx.com {
64342Smax.romanov@nginx.com     nxt_buf_t            *b, **pb;
64442Smax.romanov@nginx.com     nxt_port_mmap_msg_t  *end, *mmap_msg;
64542Smax.romanov@nginx.com 
64642Smax.romanov@nginx.com     b = msg->buf;
64742Smax.romanov@nginx.com 
64842Smax.romanov@nginx.com     mmap_msg = (nxt_port_mmap_msg_t *) b->mem.pos;
64942Smax.romanov@nginx.com     end = (nxt_port_mmap_msg_t *) b->mem.free;
65042Smax.romanov@nginx.com 
65142Smax.romanov@nginx.com     pb = &msg->buf;
65282Smax.romanov@nginx.com     msg->size = 0;
65342Smax.romanov@nginx.com 
65442Smax.romanov@nginx.com     while (mmap_msg < end) {
65542Smax.romanov@nginx.com         nxt_debug(task, "mmap_msg={%D, %D, %D} from %PI",
65642Smax.romanov@nginx.com                   mmap_msg->mmap_id, mmap_msg->chunk_id, mmap_msg->size,
65742Smax.romanov@nginx.com                   msg->port_msg.pid);
65842Smax.romanov@nginx.com 
65942Smax.romanov@nginx.com         *pb = nxt_port_mmap_get_incoming_buf(task, port, msg->port_msg.pid,
66042Smax.romanov@nginx.com                                              mmap_msg);
66142Smax.romanov@nginx.com         if (nxt_slow_path(*pb == NULL)) {
66242Smax.romanov@nginx.com             nxt_log_error(NXT_LOG_ERR, task->log, "failed to get mmap buffer");
66342Smax.romanov@nginx.com 
66442Smax.romanov@nginx.com             break;
66542Smax.romanov@nginx.com         }
66642Smax.romanov@nginx.com 
66782Smax.romanov@nginx.com         msg->size += mmap_msg->size;
66842Smax.romanov@nginx.com         pb = &(*pb)->next;
66942Smax.romanov@nginx.com         mmap_msg++;
67042Smax.romanov@nginx.com     }
67142Smax.romanov@nginx.com 
67242Smax.romanov@nginx.com     /* Mark original buf as complete. */
67342Smax.romanov@nginx.com     b->mem.pos += nxt_buf_used_size(b);
67442Smax.romanov@nginx.com }
67542Smax.romanov@nginx.com 
67642Smax.romanov@nginx.com 
67742Smax.romanov@nginx.com nxt_port_method_t
67842Smax.romanov@nginx.com nxt_port_mmap_get_method(nxt_task_t *task, nxt_port_t *port, nxt_buf_t *b)
67942Smax.romanov@nginx.com {
68080Smax.romanov@nginx.com     nxt_port_method_t       m;
68180Smax.romanov@nginx.com     nxt_port_mmap_header_t  *hdr;
68242Smax.romanov@nginx.com 
68342Smax.romanov@nginx.com     m = NXT_PORT_METHOD_ANY;
68442Smax.romanov@nginx.com 
68542Smax.romanov@nginx.com     for (; b != NULL; b = b->next) {
68642Smax.romanov@nginx.com         if (nxt_buf_used_size(b) == 0) {
68742Smax.romanov@nginx.com             /* empty buffers does not affect method */
68842Smax.romanov@nginx.com             continue;
68942Smax.romanov@nginx.com         }
69042Smax.romanov@nginx.com 
69142Smax.romanov@nginx.com         if (nxt_buf_is_port_mmap(b)) {
69280Smax.romanov@nginx.com             hdr = b->parent;
69342Smax.romanov@nginx.com 
69442Smax.romanov@nginx.com             if (m == NXT_PORT_METHOD_PLAIN) {
69542Smax.romanov@nginx.com                 nxt_log_error(NXT_LOG_ERR, task->log,
69642Smax.romanov@nginx.com                               "mixing plain and mmap buffers, "
69742Smax.romanov@nginx.com                               "using plain mode");
69842Smax.romanov@nginx.com 
69942Smax.romanov@nginx.com                 break;
70042Smax.romanov@nginx.com             }
70142Smax.romanov@nginx.com 
70280Smax.romanov@nginx.com             if (port->pid != hdr->pid) {
70342Smax.romanov@nginx.com                 nxt_log_error(NXT_LOG_ERR, task->log,
70442Smax.romanov@nginx.com                               "send mmap buffer for %PI to %PI, "
70580Smax.romanov@nginx.com                               "using plain mode", hdr->pid, port->pid);
70642Smax.romanov@nginx.com 
70742Smax.romanov@nginx.com                 m = NXT_PORT_METHOD_PLAIN;
70842Smax.romanov@nginx.com 
70942Smax.romanov@nginx.com                 break;
71042Smax.romanov@nginx.com             }
71142Smax.romanov@nginx.com 
71242Smax.romanov@nginx.com             if (m == NXT_PORT_METHOD_ANY) {
71342Smax.romanov@nginx.com                 nxt_debug(task, "using mmap mode");
71442Smax.romanov@nginx.com 
71542Smax.romanov@nginx.com                 m = NXT_PORT_METHOD_MMAP;
71642Smax.romanov@nginx.com             }
71742Smax.romanov@nginx.com         } else {
71842Smax.romanov@nginx.com             if (m == NXT_PORT_METHOD_MMAP) {
71942Smax.romanov@nginx.com                 nxt_log_error(NXT_LOG_ERR, task->log,
72042Smax.romanov@nginx.com                               "mixing mmap and plain buffers, "
72142Smax.romanov@nginx.com                               "switching to plain mode");
72242Smax.romanov@nginx.com 
72342Smax.romanov@nginx.com                 m = NXT_PORT_METHOD_PLAIN;
72442Smax.romanov@nginx.com 
72542Smax.romanov@nginx.com                 break;
72642Smax.romanov@nginx.com             }
72742Smax.romanov@nginx.com 
72842Smax.romanov@nginx.com             if (m == NXT_PORT_METHOD_ANY) {
72942Smax.romanov@nginx.com                 nxt_debug(task, "using plain mode");
73042Smax.romanov@nginx.com 
73142Smax.romanov@nginx.com                 m = NXT_PORT_METHOD_PLAIN;
73242Smax.romanov@nginx.com             }
73342Smax.romanov@nginx.com         }
73442Smax.romanov@nginx.com     }
73542Smax.romanov@nginx.com 
73642Smax.romanov@nginx.com     return m;
73742Smax.romanov@nginx.com }
738