On 12 Apr 2022, at 11:09, Peng He wrote:

> David Marchand <david.march...@redhat.com> 于2022年4月12日周二 16:40写道:
>
>> On Thu, Apr 7, 2022 at 4:13 AM Peng He <xnhp0...@gmail.com> wrote:
>>>
>>> I understand there is no need to introduce a rcu api,
>>> but at least put these codes into a separate function ? :-)
>>
>> No strong opinion.
>>
>>
>>>> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
>>>> index 6601f2346..402cdcdf5 100644
>>>> --- a/ofproto/ofproto-dpif.c
>>>> +++ b/ofproto/ofproto-dpif.c
>>>> @@ -1805,6 +1805,7 @@ destruct(struct ofproto *ofproto_, bool del)
>>>>      struct rule_dpif *rule;
>>>>      struct oftable *table;
>>>>      struct ovs_list ams;
>>>> +    struct seq *seq;
>>>>
>>>>      ofproto->backer->need_revalidate = REV_RECONFIGURE;
>>>>      xlate_txn_start();
>>>> @@ -1848,6 +1849,14 @@ destruct(struct ofproto *ofproto_, bool del)
>>>>
>>>>      seq_destroy(ofproto->ams_seq);
>>>>
>>>> +    /* Wait for all the meter rules to be destroyed. */
>>>> +    seq = seq_create();
>>>> +    ovsrcu_synchronize();
>>>> +    seq_wait(seq, seq_read(seq));
>>>> +    ovsrcu_postpone__((void (*)(void *))seq_change, seq);
>>>>
>>>> +    poll_block();
>>>
>>>
>>> after poll block, we should check if seq is changed or not, other fds
>> will
>>> wake up block too. so we need a while loop to do the seq_wait + postpone
>> + check
>>> stuff.
>>>
>>>> +    seq_destroy(seq);
>>>> +
>>
>> If there can be other event nodes registered at this point, once you
>> leave poll_block() no event node is kept.
>>
>
> IMHO, this is how OVS event loop works.
>
> even you lose all other event nodes after the first poll_block returns,
> eventually (after you got seq changes) you are not blocking and running to
> the next
> poll_block point, and every modules in between will check fd in its XX_run
> function,
> if event is ready, do the work, when EAGAIN happens, the node is added back.
>
> all the previous lost events will be added back eventually.
>
> So it's ok to call poll_block anywhere. I think.
>
> Adding to this, poll_block -> timepoll -> poll can fail (though I am
>> not clear it is something that is supposed to happen).
>
>
> why fail?
>
> Once a first poll_block() returns, the only event that this code can
>> wait for is the seq changing.
>> There should be no need to wake up with a timeout, since there is no
>> useful work to do.
>>
>> Am I missing something else?
>>
>
> yes, timeout is for the RCU api, if used here, I agree no need to add
> timeout.


Sorry for joining in late in the conversation, but I do not see any harm in 
introducing the new rcu API. It will be way cleaner, and if we add some good 
documentation (and maybe a checkpatch warning), I think we should be ok.

However looking at the implementation from patch 1, I see two problems (well 
they are is actually one):

+
+static void
+ovsrcu_barrier_func(void *seq_)
+{
+    struct seq *seq = (struct seq*)seq_;
+    seq_change(seq);
+}
+
+bool
+ovsrcu_barrier(struct seq *seq, long long int timeout)
+{
+    /* first let all threads flush their cbset */
+    ovsrcu_synchronize();
+
+    /* then register a new cbset, ensure this cbset
+     * is at the tail of global listi
+     */
+    uint64_t seqno = seq_read(seq);
+    ovsrcu_postpone__(ovsrcu_barrier_func, (void*)seq);
+    long long int now = time_msec();
+    long long int deadline = now + timeout;
+
+    do {
+        seq_wait(seq, seqno);
+        poll_timer_wait_until(deadline);
+        poll_block();
+        now = time_msec(); /* update now */
+    } while (seqno == seq_read(seq) && now < deadline);
+
+    return !(seqno == seq_read(seq));
+}

1) I do not like that you have to supply the seq as a parameter. The API should 
not take any parameters if you ask me.
2) The timeout is a problem here, if the timeout happens you will call 
seq_destroy(), so the later invocation of ovsrcu_barrier_func() would access 
invalid memory.

So I guess the API should look something like this (not tested, but also 
changed comments ;):

+void
+ovsrcu_barrier()
+{
+    struct seq *seq = seq_create();
+
+    /* First let all threads flush their cbset’s. */
+    ovsrcu_synchronize();
+
+    /* Then register a new cbset, ensure this cbset
+     * is at the tail of the global list.
+     */
+    uint64_t seqno = seq_read(seq);
+    ovsrcu_postpone__(ovsrcu_barrier_func, (void*)seq);
+
+    do {
+        seq_wait(seq, seqno);
+        poll_block();
+    } while (seqno == seq_read(seq));
+
+    seq_destroy(seq);
+}

Which you can then just use as (see comment changes ;):

+    /* Wait for all the meter destroy work to finish. */
+    ovsrcu_barrier();
     close_dpif_backer(ofproto->backer, del);
 }


>>
>>
>> In the end, that would translate to:
>>
>> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
>> index 6601f23464..dd1b9e5138 100644
>> --- a/ofproto/ofproto-dpif.c
>> +++ b/ofproto/ofproto-dpif.c
>> @@ -1805,6 +1805,8 @@ destruct(struct ofproto *ofproto_, bool del)
>>      struct rule_dpif *rule;
>>      struct oftable *table;
>>      struct ovs_list ams;
>> +    struct seq *seq;
>> +    uint64_t seqno;
>>
>>      ofproto->backer->need_revalidate = REV_RECONFIGURE;
>>      xlate_txn_start();
>> @@ -1848,6 +1850,17 @@ destruct(struct ofproto *ofproto_, bool del)
>>
>>      seq_destroy(ofproto->ams_seq);
>>
>> +    /* Wait for all the meter rules to be destroyed. */
>> +    seq = seq_create();
>> +    seqno = seq_read();
>> +    ovsrcu_synchronize();
>> +    ovsrcu_postpone__((void (*)(void *))seq_change, seq);
>> +    do {
>> +        seq_wait(seq, seqno);
>> +        poll_block();
>> +    } while (seqno == seq_read(seq));
>> +    seq_destroy(seq);
>> +
>>      close_dpif_backer(ofproto->backer, del);
>>  }
>>
>>
>> --
>> David Marchand
>>
>>
>
> -- 
> hepeng
> _______________________________________________
> dev mailing list
> d...@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to