diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 4c049cbf66dee22b3c238ff78e372d00286e7ee2..a293c243b297006f4970eb41777736002f2db856 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -14300,41 +14300,61 @@ static void *do_monitor(void *data)
 		ast_mutex_lock(&iflock);
 restartsearch:		
 		t = time(NULL);
-		for (sip = iflist; sip; sip = sip->next) {
+		/* don't scan the interface list if it hasn't been a reasonable period
+		   of time since the last time we did it (when MWI is being sent, we can
+		   get back to this point every millisecond or less)
+		*/
+		for (sip = iflist; !fastrestart && sip; sip = sip->next) {
 			ast_mutex_lock(&sip->lock);
 			/* Check RTP timeouts and kill calls if we have a timeout set and do not get RTP */
-			if (sip->rtp && sip->owner && (sip->owner->_state == AST_STATE_UP) && !sip->redirip.sin_addr.s_addr) {
-				if (sip->lastrtptx && sip->rtpkeepalive && t > sip->lastrtptx + sip->rtpkeepalive) {
+			if (sip->rtp && sip->owner &&
+			    (sip->owner->_state == AST_STATE_UP) &&
+			    !sip->redirip.sin_addr.s_addr) {
+				if (sip->lastrtptx &&
+				    sip->rtpkeepalive &&
+				    (t > sip->lastrtptx + sip->rtpkeepalive)) {
 					/* Need to send an empty RTP packet */
 					sip->lastrtptx = time(NULL);
 					ast_rtp_sendcng(sip->rtp, 0);
 				}
-				if (sip->lastrtprx && (sip->rtptimeout || sip->rtpholdtimeout) && t > sip->lastrtprx + sip->rtptimeout) {
+				if (sip->lastrtprx &&
+				    (sip->rtptimeout || sip->rtpholdtimeout) &&
+				    (t > sip->lastrtprx + sip->rtptimeout)) {
 					/* Might be a timeout now -- see if we're on hold */
 					struct sockaddr_in sin;
 					ast_rtp_get_peer(sip->rtp, &sin);
 					if (sin.sin_addr.s_addr || 
-							(sip->rtpholdtimeout && 
-							  (t > sip->lastrtprx + sip->rtpholdtimeout))) {
+					    (sip->rtpholdtimeout && 
+					     (t > sip->lastrtprx + sip->rtpholdtimeout))) {
 						/* Needs a hangup */
 						if (sip->rtptimeout) {
-							while(sip->owner && ast_channel_trylock(sip->owner)) {
+							while (sip->owner && ast_channel_trylock(sip->owner)) {
 								ast_mutex_unlock(&sip->lock);
 								usleep(1);
 								ast_mutex_lock(&sip->lock);
 							}
 							if (sip->owner) {
-								ast_log(LOG_NOTICE, "Disconnecting call '%s' for lack of RTP activity in %ld seconds\n", sip->owner->name, (long)(t - sip->lastrtprx));
+								ast_log(LOG_NOTICE,
+									"Disconnecting call '%s' for lack of RTP activity in %ld seconds\n",
+									sip->owner->name,
+									(long) (t - sip->lastrtprx));
 								/* Issue a softhangup */
-								ast_softhangup(sip->owner, AST_SOFTHANGUP_DEV);
+								ast_softhangup_nolock(sip->owner, AST_SOFTHANGUP_DEV);
 								ast_channel_unlock(sip->owner);
+								/* forget the timeouts for this call, since a hangup
+								   has already been requested and we don't want to
+								   repeatedly request hangups
+								*/
+								sip->rtptimeout = 0;
+								sip->rtpholdtimeout = 0;
 							}
 						}
 					}
 				}
 			}
 			/* If we have sessions that needs to be destroyed, do it now */
-			if (ast_test_flag(&sip->flags[0], SIP_NEEDDESTROY) && !sip->packets && !sip->owner) {
+			if (ast_test_flag(&sip->flags[0], SIP_NEEDDESTROY) && !sip->packets &&
+			    !sip->owner) {
 				ast_mutex_unlock(&sip->lock);
 				__sip_destroy(sip, 1);
 				goto restartsearch;