From 38fcfcdfacb865abc5018eddeb57de3fc13f919f Mon Sep 17 00:00:00 2001 From: goldsimon Date: Sat, 6 Mar 2010 11:29:01 +0000 Subject: [PATCH] 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. --- CHANGELOG | 6 ++ src/api/api_lib.c | 176 +++++++++++++++++++++++++---------------- src/api/sockets.c | 83 +++++++++++++------ src/include/lwip/api.h | 1 + 4 files changed, 173 insertions(+), 93 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index d528ef86..6e3d1b43 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -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, diff --git a/src/api/api_lib.c b/src/api/api_lib.c index 01fdae4a..25106d60 100644 --- a/src/api/api_lib.c +++ b/src/api/api_lib.c @@ -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; } /** diff --git a/src/api/sockets.c b/src/api/sockets.c index 81a8e63d..a988727c 100644 --- a/src/api/sockets.c +++ b/src/api/sockets.c @@ -51,6 +51,7 @@ #include "lwip/raw.h" #include "lwip/udp.h" #include "lwip/tcpip.h" +#include "lwip/pbuf.h" #include @@ -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; diff --git a/src/include/lwip/api.h b/src/include/lwip/api.h index 27aa41ec..5065d31c 100644 --- a/src/include/lwip/api.h +++ b/src/include/lwip/api.h @@ -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);