Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/kern Fix a longstanding problem with accept filters noti...



details:   https://anonhg.NetBSD.org/src/rev/95b30ee728a2
branches:  trunk
changeset: 815522:95b30ee728a2
user:      tls <tls%NetBSD.org@localhost>
date:      Mon May 23 13:54:34 2016 +0000

description:
Fix a longstanding problem with accept filters noticed by Timo Buhrmester:
sockets sitting in the accept filter can consume the entire listen queue,
such that the application is never able to handle any connections.  Handle
this by simply passing through the oldest queued cxn when the queue is full.

This is fair because the longer a cxn lingers in the queue (stays connected
but does not meet the requirements of the filter for passage) the more likely
it is to be passed through, at which point the application can dispose of it.

Works because none of our accept filters actually allocate private state
per-cxn.  If they did, we'd have to fix the API bug that there is presently
no way to tell an accf to finish/deallocate for a single cxn (accf_destroy
kills off the entire filter instance for a given listen socket).

diffstat:

 sys/kern/uipc_socket2.c |  37 +++++++++++++++++++++++++++++++++----
 1 files changed, 33 insertions(+), 4 deletions(-)

diffs (58 lines):

diff -r aec1a1eb1d70 -r 95b30ee728a2 sys/kern/uipc_socket2.c
--- a/sys/kern/uipc_socket2.c   Mon May 23 11:41:06 2016 +0000
+++ b/sys/kern/uipc_socket2.c   Mon May 23 13:54:34 2016 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: uipc_socket2.c,v 1.122 2015/08/24 22:21:26 pooka Exp $ */
+/*     $NetBSD: uipc_socket2.c,v 1.123 2016/05/23 13:54:34 tls Exp $   */
 
 /*-
  * Copyright (c) 2008 The NetBSD Foundation, Inc.
@@ -58,7 +58,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uipc_socket2.c,v 1.122 2015/08/24 22:21:26 pooka Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uipc_socket2.c,v 1.123 2016/05/23 13:54:34 tls Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_mbuftrace.h"
@@ -262,8 +262,37 @@
        KASSERT(solocked(head));
 
        if (head->so_qlen + head->so_q0len > 3 * head->so_qlimit / 2) {
-               /* Listen queue overflow. */
-               return NULL;
+               /*
+                * Listen queue overflow.  If there is an accept filter
+                * active, pass through the oldest cxn it's handling.
+                */
+               if (head->so_accf == NULL) {
+                       return NULL;
+               } else {
+                       struct socket *so2, *next;
+
+                       /* Pass the oldest connection waiting in the
+                          accept filter */
+                       for (so2 = TAILQ_FIRST(&head->so_q0);
+                            so2 != NULL; so2 = next) {
+                               next = TAILQ_NEXT(so2, so_qe);
+                               if (so2->so_upcall == NULL) {
+                                       continue;
+                               }
+                               so2->so_upcall = NULL;
+                               so2->so_upcallarg = NULL;
+                               so2->so_options &= ~SO_ACCEPTFILTER;
+                               so2->so_rcv.sb_flags &= ~SB_UPCALL;
+                               soisconnected(so2);
+                               break;
+                       }
+
+                       /* If nothing was nudged out of the acept filter, bail
+                        * out; otherwise proceed allocating the socket. */
+                       if (so2 == NULL) {
+                               return NULL;
+                       }
+               }
        }
        if ((head->so_options & SO_ACCEPTFILTER) != 0) {
                soready = false;



Home | Main Index | Thread Index | Old Index