rxrpc: Rework local endpoint management
[linux-block.git] / net / rxrpc / af_rxrpc.c
index ba373caddbebbb51473ba04ede3cc26e639cbae7..c83c3c75d665730016b35bc2a6072f1f3d2b1a83 100644 (file)
@@ -102,6 +102,8 @@ static int rxrpc_validate_address(struct rxrpc_sock *rx,
 
        switch (srx->transport.family) {
        case AF_INET:
+               if (srx->transport_len < sizeof(struct sockaddr_in))
+                       return -EINVAL;
                _debug("INET: %x @ %pI4",
                       ntohs(srx->transport.sin.sin_port),
                       &srx->transport.sin.sin_addr);
@@ -835,12 +837,27 @@ static void __exit af_rxrpc_exit(void)
        rxrpc_destroy_all_calls();
        rxrpc_destroy_all_connections();
        rxrpc_destroy_all_transports();
-       rxrpc_destroy_all_locals();
 
        ASSERTCMP(atomic_read(&rxrpc_n_skbs), ==, 0);
 
+       /* We need to flush the scheduled work twice because the local endpoint
+        * records involve a work item in their destruction as they can only be
+        * destroyed from process context.  However, a connection may have a
+        * work item outstanding - and this will pin the local endpoint record
+        * until the connection goes away.
+        *
+        * Peers don't pin locals and calls pin sockets - which prevents the
+        * module from being unloaded - so we should only need two flushes.
+        */
        _debug("flush scheduled work");
        flush_workqueue(rxrpc_workqueue);
+       _debug("flush scheduled work 2");
+       flush_workqueue(rxrpc_workqueue);
+       _debug("synchronise RCU");
+       rcu_barrier();
+       _debug("destroy locals");
+       rxrpc_destroy_all_locals();
+
        remove_proc_entry("rxrpc_conns", init_net.proc_net);
        remove_proc_entry("rxrpc_calls", init_net.proc_net);
        destroy_workqueue(rxrpc_workqueue);