[I meant to cc netdev on this originally.  Added now, preserving entire
message below for context]

On Fri, Jun 05, 2020 at 05:12:05AM -0700, Matthew Wilcox wrote:
> While doing the XArray conversion, I came across your commit
> f16a4b26f31f95dddb12cf3c2390906a735203ae
> 
> synchronize_rcu() is kind of expensive on larger systems.  Is there a
> reason to call it instead of using RCU to free the socket?
> 
> I don't know whether I've set the flag in the right place, but maybe
> something like this would be a good idea?
> 
> @@ -663,13 +663,8 @@ static void qrtr_port_remove(struct qrtr_sock *ipc)
>         if (port == QRTR_PORT_CTRL)
>                 port = 0;
>  
> -       __sock_put(&ipc->sk);
> -
>         xa_erase(&qrtr_ports, port);
> -
> -       /* Ensure that if qrtr_port_lookup() did enter the RCU read section we
> -        * wait for it to up increment the refcount */
> -       synchronize_rcu();
> +       __sock_put(&ipc->sk);
>  }
>  
>  /* Assign port number to socket.
> @@ -749,6 +744,7 @@ static int __qrtr_bind(struct socket *sock,
>                 qrtr_port_remove(ipc);
>         ipc->us.sq_port = port;
>  
> +       sock_set_flag(sk, SOCK_RCU_FREE);
>         sock_reset_flag(sk, SOCK_ZAPPED);
>  
>         /* Notify all open ports about the new controller */

I realised this isn't sufficient.  If we want to eliminate the
synchronize_rcu() call, we need to have a 'try' variant of sock_hold().
It would look something like this:

static inline __must_check bool sock_try_get(struct sock *sk)
{
        return refcount_inc_not_zero(&sk->sk_refcnt);
}

and then ...

        rcu_read_lock();
        ipc = xa_load(&qrtr_ports, port);
        if (ipc && !sock_try_get(&ipc->sk))
                ipc = NULL;
        rcu_read_unlock();

If we wanted to be able to atomically replace a pointer in the qrtr_ports
array with another without ever seeing NULL in between, we'd want to
make this:

        rcu_read_lock():
        do {
                ipc = xa_load(&qrtr_ports, port);
        } while (ipc && !sock_try_get(&ipc->sk));
        rcu_read_unlock();

but as far as I can tell, we don't need to do that for qrtr.

Reply via email to