summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNeil Brown <neilb@cse.unsw.edu.au>2002-02-25 22:24:00 -0800
committerLinus Torvalds <torvalds@penguin.transmeta.com>2002-02-25 22:24:00 -0800
commitafdb4fa2b04a7e90e0746bc3d031a552656c7709 (patch)
tree95a1e7896d322db534bf8520f2d2831f255f1c42
parent4e2f6bfe215882c3df7b2e8d6704bd28a4a9a664 (diff)
[PATCH] PATCH 15/16: NFSD: TCP: Limit number of active tcp connections to an RPC service
Limit number of active tcp connections to an RPC service If a connection comes in and that results in number of connections being more than 5 times the number of threads, then we close a connection. We randomly drop with the oldest or the newest connection. Thus if we are flooded with connection requests, some will get in and hopefully stay long enough to service at least one request.
-rw-r--r--include/linux/sunrpc/svc.h1
-rw-r--r--net/sunrpc/svcsock.c59
2 files changed, 50 insertions, 10 deletions
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
index c56c9d726bd0..2d2461bb38c8 100644
--- a/include/linux/sunrpc/svc.h
+++ b/include/linux/sunrpc/svc.h
@@ -38,6 +38,7 @@ struct svc_serv {
struct list_head sv_permsocks; /* all permanent sockets */
struct list_head sv_tempsocks; /* all temporary sockets */
+ int sv_tmpcnt; /* count of temporary sockets */
char * sv_name; /* service name */
};
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index 31d671bbd995..919a095f6953 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -243,10 +243,25 @@ void svc_reserve(struct svc_rqst *rqstp, int space)
* Release a socket after use.
*/
static inline void
+svc_sock_put(struct svc_sock *svsk)
+{
+ struct svc_serv *serv = svsk->sk_server;
+
+ spin_lock_bh(&serv->sv_lock);
+ if (!--(svsk->sk_inuse) && test_bit(SK_DEAD, &svsk->sk_flags)) {
+ spin_unlock_bh(&serv->sv_lock);
+ dprintk("svc: releasing dead socket\n");
+ sock_release(svsk->sk_sock);
+ kfree(svsk);
+ }
+ else
+ spin_unlock_bh(&serv->sv_lock);
+}
+
+static void
svc_sock_release(struct svc_rqst *rqstp)
{
struct svc_sock *svsk = rqstp->rq_sock;
- struct svc_serv *serv = svsk->sk_server;
svc_release_skb(rqstp);
@@ -265,15 +280,7 @@ svc_sock_release(struct svc_rqst *rqstp)
svc_reserve(rqstp, 0);
rqstp->rq_sock = NULL;
- spin_lock_bh(&serv->sv_lock);
- if (!--(svsk->sk_inuse) && test_bit(SK_DEAD, &svsk->sk_flags)) {
- spin_unlock_bh(&serv->sv_lock);
- dprintk("svc: releasing dead socket\n");
- sock_release(svsk->sk_sock);
- kfree(svsk);
- }
- else
- spin_unlock_bh(&serv->sv_lock);
+ svc_sock_put(svsk);
}
/*
@@ -698,6 +705,35 @@ svc_tcp_accept(struct svc_sock *svsk)
set_bit(SK_DATA, &newsvsk->sk_flags);
svc_sock_enqueue(newsvsk);
+ /* make sure that we don't have too many active connections.
+ * If we have, something must be dropped.
+ * We randomly choose between newest and oldest (in terms
+ * of recent activity) and drop it.
+ */
+ if (serv->sv_tmpcnt > serv->sv_nrthreads*5) {
+ struct svc_sock *svsk = NULL;
+ spin_lock_bh(&serv->sv_lock);
+ if (!list_empty(&serv->sv_tempsocks)) {
+ if (net_random()&1)
+ svsk = list_entry(serv->sv_tempsocks.prev,
+ struct svc_sock,
+ sk_list);
+ else
+ svsk = list_entry(serv->sv_tempsocks.next,
+ struct svc_sock,
+ sk_list);
+ set_bit(SK_CLOSE, &svsk->sk_flags);
+ svsk->sk_inuse ++;
+ }
+ spin_unlock_bh(&serv->sv_lock);
+
+ if (svsk) {
+ svc_sock_enqueue(svsk);
+ svc_sock_put(svsk);
+ }
+
+ }
+
if (serv->sv_stats)
serv->sv_stats->nettcpconn++;
@@ -1138,6 +1174,7 @@ if (svsk->sk_sk == NULL)
if (!pmap_register) {
set_bit(SK_TEMP, &svsk->sk_flags);
list_add(&svsk->sk_list, &serv->sv_tempsocks);
+ serv->sv_tmpcnt++;
} else {
clear_bit(SK_TEMP, &svsk->sk_flags);
list_add(&svsk->sk_list, &serv->sv_permsocks);
@@ -1217,6 +1254,8 @@ svc_delete_socket(struct svc_sock *svsk)
spin_lock_bh(&serv->sv_lock);
list_del(&svsk->sk_list);
+ if (test_bit(SK_TEMP, &svsk->sk_flags))
+ serv->sv_tmpcnt--;
if (test_bit(SK_QUED, &svsk->sk_flags))
list_del(&svsk->sk_ready);