On 23/02/2025 9:29, Roi Dayan wrote:
>
>
> On 20/02/2025 12:04, Eelco Chaudron wrote:
>>
>>
>> On 20 Feb 2025, at 10:55, Roi Dayan wrote:
>>
>>> On 18/02/2025 14:51, Eelco Chaudron wrote:
>>>>
>>>>
>>>> On 18 Feb 2025, at 12:03, Roi Dayan wrote:
>>>>
>>>>> From: Dima Chumak <[email protected]>
>>>>>
>>>>> The 'fdb/show' command now supports machine-readable JSON output in
>>>>> addition to the plain-text output for humans. An example would be:
>>>>>
>>>>> ovs-appctl --format=json --pretty fdb/show br-phy
>>>>> [
>>>>> {
>>>>> "age": "static",
>>>>> "mac": "e4:8c:07:08:00:02",
>>>>> "port": 2,
>>>>> "vlan": 0
>>>>> },
>>>>> {
>>>>> "age": 14,
>>>>> "mac": "e4:8c:07:08:00:03",
>>>>> "port": 3,
>>>>> "vlan": 0
>>>>> }
>>>>> ]
>>>>>
>>>>> Signed-off-by: Dima Chumak <[email protected]>
>>>>> Acked-by: Roi Dayan <[email protected]>
>>>>> ---
>>>>>
>>>>> Notes:
>>>>> v3
>>>>> - Fix assert. No need to call json_destroy() after
>>>>> unixctl_command_reply_json() as it takes
>>>>> ownership of the allocated body.
>>>>>
>>>>> v2
>>>>> - Fix sparse check error.
>>>>> - Add case checking fdb/show json output.
>>>>>
>>>>> ofproto/ofproto-dpif.c | 85 ++++++++++++++++++++++++++++++++++--------
>>>>> tests/ofproto-dpif.at | 10 +++++
>>>>> 2 files changed, 79 insertions(+), 16 deletions(-)
>>>>>
>>>>> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
>>>>> index bf43d5d4bc59..9926c016fb00 100644
>>>>> --- a/ofproto/ofproto-dpif.c
>>>>> +++ b/ofproto/ofproto-dpif.c
>>>>> @@ -6151,20 +6151,12 @@ ofbundle_get_a_port(const struct ofbundle *bundle)
>>>>> }
>>>>>
>>>>> static void
>>>>> -ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
>>>>> - const char *argv[], void *aux OVS_UNUSED)
>>>>> +ofproto_unixctl_fdb_show_text(const struct ofproto_dpif *ofproto,
>>>>> + struct ds *ds)
>>>>> {
>>>>> - struct ds ds = DS_EMPTY_INITIALIZER;
>>>>> - const struct ofproto_dpif *ofproto;
>>>>> const struct mac_entry *e;
>>>>>
>>>>> - ofproto = ofproto_dpif_lookup_by_name(argv[1]);
>>>>> - if (!ofproto) {
>>>>> - unixctl_command_reply_error(conn, "no such bridge");
>>>>> - return;
>>>>> - }
>>>>> -
>>>>> - ds_put_cstr(&ds, " port VLAN MAC Age\n");
>>>>> + ds_put_cstr(ds, " port VLAN MAC Age\n");
>>>>> ovs_rwlock_rdlock(&ofproto->ml->rwlock);
>>>>> LIST_FOR_EACH (e, lru_node, &ofproto->ml->lrus) {
>>>>> struct ofbundle *bundle = mac_entry_get_port(ofproto->ml, e);
>>>>> @@ -6173,17 +6165,78 @@ ofproto_unixctl_fdb_show(struct unixctl_conn
>>>>> *conn, int argc OVS_UNUSED,
>>>>>
>>>>> ofputil_port_to_string(ofbundle_get_a_port(bundle)->up.ofp_port,
>>>>> NULL, name, sizeof name);
>>>>> - ds_put_format(&ds, "%5s %4d "ETH_ADDR_FMT" ",
>>>>> + ds_put_format(ds, "%5s %4d "ETH_ADDR_FMT" ",
>>>>> name, e->vlan, ETH_ADDR_ARGS(e->mac));
>>>>> if (MAC_ENTRY_AGE_STATIC_ENTRY == age) {
>>>>> - ds_put_format(&ds, "static\n");
>>>>> + ds_put_format(ds, "static\n");
>>>>> } else {
>>>>> - ds_put_format(&ds, "%3d\n", age);
>>>>> + ds_put_format(ds, "%3d\n", age);
>>>>> }
>>>>> }
>>>>> ovs_rwlock_unlock(&ofproto->ml->rwlock);
>>>>> - unixctl_command_reply(conn, ds_cstr(&ds));
>>>>> - ds_destroy(&ds);
>>>>> +}
>>>>> +
>>>>> +static void
>>>>> +ofproto_unixctl_fdb_show_json(const struct ofproto_dpif *ofproto,
>>>>> + struct json **fdb_entries)
>>>>> +{
>>>>> + size_t num_entries = hmap_count(&ofproto->ml->table);
>>>>
>>>> This needs to happen under the rw lock, or else entries could be deleted
>>>> (added).
>>>
>>> you mean the entire thing to be under rwlock instead of rdlock ?
>>> but if we add what you suggested below with checking 'i' then isn't
>>> it ok now? we fill the array with mini between 'i' and num_entries.
>>
>> Sorry, yes the entire thing between the read lock. The hmap_count() should
>> be under the read lock anyway.
>> And if we assert, as suggested below, it should not be unlocked in between.
>>
>
> Hi sorry didn't fully understand the suggested change here :)
> I don't think we need to assert, checking 'i' was better.
> The hmap_count is just for allocating "enough" room to prepare the output.
> Using the read look and limiting to the min between i and num_entries
> should be enough. if item being removed/added during this the worst
> is we miss an output of an item which will appear in next dump.
> The text output also uses the same read lock.
> Shouldn't this be enough ? so I don't think the write lock is needed?
> Also just taking the hmap_count should be ok outside the read lock.
> Let me know what I miss or if you still think we should replace to rw lock
> and if to move hmap_count inside.
>
>
I sent v4 with the changes about the safety check and ilya comment on the json
output.
Can you please check and reply on v4 if you think the rw-lock change is still
relevant
thanks
>>>>
>>>>> + struct json **json_entries = NULL;
>>>>> + const struct mac_entry *entry;
>>>>> + int i = 0;
>>>>> +
>>>>> + if (!num_entries) {
>>>>> + goto done;
>>>>> + }
>>>>> +
>>>>> + json_entries = xmalloc(num_entries * sizeof *json_entries);
>>>>> + ovs_rwlock_rdlock(&ofproto->ml->rwlock);
>>>>> + LIST_FOR_EACH (entry, lru_node, &ofproto->ml->lrus) {
>>>>> + struct ofbundle *bundle = mac_entry_get_port(ofproto->ml, entry);
>>>>> + struct json *json_entry = json_object_create();
>>>>> + int age = mac_entry_age(ofproto->ml, entry);
>>>>> + long long port;
>>>>> +
>>>>> + port = (OVS_FORCE long long)
>>>>> ofbundle_get_a_port(bundle)->up.ofp_port;
>>>>> + json_object_put(json_entry, "port", json_integer_create(port));
>>>>> + json_object_put(json_entry, "vlan",
>>>>> json_integer_create(entry->vlan));
>>>>> + json_object_put_format(json_entry, "mac", ETH_ADDR_FMT,
>>>>> + ETH_ADDR_ARGS(entry->mac));
>>>>> + if (MAC_ENTRY_AGE_STATIC_ENTRY == age) {
>>>>> + json_object_put_string(json_entry, "age", "static");
>>>>> + } else {
>>>>> + json_object_put(json_entry, "age", json_integer_create(age));
>>>>> + }
>>>>> + json_entries[i++] = json_entry;
>>>>
>>>> Add a safety check here;
>>>> if (i >= json_entries) {
>>>> break;
>>>> }
>>>>
>>>
>>> added. prefered than hitting an assert just for showing some result.
>>>
>>>> or an ovs_assert() at the beginning of the loop.
>>>>
>>>>> + }
>>>>> + ovs_rwlock_unlock(&ofproto->ml->rwlock);
>>>>> +done:
>>>>> + *fdb_entries = json_array_create(json_entries, num_entries);
>>>>
>>>> For safety reasons, I would use ‘i’ here instead of ‘num_entries’ to make
>>>> sure we have no invalid pointers if, for some reason, the number of
>>>> entries added is less than the number initially calculated.
>>>>
>>>
>>> updated.
>>>
>>>>> +}
>>>>> +
>>>>> +static void
>>>>> +ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
>>>>> + const char *argv[] OVS_UNUSED, void *aux
>>>>> OVS_UNUSED)
>>>>> +{
>>>>> + const struct ofproto_dpif *ofproto =
>>>>> ofproto_dpif_lookup_by_name(argv[1]);
>>>>> +
>>>>> + if (!ofproto) {
>>>>> + unixctl_command_reply_error(conn, "no such bridge");
>>>>> + return;
>>>>> + }
>>>>> +
>>>>> + if (unixctl_command_get_output_format(conn) ==
>>>>> UNIXCTL_OUTPUT_FMT_JSON) {
>>>>> + struct json *fdb_entries;
>>>>> +
>>>>> + ofproto_unixctl_fdb_show_json(ofproto, &fdb_entries);
>>>>> + unixctl_command_reply_json(conn, fdb_entries);
>>>>> + } else {
>>>>> + struct ds ds = DS_EMPTY_INITIALIZER;
>>>>> +
>>>>> + ofproto_unixctl_fdb_show_text(ofproto, &ds);
>>>>> + unixctl_command_reply(conn, ds_cstr(&ds));
>>>>> + ds_destroy(&ds);
>>>>> + }
>>>>> }
>>>>>
>>>>> static void
>>>>> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
>>>>> index fa5f148b4c28..8a975e6ffd9c 100644
>>>>> --- a/tests/ofproto-dpif.at
>>>>> +++ b/tests/ofproto-dpif.at
>>>>> @@ -7085,6 +7085,16 @@ AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed
>>>>> 's/[[0-9]]\{1,\}$/?/'], [0], [d
>>>>> 3 0 50:54:00:00:00:05 ?
>>>>> ])
>>>>>
>>>>> +dnl Check json output.
>>>>> +AT_CHECK([ovs-appctl --format json --pretty fdb/show br0], [0], [dnl
>>>>> +[[
>>>>> + {
>>>>> + "age": 0,
>>>>> + "mac": "50:54:00:00:00:05",
>>>>> + "port": 3,
>>>>> + "vlan": 0}]]
>>>>> +])
>>>>> +
>>>>
>>>> If you run the tests enough times, some times the age is 1, and the test
>>>> fails. In addition I think we should move this to a place where we have at
>>>> least two entries.
>>>> For example:
>>>>
>>>> @@ -7110,6 +7100,22 @@ AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed
>>>> 's/[[0-9]]\{1,\}$/?/'], [0], [d
>>>> 1 0 50:54:00:00:00:06 ?
>>>> ])
>>>>
>>>> +dnl Check json output.
>>>> +AT_CHECK([ovs-appctl --format json --pretty fdb/show br0 \
>>>> + | sed 's/"age": [[0-9]]\+/"age": ?/g'], [0], [dnl
>>>> +[[
>>>> + {
>>>> + "age": ?,
>>>> + "mac": "50:54:00:00:00:05",
>>>> + "port": 3,
>>>> + "vlan": 0},
>>>> + {
>>>> + "age": ?,
>>>> + "mac": "50:54:00:00:00:06",
>>>> + "port": 1,
>>>> + "vlan": 0}]]
>>>> +])
>>>> +
>>>>
>>>
>>> ok updated.
>>>
>>>>> # Trace a packet arrival destined for the learned MAC.
>>>>> # (This will also learn a MAC.)
>>>>> OFPROTO_TRACE(
>>>>> --
>>>>> 2.21.0
>>>>
>>
>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev