[PATCH v2] um: vector: Replace locks guarding queue depth with atomics
Anton Ivanov
anton.ivanov at kot-begemot.co.uk
Thu Jul 4 13:16:45 PDT 2024
On 04/07/2024 17:12, Johannes Berg wrote:
> On Thu, 2024-07-04 at 13:22 +0100, anton.ivanov at cambridgegreys.com
> wrote:
>> @@ -675,11 +657,20 @@ static void prep_queue_for_rx(struct vector_queue *qi)
>> struct vector_private *vp = netdev_priv(qi->dev);
>> struct mmsghdr *mmsg_vector = qi->mmsg_vector;
>> void **skbuff_vector = qi->skbuff_vector;
>> - int i;
>> + int i, queue_depth;
>> +
>> + queue_depth = atomic_read(&qi->queue_depth);
>>
>> - if (qi->queue_depth == 0)
>> + if (queue_depth == 0)
>> return;
>> - for (i = 0; i < qi->queue_depth; i++) {
>> +
>> + /* RX is always emptied 100% during each cycle, so we do not
>> + * want to do the tail wraparound math for it
>> + */
>> +
>> + qi->head = qi->tail = 0;
>> +
>> + for (i = 0; i < queue_depth; i++) {
>> /* it is OK if allocation fails - recvmmsg with NULL data in
>> * iov argument still performs an RX, just drops the packet
>> * This allows us stop faffing around with a "drop buffer"
>> @@ -689,7 +680,8 @@ static void prep_queue_for_rx(struct vector_queue *qi)
>> skbuff_vector++;
>> mmsg_vector++;
>> }
>> - qi->queue_depth = 0;
>> + wmb(); /* Ensure that RX processing elsewhere sees the changes */
>> + atomic_set(&qi->queue_depth, 0);
>> }
> I don't understand this.
>
> prep_queue_for_rx() is called by vector_mmsg_rx(), not in parallel or in
> a different thread or something, inside the NAPI polling. All it does is
> reset the queue to empty with all the SKBs allocated.
>
> After that, prep_queue_for_rx() calls uml_vector_recvmmsg() [1] which
> fills the SKBs, and then vector_mmsg_rx() itself consumes them by going
> over it and calling napi_gro_receive() (or whatever else).
>
> There's no parallelism here? The RX queue wouldn't even need locking or
> atomic instructions at all at this point, since it's just "refill
> buffers, fill buffers, release buffers to network stack".
>
> What am I missing?
You are not missing anything.
The rx and tx are using the same infra to map vectors to skbs arrays.
I can make the RX use a set of lockless and atomicless functions, but
this means duplicating some of the code.
>
>
> [1] btw, it should pass 'budget' to uml_vector_recvmmsg(), not the depth
> (qi->max_depth), and possibly even pass that to prep_queue_for_rx(), but
> that's a different patch.
Good point. Let's sort out the locking and parallelism first.
>
>> static struct vector_device *find_device(int n)
>> @@ -987,7 +979,7 @@ static int vector_mmsg_rx(struct vector_private *vp, int budget)
>> * many do we need to prep the next time prep_queue_for_rx() is called.
>> */
>>
>> - qi->queue_depth = packet_count;
>> + atomic_add(packet_count, &qi->queue_depth);
> and that also means my earlier analysis was just confused - there's no
> need for _add here, _set is fine - in fact even non-atomic would be
> fine.
>
>> @@ -1176,6 +1168,7 @@ static int vector_poll(struct napi_struct *napi, int budget)
>>
>> if ((vp->options & VECTOR_TX) != 0)
>> tx_enqueued = (vector_send(vp->tx_queue) > 0);
>> + spin_lock(&vp->rx_queue->head_lock);
>> if ((vp->options & VECTOR_RX) > 0)
>> err = vector_mmsg_rx(vp, budget);
>> else {
>> @@ -1183,6 +1176,7 @@ static int vector_poll(struct napi_struct *napi, int budget)
>> if (err > 0)
>> err = 1;
>> }
>> + spin_unlock(&vp->rx_queue->head_lock);
> that's needed for ... stats?
Yes, unfortunately.
I am reusing the lock.
And that one may be accessed in parallel :(
>
> johannes
>
>
--
Anton R. Ivanov
https://www.kot-begemot.co.uk/
More information about the linux-um
mailing list