diff options
author | andre <andre@FreeBSD.org> | 2007-03-19 18:35:13 +0000 |
---|---|---|
committer | andre <andre@FreeBSD.org> | 2007-03-19 18:35:13 +0000 |
commit | 515a501549ecde68391cfcda8fa7589d3be40b09 (patch) | |
tree | 2990be3a6e9bba0c00c062b7e5afbdc1fb30ba4f /sys/kern/uipc_sockbuf.c | |
parent | 52a90d88fa8297a87f870f937e67657a45d52f71 (diff) | |
download | FreeBSD-src-515a501549ecde68391cfcda8fa7589d3be40b09.zip FreeBSD-src-515a501549ecde68391cfcda8fa7589d3be40b09.tar.gz |
Maintain a pointer and offset pair into the socket buffer mbuf chain to
avoid traversal of the entire socket buffer for larger offsets on stream
sockets.
Adjust tcp_output() make use of it.
Tested by: gallatin
Diffstat (limited to 'sys/kern/uipc_sockbuf.c')
-rw-r--r-- | sys/kern/uipc_sockbuf.c | 41 |
1 files changed, 41 insertions, 0 deletions
diff --git a/sys/kern/uipc_sockbuf.c b/sys/kern/uipc_sockbuf.c index f34df44..638f485 100644 --- a/sys/kern/uipc_sockbuf.c +++ b/sys/kern/uipc_sockbuf.c @@ -852,6 +852,8 @@ sbdrop_internal(struct sockbuf *sb, int len) m->m_len -= len; m->m_data += len; sb->sb_cc -= len; + if (sb->sb_sndptroff != 0) + sb->sb_sndptroff -= len; if (m->m_type != MT_DATA && m->m_type != MT_OOBDATA) sb->sb_ctl -= len; break; @@ -903,6 +905,45 @@ sbdrop(struct sockbuf *sb, int len) SOCKBUF_UNLOCK(sb); } + +/* + * Maintain a pointer and offset pair into the socket buffer mbuf chain to + * avoid traversal of the entire socket buffer for larger offsets. + */ +struct mbuf * +sbsndptr(struct sockbuf *sb, u_int off, u_int len, u_int *moff) +{ + struct mbuf *m, *ret; + + KASSERT(sb->sb_mb != NULL, ("%s: sb_mb is NULL", __func__)); + KASSERT(off + len <= sb->sb_cc, ("%s: beyond sb", __func__)); + KASSERT(sb->sb_sndptroff <= sb->sb_cc, ("%s: sndptroff broken", __func__)); + + /* + * Is off below stored offset? Happens on retransmits. + * Just return, we can't help here. + */ + if (sb->sb_sndptroff > off) { + *moff = off; + return (sb->sb_mb); + } + + /* Return closest mbuf in chain for current offset. */ + *moff = off - sb->sb_sndptroff; + m = ret = sb->sb_sndptr ? sb->sb_sndptr : sb->sb_mb; + + /* Advance by len to be as close as possible for the next transmit. */ + for (off = off - sb->sb_sndptroff + len - 1; + off > 0 && off >= m->m_len; + m = m->m_next) { + sb->sb_sndptroff += m->m_len; + off -= m->m_len; + } + sb->sb_sndptr = m; + + return (ret); +} + /* * Drop a record off the front of a sockbuf and move the next record to the * front. |