Re: [PATCH net-next v12 10/13] tcp: RX path for devmem TCP

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

 



On 6/21/24 21:31, Mina Almasry wrote:
On Mon, Jun 17, 2024 at 9:36 AM Pavel Begunkov <asml.silence@xxxxxxxxx> wrote:

On 6/13/24 02:35, Mina Almasry wrote:

The pages awaiting freeing are stored in the newly added
sk->sk_user_frags, and each page passed to userspace is get_page()'d.
This reference is dropped once the userspace indicates that it is
done reading this page.  All pages are released when the socket is
destroyed.

One small concern is that if the pool gets destroyed (i.e.
page_pool_destroy) before sockets holding netiov, page pool will
semi-busily poll until the sockets die or such and will spam with
pr_warn(). E.g. when a user drops the nl but leaks data sockets
and continues with its userspace business. You can probably do
it in a loop and create dozens of such pending
page_pool_release_retry().


Yes, true, but this is not really an issue with netiovs per se, it's a
quirk with the page_pool in general. If a non-devmem page_pool is

True, devmem is just a new convenient way of doing that ...

destroyed while there are pages waiting in the receive queues to be
recvmsg'd, the behavior you described happens anyway AFAIU.

Jakub did some work to improve this. IIRC he disabled the regular
warning and he reparents the orphan page_pools so they appear in the
stats of his netlink API.

Since this is behavior already applying to pages, I did not seek to
improve it as I add devmem support, I just retain it. We could improve
it in a separate patchset, but I do not see this behavior as a
critical issue really, especially since the alarming pr_warn has been
removed.

... fair enough. I haven't noticed it being removed, but was
thinking to suggest to conver to ratelimited.

+static int tcp_xa_pool_refill(struct sock *sk, struct tcp_xa_pool *p,
+                           unsigned int max_frags)
+{
+     int err, k;
+
+     if (p->idx < p->max)
+             return 0;
+
+     xa_lock_bh(&sk->sk_user_frags);
+
+     tcp_xa_pool_commit_locked(sk, p);
+
+     for (k = 0; k < max_frags; k++) {
+             err = __xa_alloc(&sk->sk_user_frags, &p->tokens[k],
+                              XA_ZERO_ENTRY, xa_limit_31b, GFP_KERNEL);
+             if (err)
+                     break;
+     }
+
+     xa_unlock_bh(&sk->sk_user_frags);
+
+     p->max = k;
+     p->idx = 0;
+     return k ? 0 : err;
+}

Personally, I'd prefer this optimisation to be in a separate patch,
especially since there is some degree of hackiness to it.



To be honest this optimization is very necessary from my POV. We ran
into real production problems due to the excessive locking when we use
regular xa_alloc(), and Eric implemented this optimization to resolve
that. I simply squashed the optimization for this upstream series.

If absolutely necessary I can refactor it into a separate patch or
carry the optimization locally, but this seems like a problem everyone
looking to use devmem TCP will re-discover, so probably worth just
having here?

I specifically mean how it's split into patches within the set. It'd
have been easier to review, understand for people looking it up in
history and so on. However, not insisting on changing it now, might
be safer to leave it alone

+             /* if remaining_len is not satisfied yet, we need to go to the
+              * next frag in the frag_list to satisfy remaining_len.
+              */
+             skb = skb_shinfo(skb)->frag_list ?: skb->next;
+
+             offset = offset - start;

It's an offset into the current skb, isn't it? Wouldn't
offset = 0; be less confusing?


Seems so, AFAICT. Let me try to apply this and see if it trips up any tests.

+     } while (skb);
+
+     if (remaining_len) {
+             err = -EFAULT;
+             goto out;
+     }

Having data left is not a fault,

I think it is. The caller of tcp_recvmsg_dmabuf() expects all of
remaining_len to be used up, otherwise it messes up with the math in
the caller. __skb_datagram_iter(), which is the equivalent to this one
for pages, regards having left over data as a fault and also returns
-EFAULT, AFAICT.

I mean "Having data left is not a fault, not receiving
anything is", and you correctly return a partial result
if that was the case.

and to get here you
need to get an skb with no data left, which shouldn't
happen. Seems like everything you need is covered by
the "!sent" check below.


I think we can get here if we run out of skbs with data, no?

IIRC the caller clamps it so that it's within the skb with
its frags. Well, safer to have the check, I agree. It's just
looked a bit odd since the value is complementary to @sent,
but I guess it's just a way to propagate -EFAULT.

@@ -2503,6 +2504,15 @@ static void tcp_md5sig_info_free_rcu(struct rcu_head *head)
   void tcp_v4_destroy_sock(struct sock *sk)
   {
       struct tcp_sock *tp = tcp_sk(sk);
+     __maybe_unused unsigned long index;
+     __maybe_unused void *netmem;

How about adding a function to get rid of __maybe_unused?.

static void sock_release_devmem_frags() {
#ifdef PP
         unsigned index;
         ...
#endif PP
}


Will do.

Also, even though you wire it up for TCP, since ->sk_user_frags
is in struct sock I'd expect the release to be somewhere in the
generic sock path like __sk_destruct(), and same for init.
Perhpas, it's better to leave it for later.

--
Pavel Begunkov




[Index of Archives]     [Kernel Development]     [DCCP]     [Linux ARM Development]     [Linux]     [Photo]     [Yosemite Help]     [Linux ARM Kernel]     [Linux SCSI]     [Linux x86_64]     [Linux Hams]

  Powered by Linux