10Sigor@sysoev.ru 20Sigor@sysoev.ru /* 30Sigor@sysoev.ru * Copyright (C) Igor Sysoev 40Sigor@sysoev.ru * Copyright (C) NGINX, Inc. 50Sigor@sysoev.ru */ 60Sigor@sysoev.ru 70Sigor@sysoev.ru #include <nxt_main.h> 80Sigor@sysoev.ru 90Sigor@sysoev.ru 100Sigor@sysoev.ru static nxt_bool_t nxt_sendbuf_copy(nxt_buf_mem_t *bm, nxt_buf_t *b, 110Sigor@sysoev.ru size_t *copied); 120Sigor@sysoev.ru 130Sigor@sysoev.ru 140Sigor@sysoev.ru nxt_uint_t 1513Sigor@sysoev.ru nxt_sendbuf_mem_coalesce0(nxt_task_t *task, nxt_sendbuf_t *sb, 1613Sigor@sysoev.ru struct iovec *iov, nxt_uint_t niov_max) 1713Sigor@sysoev.ru { 1813Sigor@sysoev.ru u_char *last; 1913Sigor@sysoev.ru size_t size, total; 2013Sigor@sysoev.ru nxt_buf_t *b; 2113Sigor@sysoev.ru nxt_uint_t n; 2213Sigor@sysoev.ru 2313Sigor@sysoev.ru total = sb->size; 2413Sigor@sysoev.ru last = NULL; 2513Sigor@sysoev.ru n = (nxt_uint_t) -1; 2613Sigor@sysoev.ru 2713Sigor@sysoev.ru for (b = sb->buf; b != NULL && total < sb->limit; b = b->next) { 2813Sigor@sysoev.ru 2913Sigor@sysoev.ru nxt_prefetch(b->next); 3013Sigor@sysoev.ru 3113Sigor@sysoev.ru if (nxt_buf_is_file(b)) { 3213Sigor@sysoev.ru break; 3313Sigor@sysoev.ru } 3413Sigor@sysoev.ru 3513Sigor@sysoev.ru if (nxt_buf_is_mem(b)) { 3613Sigor@sysoev.ru 3713Sigor@sysoev.ru size = b->mem.free - b->mem.pos; 3813Sigor@sysoev.ru 3913Sigor@sysoev.ru if (size != 0) { 4013Sigor@sysoev.ru 4113Sigor@sysoev.ru if (total + size > sb->limit) { 4213Sigor@sysoev.ru size = sb->limit - total; 4313Sigor@sysoev.ru 4413Sigor@sysoev.ru if (size == 0) { 4513Sigor@sysoev.ru break; 4613Sigor@sysoev.ru } 4713Sigor@sysoev.ru } 4813Sigor@sysoev.ru 4913Sigor@sysoev.ru if (b->mem.pos != last) { 5013Sigor@sysoev.ru 5113Sigor@sysoev.ru if (++n >= niov_max) { 5213Sigor@sysoev.ru goto done; 5313Sigor@sysoev.ru } 5413Sigor@sysoev.ru 5513Sigor@sysoev.ru iov[n].iov_base = b->mem.pos; 5613Sigor@sysoev.ru iov[n].iov_len = size; 5713Sigor@sysoev.ru 5813Sigor@sysoev.ru } else { 5913Sigor@sysoev.ru iov[n].iov_len += size; 6013Sigor@sysoev.ru } 6113Sigor@sysoev.ru 6213Sigor@sysoev.ru nxt_debug(task, "sendbuf: %ui, %p, %uz", 6313Sigor@sysoev.ru n, iov[n].iov_base, iov[n].iov_len); 6413Sigor@sysoev.ru 6513Sigor@sysoev.ru total += size; 6613Sigor@sysoev.ru last = b->mem.pos + size; 6713Sigor@sysoev.ru } 6813Sigor@sysoev.ru 6913Sigor@sysoev.ru } else { 7013Sigor@sysoev.ru sb->sync = 1; 7113Sigor@sysoev.ru sb->last |= nxt_buf_is_last(b); 7213Sigor@sysoev.ru } 7313Sigor@sysoev.ru } 7413Sigor@sysoev.ru 7513Sigor@sysoev.ru n++; 7613Sigor@sysoev.ru 7713Sigor@sysoev.ru done: 7813Sigor@sysoev.ru 7913Sigor@sysoev.ru sb->buf = b; 8013Sigor@sysoev.ru 8113Sigor@sysoev.ru return n; 8213Sigor@sysoev.ru } 8313Sigor@sysoev.ru 8413Sigor@sysoev.ru 8513Sigor@sysoev.ru nxt_uint_t 861Sigor@sysoev.ru nxt_sendbuf_mem_coalesce(nxt_task_t *task, nxt_sendbuf_coalesce_t *sb) 870Sigor@sysoev.ru { 880Sigor@sysoev.ru u_char *last; 890Sigor@sysoev.ru size_t size, total; 900Sigor@sysoev.ru nxt_buf_t *b; 910Sigor@sysoev.ru nxt_uint_t n; 920Sigor@sysoev.ru 930Sigor@sysoev.ru total = sb->size; 940Sigor@sysoev.ru last = NULL; 950Sigor@sysoev.ru n = (nxt_uint_t) -1; 960Sigor@sysoev.ru 970Sigor@sysoev.ru for (b = sb->buf; b != NULL && total < sb->limit; b = b->next) { 980Sigor@sysoev.ru 990Sigor@sysoev.ru nxt_prefetch(b->next); 1000Sigor@sysoev.ru 1010Sigor@sysoev.ru if (nxt_buf_is_file(b)) { 1020Sigor@sysoev.ru break; 1030Sigor@sysoev.ru } 1040Sigor@sysoev.ru 1050Sigor@sysoev.ru if (nxt_buf_is_mem(b)) { 1060Sigor@sysoev.ru 1070Sigor@sysoev.ru size = b->mem.free - b->mem.pos; 1080Sigor@sysoev.ru 1090Sigor@sysoev.ru if (size != 0) { 1100Sigor@sysoev.ru 1110Sigor@sysoev.ru if (total + size > sb->limit) { 1120Sigor@sysoev.ru size = sb->limit - total; 1130Sigor@sysoev.ru 114352Smax.romanov@nginx.com sb->limit_reached = 1; 115352Smax.romanov@nginx.com 116352Smax.romanov@nginx.com if (nxt_slow_path(size == 0)) { 1170Sigor@sysoev.ru break; 1180Sigor@sysoev.ru } 1190Sigor@sysoev.ru } 1200Sigor@sysoev.ru 1210Sigor@sysoev.ru if (b->mem.pos != last) { 1220Sigor@sysoev.ru 1230Sigor@sysoev.ru if (++n >= sb->nmax) { 124352Smax.romanov@nginx.com sb->nmax_reached = 1; 125352Smax.romanov@nginx.com 1260Sigor@sysoev.ru goto done; 1270Sigor@sysoev.ru } 1280Sigor@sysoev.ru 12914Sigor@sysoev.ru sb->iobuf[n].iov_base = b->mem.pos; 13014Sigor@sysoev.ru sb->iobuf[n].iov_len = size; 1310Sigor@sysoev.ru 1320Sigor@sysoev.ru } else { 13314Sigor@sysoev.ru sb->iobuf[n].iov_len += size; 1340Sigor@sysoev.ru } 1350Sigor@sysoev.ru 13614Sigor@sysoev.ru nxt_debug(task, "sendbuf: %ui, %p, %uz", 13714Sigor@sysoev.ru n, sb->iobuf[n].iov_base, sb->iobuf[n].iov_len); 1380Sigor@sysoev.ru 1390Sigor@sysoev.ru total += size; 1400Sigor@sysoev.ru last = b->mem.pos + size; 1410Sigor@sysoev.ru } 1420Sigor@sysoev.ru 1430Sigor@sysoev.ru } else { 1440Sigor@sysoev.ru sb->sync = 1; 1450Sigor@sysoev.ru sb->last |= nxt_buf_is_last(b); 1460Sigor@sysoev.ru } 1470Sigor@sysoev.ru } 1480Sigor@sysoev.ru 1490Sigor@sysoev.ru n++; 1500Sigor@sysoev.ru 1510Sigor@sysoev.ru done: 1520Sigor@sysoev.ru 1530Sigor@sysoev.ru sb->buf = b; 1540Sigor@sysoev.ru sb->size = total; 15542Smax.romanov@nginx.com sb->niov = n; 1560Sigor@sysoev.ru 1570Sigor@sysoev.ru return n; 1580Sigor@sysoev.ru } 1590Sigor@sysoev.ru 1600Sigor@sysoev.ru 1610Sigor@sysoev.ru size_t 1620Sigor@sysoev.ru nxt_sendbuf_file_coalesce(nxt_sendbuf_coalesce_t *sb) 1630Sigor@sysoev.ru { 1640Sigor@sysoev.ru size_t file_start, total; 1650Sigor@sysoev.ru nxt_fd_t fd; 1660Sigor@sysoev.ru nxt_off_t size, last; 1670Sigor@sysoev.ru nxt_buf_t *b; 1680Sigor@sysoev.ru 1690Sigor@sysoev.ru b = sb->buf; 1700Sigor@sysoev.ru fd = b->file->fd; 1710Sigor@sysoev.ru 1720Sigor@sysoev.ru total = sb->size; 1730Sigor@sysoev.ru 1740Sigor@sysoev.ru for ( ;; ) { 1750Sigor@sysoev.ru 1760Sigor@sysoev.ru nxt_prefetch(b->next); 1770Sigor@sysoev.ru 1780Sigor@sysoev.ru size = b->file_end - b->file_pos; 1790Sigor@sysoev.ru 1800Sigor@sysoev.ru if (total + size >= sb->limit) { 1810Sigor@sysoev.ru total = sb->limit; 1820Sigor@sysoev.ru break; 1830Sigor@sysoev.ru } 1840Sigor@sysoev.ru 1850Sigor@sysoev.ru total += size; 1860Sigor@sysoev.ru last = b->file_pos + size; 1870Sigor@sysoev.ru 1880Sigor@sysoev.ru b = b->next; 1890Sigor@sysoev.ru 1900Sigor@sysoev.ru if (b == NULL || !nxt_buf_is_file(b)) { 1910Sigor@sysoev.ru break; 1920Sigor@sysoev.ru } 1930Sigor@sysoev.ru 1940Sigor@sysoev.ru if (b->file_pos != last || b->file->fd != fd) { 1950Sigor@sysoev.ru break; 1960Sigor@sysoev.ru } 1970Sigor@sysoev.ru } 1980Sigor@sysoev.ru 1990Sigor@sysoev.ru sb->buf = b; 2000Sigor@sysoev.ru 2010Sigor@sysoev.ru file_start = sb->size; 2020Sigor@sysoev.ru sb->size = total; 2030Sigor@sysoev.ru 2040Sigor@sysoev.ru return total - file_start; 2050Sigor@sysoev.ru } 2060Sigor@sysoev.ru 2070Sigor@sysoev.ru 2080Sigor@sysoev.ru ssize_t 20962Sigor@sysoev.ru nxt_sendbuf_copy_coalesce(nxt_conn_t *c, nxt_buf_mem_t *bm, nxt_buf_t *b, 21062Sigor@sysoev.ru size_t limit) 2110Sigor@sysoev.ru { 2120Sigor@sysoev.ru size_t size, bsize, copied; 2130Sigor@sysoev.ru ssize_t n; 2140Sigor@sysoev.ru nxt_bool_t flush; 2150Sigor@sysoev.ru 2160Sigor@sysoev.ru size = nxt_buf_mem_used_size(&b->mem); 2170Sigor@sysoev.ru bsize = nxt_buf_mem_size(bm); 2180Sigor@sysoev.ru 2190Sigor@sysoev.ru if (bsize != 0) { 2200Sigor@sysoev.ru 2210Sigor@sysoev.ru if (size > bsize && bm->pos == bm->free) { 2220Sigor@sysoev.ru /* 2230Sigor@sysoev.ru * A data buffer size is larger than the internal 2240Sigor@sysoev.ru * buffer size and the internal buffer is empty. 2250Sigor@sysoev.ru */ 2260Sigor@sysoev.ru goto no_buffer; 2270Sigor@sysoev.ru } 2280Sigor@sysoev.ru 2290Sigor@sysoev.ru if (bm->pos == NULL) { 2300Sigor@sysoev.ru bm->pos = nxt_malloc(bsize); 2310Sigor@sysoev.ru if (nxt_slow_path(bm->pos == NULL)) { 2320Sigor@sysoev.ru return NXT_ERROR; 2330Sigor@sysoev.ru } 2340Sigor@sysoev.ru 2350Sigor@sysoev.ru bm->start = bm->pos; 2360Sigor@sysoev.ru bm->free = bm->pos; 2370Sigor@sysoev.ru bm->end += (uintptr_t) bm->pos; 2380Sigor@sysoev.ru } 2390Sigor@sysoev.ru 2400Sigor@sysoev.ru copied = 0; 2410Sigor@sysoev.ru 2420Sigor@sysoev.ru flush = nxt_sendbuf_copy(bm, b, &copied); 2430Sigor@sysoev.ru 2440Sigor@sysoev.ru nxt_log_debug(c->socket.log, "sendbuf copy:%uz fl:%b", copied, flush); 2450Sigor@sysoev.ru 2460Sigor@sysoev.ru if (flush == 0) { 2470Sigor@sysoev.ru return copied; 2480Sigor@sysoev.ru } 2490Sigor@sysoev.ru 2500Sigor@sysoev.ru size = nxt_buf_mem_used_size(bm); 2510Sigor@sysoev.ru 2520Sigor@sysoev.ru if (size == 0 && nxt_buf_is_sync(b)) { 2530Sigor@sysoev.ru goto done; 2540Sigor@sysoev.ru } 2550Sigor@sysoev.ru 2560Sigor@sysoev.ru n = c->io->send(c, bm->pos, nxt_min(size, limit)); 2570Sigor@sysoev.ru 2580Sigor@sysoev.ru nxt_log_debug(c->socket.log, "sendbuf sent:%z", n); 2590Sigor@sysoev.ru 2600Sigor@sysoev.ru if (n > 0) { 2610Sigor@sysoev.ru bm->pos += n; 2620Sigor@sysoev.ru 2630Sigor@sysoev.ru if (bm->pos == bm->free) { 2640Sigor@sysoev.ru bm->pos = bm->start; 2650Sigor@sysoev.ru bm->free = bm->start; 2660Sigor@sysoev.ru } 2670Sigor@sysoev.ru 2680Sigor@sysoev.ru n = 0; 2690Sigor@sysoev.ru } 2700Sigor@sysoev.ru 2710Sigor@sysoev.ru return (copied != 0) ? (ssize_t) copied : n; 2720Sigor@sysoev.ru } 2730Sigor@sysoev.ru 2740Sigor@sysoev.ru /* No internal buffering. */ 2750Sigor@sysoev.ru 2760Sigor@sysoev.ru if (size == 0 && nxt_buf_is_sync(b)) { 2770Sigor@sysoev.ru goto done; 2780Sigor@sysoev.ru } 2790Sigor@sysoev.ru 2800Sigor@sysoev.ru no_buffer: 2810Sigor@sysoev.ru 2820Sigor@sysoev.ru return c->io->send(c, b->mem.pos, nxt_min(size, limit)); 2830Sigor@sysoev.ru 2840Sigor@sysoev.ru done: 2850Sigor@sysoev.ru 2860Sigor@sysoev.ru nxt_log_debug(c->socket.log, "sendbuf done"); 2870Sigor@sysoev.ru 2880Sigor@sysoev.ru return 0; 2890Sigor@sysoev.ru } 2900Sigor@sysoev.ru 2910Sigor@sysoev.ru 2920Sigor@sysoev.ru static nxt_bool_t 2930Sigor@sysoev.ru nxt_sendbuf_copy(nxt_buf_mem_t *bm, nxt_buf_t *b, size_t *copied) 2940Sigor@sysoev.ru { 2950Sigor@sysoev.ru size_t size, bsize; 2960Sigor@sysoev.ru nxt_bool_t flush; 2970Sigor@sysoev.ru 2980Sigor@sysoev.ru flush = 0; 2990Sigor@sysoev.ru 3000Sigor@sysoev.ru do { 3010Sigor@sysoev.ru nxt_prefetch(b->next); 3020Sigor@sysoev.ru 3030Sigor@sysoev.ru if (nxt_buf_is_mem(b)) { 3040Sigor@sysoev.ru bsize = bm->end - bm->free; 3050Sigor@sysoev.ru size = b->mem.free - b->mem.pos; 3060Sigor@sysoev.ru size = nxt_min(size, bsize); 3070Sigor@sysoev.ru 3080Sigor@sysoev.ru nxt_memcpy(bm->free, b->mem.pos, size); 3090Sigor@sysoev.ru 3100Sigor@sysoev.ru *copied += size; 3110Sigor@sysoev.ru bm->free += size; 3120Sigor@sysoev.ru 3130Sigor@sysoev.ru if (bm->free == bm->end) { 3140Sigor@sysoev.ru return 1; 3150Sigor@sysoev.ru } 3160Sigor@sysoev.ru } 3170Sigor@sysoev.ru 3180Sigor@sysoev.ru flush |= nxt_buf_is_flush(b) || nxt_buf_is_last(b); 3190Sigor@sysoev.ru 3200Sigor@sysoev.ru b = b->next; 3210Sigor@sysoev.ru 3220Sigor@sysoev.ru } while (b != NULL); 3230Sigor@sysoev.ru 3240Sigor@sysoev.ru return flush; 3250Sigor@sysoev.ru } 3260Sigor@sysoev.ru 3270Sigor@sysoev.ru 3280Sigor@sysoev.ru nxt_buf_t * 3290Sigor@sysoev.ru nxt_sendbuf_update(nxt_buf_t *b, size_t sent) 3300Sigor@sysoev.ru { 3310Sigor@sysoev.ru size_t size; 3320Sigor@sysoev.ru 3330Sigor@sysoev.ru while (b != NULL) { 3340Sigor@sysoev.ru 3350Sigor@sysoev.ru nxt_prefetch(b->next); 3360Sigor@sysoev.ru 3370Sigor@sysoev.ru if (!nxt_buf_is_sync(b)) { 3380Sigor@sysoev.ru 3390Sigor@sysoev.ru size = nxt_buf_used_size(b); 3400Sigor@sysoev.ru 3410Sigor@sysoev.ru if (size != 0) { 3420Sigor@sysoev.ru 3430Sigor@sysoev.ru if (sent == 0) { 3440Sigor@sysoev.ru break; 3450Sigor@sysoev.ru } 3460Sigor@sysoev.ru 3470Sigor@sysoev.ru if (sent < size) { 3480Sigor@sysoev.ru 3490Sigor@sysoev.ru if (nxt_buf_is_mem(b)) { 3500Sigor@sysoev.ru b->mem.pos += sent; 3510Sigor@sysoev.ru } 3520Sigor@sysoev.ru 3530Sigor@sysoev.ru if (nxt_buf_is_file(b)) { 3540Sigor@sysoev.ru b->file_pos += sent; 3550Sigor@sysoev.ru } 3560Sigor@sysoev.ru 3570Sigor@sysoev.ru break; 3580Sigor@sysoev.ru } 3590Sigor@sysoev.ru 3600Sigor@sysoev.ru /* b->mem.free is NULL in file-only buffer. */ 3610Sigor@sysoev.ru b->mem.pos = b->mem.free; 3620Sigor@sysoev.ru 3630Sigor@sysoev.ru if (nxt_buf_is_file(b)) { 3640Sigor@sysoev.ru b->file_pos = b->file_end; 3650Sigor@sysoev.ru } 3660Sigor@sysoev.ru 3670Sigor@sysoev.ru sent -= size; 3680Sigor@sysoev.ru } 3690Sigor@sysoev.ru } 3700Sigor@sysoev.ru 3710Sigor@sysoev.ru b = b->next; 3720Sigor@sysoev.ru } 3730Sigor@sysoev.ru 3740Sigor@sysoev.ru return b; 3750Sigor@sysoev.ru } 3760Sigor@sysoev.ru 3770Sigor@sysoev.ru 3780Sigor@sysoev.ru nxt_buf_t * 3791Sigor@sysoev.ru nxt_sendbuf_completion(nxt_task_t *task, nxt_work_queue_t *wq, nxt_buf_t *b, 380203Smax.romanov@nginx.com size_t sent, nxt_bool_t mmap_mode) 3810Sigor@sysoev.ru { 3820Sigor@sysoev.ru size_t size; 3830Sigor@sysoev.ru 3840Sigor@sysoev.ru while (b != NULL) { 3850Sigor@sysoev.ru 3860Sigor@sysoev.ru nxt_prefetch(b->next); 3870Sigor@sysoev.ru 3880Sigor@sysoev.ru if (!nxt_buf_is_sync(b)) { 3890Sigor@sysoev.ru 3900Sigor@sysoev.ru size = nxt_buf_used_size(b); 3910Sigor@sysoev.ru 3920Sigor@sysoev.ru if (size != 0) { 3930Sigor@sysoev.ru 3940Sigor@sysoev.ru if (sent == 0) { 3950Sigor@sysoev.ru break; 3960Sigor@sysoev.ru } 3970Sigor@sysoev.ru 398203Smax.romanov@nginx.com if (nxt_buf_is_port_mmap(b) && mmap_mode) { 39942Smax.romanov@nginx.com /* 40042Smax.romanov@nginx.com * buffer has been sent to other side which is now 40142Smax.romanov@nginx.com * responsible for shared memory bucket release 40242Smax.romanov@nginx.com */ 40342Smax.romanov@nginx.com b->is_port_mmap_sent = 1; 40442Smax.romanov@nginx.com } 40542Smax.romanov@nginx.com 4060Sigor@sysoev.ru if (sent < size) { 4070Sigor@sysoev.ru 4080Sigor@sysoev.ru if (nxt_buf_is_mem(b)) { 4090Sigor@sysoev.ru b->mem.pos += sent; 4100Sigor@sysoev.ru } 4110Sigor@sysoev.ru 4120Sigor@sysoev.ru if (nxt_buf_is_file(b)) { 4130Sigor@sysoev.ru b->file_pos += sent; 4140Sigor@sysoev.ru } 4150Sigor@sysoev.ru 4160Sigor@sysoev.ru break; 4170Sigor@sysoev.ru } 4180Sigor@sysoev.ru 4190Sigor@sysoev.ru /* b->mem.free is NULL in file-only buffer. */ 4200Sigor@sysoev.ru b->mem.pos = b->mem.free; 4210Sigor@sysoev.ru 4220Sigor@sysoev.ru if (nxt_buf_is_file(b)) { 4230Sigor@sysoev.ru b->file_pos = b->file_end; 4240Sigor@sysoev.ru } 4250Sigor@sysoev.ru 4260Sigor@sysoev.ru sent -= size; 4270Sigor@sysoev.ru } 4280Sigor@sysoev.ru } 4290Sigor@sysoev.ru 4304Sigor@sysoev.ru nxt_work_queue_add(wq, b->completion_handler, task, b, b->parent); 4310Sigor@sysoev.ru 4320Sigor@sysoev.ru b = b->next; 4330Sigor@sysoev.ru } 4340Sigor@sysoev.ru 4350Sigor@sysoev.ru return b; 4360Sigor@sysoev.ru } 437*431Sigor@sysoev.ru 438*431Sigor@sysoev.ru 439*431Sigor@sysoev.ru nxt_buf_t * 440*431Sigor@sysoev.ru nxt_sendbuf_completion0(nxt_task_t *task, nxt_work_queue_t *wq, nxt_buf_t *b) 441*431Sigor@sysoev.ru { 442*431Sigor@sysoev.ru while (b != NULL) { 443*431Sigor@sysoev.ru 444*431Sigor@sysoev.ru nxt_prefetch(b->next); 445*431Sigor@sysoev.ru 446*431Sigor@sysoev.ru if (nxt_buf_used_size(b) != 0) { 447*431Sigor@sysoev.ru break; 448*431Sigor@sysoev.ru } 449*431Sigor@sysoev.ru 450*431Sigor@sysoev.ru nxt_work_queue_add(wq, b->completion_handler, task, b, b->parent); 451*431Sigor@sysoev.ru 452*431Sigor@sysoev.ru b = b->next; 453*431Sigor@sysoev.ru } 454*431Sigor@sysoev.ru 455*431Sigor@sysoev.ru return b; 456*431Sigor@sysoev.ru } 457