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 29*141Smax.romanov@nginx.com static nxt_array_t * 30*141Smax.romanov@nginx.com nxt_port_mmaps_create() 31*141Smax.romanov@nginx.com { 32*141Smax.romanov@nginx.com nxt_mp_t *mp; 33*141Smax.romanov@nginx.com 34*141Smax.romanov@nginx.com mp = nxt_mp_create(1024, 128, 256, 32); 35*141Smax.romanov@nginx.com 36*141Smax.romanov@nginx.com if (nxt_slow_path(mp == NULL)) { 37*141Smax.romanov@nginx.com return NULL; 38*141Smax.romanov@nginx.com } 39*141Smax.romanov@nginx.com 40*141Smax.romanov@nginx.com return nxt_array_create(mp, 1, sizeof(nxt_port_mmap_t)); 41*141Smax.romanov@nginx.com } 42*141Smax.romanov@nginx.com 43*141Smax.romanov@nginx.com 44*141Smax.romanov@nginx.com static nxt_port_mmap_t * 45*141Smax.romanov@nginx.com nxt_port_mmap_add(nxt_array_t *port_mmaps) 46*141Smax.romanov@nginx.com { 47*141Smax.romanov@nginx.com nxt_mp_thread_adopt(port_mmaps->mem_pool); 48*141Smax.romanov@nginx.com 49*141Smax.romanov@nginx.com return nxt_array_zero_add(port_mmaps); 50*141Smax.romanov@nginx.com } 51*141Smax.romanov@nginx.com 52*141Smax.romanov@nginx.com 53*141Smax.romanov@nginx.com void 54*141Smax.romanov@nginx.com nxt_port_mmaps_destroy(nxt_array_t *port_mmaps, nxt_bool_t destroy_pool) 55*141Smax.romanov@nginx.com { 56*141Smax.romanov@nginx.com uint32_t i; 57*141Smax.romanov@nginx.com nxt_port_mmap_t *port_mmap; 58*141Smax.romanov@nginx.com 59*141Smax.romanov@nginx.com if (port_mmaps == NULL) { 60*141Smax.romanov@nginx.com return; 61*141Smax.romanov@nginx.com } 62*141Smax.romanov@nginx.com 63*141Smax.romanov@nginx.com nxt_mp_thread_adopt(port_mmaps->mem_pool); 64*141Smax.romanov@nginx.com 65*141Smax.romanov@nginx.com port_mmap = port_mmaps->elts; 66*141Smax.romanov@nginx.com 67*141Smax.romanov@nginx.com for (i = 0; i < port_mmaps->nelts; i++) { 68*141Smax.romanov@nginx.com nxt_port_mmap_destroy(port_mmap); 69*141Smax.romanov@nginx.com } 70*141Smax.romanov@nginx.com 71*141Smax.romanov@nginx.com port_mmaps->nelts = 0; 72*141Smax.romanov@nginx.com 73*141Smax.romanov@nginx.com if (destroy_pool != 0) { 74*141Smax.romanov@nginx.com nxt_mp_destroy(port_mmaps->mem_pool); 75*141Smax.romanov@nginx.com } 76*141Smax.romanov@nginx.com } 77*141Smax.romanov@nginx.com 78*141Smax.romanov@nginx.com 7942Smax.romanov@nginx.com static void 8042Smax.romanov@nginx.com nxt_port_mmap_buf_completion(nxt_task_t *task, void *obj, void *data) 8142Smax.romanov@nginx.com { 8242Smax.romanov@nginx.com u_char *p; 8365Sigor@sysoev.ru nxt_mp_t *mp; 8442Smax.romanov@nginx.com nxt_buf_t *b; 8542Smax.romanov@nginx.com nxt_chunk_id_t c; 8642Smax.romanov@nginx.com nxt_port_mmap_header_t *hdr; 8742Smax.romanov@nginx.com 88122Smax.romanov@nginx.com if (nxt_buf_ts_handle(task, obj, data)) { 89122Smax.romanov@nginx.com return; 90122Smax.romanov@nginx.com } 91122Smax.romanov@nginx.com 9242Smax.romanov@nginx.com b = obj; 9342Smax.romanov@nginx.com 9442Smax.romanov@nginx.com nxt_debug(task, "mmap buf completion: %p %p", b, b->mem.start); 9542Smax.romanov@nginx.com 9642Smax.romanov@nginx.com mp = b->data; 9742Smax.romanov@nginx.com 9879Smax.romanov@nginx.com #if (NXT_DEBUG) 9979Smax.romanov@nginx.com if (nxt_slow_path(data != b->parent)) { 10079Smax.romanov@nginx.com nxt_log_alert(task->log, "completion data (%p) != b->parent (%p)", 10179Smax.romanov@nginx.com data, b->parent); 10279Smax.romanov@nginx.com nxt_abort(); 10379Smax.romanov@nginx.com } 10479Smax.romanov@nginx.com #endif 10579Smax.romanov@nginx.com 10680Smax.romanov@nginx.com hdr = data; 10742Smax.romanov@nginx.com 10842Smax.romanov@nginx.com if (b->is_port_mmap_sent && b->mem.pos > b->mem.start) { 10942Smax.romanov@nginx.com /* 11042Smax.romanov@nginx.com * Chunks until b->mem.pos has been sent to other side, 11142Smax.romanov@nginx.com * let's release rest (if any). 11242Smax.romanov@nginx.com */ 11342Smax.romanov@nginx.com p = b->mem.pos - 1; 11480Smax.romanov@nginx.com c = nxt_port_mmap_chunk_id(hdr, p) + 1; 11580Smax.romanov@nginx.com p = nxt_port_mmap_chunk_start(hdr, c); 116*141Smax.romanov@nginx.com 11742Smax.romanov@nginx.com } else { 11842Smax.romanov@nginx.com p = b->mem.start; 11980Smax.romanov@nginx.com c = nxt_port_mmap_chunk_id(hdr, p); 12042Smax.romanov@nginx.com } 12142Smax.romanov@nginx.com 12242Smax.romanov@nginx.com while (p < b->mem.end) { 12342Smax.romanov@nginx.com nxt_port_mmap_set_chunk_free(hdr, c); 12442Smax.romanov@nginx.com 12542Smax.romanov@nginx.com p += PORT_MMAP_CHUNK_SIZE; 12642Smax.romanov@nginx.com c++; 12742Smax.romanov@nginx.com } 12842Smax.romanov@nginx.com 129122Smax.romanov@nginx.com nxt_mp_release(mp, b); 13042Smax.romanov@nginx.com } 13142Smax.romanov@nginx.com 13242Smax.romanov@nginx.com 13380Smax.romanov@nginx.com nxt_port_mmap_header_t * 13442Smax.romanov@nginx.com nxt_port_incoming_port_mmap(nxt_task_t *task, nxt_process_t *process, 13542Smax.romanov@nginx.com nxt_fd_t fd) 13642Smax.romanov@nginx.com { 13780Smax.romanov@nginx.com void *mem; 13880Smax.romanov@nginx.com struct stat mmap_stat; 13980Smax.romanov@nginx.com nxt_port_mmap_t *port_mmap; 14080Smax.romanov@nginx.com nxt_port_mmap_header_t *hdr; 14142Smax.romanov@nginx.com 14242Smax.romanov@nginx.com nxt_debug(task, "got new mmap fd #%FD from process %PI", 14342Smax.romanov@nginx.com fd, process->pid); 14442Smax.romanov@nginx.com 14580Smax.romanov@nginx.com port_mmap = NULL; 14680Smax.romanov@nginx.com hdr = NULL; 14780Smax.romanov@nginx.com 14842Smax.romanov@nginx.com if (fstat(fd, &mmap_stat) == -1) { 14942Smax.romanov@nginx.com nxt_log(task, NXT_LOG_WARN, "fstat(%FD) failed %E", fd, nxt_errno); 15042Smax.romanov@nginx.com 15142Smax.romanov@nginx.com return NULL; 15242Smax.romanov@nginx.com } 15342Smax.romanov@nginx.com 15490Smax.romanov@nginx.com nxt_thread_mutex_lock(&process->incoming_mutex); 15590Smax.romanov@nginx.com 15642Smax.romanov@nginx.com if (process->incoming == NULL) { 157*141Smax.romanov@nginx.com process->incoming = nxt_port_mmaps_create(); 15842Smax.romanov@nginx.com } 15942Smax.romanov@nginx.com 16042Smax.romanov@nginx.com if (nxt_slow_path(process->incoming == NULL)) { 16142Smax.romanov@nginx.com nxt_log(task, NXT_LOG_WARN, "failed to allocate incoming array"); 16242Smax.romanov@nginx.com 16380Smax.romanov@nginx.com goto fail; 16442Smax.romanov@nginx.com } 16542Smax.romanov@nginx.com 166*141Smax.romanov@nginx.com port_mmap = nxt_port_mmap_add(process->incoming); 16742Smax.romanov@nginx.com if (nxt_slow_path(port_mmap == NULL)) { 16842Smax.romanov@nginx.com nxt_log(task, NXT_LOG_WARN, "failed to add mmap to incoming array"); 16942Smax.romanov@nginx.com 17080Smax.romanov@nginx.com goto fail; 17142Smax.romanov@nginx.com } 17242Smax.romanov@nginx.com 17380Smax.romanov@nginx.com mem = nxt_mem_mmap(NULL, mmap_stat.st_size, 17480Smax.romanov@nginx.com PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 17542Smax.romanov@nginx.com 17680Smax.romanov@nginx.com if (nxt_slow_path(mem == MAP_FAILED)) { 17742Smax.romanov@nginx.com nxt_log(task, NXT_LOG_WARN, "mmap() failed %E", nxt_errno); 17842Smax.romanov@nginx.com 17980Smax.romanov@nginx.com port_mmap = NULL; 18042Smax.romanov@nginx.com 18180Smax.romanov@nginx.com goto fail; 18242Smax.romanov@nginx.com } 18342Smax.romanov@nginx.com 18480Smax.romanov@nginx.com port_mmap->hdr = mem; 18580Smax.romanov@nginx.com hdr = port_mmap->hdr; 18680Smax.romanov@nginx.com 18780Smax.romanov@nginx.com if (nxt_slow_path(port_mmap->hdr->id != process->incoming->nelts - 1)) { 18880Smax.romanov@nginx.com nxt_log(task, NXT_LOG_WARN, "port mmap id mismatch (%d != %d)", 18980Smax.romanov@nginx.com port_mmap->hdr->id, process->incoming->nelts - 1); 190*141Smax.romanov@nginx.com nxt_abort(); 19180Smax.romanov@nginx.com } 19280Smax.romanov@nginx.com 19380Smax.romanov@nginx.com fail: 19480Smax.romanov@nginx.com 19590Smax.romanov@nginx.com nxt_thread_mutex_unlock(&process->incoming_mutex); 19690Smax.romanov@nginx.com 19780Smax.romanov@nginx.com return hdr; 19842Smax.romanov@nginx.com } 19942Smax.romanov@nginx.com 20042Smax.romanov@nginx.com 20142Smax.romanov@nginx.com static void 20242Smax.romanov@nginx.com nxt_port_mmap_send_fd_buf_completion(nxt_task_t *task, void *obj, void *data) 20342Smax.romanov@nginx.com { 20480Smax.romanov@nginx.com nxt_fd_t fd; 20580Smax.romanov@nginx.com nxt_buf_t *b; 20680Smax.romanov@nginx.com nxt_mp_t *mp; 20742Smax.romanov@nginx.com 208122Smax.romanov@nginx.com if (nxt_buf_ts_handle(task, obj, data)) { 209122Smax.romanov@nginx.com return; 210122Smax.romanov@nginx.com } 211122Smax.romanov@nginx.com 21242Smax.romanov@nginx.com b = obj; 21379Smax.romanov@nginx.com mp = b->data; 21480Smax.romanov@nginx.com fd = (nxt_fd_t) (intptr_t) data; 21542Smax.romanov@nginx.com 21679Smax.romanov@nginx.com #if (NXT_DEBUG) 21779Smax.romanov@nginx.com if (nxt_slow_path(data != b->parent)) { 21879Smax.romanov@nginx.com nxt_log_alert(task->log, "completion data (%p) != b->parent (%p)", 21979Smax.romanov@nginx.com data, b->parent); 22079Smax.romanov@nginx.com nxt_abort(); 22179Smax.romanov@nginx.com } 22279Smax.romanov@nginx.com #endif 22342Smax.romanov@nginx.com 22480Smax.romanov@nginx.com nxt_debug(task, "mmap fd %FD has been sent", fd); 22579Smax.romanov@nginx.com 22680Smax.romanov@nginx.com nxt_fd_close(fd); 22742Smax.romanov@nginx.com 228122Smax.romanov@nginx.com nxt_mp_release(mp, b); 22942Smax.romanov@nginx.com } 23042Smax.romanov@nginx.com 23142Smax.romanov@nginx.com 23280Smax.romanov@nginx.com static nxt_port_mmap_header_t * 23342Smax.romanov@nginx.com nxt_port_new_port_mmap(nxt_task_t *task, nxt_process_t *process, 23442Smax.romanov@nginx.com nxt_port_t *port) 23542Smax.romanov@nginx.com { 23680Smax.romanov@nginx.com void *mem; 23742Smax.romanov@nginx.com u_char *p, name[64]; 23880Smax.romanov@nginx.com nxt_fd_t fd; 23942Smax.romanov@nginx.com nxt_buf_t *b; 24042Smax.romanov@nginx.com nxt_port_mmap_t *port_mmap; 24142Smax.romanov@nginx.com nxt_port_mmap_header_t *hdr; 24242Smax.romanov@nginx.com 24380Smax.romanov@nginx.com port_mmap = NULL; 24480Smax.romanov@nginx.com 24542Smax.romanov@nginx.com if (process->outgoing == NULL) { 246*141Smax.romanov@nginx.com process->outgoing = nxt_port_mmaps_create(); 24742Smax.romanov@nginx.com } 24842Smax.romanov@nginx.com 24942Smax.romanov@nginx.com if (nxt_slow_path(process->outgoing == NULL)) { 25042Smax.romanov@nginx.com nxt_log(task, NXT_LOG_WARN, "failed to allocate outgoing array"); 25142Smax.romanov@nginx.com 25242Smax.romanov@nginx.com return NULL; 25342Smax.romanov@nginx.com } 25442Smax.romanov@nginx.com 255*141Smax.romanov@nginx.com port_mmap = nxt_port_mmap_add(process->outgoing); 25642Smax.romanov@nginx.com if (nxt_slow_path(port_mmap == NULL)) { 25742Smax.romanov@nginx.com nxt_log(task, NXT_LOG_WARN, 25842Smax.romanov@nginx.com "failed to add port mmap to outgoing array"); 25942Smax.romanov@nginx.com 26042Smax.romanov@nginx.com return NULL; 26142Smax.romanov@nginx.com } 26242Smax.romanov@nginx.com 26376Smax.romanov@nginx.com p = nxt_sprintf(name, name + sizeof(name), "/nginext.%PI.%uxD", 264138Sigor@sysoev.ru nxt_pid, nxt_random(&task->thread->random)); 26542Smax.romanov@nginx.com *p = '\0'; 26642Smax.romanov@nginx.com 26742Smax.romanov@nginx.com #if (NXT_HAVE_MEMFD_CREATE) 26880Smax.romanov@nginx.com fd = syscall(SYS_memfd_create, name, MFD_CLOEXEC); 26942Smax.romanov@nginx.com 27080Smax.romanov@nginx.com if (nxt_slow_path(fd == -1)) { 27142Smax.romanov@nginx.com nxt_log(task, NXT_LOG_CRIT, "memfd_create(%s) failed %E", 27242Smax.romanov@nginx.com name, nxt_errno); 27342Smax.romanov@nginx.com 27442Smax.romanov@nginx.com goto remove_fail; 27542Smax.romanov@nginx.com } 27642Smax.romanov@nginx.com 27780Smax.romanov@nginx.com nxt_debug(task, "memfd_create(%s): %FD", name, fd); 27842Smax.romanov@nginx.com 27942Smax.romanov@nginx.com #elif (NXT_HAVE_SHM_OPEN) 28042Smax.romanov@nginx.com shm_unlink((char *) name); // just in case 28142Smax.romanov@nginx.com 28280Smax.romanov@nginx.com fd = shm_open((char *) name, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR); 28342Smax.romanov@nginx.com 28480Smax.romanov@nginx.com nxt_debug(task, "shm_open(%s): %FD", name, fd); 28542Smax.romanov@nginx.com 28680Smax.romanov@nginx.com if (nxt_slow_path(fd == -1)) { 28742Smax.romanov@nginx.com nxt_log(task, NXT_LOG_CRIT, "shm_open(%s) failed %E", name, nxt_errno); 28842Smax.romanov@nginx.com 28942Smax.romanov@nginx.com goto remove_fail; 29042Smax.romanov@nginx.com } 29142Smax.romanov@nginx.com 29242Smax.romanov@nginx.com if (nxt_slow_path(shm_unlink((char *) name) == -1)) { 29342Smax.romanov@nginx.com nxt_log(task, NXT_LOG_WARN, "shm_unlink(%s) failed %E", name, 29442Smax.romanov@nginx.com nxt_errno); 29542Smax.romanov@nginx.com } 29642Smax.romanov@nginx.com #endif 29742Smax.romanov@nginx.com 29880Smax.romanov@nginx.com if (nxt_slow_path(ftruncate(fd, PORT_MMAP_SIZE) == -1)) { 29942Smax.romanov@nginx.com nxt_log(task, NXT_LOG_WARN, "ftruncate() failed %E", nxt_errno); 30042Smax.romanov@nginx.com 30142Smax.romanov@nginx.com goto remove_fail; 30242Smax.romanov@nginx.com } 30342Smax.romanov@nginx.com 30480Smax.romanov@nginx.com mem = nxt_mem_mmap(NULL, PORT_MMAP_SIZE, 30580Smax.romanov@nginx.com PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 30642Smax.romanov@nginx.com 30780Smax.romanov@nginx.com if (nxt_slow_path(mem == MAP_FAILED)) { 30842Smax.romanov@nginx.com goto remove_fail; 30942Smax.romanov@nginx.com } 31042Smax.romanov@nginx.com 31180Smax.romanov@nginx.com port_mmap->hdr = mem; 31280Smax.romanov@nginx.com 313122Smax.romanov@nginx.com b = nxt_buf_mem_ts_alloc(task, port->mem_pool, 0); 31479Smax.romanov@nginx.com if (nxt_slow_path(b == NULL)) { 31579Smax.romanov@nginx.com goto remove_fail; 31679Smax.romanov@nginx.com } 31779Smax.romanov@nginx.com 31879Smax.romanov@nginx.com b->completion_handler = nxt_port_mmap_send_fd_buf_completion; 31980Smax.romanov@nginx.com b->parent = (void *) (intptr_t) fd; 32079Smax.romanov@nginx.com 32142Smax.romanov@nginx.com /* Init segment header. */ 32280Smax.romanov@nginx.com hdr = port_mmap->hdr; 32342Smax.romanov@nginx.com 32442Smax.romanov@nginx.com nxt_memset(hdr->free_map, 0xFFU, sizeof(hdr->free_map)); 32542Smax.romanov@nginx.com 32680Smax.romanov@nginx.com hdr->id = process->outgoing->nelts - 1; 32780Smax.romanov@nginx.com hdr->pid = process->pid; 32880Smax.romanov@nginx.com 32980Smax.romanov@nginx.com /* Mark first chunk as busy */ 33080Smax.romanov@nginx.com nxt_port_mmap_set_chunk_busy(hdr, 0); 33180Smax.romanov@nginx.com 33242Smax.romanov@nginx.com /* Mark as busy chunk followed the last available chunk. */ 33342Smax.romanov@nginx.com nxt_port_mmap_set_chunk_busy(hdr, PORT_MMAP_CHUNK_COUNT); 33442Smax.romanov@nginx.com 33580Smax.romanov@nginx.com nxt_debug(task, "send mmap fd %FD to process %PI", fd, 33642Smax.romanov@nginx.com port->pid); 33742Smax.romanov@nginx.com 33842Smax.romanov@nginx.com /* TODO handle error */ 33980Smax.romanov@nginx.com (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_MMAP, fd, 0, 0, b); 34042Smax.romanov@nginx.com 34142Smax.romanov@nginx.com nxt_log(task, NXT_LOG_DEBUG, "new mmap #%D created for %PI -> %PI", 34280Smax.romanov@nginx.com hdr->id, nxt_pid, process->pid); 34342Smax.romanov@nginx.com 34480Smax.romanov@nginx.com return hdr; 34542Smax.romanov@nginx.com 34642Smax.romanov@nginx.com remove_fail: 34742Smax.romanov@nginx.com 34842Smax.romanov@nginx.com nxt_array_remove(process->outgoing, port_mmap); 34942Smax.romanov@nginx.com 35042Smax.romanov@nginx.com return NULL; 35142Smax.romanov@nginx.com } 35242Smax.romanov@nginx.com 35342Smax.romanov@nginx.com 35480Smax.romanov@nginx.com static nxt_port_mmap_header_t * 35542Smax.romanov@nginx.com nxt_port_mmap_get(nxt_task_t *task, nxt_port_t *port, nxt_chunk_id_t *c, 35642Smax.romanov@nginx.com size_t size) 35742Smax.romanov@nginx.com { 35880Smax.romanov@nginx.com nxt_array_t *outgoing; 35980Smax.romanov@nginx.com nxt_process_t *process; 36080Smax.romanov@nginx.com nxt_port_mmap_t *port_mmap; 36180Smax.romanov@nginx.com nxt_port_mmap_t *end_port_mmap; 36280Smax.romanov@nginx.com nxt_port_mmap_header_t *hdr; 36342Smax.romanov@nginx.com 36480Smax.romanov@nginx.com process = port->process; 36542Smax.romanov@nginx.com if (nxt_slow_path(process == NULL)) { 36642Smax.romanov@nginx.com return NULL; 36742Smax.romanov@nginx.com } 36842Smax.romanov@nginx.com 36942Smax.romanov@nginx.com *c = 0; 37080Smax.romanov@nginx.com port_mmap = NULL; 37180Smax.romanov@nginx.com hdr = NULL; 37242Smax.romanov@nginx.com 37390Smax.romanov@nginx.com nxt_thread_mutex_lock(&process->outgoing_mutex); 37490Smax.romanov@nginx.com 37542Smax.romanov@nginx.com if (process->outgoing == NULL) { 37680Smax.romanov@nginx.com hdr = nxt_port_new_port_mmap(task, process, port); 37780Smax.romanov@nginx.com 37880Smax.romanov@nginx.com goto unlock_return; 37942Smax.romanov@nginx.com } 38042Smax.romanov@nginx.com 38142Smax.romanov@nginx.com outgoing = process->outgoing; 38242Smax.romanov@nginx.com port_mmap = outgoing->elts; 38342Smax.romanov@nginx.com end_port_mmap = port_mmap + outgoing->nelts; 38442Smax.romanov@nginx.com 38542Smax.romanov@nginx.com while (port_mmap < end_port_mmap) { 38642Smax.romanov@nginx.com 38780Smax.romanov@nginx.com if (nxt_port_mmap_get_free_chunk(port_mmap->hdr, c)) { 38880Smax.romanov@nginx.com hdr = port_mmap->hdr; 38980Smax.romanov@nginx.com 39080Smax.romanov@nginx.com goto unlock_return; 39142Smax.romanov@nginx.com } 39242Smax.romanov@nginx.com 39342Smax.romanov@nginx.com port_mmap++; 39442Smax.romanov@nginx.com } 39542Smax.romanov@nginx.com 39642Smax.romanov@nginx.com /* TODO introduce port_mmap limit and release wait. */ 39780Smax.romanov@nginx.com 39880Smax.romanov@nginx.com hdr = nxt_port_new_port_mmap(task, process, port); 39980Smax.romanov@nginx.com 40080Smax.romanov@nginx.com unlock_return: 40180Smax.romanov@nginx.com 40290Smax.romanov@nginx.com nxt_thread_mutex_unlock(&process->outgoing_mutex); 40390Smax.romanov@nginx.com 40480Smax.romanov@nginx.com return hdr; 40542Smax.romanov@nginx.com } 40642Smax.romanov@nginx.com 40742Smax.romanov@nginx.com 40880Smax.romanov@nginx.com static nxt_port_mmap_header_t * 40942Smax.romanov@nginx.com nxt_port_get_port_incoming_mmap(nxt_task_t *task, nxt_pid_t spid, uint32_t id) 41042Smax.romanov@nginx.com { 41180Smax.romanov@nginx.com nxt_array_t *incoming; 41280Smax.romanov@nginx.com nxt_process_t *process; 41380Smax.romanov@nginx.com nxt_port_mmap_t *port_mmap; 41480Smax.romanov@nginx.com nxt_port_mmap_header_t *hdr; 41542Smax.romanov@nginx.com 41642Smax.romanov@nginx.com process = nxt_runtime_process_get(task->thread->runtime, spid); 41742Smax.romanov@nginx.com if (nxt_slow_path(process == NULL)) { 41842Smax.romanov@nginx.com return NULL; 41942Smax.romanov@nginx.com } 42042Smax.romanov@nginx.com 42180Smax.romanov@nginx.com hdr = NULL; 42280Smax.romanov@nginx.com 42390Smax.romanov@nginx.com nxt_thread_mutex_lock(&process->incoming_mutex); 42490Smax.romanov@nginx.com 42542Smax.romanov@nginx.com incoming = process->incoming; 42680Smax.romanov@nginx.com 42780Smax.romanov@nginx.com if (nxt_fast_path(incoming != NULL && incoming->nelts > id)) { 42880Smax.romanov@nginx.com port_mmap = incoming->elts; 42980Smax.romanov@nginx.com hdr = port_mmap[id].hdr; 43080Smax.romanov@nginx.com } else { 43180Smax.romanov@nginx.com nxt_log(task, NXT_LOG_WARN, 43280Smax.romanov@nginx.com "failed to get incoming mmap #%d for process %PI", id, spid); 43342Smax.romanov@nginx.com } 43442Smax.romanov@nginx.com 43590Smax.romanov@nginx.com nxt_thread_mutex_unlock(&process->incoming_mutex); 43690Smax.romanov@nginx.com 43780Smax.romanov@nginx.com return hdr; 43842Smax.romanov@nginx.com } 43942Smax.romanov@nginx.com 44042Smax.romanov@nginx.com 44142Smax.romanov@nginx.com nxt_buf_t * 44242Smax.romanov@nginx.com nxt_port_mmap_get_buf(nxt_task_t *task, nxt_port_t *port, size_t size) 44342Smax.romanov@nginx.com { 44442Smax.romanov@nginx.com size_t nchunks; 44542Smax.romanov@nginx.com nxt_buf_t *b; 44642Smax.romanov@nginx.com nxt_chunk_id_t c; 44742Smax.romanov@nginx.com nxt_port_mmap_header_t *hdr; 44842Smax.romanov@nginx.com 44942Smax.romanov@nginx.com nxt_debug(task, "request %z bytes shm buffer", size); 45042Smax.romanov@nginx.com 45180Smax.romanov@nginx.com if (nxt_slow_path(size > PORT_MMAP_DATA_SIZE)) { 45280Smax.romanov@nginx.com nxt_debug(task, "requested size (%z bytes) too big", size); 45380Smax.romanov@nginx.com return NULL; 45480Smax.romanov@nginx.com } 45580Smax.romanov@nginx.com 456122Smax.romanov@nginx.com b = nxt_buf_mem_ts_alloc(task, port->mem_pool, 0); 45742Smax.romanov@nginx.com if (nxt_slow_path(b == NULL)) { 45842Smax.romanov@nginx.com return NULL; 45942Smax.romanov@nginx.com } 46042Smax.romanov@nginx.com 46142Smax.romanov@nginx.com b->completion_handler = nxt_port_mmap_buf_completion; 46242Smax.romanov@nginx.com nxt_buf_set_port_mmap(b); 46342Smax.romanov@nginx.com 46480Smax.romanov@nginx.com hdr = nxt_port_mmap_get(task, port, &c, size); 46580Smax.romanov@nginx.com if (nxt_slow_path(hdr == NULL)) { 466122Smax.romanov@nginx.com nxt_mp_release(port->mem_pool, b); 46742Smax.romanov@nginx.com return NULL; 46842Smax.romanov@nginx.com } 46942Smax.romanov@nginx.com 47080Smax.romanov@nginx.com b->parent = hdr; 471122Smax.romanov@nginx.com 47280Smax.romanov@nginx.com b->mem.start = nxt_port_mmap_chunk_start(hdr, c); 47342Smax.romanov@nginx.com b->mem.pos = b->mem.start; 47442Smax.romanov@nginx.com b->mem.free = b->mem.start; 47542Smax.romanov@nginx.com b->mem.end = b->mem.start + PORT_MMAP_CHUNK_SIZE; 47642Smax.romanov@nginx.com 47742Smax.romanov@nginx.com nchunks = size / PORT_MMAP_CHUNK_SIZE; 47842Smax.romanov@nginx.com if ((size % PORT_MMAP_CHUNK_SIZE) != 0 || nchunks == 0) { 47942Smax.romanov@nginx.com nchunks++; 48042Smax.romanov@nginx.com } 48142Smax.romanov@nginx.com 48242Smax.romanov@nginx.com c++; 48342Smax.romanov@nginx.com nchunks--; 48442Smax.romanov@nginx.com 48542Smax.romanov@nginx.com /* Try to acquire as much chunks as required. */ 48642Smax.romanov@nginx.com while (nchunks > 0) { 48742Smax.romanov@nginx.com 48880Smax.romanov@nginx.com if (nxt_port_mmap_chk_set_chunk_busy(hdr, c) == 0) { 48942Smax.romanov@nginx.com break; 49042Smax.romanov@nginx.com } 49142Smax.romanov@nginx.com 49242Smax.romanov@nginx.com b->mem.end += PORT_MMAP_CHUNK_SIZE; 49342Smax.romanov@nginx.com c++; 49442Smax.romanov@nginx.com nchunks--; 49542Smax.romanov@nginx.com } 49642Smax.romanov@nginx.com 49742Smax.romanov@nginx.com return b; 49842Smax.romanov@nginx.com } 49942Smax.romanov@nginx.com 50042Smax.romanov@nginx.com 50180Smax.romanov@nginx.com nxt_int_t 50280Smax.romanov@nginx.com nxt_port_mmap_increase_buf(nxt_task_t *task, nxt_buf_t *b, size_t size) 50380Smax.romanov@nginx.com { 50480Smax.romanov@nginx.com size_t nchunks; 50580Smax.romanov@nginx.com nxt_chunk_id_t c, start; 50680Smax.romanov@nginx.com nxt_port_mmap_header_t *hdr; 50780Smax.romanov@nginx.com 50880Smax.romanov@nginx.com nxt_debug(task, "request increase %z bytes shm buffer", size); 50980Smax.romanov@nginx.com 51080Smax.romanov@nginx.com if (nxt_slow_path(nxt_buf_is_port_mmap(b) == 0)) { 51180Smax.romanov@nginx.com nxt_log(task, NXT_LOG_WARN, 51280Smax.romanov@nginx.com "failed to increase, not a mmap buffer"); 51380Smax.romanov@nginx.com return NXT_ERROR; 51480Smax.romanov@nginx.com } 51580Smax.romanov@nginx.com 51680Smax.romanov@nginx.com if (nxt_slow_path(size <= (size_t) nxt_buf_mem_free_size(&b->mem))) { 51780Smax.romanov@nginx.com return NXT_OK; 51880Smax.romanov@nginx.com } 51980Smax.romanov@nginx.com 52080Smax.romanov@nginx.com hdr = b->parent; 52180Smax.romanov@nginx.com 52280Smax.romanov@nginx.com start = nxt_port_mmap_chunk_id(hdr, b->mem.end); 52380Smax.romanov@nginx.com 52480Smax.romanov@nginx.com size -= nxt_buf_mem_free_size(&b->mem); 52580Smax.romanov@nginx.com 52680Smax.romanov@nginx.com nchunks = size / PORT_MMAP_CHUNK_SIZE; 52780Smax.romanov@nginx.com if ((size % PORT_MMAP_CHUNK_SIZE) != 0 || nchunks == 0) { 52880Smax.romanov@nginx.com nchunks++; 52980Smax.romanov@nginx.com } 53080Smax.romanov@nginx.com 53180Smax.romanov@nginx.com c = start; 53280Smax.romanov@nginx.com 53380Smax.romanov@nginx.com /* Try to acquire as much chunks as required. */ 53480Smax.romanov@nginx.com while (nchunks > 0) { 53580Smax.romanov@nginx.com 53680Smax.romanov@nginx.com if (nxt_port_mmap_chk_set_chunk_busy(hdr, c) == 0) { 53780Smax.romanov@nginx.com break; 53880Smax.romanov@nginx.com } 53980Smax.romanov@nginx.com 54080Smax.romanov@nginx.com c++; 54180Smax.romanov@nginx.com nchunks--; 54280Smax.romanov@nginx.com } 54380Smax.romanov@nginx.com 54480Smax.romanov@nginx.com if (nchunks != 0) { 54580Smax.romanov@nginx.com c--; 54680Smax.romanov@nginx.com while (c >= start) { 54780Smax.romanov@nginx.com nxt_port_mmap_set_chunk_free(hdr, c); 54880Smax.romanov@nginx.com c--; 54980Smax.romanov@nginx.com } 55080Smax.romanov@nginx.com 55180Smax.romanov@nginx.com nxt_debug(task, "failed to increase, %d chunks busy", nchunks); 55280Smax.romanov@nginx.com 55380Smax.romanov@nginx.com return NXT_ERROR; 55480Smax.romanov@nginx.com } else { 55580Smax.romanov@nginx.com b->mem.end += PORT_MMAP_CHUNK_SIZE * (c - start); 55680Smax.romanov@nginx.com 55780Smax.romanov@nginx.com return NXT_OK; 55880Smax.romanov@nginx.com } 55980Smax.romanov@nginx.com } 56080Smax.romanov@nginx.com 56180Smax.romanov@nginx.com 56242Smax.romanov@nginx.com static nxt_buf_t * 56342Smax.romanov@nginx.com nxt_port_mmap_get_incoming_buf(nxt_task_t *task, nxt_port_t *port, 56442Smax.romanov@nginx.com nxt_pid_t spid, nxt_port_mmap_msg_t *mmap_msg) 56542Smax.romanov@nginx.com { 56642Smax.romanov@nginx.com size_t nchunks; 56742Smax.romanov@nginx.com nxt_buf_t *b; 56880Smax.romanov@nginx.com nxt_port_mmap_header_t *hdr; 56980Smax.romanov@nginx.com 57080Smax.romanov@nginx.com hdr = nxt_port_get_port_incoming_mmap(task, spid, mmap_msg->mmap_id); 57180Smax.romanov@nginx.com if (nxt_slow_path(hdr == NULL)) { 57280Smax.romanov@nginx.com return NULL; 57380Smax.romanov@nginx.com } 57442Smax.romanov@nginx.com 575122Smax.romanov@nginx.com b = nxt_buf_mem_ts_alloc(task, port->mem_pool, 0); 57642Smax.romanov@nginx.com if (nxt_slow_path(b == NULL)) { 57742Smax.romanov@nginx.com return NULL; 57842Smax.romanov@nginx.com } 57942Smax.romanov@nginx.com 58042Smax.romanov@nginx.com b->completion_handler = nxt_port_mmap_buf_completion; 58142Smax.romanov@nginx.com 58242Smax.romanov@nginx.com nxt_buf_set_port_mmap(b); 58342Smax.romanov@nginx.com 58442Smax.romanov@nginx.com nchunks = mmap_msg->size / PORT_MMAP_CHUNK_SIZE; 58542Smax.romanov@nginx.com if ((mmap_msg->size % PORT_MMAP_CHUNK_SIZE) != 0) { 58642Smax.romanov@nginx.com nchunks++; 58742Smax.romanov@nginx.com } 58842Smax.romanov@nginx.com 58980Smax.romanov@nginx.com b->mem.start = nxt_port_mmap_chunk_start(hdr, mmap_msg->chunk_id); 59042Smax.romanov@nginx.com b->mem.pos = b->mem.start; 59142Smax.romanov@nginx.com b->mem.free = b->mem.start + mmap_msg->size; 59242Smax.romanov@nginx.com b->mem.end = b->mem.start + nchunks * PORT_MMAP_CHUNK_SIZE; 59342Smax.romanov@nginx.com 59480Smax.romanov@nginx.com b->parent = hdr; 59542Smax.romanov@nginx.com 59642Smax.romanov@nginx.com return b; 59742Smax.romanov@nginx.com } 59842Smax.romanov@nginx.com 59942Smax.romanov@nginx.com 60042Smax.romanov@nginx.com void 60142Smax.romanov@nginx.com nxt_port_mmap_write(nxt_task_t *task, nxt_port_t *port, 60242Smax.romanov@nginx.com nxt_port_send_msg_t *msg, nxt_sendbuf_coalesce_t *sb) 60342Smax.romanov@nginx.com { 60480Smax.romanov@nginx.com size_t bsize; 60580Smax.romanov@nginx.com nxt_buf_t *b, *bmem; 60680Smax.romanov@nginx.com nxt_uint_t i; 60780Smax.romanov@nginx.com nxt_port_mmap_msg_t *mmap_msg; 60880Smax.romanov@nginx.com nxt_port_mmap_header_t *hdr; 60942Smax.romanov@nginx.com 61042Smax.romanov@nginx.com nxt_debug(task, "prepare %z bytes message for transfer to process %PI " 61142Smax.romanov@nginx.com "via shared memory", sb->size, port->pid); 61242Smax.romanov@nginx.com 61342Smax.romanov@nginx.com bsize = sb->niov * sizeof(nxt_port_mmap_msg_t); 61442Smax.romanov@nginx.com 615122Smax.romanov@nginx.com b = nxt_buf_mem_ts_alloc(task, port->mem_pool, bsize); 61642Smax.romanov@nginx.com if (nxt_slow_path(b == NULL)) { 61742Smax.romanov@nginx.com return; 61842Smax.romanov@nginx.com } 61942Smax.romanov@nginx.com 62042Smax.romanov@nginx.com mmap_msg = (nxt_port_mmap_msg_t *) b->mem.start; 62142Smax.romanov@nginx.com bmem = msg->buf; 62242Smax.romanov@nginx.com 62342Smax.romanov@nginx.com for (i = 0; i < sb->niov; i++, mmap_msg++) { 62442Smax.romanov@nginx.com 62542Smax.romanov@nginx.com /* Lookup buffer which starts current iov_base. */ 62642Smax.romanov@nginx.com while (bmem && sb->iobuf[i].iov_base != bmem->mem.pos) { 62742Smax.romanov@nginx.com bmem = bmem->next; 62842Smax.romanov@nginx.com } 62942Smax.romanov@nginx.com 63042Smax.romanov@nginx.com if (nxt_slow_path(bmem == NULL)) { 63142Smax.romanov@nginx.com nxt_log_error(NXT_LOG_ERR, task->log, "failed to find buf for " 63242Smax.romanov@nginx.com "iobuf[%d]", i); 63342Smax.romanov@nginx.com return; 63442Smax.romanov@nginx.com /* TODO clear b and exit */ 63542Smax.romanov@nginx.com } 63642Smax.romanov@nginx.com 63780Smax.romanov@nginx.com hdr = bmem->parent; 63842Smax.romanov@nginx.com 63980Smax.romanov@nginx.com mmap_msg->mmap_id = hdr->id; 64080Smax.romanov@nginx.com mmap_msg->chunk_id = nxt_port_mmap_chunk_id(hdr, bmem->mem.pos); 64142Smax.romanov@nginx.com mmap_msg->size = sb->iobuf[i].iov_len; 64242Smax.romanov@nginx.com 64342Smax.romanov@nginx.com nxt_debug(task, "mmap_msg={%D, %D, %D} to %PI", 64442Smax.romanov@nginx.com mmap_msg->mmap_id, mmap_msg->chunk_id, mmap_msg->size, 64542Smax.romanov@nginx.com port->pid); 64642Smax.romanov@nginx.com } 64742Smax.romanov@nginx.com 64842Smax.romanov@nginx.com msg->buf = b; 64942Smax.romanov@nginx.com b->mem.free += bsize; 65042Smax.romanov@nginx.com 65142Smax.romanov@nginx.com sb->iobuf[0].iov_base = b->mem.pos; 65242Smax.romanov@nginx.com sb->iobuf[0].iov_len = bsize; 65342Smax.romanov@nginx.com sb->niov = 1; 65442Smax.romanov@nginx.com sb->size = bsize; 65542Smax.romanov@nginx.com 65642Smax.romanov@nginx.com msg->port_msg.mmap = 1; 65742Smax.romanov@nginx.com } 65842Smax.romanov@nginx.com 65942Smax.romanov@nginx.com 66042Smax.romanov@nginx.com void 66142Smax.romanov@nginx.com nxt_port_mmap_read(nxt_task_t *task, nxt_port_t *port, 66282Smax.romanov@nginx.com nxt_port_recv_msg_t *msg) 66342Smax.romanov@nginx.com { 66442Smax.romanov@nginx.com nxt_buf_t *b, **pb; 66542Smax.romanov@nginx.com nxt_port_mmap_msg_t *end, *mmap_msg; 66642Smax.romanov@nginx.com 66742Smax.romanov@nginx.com b = msg->buf; 66842Smax.romanov@nginx.com 66942Smax.romanov@nginx.com mmap_msg = (nxt_port_mmap_msg_t *) b->mem.pos; 67042Smax.romanov@nginx.com end = (nxt_port_mmap_msg_t *) b->mem.free; 67142Smax.romanov@nginx.com 67242Smax.romanov@nginx.com pb = &msg->buf; 67382Smax.romanov@nginx.com msg->size = 0; 67442Smax.romanov@nginx.com 67542Smax.romanov@nginx.com while (mmap_msg < end) { 67642Smax.romanov@nginx.com nxt_debug(task, "mmap_msg={%D, %D, %D} from %PI", 67742Smax.romanov@nginx.com mmap_msg->mmap_id, mmap_msg->chunk_id, mmap_msg->size, 67842Smax.romanov@nginx.com msg->port_msg.pid); 67942Smax.romanov@nginx.com 68042Smax.romanov@nginx.com *pb = nxt_port_mmap_get_incoming_buf(task, port, msg->port_msg.pid, 68142Smax.romanov@nginx.com mmap_msg); 68242Smax.romanov@nginx.com if (nxt_slow_path(*pb == NULL)) { 68342Smax.romanov@nginx.com nxt_log_error(NXT_LOG_ERR, task->log, "failed to get mmap buffer"); 68442Smax.romanov@nginx.com 68542Smax.romanov@nginx.com break; 68642Smax.romanov@nginx.com } 68742Smax.romanov@nginx.com 68882Smax.romanov@nginx.com msg->size += mmap_msg->size; 68942Smax.romanov@nginx.com pb = &(*pb)->next; 69042Smax.romanov@nginx.com mmap_msg++; 69142Smax.romanov@nginx.com } 69242Smax.romanov@nginx.com 69342Smax.romanov@nginx.com /* Mark original buf as complete. */ 69442Smax.romanov@nginx.com b->mem.pos += nxt_buf_used_size(b); 69542Smax.romanov@nginx.com } 69642Smax.romanov@nginx.com 69742Smax.romanov@nginx.com 69842Smax.romanov@nginx.com nxt_port_method_t 69942Smax.romanov@nginx.com nxt_port_mmap_get_method(nxt_task_t *task, nxt_port_t *port, nxt_buf_t *b) 70042Smax.romanov@nginx.com { 70180Smax.romanov@nginx.com nxt_port_method_t m; 70280Smax.romanov@nginx.com nxt_port_mmap_header_t *hdr; 70342Smax.romanov@nginx.com 70442Smax.romanov@nginx.com m = NXT_PORT_METHOD_ANY; 70542Smax.romanov@nginx.com 70642Smax.romanov@nginx.com for (; b != NULL; b = b->next) { 70742Smax.romanov@nginx.com if (nxt_buf_used_size(b) == 0) { 70842Smax.romanov@nginx.com /* empty buffers does not affect method */ 70942Smax.romanov@nginx.com continue; 71042Smax.romanov@nginx.com } 71142Smax.romanov@nginx.com 71242Smax.romanov@nginx.com if (nxt_buf_is_port_mmap(b)) { 71380Smax.romanov@nginx.com hdr = b->parent; 71442Smax.romanov@nginx.com 71542Smax.romanov@nginx.com if (m == NXT_PORT_METHOD_PLAIN) { 71642Smax.romanov@nginx.com nxt_log_error(NXT_LOG_ERR, task->log, 71742Smax.romanov@nginx.com "mixing plain and mmap buffers, " 71842Smax.romanov@nginx.com "using plain mode"); 71942Smax.romanov@nginx.com 72042Smax.romanov@nginx.com break; 72142Smax.romanov@nginx.com } 72242Smax.romanov@nginx.com 72380Smax.romanov@nginx.com if (port->pid != hdr->pid) { 72442Smax.romanov@nginx.com nxt_log_error(NXT_LOG_ERR, task->log, 72542Smax.romanov@nginx.com "send mmap buffer for %PI to %PI, " 72680Smax.romanov@nginx.com "using plain mode", hdr->pid, port->pid); 72742Smax.romanov@nginx.com 72842Smax.romanov@nginx.com m = NXT_PORT_METHOD_PLAIN; 72942Smax.romanov@nginx.com 73042Smax.romanov@nginx.com break; 73142Smax.romanov@nginx.com } 73242Smax.romanov@nginx.com 73342Smax.romanov@nginx.com if (m == NXT_PORT_METHOD_ANY) { 73442Smax.romanov@nginx.com nxt_debug(task, "using mmap mode"); 73542Smax.romanov@nginx.com 73642Smax.romanov@nginx.com m = NXT_PORT_METHOD_MMAP; 73742Smax.romanov@nginx.com } 73842Smax.romanov@nginx.com } else { 73942Smax.romanov@nginx.com if (m == NXT_PORT_METHOD_MMAP) { 74042Smax.romanov@nginx.com nxt_log_error(NXT_LOG_ERR, task->log, 74142Smax.romanov@nginx.com "mixing mmap and plain buffers, " 74242Smax.romanov@nginx.com "switching to plain mode"); 74342Smax.romanov@nginx.com 74442Smax.romanov@nginx.com m = NXT_PORT_METHOD_PLAIN; 74542Smax.romanov@nginx.com 74642Smax.romanov@nginx.com break; 74742Smax.romanov@nginx.com } 74842Smax.romanov@nginx.com 74942Smax.romanov@nginx.com if (m == NXT_PORT_METHOD_ANY) { 75042Smax.romanov@nginx.com nxt_debug(task, "using plain mode"); 75142Smax.romanov@nginx.com 75242Smax.romanov@nginx.com m = NXT_PORT_METHOD_PLAIN; 75342Smax.romanov@nginx.com } 75442Smax.romanov@nginx.com } 75542Smax.romanov@nginx.com } 75642Smax.romanov@nginx.com 75742Smax.romanov@nginx.com return m; 75842Smax.romanov@nginx.com } 759