]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
rxrpc: Impose a maximum number of client calls
authorDavid Howells <dhowells@redhat.com>
Thu, 2 Jul 2020 13:59:46 +0000 (14:59 +0100)
committerDavid Howells <dhowells@redhat.com>
Tue, 8 Sep 2020 20:10:45 +0000 (21:10 +0100)
Impose a maximum on the number of client rxrpc calls that are allowed
simultaneously.  This will be in lieu of a maximum number of client
connections as this is easier to administed as, unlike connections, calls
aren't reusable (to be changed in a subsequent patch)..

This doesn't affect the limits on service calls and connections.

Signed-off-by: David Howells <dhowells@redhat.com>
net/rxrpc/af_rxrpc.c
net/rxrpc/ar-internal.h
net/rxrpc/call_object.c

index 186c8a889b164018c5ad4a16f82dea756220c04c..0a2f4817ec6cf2b8b2847aa26ccd7f16e4c6f10d 100644 (file)
@@ -308,9 +308,10 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock,
                key = NULL; /* a no-security key */
 
        memset(&p, 0, sizeof(p));
-       p.user_call_ID = user_call_ID;
-       p.tx_total_len = tx_total_len;
-       p.interruptibility = interruptibility;
+       p.user_call_ID          = user_call_ID;
+       p.tx_total_len          = tx_total_len;
+       p.interruptibility      = interruptibility;
+       p.kernel                = true;
 
        memset(&cp, 0, sizeof(cp));
        cp.local                = rx->local;
index 884cff7bb169a6add2fce6635aa250d9f6caacf7..de84198b3285c52564d381f03aa571fa271fcbd3 100644 (file)
@@ -493,6 +493,7 @@ enum rxrpc_call_flag {
        RXRPC_CALL_RX_HEARD,            /* The peer responded at least once to this call */
        RXRPC_CALL_RX_UNDERRUN,         /* Got data underrun */
        RXRPC_CALL_DISCONNECTED,        /* The call has been disconnected */
+       RXRPC_CALL_KERNEL,              /* The call was made by the kernel */
 };
 
 /*
@@ -727,6 +728,7 @@ struct rxrpc_call_params {
                u32             normal;         /* Max time since last call packet (msec) */
        } timeouts;
        u8                      nr_timeouts;    /* Number of timeouts specified */
+       bool                    kernel;         /* T if kernel is making the call */
        enum rxrpc_interruptibility interruptibility; /* How is interruptible is the call? */
 };
 
index a40fae0139423a6672d99a0095bf52d42d2156cb..c8015c76a81c58dcffdd70f1e7a99ea250640ea0 100644 (file)
@@ -41,6 +41,11 @@ const char *const rxrpc_call_completions[NR__RXRPC_CALL_COMPLETIONS] = {
 
 struct kmem_cache *rxrpc_call_jar;
 
+static struct semaphore rxrpc_call_limiter =
+       __SEMAPHORE_INITIALIZER(rxrpc_call_limiter, 1000);
+static struct semaphore rxrpc_kernel_call_limiter =
+       __SEMAPHORE_INITIALIZER(rxrpc_kernel_call_limiter, 1000);
+
 static void rxrpc_call_timer_expired(struct timer_list *t)
 {
        struct rxrpc_call *call = from_timer(call, t, timer);
@@ -209,6 +214,34 @@ static void rxrpc_start_call_timer(struct rxrpc_call *call)
        call->timer.expires = now;
 }
 
+/*
+ * Wait for a call slot to become available.
+ */
+static struct semaphore *rxrpc_get_call_slot(struct rxrpc_call_params *p, gfp_t gfp)
+{
+       struct semaphore *limiter = &rxrpc_call_limiter;
+
+       if (p->kernel)
+               limiter = &rxrpc_kernel_call_limiter;
+       if (p->interruptibility == RXRPC_UNINTERRUPTIBLE) {
+               down(limiter);
+               return limiter;
+       }
+       return down_interruptible(limiter) < 0 ? NULL : limiter;
+}
+
+/*
+ * Release a call slot.
+ */
+static void rxrpc_put_call_slot(struct rxrpc_call *call)
+{
+       struct semaphore *limiter = &rxrpc_call_limiter;
+
+       if (test_bit(RXRPC_CALL_KERNEL, &call->flags))
+               limiter = &rxrpc_kernel_call_limiter;
+       up(limiter);
+}
+
 /*
  * Set up a call for the given parameters.
  * - Called with the socket lock held, which it must release.
@@ -225,15 +258,21 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx,
 {
        struct rxrpc_call *call, *xcall;
        struct rxrpc_net *rxnet;
+       struct semaphore *limiter;
        struct rb_node *parent, **pp;
        const void *here = __builtin_return_address(0);
        int ret;
 
        _enter("%p,%lx", rx, p->user_call_ID);
 
+       limiter = rxrpc_get_call_slot(p, gfp);
+       if (!limiter)
+               return ERR_PTR(-ERESTARTSYS);
+
        call = rxrpc_alloc_client_call(rx, srx, gfp, debug_id);
        if (IS_ERR(call)) {
                release_sock(&rx->sk);
+               up(limiter);
                _leave(" = %ld", PTR_ERR(call));
                return call;
        }
@@ -243,6 +282,8 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx,
        trace_rxrpc_call(call->debug_id, rxrpc_call_new_client,
                         atomic_read(&call->usage),
                         here, (const void *)p->user_call_ID);
+       if (p->kernel)
+               __set_bit(RXRPC_CALL_KERNEL, &call->flags);
 
        /* We need to protect a partially set up call against the user as we
         * will be acting outside the socket lock.
@@ -471,6 +512,8 @@ void rxrpc_release_call(struct rxrpc_sock *rx, struct rxrpc_call *call)
                BUG();
        spin_unlock_bh(&call->lock);
 
+       rxrpc_put_call_slot(call);
+
        del_timer_sync(&call->timer);
 
        /* Make sure we don't get any more notifications */