task #10167 (sockets: speed up TCP recv by not allocating a netbuf): added function netconn_recv_tcp_pbuf() for tcp netconns to receive pbufs, not netbufs; use that function for tcp sockets.

This commit is contained in:
goldsimon 2010-03-06 11:29:01 +00:00
parent 957f4d8096
commit 38fcfcdfac
4 changed files with 173 additions and 93 deletions

View File

@ -13,6 +13,12 @@ HISTORY
++ New features:
2010-03-06: Simon Goldschmidt
* api.h, api_lib.c, sockets.c: task #10167 (sockets: speed up TCP recv
by not allocating a netbuf): added function netconn_recv_tcp_pbuf()
for tcp netconns to receive pbufs, not netbufs; use that function
for tcp sockets.
2010-03-05: Jakob Ole Stoklundsen / Simon Goldschmidt
* opt.h, tcp.h, tcp_impl.h, tcp.c, tcp_in.c, tcp_out.c: task #7040:
Work on tcp_enqueue: Don't waste memory when chaining segments,

View File

@ -316,19 +316,19 @@ netconn_accept(struct netconn *conn, struct netconn **new_conn)
}
/**
* Receive data (in form of a netbuf containing a packet buffer) from a netconn
* Receive data: actual implementation that doesn't care whether pbuf or netbuf
* is received
*
* @param conn the netconn from which to receive data
* @param new_buf pointer where a new netbuf is stored when received data
* @param new_buf pointer where a new pbuf/netbuf is stored when received data
* @return ERR_OK if data has been received, an error code otherwise (timeout,
* memory error or another error)
*/
err_t
netconn_recv(struct netconn *conn, struct netbuf **new_buf)
static err_t
netconn_recv_data(struct netconn *conn, void **new_buf)
{
struct api_msg msg;
struct netbuf *buf = NULL;
struct pbuf *p;
void *buf = NULL;
u16_t len;
err_t err;
@ -346,8 +346,103 @@ netconn_recv(struct netconn *conn, struct netbuf **new_buf)
return err;
}
#if LWIP_SO_RCVTIMEO
if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT);
return ERR_TIMEOUT;
}
#else
sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0);
#endif /* LWIP_SO_RCVTIMEO*/
#if LWIP_TCP
if (conn->type == NETCONN_TCP) {
if (!netconn_get_noautorecved(conn) || (buf == NULL)) {
/* Let the stack know that we have taken the data. */
/* TODO: Speedup: Don't block and wait for the answer here
(to prevent multiple thread-switches). */
msg.function = do_recv;
msg.msg.conn = conn;
if (buf != NULL) {
msg.msg.msg.r.len = ((struct pbuf *)buf)->tot_len;
} else {
msg.msg.msg.r.len = 1;
}
/* don't care for the return value of do_recv */
TCPIP_APIMSG(&msg);
}
/* If we are closed, we indicate that we no longer wish to use the socket */
if (buf == NULL) {
API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
/* Avoid to lose any previous error code */
NETCONN_SET_SAFE_ERR(conn, ERR_CLSD);
return ERR_CLSD;
}
len = ((struct pbuf *)buf)->tot_len;
}
#endif /* LWIP_TCP */
#if LWIP_TCP && (LWIP_UDP || LWIP_RAW)
else
#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
#if (LWIP_UDP || LWIP_RAW)
{
LWIP_ASSERT("buf != NULL", buf != NULL);
len = netbuf_len((struct netbuf *)buf);
}
#endif /* (LWIP_UDP || LWIP_RAW) */
SYS_ARCH_DEC(conn->recv_avail, len);
/* Register event with callback */
API_EVENT(conn, NETCONN_EVT_RCVMINUS, len);
LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv_data: received %p, len=%"U16_F"\n", buf, len));
*new_buf = buf;
/* don't set conn->last_err: it's only ERR_OK, anyway */
return ERR_OK;
}
/**
* Receive data (in form of a pbuf) from a TCP netconn
*
* @param conn the netconn from which to receive data
* @param new_buf pointer where a new pbuf is stored when received data
* @return ERR_OK if data has been received, an error code otherwise (timeout,
* memory error or another error)
* ERR_ARG if conn is not a TCP netconn
*/
err_t
netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf)
{
LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL) &&
netconn_type(conn) == NETCONN_TCP, return ERR_ARG;);
return netconn_recv_data(conn, new_buf);
}
/**
* Receive data (in form of a netbuf containing a packet buffer) from a netconn
*
* @param conn the netconn from which to receive data
* @param new_buf pointer where a new netbuf is stored when received data
* @return ERR_OK if data has been received, an error code otherwise (timeout,
* memory error or another error)
*/
err_t
netconn_recv(struct netconn *conn, struct netbuf **new_buf)
{
struct netbuf *buf = NULL;
err_t err;
LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
*new_buf = NULL;
LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;);
LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
if (conn->type == NETCONN_TCP) {
#if LWIP_TCP
struct pbuf *p = NULL;
/* This is not a listening netconn, since recvmbox is set */
buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
@ -356,79 +451,26 @@ netconn_recv(struct netconn *conn, struct netbuf **new_buf)
return ERR_MEM;
}
#if LWIP_SO_RCVTIMEO
if (sys_arch_mbox_fetch(&conn->recvmbox, (void **)&p, conn->recv_timeout)==SYS_ARCH_TIMEOUT) {
err = netconn_recv_data(conn, &p);
if (err != ERR_OK) {
memp_free(MEMP_NETBUF, buf);
NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT);
return ERR_TIMEOUT;
}
#else
sys_arch_mbox_fetch(&conn->recvmbox, (void **)&p, 0);
#endif /* LWIP_SO_RCVTIMEO*/
if (p != NULL) {
len = p->tot_len;
SYS_ARCH_DEC(conn->recv_avail, len);
} else {
/* This means the connection has been closed */
len = 0;
}
/* Register event with callback */
API_EVENT(conn, NETCONN_EVT_RCVMINUS, len);
/* If we are closed, we indicate that we no longer wish to use the socket */
if (p == NULL) {
memp_free(MEMP_NETBUF, buf);
/* Avoid to lose any previous error code */
NETCONN_SET_SAFE_ERR(conn, ERR_CLSD);
return ERR_CLSD;
return err;
}
LWIP_ASSERT("p != NULL", p != NULL);
buf->p = p;
buf->ptr = p;
buf->port = 0;
buf->addr = NULL;
if (!netconn_get_noautorecved(conn) || (buf == NULL)) {
/* Let the stack know that we have taken the data. */
/* TODO: Speedup: Don't block and wait for the answer here
(to prevent multiple thread-switches). */
msg.function = do_recv;
msg.msg.conn = conn;
if (buf != NULL) {
msg.msg.msg.r.len = buf->p->tot_len;
} else {
msg.msg.msg.r.len = 1;
}
/* don't care for the return value of do_recv */
TCPIP_APIMSG(&msg);
}
*new_buf = buf;
/* don't set conn->last_err: it's only ERR_OK, anyway */
return ERR_OK;
#endif /* LWIP_TCP */
} else {
#if (LWIP_UDP || LWIP_RAW)
#if LWIP_SO_RCVTIMEO
if (sys_arch_mbox_fetch(&conn->recvmbox, (void **)&buf, conn->recv_timeout)==SYS_ARCH_TIMEOUT) {
NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT);
return ERR_TIMEOUT;
}
#else
sys_arch_mbox_fetch(&conn->recvmbox, (void **)&buf, 0);
#endif /* LWIP_SO_RCVTIMEO*/
LWIP_ASSERT("buf != NULL", buf != NULL);
SYS_ARCH_DEC(conn->recv_avail, buf->p->tot_len);
/* Register event with callback */
API_EVENT(conn, NETCONN_EVT_RCVMINUS, buf->p->tot_len);
return netconn_recv_data(conn, new_buf);
#endif /* (LWIP_UDP || LWIP_RAW) */
}
LWIP_ASSERT("buf != NULL", buf != NULL);
LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv: received %p\n", (void *)buf));
*new_buf = buf;
/* don't set conn->last_err: it's only ERR_OK, anyway */
return ERR_OK;
}
/**

View File

@ -51,6 +51,7 @@
#include "lwip/raw.h"
#include "lwip/udp.h"
#include "lwip/tcpip.h"
#include "lwip/pbuf.h"
#include <string.h>
@ -61,7 +62,7 @@ struct lwip_sock {
/** sockets currently are built on netconns, each socket has one netconn */
struct netconn *conn;
/** data that was left from the previous read */
struct netbuf *lastdata;
void *lastdata;
/** offset in the data that was left from the previous read */
u16_t lastoffset;
/** number of times data was received, set by event_callback(),
@ -248,11 +249,12 @@ alloc_socket(struct netconn *newconn, int accepted)
* delete before!
*
* @param sock the socket to free
* @param is_tcp != 0 for TCP sockets, used to free lastdata
*/
static void
free_socket(struct lwip_sock *sock)
free_socket(struct lwip_sock *sock, int is_tcp)
{
struct netbuf *lastdata;
void *lastdata;
SYS_ARCH_DECL_PROTECT(lev);
lastdata = sock->lastdata;
@ -266,7 +268,11 @@ free_socket(struct lwip_sock *sock)
SYS_ARCH_UNPROTECT(lev);
if (lastdata != NULL) {
netbuf_delete(lastdata);
if (is_tcp) {
pbuf_free((struct pbuf *)lastdata);
} else {
netbuf_delete((struct netbuf *)lastdata);
}
}
}
@ -404,6 +410,7 @@ int
lwip_close(int s)
{
struct lwip_sock *sock;
int is_tcp = 0;
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s));
@ -412,9 +419,15 @@ lwip_close(int s)
return -1;
}
if(sock->conn != NULL) {
is_tcp = netconn_type(sock->conn) == NETCONN_TCP;
} else {
LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata == NULL);
}
netconn_delete(sock->conn);
free_socket(sock);
free_socket(sock, is_tcp);
set_errno(0);
return 0;
}
@ -501,13 +514,14 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags,
struct sockaddr *from, socklen_t *fromlen)
{
struct lwip_sock *sock;
struct netbuf *buf;
u16_t buflen, copylen;
int off = 0;
ip_addr_t *addr;
u16_t port;
u8_t done = 0;
err_t err;
void *buf = NULL;
struct pbuf *p;
u16_t buflen, copylen;
int off = 0;
ip_addr_t *addr;
u16_t port;
u8_t done = 0;
err_t err;
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %"SZT_F", 0x%x, ..)\n", s, mem, len, flags));
sock = get_socket(s);
@ -515,7 +529,7 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags,
return -1;
do {
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: top while sock->lastdata=%p\n", (void*)sock->lastdata));
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: top while sock->lastdata=%p\n", sock->lastdata));
/* Check if there is data left from the last recv operation. */
if (sock->lastdata) {
buf = sock->lastdata;
@ -537,9 +551,13 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags,
/* No data was left from the previous operation, so we try to get
some from the network. */
err = netconn_recv(sock->conn, &buf);
if (netconn_type(sock->conn) == NETCONN_TCP) {
err = netconn_recv_tcp_pbuf(sock->conn, &(struct pbuf *)buf);
} else {
err = netconn_recv(sock->conn, &(struct netbuf *)buf);
}
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: netconn_recv err=%d, netbuf=%p\n",
err, (void*)buf));
err, buf));
if (err != ERR_OK) {
if (off > 0) {
@ -563,7 +581,12 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags,
sock->lastdata = buf;
}
buflen = netbuf_len(buf);
if (netconn_type(sock->conn) == NETCONN_TCP) {
p = (struct pbuf *)buf;
} else {
p = ((struct netbuf *)buf)->p;
}
buflen = p->tot_len;
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: buflen=%"U16_F" len=%"SZT_F" off=%d sock->lastoffset=%"U16_F"\n",
buflen, len, off, sock->lastoffset));
@ -577,7 +600,7 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags,
/* copy the contents of the received buffer into
the supplied memory pointer mem */
netbuf_copy_partial(buf, (u8_t*)mem + off, copylen, sock->lastoffset);
pbuf_copy_partial(p, (u8_t*)mem + off, copylen, sock->lastoffset);
off += copylen;
@ -585,7 +608,7 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags,
LWIP_ASSERT("invalid copylen, len would underflow", len >= copylen);
len -= copylen;
if ( (len <= 0) ||
(buf->p->flags & PBUF_FLAG_PUSH) ||
(p->flags & PBUF_FLAG_PUSH) ||
(sock->rcvevent <= 0) ||
((flags & MSG_PEEK)!=0)) {
done = 1;
@ -604,8 +627,8 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags,
addr = &fromaddr;
netconn_getaddr(sock->conn, addr, &port, 0);
} else {
addr = netbuf_fromaddr(buf);
port = netbuf_fromport(buf);
addr = netbuf_fromaddr((struct netbuf *)buf);
port = netbuf_fromport((struct netbuf *)buf);
}
memset(&sin, 0, sizeof(sin));
@ -629,8 +652,8 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags,
addr = &fromaddr;
netconn_getaddr(sock->conn, addr, &port, 0);
} else {
addr = netbuf_fromaddr(buf);
port = netbuf_fromport(buf);
addr = netbuf_fromaddr((struct netbuf *)buf);
port = netbuf_fromport((struct netbuf *)buf);
}
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s));
@ -648,12 +671,16 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags,
if ((netconn_type(sock->conn) == NETCONN_TCP) && (buflen - copylen > 0)) {
sock->lastdata = buf;
sock->lastoffset += copylen;
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: lastdata now netbuf=%p\n", (void*)buf));
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: lastdata now netbuf=%p\n", buf));
} else {
sock->lastdata = NULL;
sock->lastoffset = 0;
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: deleting netbuf=%p\n", (void*)buf));
netbuf_delete(buf);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: deleting netbuf=%p\n", buf));
if (netconn_type(sock->conn) == NETCONN_TCP) {
pbuf_free((struct pbuf *)buf);
} else {
netbuf_delete((struct netbuf *)buf);
}
}
}
} while (!done);
@ -2031,7 +2058,11 @@ lwip_ioctl(int s, long cmd, void *argp)
/* Check if there is data left from the last recv operation. /maq 041215 */
if (sock->lastdata) {
buflen = netbuf_len(sock->lastdata);
struct pbuf *p = (struct pbuf *)sock->lastdata;
if (netconn_type(sock->conn) != NETCONN_TCP) {
p = ((struct netbuf *)p)->p;
}
buflen = p->tot_len;
buflen -= sock->lastoffset;
*((u16_t*)argp) += buflen;

View File

@ -224,6 +224,7 @@ err_t netconn_listen_with_backlog(struct netconn *conn, u8_t backlog);
#define netconn_listen(conn) netconn_listen_with_backlog(conn, TCP_DEFAULT_LISTEN_BACKLOG)
err_t netconn_accept(struct netconn *conn, struct netconn **new_conn);
err_t netconn_recv(struct netconn *conn, struct netbuf **new_buf);
err_t netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf);
void netconn_recved(struct netconn *conn, u32_t length);
err_t netconn_sendto(struct netconn *conn, struct netbuf *buf,
ip_addr_t *addr, u16_t port);