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