Index: kern/uipc_sockbuf.c =================================================================== RCS file: /home/ncvs/src/sys/kern/uipc_sockbuf.c,v retrieving revision 1.165 diff -u -p -r1.165 uipc_sockbuf.c --- kern/uipc_sockbuf.c 6 Sep 2006 21:59:36 -0000 1.165 +++ kern/uipc_sockbuf.c 1 Mar 2007 14:11:56 -0000 @@ -527,6 +568,9 @@ void sbappendstream(struct sockbuf *sb, struct mbuf *m) { + /* Get rid of packet headers. */ + m_demote(m, 0); + SOCKBUF_LOCK(sb); sbappendstream_locked(sb, m); SOCKBUF_UNLOCK(sb); @@ -547,7 +591,7 @@ sbcheck(struct sockbuf *sb) for (; m; m = m->m_next) { len += m->m_len; mbcnt += MSIZE; - if (m->m_flags & M_EXT) /*XXX*/ /* pretty sure this is bogus */ + if (m->m_flags & M_EXT) mbcnt += m->m_ext.ext_size; } } @@ -839,10 +963,10 @@ sbdrop_internal(struct sockbuf *sb, int struct mbuf *m; struct mbuf *next; - next = (m = sb->sb_mb) ? m->m_nextpkt : 0; + next = (m = sb->sb_mb) ? m->m_nextpkt : NULL; while (len > 0) { - if (m == 0) { - if (next == 0) + if (m == NULL) { + if (next == NULL) panic("sbdrop"); m = next; next = m->m_nextpkt; @@ -852,6 +976,8 @@ sbdrop_internal(struct sockbuf *sb, int 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 +1029,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. Index: netinet/tcp_output.c =================================================================== RCS file: /home/ncvs/src/sys/netinet/tcp_output.c,v retrieving revision 1.125 diff -u -p -r1.125 tcp_output.c --- netinet/tcp_output.c 1 Mar 2007 13:12:09 -0000 1.125 +++ netinet/tcp_output.c 1 Mar 2007 14:11:57 -0000 @@ -834,6 +874,9 @@ send: * the template for sends on this connection. */ if (len) { + struct mbuf *mb; /* XXX */ + u_int moff; /* XXX */ + if ((tp->t_flags & TF_FORCEDATA) && len == 1) tcpstat.tcps_sndprobe++; else if (SEQ_LT(tp->snd_nxt, tp->snd_max)) { @@ -875,13 +918,16 @@ send: #endif m->m_data += max_linkhdr; m->m_len = hdrlen; + + mb = sbsndptr(&so->so_snd, off, len, &moff); + if (len <= MHLEN - hdrlen - max_linkhdr) { - m_copydata(so->so_snd.sb_mb, off, (int) len, + m_copydata(mb, moff, (int)len, mtod(m, caddr_t) + hdrlen); m->m_len += len; } else { - m->m_next = m_copy(so->so_snd.sb_mb, off, (int) len); - if (m->m_next == 0) { + m->m_next = m_copy(mb, moff, (int)len); + if (m->m_next == NULL) { SOCKBUF_UNLOCK(&so->so_snd); (void) m_free(m); error = ENOBUFS; Index: sys/socketvar.h =================================================================== RCS file: /home/ncvs/src/sys/sys/socketvar.h,v retrieving revision 1.155 diff -u -p -r1.155 socketvar.h --- sys/socketvar.h 1 Feb 2007 17:53:41 -0000 1.155 +++ sys/socketvar.h 1 Mar 2007 14:11:57 -0000 @@ -103,6 +103,8 @@ struct socket { struct mbuf *sb_mbtail; /* (c/d) the last mbuf in the chain */ struct mbuf *sb_lastrecord; /* (c/d) first mbuf of last * record in socket buffer */ + struct mbuf *sb_sndptr; /* (c/d) pointer into mbuf chain */ + u_int sb_sndptroff; /* (c/d) byte offset of ptr into chain */ u_int sb_cc; /* (c/d) actual chars in buffer */ u_int sb_hiwat; /* (c/d) max actual char count */ u_int sb_mbcnt; /* (c/d) chars of mbufs used */ @@ -321,6 +329,12 @@ struct xsocket { (sb)->sb_mbcnt -= MSIZE; \ if ((m)->m_flags & M_EXT) \ (sb)->sb_mbcnt -= (m)->m_ext.ext_size; \ + if ((sb)->sb_sndptr == (m)) { \ + (sb)->sb_sndptr = NULL; \ + (sb)->sb_sndptroff = 0; \ + } \ + if ((sb)->sb_sndptroff != 0) \ + (sb)->sb_sndptroff -= (m)->m_len; \ } /* @@ -485,12 +499,17 @@ void sbdroprecord(struct sockbuf *sb); void sbdroprecord_locked(struct sockbuf *sb); void sbflush(struct sockbuf *sb); void sbflush_locked(struct sockbuf *sb); +void sbprepend_locked(struct sockbuf *sb, struct mbuf *m); +struct mbuf * + sbpull_locked(struct sockbuf *sb, int len, int how); void sbrelease(struct sockbuf *sb, struct socket *so); void sbrelease_locked(struct sockbuf *sb, struct socket *so); int sbreserve(struct sockbuf *sb, u_long cc, struct socket *so, struct thread *td); int sbreserve_locked(struct sockbuf *sb, u_long cc, struct socket *so, struct thread *td); +struct mbuf * + sbsndptr(struct sockbuf *sb, u_int off, u_int len, u_int *moff); void sbtoxsockbuf(struct sockbuf *sb, struct xsockbuf *xsb); int sbwait(struct sockbuf *sb); int sb_lock(struct sockbuf *sb);