Mercurial > illumos > illumos-gate
changeset 4011:c21745e4c9ae
6449337 kmem exhaustion caused by tcp fusion flow control logic error
author | udpa |
---|---|
date | Wed, 11 Apr 2007 10:58:03 -0700 |
parents | d83e031dd201 |
children | d73a7d74af00 |
files | usr/src/uts/common/inet/tcp/tcp.c usr/src/uts/common/inet/tcp/tcp_fusion.c |
diffstat | 2 files changed, 29 insertions(+), 8 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/uts/common/inet/tcp/tcp.c Wed Apr 11 10:50:16 2007 -0700 +++ b/usr/src/uts/common/inet/tcp/tcp.c Wed Apr 11 10:58:03 2007 -0700 @@ -15929,7 +15929,22 @@ if (tcp->tcp_rcv_list != NULL) (void) tcp_rcv_drain(tcp->tcp_rq, tcp); - tcp_clrqfull(peer_tcp); + if (peer_tcp > tcp) { + mutex_enter(&peer_tcp->tcp_non_sq_lock); + mutex_enter(&tcp->tcp_non_sq_lock); + } else { + mutex_enter(&tcp->tcp_non_sq_lock); + mutex_enter(&peer_tcp->tcp_non_sq_lock); + } + + if (peer_tcp->tcp_flow_stopped && + (TCP_UNSENT_BYTES(peer_tcp) <= + peer_tcp->tcp_xmit_lowater)) { + tcp_clrqfull(peer_tcp); + } + mutex_exit(&peer_tcp->tcp_non_sq_lock); + mutex_exit(&tcp->tcp_non_sq_lock); + TCP_FUSE_SYNCSTR_UNPLUG_DRAIN(tcp); TCP_STAT(tcps, tcp_fusion_backenabled); return;
--- a/usr/src/uts/common/inet/tcp/tcp_fusion.c Wed Apr 11 10:50:16 2007 -0700 +++ b/usr/src/uts/common/inet/tcp/tcp_fusion.c Wed Apr 11 10:58:03 2007 -0700 @@ -496,7 +496,7 @@ { tcp_t *peer_tcp = tcp->tcp_loopback_peer; uint_t max_unread; - boolean_t flow_stopped; + boolean_t flow_stopped, peer_data_queued = B_FALSE; boolean_t urgent = (DB_TYPE(mp) != M_DATA); mblk_t *mp1 = mp; ill_t *ilp, *olp; @@ -697,20 +697,24 @@ mutex_enter(&tcp->tcp_non_sq_lock); } flow_stopped = tcp->tcp_flow_stopped; - if (!flow_stopped && - (((peer_tcp->tcp_direct_sockfs || TCP_IS_DETACHED(peer_tcp)) && + if (((peer_tcp->tcp_direct_sockfs || TCP_IS_DETACHED(peer_tcp)) && (peer_tcp->tcp_rcv_cnt >= peer_tcp->tcp_fuse_rcv_hiwater || ++peer_tcp->tcp_fuse_rcv_unread_cnt >= max_unread)) || (!peer_tcp->tcp_direct_sockfs && - !TCP_IS_DETACHED(peer_tcp) && !canputnext(peer_tcp->tcp_rq)))) { + !TCP_IS_DETACHED(peer_tcp) && !canputnext(peer_tcp->tcp_rq))) { + peer_data_queued = B_TRUE; + } + + if (!flow_stopped && (peer_data_queued || + (TCP_UNSENT_BYTES(tcp) >= tcp->tcp_xmit_hiwater))) { tcp_setqfull(tcp); flow_stopped = B_TRUE; TCP_STAT(tcps, tcp_fusion_flowctl); DTRACE_PROBE4(tcp__fuse__output__flowctl, tcp_t *, tcp, uint_t, send_size, uint_t, peer_tcp->tcp_rcv_cnt, uint_t, peer_tcp->tcp_fuse_rcv_unread_cnt); - } else if (flow_stopped && - TCP_UNSENT_BYTES(tcp) <= tcp->tcp_xmit_lowater) { + } else if (flow_stopped && !peer_data_queued && + (TCP_UNSENT_BYTES(tcp) <= tcp->tcp_xmit_lowater)) { tcp_clrqfull(tcp); flow_stopped = B_FALSE; } @@ -976,7 +980,9 @@ tcp->tcp_rcv_cnt = 0; tcp->tcp_fuse_rcv_unread_cnt = 0; - if (peer_tcp->tcp_flow_stopped) { + if (peer_tcp->tcp_flow_stopped && + (TCP_UNSENT_BYTES(peer_tcp) <= + peer_tcp->tcp_xmit_lowater)) { tcp_clrqfull(peer_tcp); TCP_STAT(tcps, tcp_fusion_backenabled); }