uint16_t tcp_datahandler(FAR struct tcp_conn_s *conn, FAR uint8_t *buffer, uint16_t buflen) { FAR struct iob_s *iob; int ret; /* Try to allocate on I/O buffer to start the chain without waiting (and * throttling as necessary). If we would have to wait, then drop the * packet. */ iob = iob_tryalloc(true); if (iob == NULL) { nlldbg("ERROR: Failed to create new I/O buffer chain\n"); return 0; } /* Copy the new appdata into the I/O buffer chain (without waiting) */ ret = iob_trycopyin(iob, buffer, buflen, 0, true); if (ret < 0) { /* On a failure, iob_copyin return a negated error value but does * not free any I/O buffers. */ nlldbg("ERROR: Failed to add data to the I/O buffer chain: %d\n", ret); (void)iob_free_chain(iob); return 0; } /* Add the new I/O buffer chain to the tail of the read-ahead queue (again * without waiting). */ ret = iob_tryadd_queue(iob, &conn->readahead); if (ret < 0) { nlldbg("ERROR: Failed to queue the I/O buffer chain: %d\n", ret); (void)iob_free_chain(iob); return 0; } nllvdbg("Buffered %d bytes\n", buflen); return buflen; }
FAR struct iob_s *iob_alloc(bool throttled) { /* Were we called from the interrupt level? */ if (up_interrupt_context()) { /* Yes, then try to allocate an I/O buffer without waiting */ return iob_tryalloc(throttled); } else { /* Then allocate an I/O buffer, waiting as necessary */ return iob_allocwait(throttled); } }
static int iob_copyin_internal(FAR struct iob_s *iob, FAR const uint8_t *src, unsigned int len, unsigned int offset, bool throttled, bool can_block) { FAR struct iob_s *head = iob; FAR struct iob_s *next; FAR uint8_t *dest; unsigned int ncopy; unsigned int avail; unsigned int total = len; ninfo("iob=%p len=%u offset=%u\n", iob, len, offset); DEBUGASSERT(iob && src); /* The offset must applied to data that is already in the I/O buffer chain */ if (offset > iob->io_pktlen) { nerr("ERROR: offset is past the end of data: %u > %u\n", offset, iob->io_pktlen); return -ESPIPE; } /* Skip to the I/O buffer containing the data offset */ while (offset > iob->io_len) { offset -= iob->io_len; iob = iob->io_flink; } /* Then loop until all of the I/O data is copied from the user buffer */ while (len > 0) { next = iob->io_flink; /* Get the destination I/O buffer address and the amount of data * available from that address. */ dest = &iob->io_data[iob->io_offset + offset]; avail = iob->io_len - offset; ninfo("iob=%p avail=%u len=%u next=%p\n", iob, avail, len, next); /* Will the rest of the copy fit into this buffer, overwriting * existing data. */ if (len > avail) { /* No.. Is this the last buffer in the chain? */ if (next) { /* No.. clip to size that will overwrite. We cannot * extend the length of an I/O block in mid-chain. */ ncopy = avail; } else { unsigned int maxlen; unsigned int newlen; /* Yes.. We can extend this buffer to the up to the very end. */ maxlen = CONFIG_IOB_BUFSIZE - iob->io_offset; /* This is the new buffer length that we need. Of course, * clipped to the maximum possible size in this buffer. */ newlen = len + offset; if (newlen > maxlen) { newlen = maxlen; } /* Set the new length and increment the packet length */ head->io_pktlen += (newlen - iob->io_len); iob->io_len = newlen; /* Set the new number of bytes to copy */ ncopy = newlen - offset; } } else { /* Yes.. Copy all of the remaining bytes */ ncopy = len; } /* Copy from the user buffer to the I/O buffer. */ memcpy(dest, src, ncopy); ninfo("iob=%p Copy %u bytes new len=%u\n", iob, ncopy, iob->io_len); /* Adjust the total length of the copy and the destination address in * the user buffer. */ len -= ncopy; src += ncopy; /* Skip to the next I/O buffer in the chain. First, check if we * are at the end of the buffer chain. */ if (len > 0 && !next) { /* Yes.. allocate a new buffer. * * Copy as many bytes as possible. If we have successfully copied * any already don't block, otherwise block if we're allowed. */ if (!can_block || len < total) { next = iob_tryalloc(throttled); } else { next = iob_alloc(throttled); } if (next == NULL) { nerr("ERROR: Failed to allocate I/O buffer\n"); return len; } /* Add the new, empty I/O buffer to the end of the buffer chain. */ iob->io_flink = next; ninfo("iob=%p added to the chain\n", iob); } iob = next; offset = 0; } return 0; }
static uint16_t udp_datahandler(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn, FAR uint8_t *buffer, uint16_t buflen) { FAR struct iob_s *iob; int ret; #ifdef CONFIG_NET_IPv6 FAR struct sockaddr_in6 src_addr6; #endif #ifdef CONFIG_NET_IPv4 FAR struct sockaddr_in src_addr4; #endif FAR void *src_addr; uint8_t src_addr_size; /* Allocate on I/O buffer to start the chain (throttling as necessary). * We will not wait for an I/O buffer to become available in this context. */ iob = iob_tryalloc(true); if (iob == NULL) { nerr("ERROR: Failed to create new I/O buffer chain\n"); return 0; } #ifdef CONFIG_NET_IPv6 #ifdef CONFIG_NET_IPv4 if (IFF_IS_IPv6(dev->d_flags)) #endif { FAR struct udp_hdr_s *udp = UDPIPv6BUF; FAR struct ipv6_hdr_s *ipv6 = IPv6BUF; src_addr6.sin6_family = AF_INET6; src_addr6.sin6_port = udp->srcport; net_ipv6addr_copy(src_addr6.sin6_addr.s6_addr, ipv6->srcipaddr); src_addr_size = sizeof(src_addr6); src_addr = &src_addr6; } #endif /* CONFIG_NET_IPv6 */ #ifdef CONFIG_NET_IPv4 #ifdef CONFIG_NET_IPv6 else #endif { #ifdef CONFIG_NET_IPv6 /* Hybrid dual-stack IPv6/IPv4 implementations recognize a special * class of addresses, the IPv4-mapped IPv6 addresses. */ if (conn->domain == PF_INET6) { FAR struct udp_hdr_s *udp = UDPIPv6BUF; FAR struct ipv6_hdr_s *ipv6 = IPv6BUF; in_addr_t ipv4addr; /* Encode the IPv4 address as an IPv-mapped IPv6 address */ src_addr6.sin6_family = AF_INET6; src_addr6.sin6_port = udp->srcport; ipv4addr = net_ip4addr_conv32(ipv6->srcipaddr); ip6_map_ipv4addr(ipv4addr, src_addr6.sin6_addr.s6_addr16); src_addr_size = sizeof(src_addr6); src_addr = &src_addr6; } else #endif { FAR struct udp_hdr_s *udp = UDPIPv4BUF; FAR struct ipv4_hdr_s *ipv4 = IPv4BUF; src_addr4.sin_family = AF_INET; src_addr4.sin_port = udp->srcport; net_ipv4addr_copy(src_addr4.sin_addr.s_addr, net_ip4addr_conv32(ipv4->srcipaddr)); src_addr_size = sizeof(src_addr4); src_addr = &src_addr4; } } #endif /* CONFIG_NET_IPv4 */ /* Copy the src address info into the I/O buffer chain. We will not wait * for an I/O buffer to become available in this context. It there is * any failure to allocated, the entire I/O buffer chain will be discarded. */ ret = iob_trycopyin(iob, (FAR const uint8_t *)&src_addr_size, sizeof(uint8_t), 0, true); if (ret < 0) { /* On a failure, iob_trycopyin return a negated error value but does * not free any I/O buffers. */ nerr("ERROR: Failed to add data to the I/O buffer chain: %d\n", ret); (void)iob_free_chain(iob); return 0; } ret = iob_trycopyin(iob, (FAR const uint8_t *)src_addr, src_addr_size, sizeof(uint8_t), true); if (ret < 0) { /* On a failure, iob_trycopyin return a negated error value but does * not free any I/O buffers. */ nerr("ERROR: Failed to add data to the I/O buffer chain: %d\n", ret); (void)iob_free_chain(iob); return 0; } if (buflen > 0) { /* Copy the new appdata into the I/O buffer chain */ ret = iob_trycopyin(iob, buffer, buflen, src_addr_size + sizeof(uint8_t), true); if (ret < 0) { /* On a failure, iob_trycopyin return a negated error value but * does not free any I/O buffers. */ nerr("ERROR: Failed to add data to the I/O buffer chain: %d\n", ret); (void)iob_free_chain(iob); return 0; } } /* Add the new I/O buffer chain to the tail of the read-ahead queue */ ret = iob_tryadd_queue(iob, &conn->readahead); if (ret < 0) { nerr("ERROR: Failed to queue the I/O buffer chain: %d\n", ret); (void)iob_free_chain(iob); return 0; } #ifdef CONFIG_UDP_READAHEAD_NOTIFIER /* Provided notification(s) that additional UDP read-ahead data is * available. */ udp_notifier_signal(conn); #endif ninfo("Buffered %d bytes\n", buflen); return buflen; }
static FAR struct iob_s *iob_allocwait(bool throttled) { FAR struct iob_s *iob; irqstate_t flags; FAR sem_t *sem; int ret = OK; #if CONFIG_IOB_THROTTLE > 0 /* Select the semaphore count to check. */ sem = (throttled ? &g_throttle_sem : &g_iob_sem); #else sem = &g_iob_sem; #endif /* The following must be atomic; interrupt must be disabled so that there * is no conflict with interrupt level I/O buffer allocations. This is * not as bad as it sounds because interrupts will be re-enabled while * we are waiting for I/O buffers to become free. */ flags = irqsave(); do { /* Try to get an I/O buffer. If successful, the semaphore count * will be decremented atomically. */ iob = iob_tryalloc(throttled); if (!iob) { /* If not successful, then the semaphore count was less than or * equal to zero (meaning that there are no free buffers). We * need to wait for an I/O buffer to be released when the semaphore * count will be incremented. */ ret = sem_wait(sem); if (ret < 0) { int errcode = get_errno(); /* EINTR is not an error! EINTR simply means that we were * awakened by a signal and we should try again. * * REVISIT: Many end-user interfaces are required to return * with an error if EINTR is set. Most uses of this function * is in internal, non-user logic. But are there cases where * the error should be returned. */ if (errcode == EINTR) { /* Force a success indication so that we will continue * looping. */ ret = 0; } else { /* Stop the loop and return a error */ DEBUGASSERT(errcode > 0); ret = -errcode; } } else { /* When we wake up from wait successfully, an I/O buffer was * returned to the free list. However, if there are concurrent * allocations from interrupt handling, then I suspect that * there is a race condition. But no harm, we will just wait * again in that case. * * We need release our count so that it is available to * iob_tryalloc(), perhaps allowing another thread to take our * count. In that event, iob_tryalloc() will fail above and * we will have to wait again. * * TODO: Consider a design modification to permit us to * complete the allocation without losing our count. */ sem_post(sem); } } } while (ret == OK && iob == NULL); irqrestore(flags); return iob; }