diff --git a/lib/isc/include/isc/socket.h b/lib/isc/include/isc/socket.h index b18dadef69..a4eed16dfd 100644 --- a/lib/isc/include/isc/socket.h +++ b/lib/isc/include/isc/socket.h @@ -61,7 +61,7 @@ #include #include -#include +#include #include #include #include @@ -97,7 +97,7 @@ struct isc_socketevent { unsigned int n; /* bytes read or written */ unsigned int offset; /* offset into buffer list */ isc_region_t region; /* for single-buffer i/o */ - ISC_LIST(isc_buffer_t) bufferlist; /* list of buffers */ + isc_bufferlist_t bufferlist; /* list of buffers */ isc_sockaddr_t address; /* source address */ }; @@ -428,12 +428,17 @@ isc_result_t isc_socket_recv(isc_socket_t *sock, isc_region_t *region, unsigned int minimum, isc_task_t *task, isc_taskaction_t action, void *arg); +isc_result_t +isc_socket_recvv(isc_socket_t *sock, isc_bufferlist_t *buflist, + unsigned int minimum, + isc_task_t *task, isc_taskaction_t action, void *arg); /* * Receive from 'socket', storing the results in region. * * Notes: * - * Let 'length' refer to the length of 'region'. + * Let 'length' refer to the length of 'region' or to the sum of all + * available regions in the list of buffers '*buflist'. * * If 'minimum' is non-zero and at least that many bytes are read, * the completion event will be posted to the task 'task.' If minimum @@ -446,59 +451,11 @@ isc_socket_recv(isc_socket_t *sock, isc_region_t *region, * event with the given 'action' and 'arg' will be posted to the * event queue of 'task'. * - * The caller may neither read from nor write to 'region' until it - * has received the read completion event. + * The caller may not modify 'region', the buffers which are passed + * into this function, or any data they refer to until the completion + * event is received. * - * Requires: - * - * 'socket' is a valid socket - * - * 'region' is a valid region - * - * 'minimum' <= region.length - * - * 'task' is a valid task - * - * action != NULL and is a valid action - * - * Returns: - * - * ISC_R_SUCCESS - * ISC_R_NOMEMORY - * ISC_R_UNEXPECTED - * - * Event results: - * - * ISC_R_SUCCESS - * ISC_R_UNEXPECTED - * XXX needs other net-type errors - */ - -isc_result_t -isc_socket_recvv(isc_socket_t *sock, isc_bufferlist_t *buflist, - unsigned int minimum, - isc_task_t *task, isc_taskaction_t action, void *arg); -/* - * Receive from 'socket', storing the results in region. - * - * Notes: - * - * Let 'length' refer to the sum of all available regions in the - * list of buffers 'buflist'. - * - * If 'minimum' is non-zero and at least that many bytes are read, - * the completion event will be posted to the task 'task.' If minimum - * is zero, the exact number of bytes requested in the buflist must - * be read for an event to be posted. This only makes sense for TCP - * connections, and is always set to 1 byte for UDP. - * - * The read will complete when the desired number of bytes have been - * read, if end-of-input occurs, or if an error occurs. A read done - * event with the given 'action' and 'arg' will be posted to the - * event queue of 'task'. - * - * The caller may neither read from nor write to any buffer in the - * buffer list, nor may it link or unlink these buffers from any list. + * For isc_socket_recvv(): * On successful completion, '*buflist' will be empty, and the list of * all buffers will be returned in the done event's 'bufferlist' * member. On error return, '*buflist' will be unchanged. @@ -507,9 +464,11 @@ isc_socket_recvv(isc_socket_t *sock, isc_bufferlist_t *buflist, * * 'socket' is a valid socket * - * 'buflist' is non-NULL, and '*buflist' contain at least one buffer. + * For isc_socket_recv(): + * 'region' is a valid region * - * 'minimum' is <= sum of all available regions. + * For isc_socket_recvv(): + * 'buflist' is non-NULL, and '*buflist' contain at least one buffer. * * 'task' is a valid task * @@ -535,34 +494,49 @@ isc_result_t isc_socket_sendto(isc_socket_t *sock, isc_region_t *region, isc_task_t *task, isc_taskaction_t action, void *arg, isc_sockaddr_t *address); +isc_result_t +isc_socket_sendv(isc_socket_t *sock, isc_bufferlist_t *buflist, + isc_task_t *task, isc_taskaction_t action, void *arg); +isc_result_t +isc_socket_sendtov(isc_socket_t *sock, isc_bufferlist_t *buflist, + isc_task_t *task, isc_taskaction_t action, void *arg, + isc_sockaddr_t *address); /* * Send the contents of 'region' to the socket's peer. * * Notes: * * Shutting down the requestor's task *may* result in any - * still pending writes being dropped. + * still pending writes being dropped or completed, depending on the + * underlying OS implementation. * * If 'action' is NULL, then no completion event will be posted. * - * The caller may neither read from nor write to 'region' until it - * has received the write completion event, or all references to the - * socket have been detached. + * The caller may not modify 'region', the buffers which are passed + * into this function, or any data they refer to until the completion + * event is received. + * + * For isc_socket_sendv() and isc_socket_sendtov(): + * On successful completion, '*buflist' will be empty, and the list of + * all buffers will be returned in the done event's 'bufferlist' + * member. On error return, '*buflist' will be unchanged. * * Requires: * * 'socket' is a valid socket * + * For isc_socket_send(): * 'region' is a valid region * + * For isc_socket_sendv() and isc_socket_sendtov(): + * 'buflist' is non-NULL, and '*buflist' contain at least one buffer. + * * 'task' is a valid task * * action == NULL or is a valid action * * Returns: * - * Returns: - * * ISC_R_SUCCESS * ISC_R_NOMEMORY * ISC_R_UNEXPECTED diff --git a/lib/isc/unix/socket.c b/lib/isc/unix/socket.c index 51b20ef9a9..a9f926a54e 100644 --- a/lib/isc/unix/socket.c +++ b/lib/isc/unix/socket.c @@ -1879,7 +1879,6 @@ isc_socket_recvv(isc_socket_t *sock, isc_bufferlist_t *buflist, isc_task_t *ntask = NULL; isc_boolean_t was_empty; unsigned int iocount; - isc_region_t available; isc_buffer_t *buffer; REQUIRE(VALID_SOCKET(sock)); @@ -1891,12 +1890,7 @@ isc_socket_recvv(isc_socket_t *sock, isc_bufferlist_t *buflist, manager = sock->manager; REQUIRE(VALID_MANAGER(manager)); - iocount = 0; - buffer = ISC_LIST_HEAD(*buflist); - while (buffer != NULL) { - isc_buffer_available(buffer, &available); - iocount += available.length; - } + iocount = isc_bufferlist_availablecount(buflist); REQUIRE(iocount > 0); LOCK(&sock->lock); @@ -1925,8 +1919,6 @@ isc_socket_recvv(isc_socket_t *sock, isc_bufferlist_t *buflist, dev->minimum = minimum; } - dev->result = ISC_R_SUCCESS; - dev->n = 0; dev->sender = task; /* @@ -1936,13 +1928,13 @@ isc_socket_recvv(isc_socket_t *sock, isc_bufferlist_t *buflist, while (buffer != NULL) { ISC_LIST_DEQUEUE(*buflist, buffer, link); ISC_LIST_ENQUEUE(dev->bufferlist, buffer, link); + buffer = ISC_LIST_HEAD(*buflist); } - was_empty = ISC_LIST_EMPTY(sock->recv_list); - /* * If the read queue is empty, try to do the I/O right now. */ + was_empty = ISC_LIST_EMPTY(sock->recv_list); if (!was_empty) goto queue; @@ -2127,8 +2119,6 @@ isc_socket_sendto(isc_socket_t *sock, isc_region_t *region, LOCK(&sock->lock); - manager = sock->manager; - dev = allocate_socketevent(sock, ISC_SOCKEVENT_SENDDONE, action, arg); if (dev == NULL) { UNLOCK(&sock->lock); @@ -2189,6 +2179,114 @@ isc_socket_sendto(isc_socket_t *sock, isc_region_t *region, return (ISC_R_SUCCESS); } +isc_result_t +isc_socket_sendv(isc_socket_t *sock, isc_bufferlist_t *buflist, + isc_task_t *task, isc_taskaction_t action, void *arg) +{ + return (isc_socket_sendtov(sock, buflist, task, action, arg, NULL)); +} + +isc_result_t +isc_socket_sendtov(isc_socket_t *sock, isc_bufferlist_t *buflist, + isc_task_t *task, isc_taskaction_t action, void *arg, + isc_sockaddr_t *address) +{ + isc_socketevent_t *dev; + isc_socketmgr_t *manager; + isc_task_t *ntask = NULL; + isc_boolean_t was_empty; + unsigned int iocount; + isc_buffer_t *buffer; + + REQUIRE(VALID_SOCKET(sock)); + REQUIRE(buflist != NULL); + REQUIRE(!ISC_LIST_EMPTY(*buflist)); + REQUIRE(task != NULL); + REQUIRE(action != NULL); + + manager = sock->manager; + REQUIRE(VALID_MANAGER(manager)); + + iocount = isc_bufferlist_usedcount(buflist); + REQUIRE(iocount > 0); + + LOCK(&sock->lock); + + dev = allocate_socketevent(sock, ISC_SOCKEVENT_SENDDONE, action, arg); + if (dev == NULL) { + UNLOCK(&sock->lock); + return (ISC_R_NOMEMORY); + } + + /*** + *** From here down, only ISC_R_SUCCESS can be returned. Any further + *** error information will result in the done event being posted + *** to the task rather than this function failing. + ***/ + + dev->sender = task; + + set_dev_address(address, sock, dev); + + /* + * Move each buffer from the passed in list to our internal one. + */ + buffer = ISC_LIST_HEAD(*buflist); + while (buffer != NULL) { + ISC_LIST_DEQUEUE(*buflist, buffer, link); + ISC_LIST_ENQUEUE(dev->bufferlist, buffer, link); + buffer = ISC_LIST_HEAD(*buflist); + } + + /* + * If the read queue is empty, try to do the I/O right now. + */ + was_empty = ISC_LIST_EMPTY(sock->send_list); + if (!was_empty) + goto queue; + + if (sock->send_result != ISC_R_SUCCESS) { + send_senddone_event(sock, &dev, sock->send_result); + UNLOCK(&sock->lock); + return (ISC_R_SUCCESS); + } + + switch (doio_send(sock, dev)) { + case DOIO_SOFT: + goto queue; + break; + + case DOIO_HARD: + case DOIO_UNEXPECTED: + case DOIO_SUCCESS: + UNLOCK(&sock->lock); + return (ISC_R_SUCCESS); + break; + } + + queue: + /* + * We couldn't send all or part of the request right now, so queue + * it. + */ + isc_task_attach(task, &ntask); + dev->attributes |= ISC_SOCKEVENTATTR_ATTACHED; + + /* + * Enqueue the request. If the socket was previously not being + * watched, poke the watcher to start paying attention to it. + */ + ISC_LIST_ENQUEUE(sock->send_list, dev, link); + if (was_empty) + select_poke(sock->manager, sock->fd); + + XTRACE(TRACE_SEND, + ("isc_socket_send: queued event %p, task %p\n", dev, ntask)); + + UNLOCK(&sock->lock); + return (ISC_R_SUCCESS); +} + isc_result_t isc_socket_bind(isc_socket_t *sock, isc_sockaddr_t *sockaddr) {