Re: [ovs-dev] [PATCH v2] netdev-offload-tc: Preserve tc statistics when flow gets modified.

2023-01-13 Thread Eelco Chaudron


On 12 Jan 2023, at 18:29, Michael Santana wrote:

> On 12/15/22 09:52, Eelco Chaudron wrote:
>> When a flow gets modified, i.e. the actions are changes, the tc layer will
>> remove, and re-add the flow. This is causing all the counters to be reset.
>>
>> This patch will remember the previous tc counters and adjust any requests
>> for statistics. This is done in a similar way as the rte_flow implementation.
>>
>> It also updates the check_pkt_len tc test to purge the flows, so we do
>> not use updated tc flows, with their counters.
>>
>> Signed-off-by: Eelco Chaudron 
>> ---
>> Please note that for now two copies of the test case exists, but I will clean
>> this up once this gets applied by submitting a new revision of the
>> 'tests: Add system-traffic.at tests to check-offloads' series.
>>
>> v2: Do not update the stats->used, as in terse dump they should be 0.
>>
>>   lib/netdev-offload-tc.c  |   92 
>> --
>>   lib/tc.h |1
>>   tests/system-offloads-traffic.at |   65 +--
>>   tests/system-traffic.at  |   64 ++
>>   4 files changed, 201 insertions(+), 21 deletions(-)
>>
>> diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c
>> index ce7f8ad97..03c25fc38 100644
>> --- a/lib/netdev-offload-tc.c
>> +++ b/lib/netdev-offload-tc.c
>> @@ -97,6 +97,12 @@ static int netdev_tc_parse_nl_actions(struct netdev 
>> *netdev,
>> bool *recirc_act, bool more_actions,
>> struct tc_action **need_jump_update);
>>  +static void parse_tc_flower_to_stats(struct tc_flower *flower,
>> + struct dpif_flow_stats *stats);
>> +
>> +static int get_ufid_adjust_stats(const ovs_u128 *ufid,
>> + struct dpif_flow_stats *stats);
>> +
>>   static bool
>>   is_internal_port(const char *type)
>>   {
>> @@ -193,6 +199,9 @@ static struct ovs_mutex ufid_lock = 
>> OVS_MUTEX_INITIALIZER;
>>* @ufid: ufid assigned to the flow
>>* @id: tc filter id (tcf_id)
>>* @netdev: netdev associated with the tc rule
>> + * @adjust_stats: When flow gets updated with new actions, we need to adjust
>> + *the reported stats to include previous values as the 
>> hardware
>> + *rule is removed and re-added. This stats copy is used for 
>> it.
>>*/
>>   struct ufid_tc_data {
>>   struct hmap_node ufid_to_tc_node;
>> @@ -200,6 +209,7 @@ struct ufid_tc_data {
>>   ovs_u128 ufid;
>>   struct tcf_id id;
>>   struct netdev *netdev;
>> +struct dpif_flow_stats adjust_stats;
>>   };
>>static void
>> @@ -233,12 +243,36 @@ del_ufid_tc_mapping(const ovs_u128 *ufid)
>>   ovs_mutex_unlock(_lock);
>>   }
>>  +static void
>> +netdev_tc_adjust_stats(struct dpif_flow_stats *stats,
>> +   struct dpif_flow_stats *adjust_stats)
>> +{
>> +/* Do not try to restore the stats->used, as in terse
>> + * mode dumps we will always report them as 0. */
>> +stats->n_bytes += adjust_stats->n_bytes;
>> +stats->n_packets += adjust_stats->n_packets;
> Why not also update the other members of dpif_flow_stats?

tcp_flags is not used by TC, so no need to use it. Will update the comment 
above.

>> +}
>> +
>>   /* Wrapper function to delete filter and ufid tc mapping */
>>   static int
>> -del_filter_and_ufid_mapping(struct tcf_id *id, const ovs_u128 *ufid)
>> +del_filter_and_ufid_mapping(struct tcf_id *id, const ovs_u128 *ufid,
>> +struct dpif_flow_stats *stats)
>>   {
>> +struct tc_flower flower;
>>   int err;
>>  +if (stats) {
>> +memset(stats, 0, sizeof *stats);
>> +if (!tc_get_flower(id, )) {
>> +struct dpif_flow_stats adjust_stats;
>> +
>> +parse_tc_flower_to_stats(, stats);
>> +if (!get_ufid_adjust_stats(ufid, _stats)) {
>> +netdev_tc_adjust_stats(stats, _stats);
>> +}
>> +}
>> +}
>> +
>>   err = tc_del_filter(id);
>>   if (!err) {
>>   del_ufid_tc_mapping(ufid);
>> @@ -249,7 +283,7 @@ del_filter_and_ufid_mapping(struct tcf_id *id, const 
>> ovs_u128 *ufid)
>>   /* Add ufid entry to ufid_to_tc hashmap. */
>>   static void
>>   add_ufid_tc_mapping(struct netdev *netdev, const ovs_u128 *ufid,
>> -struct tcf_id *id)
>> +struct tcf_id *id, struct dpif_flow_stats *stats)
>>   {
>>   struct ufid_tc_data *new_data = xzalloc(sizeof *new_data);
>>   size_t ufid_hash = hash_bytes(ufid, sizeof *ufid, 0);
>> @@ -261,6 +295,9 @@ add_ufid_tc_mapping(struct netdev *netdev, const 
>> ovs_u128 *ufid,
>>   new_data->ufid = *ufid;
>>   new_data->id = *id;
>>   new_data->netdev = netdev_ref(netdev);
>> +if (stats) {
>> +new_data->adjust_stats = *stats;
>> +}
> lgtm
>>ovs_mutex_lock(_lock);
>>   

Re: [ovs-dev] [PATCH v2] netdev-offload-tc: Preserve tc statistics when flow gets modified.

2023-01-12 Thread Michael Santana




On 12/15/22 09:52, Eelco Chaudron wrote:

When a flow gets modified, i.e. the actions are changes, the tc layer will
remove, and re-add the flow. This is causing all the counters to be reset.

This patch will remember the previous tc counters and adjust any requests
for statistics. This is done in a similar way as the rte_flow implementation.

It also updates the check_pkt_len tc test to purge the flows, so we do
not use updated tc flows, with their counters.

Signed-off-by: Eelco Chaudron 
---
Please note that for now two copies of the test case exists, but I will clean
this up once this gets applied by submitting a new revision of the
'tests: Add system-traffic.at tests to check-offloads' series.

v2: Do not update the stats->used, as in terse dump they should be 0.

  lib/netdev-offload-tc.c  |   92 --
  lib/tc.h |1
  tests/system-offloads-traffic.at |   65 +--
  tests/system-traffic.at  |   64 ++
  4 files changed, 201 insertions(+), 21 deletions(-)

diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c
index ce7f8ad97..03c25fc38 100644
--- a/lib/netdev-offload-tc.c
+++ b/lib/netdev-offload-tc.c
@@ -97,6 +97,12 @@ static int netdev_tc_parse_nl_actions(struct netdev *netdev,
bool *recirc_act, bool more_actions,
struct tc_action **need_jump_update);
  
+static void parse_tc_flower_to_stats(struct tc_flower *flower,

+ struct dpif_flow_stats *stats);
+
+static int get_ufid_adjust_stats(const ovs_u128 *ufid,
+ struct dpif_flow_stats *stats);
+
  static bool
  is_internal_port(const char *type)
  {
@@ -193,6 +199,9 @@ static struct ovs_mutex ufid_lock = OVS_MUTEX_INITIALIZER;
   * @ufid: ufid assigned to the flow
   * @id: tc filter id (tcf_id)
   * @netdev: netdev associated with the tc rule
+ * @adjust_stats: When flow gets updated with new actions, we need to adjust
+ *the reported stats to include previous values as the hardware
+ *rule is removed and re-added. This stats copy is used for it.
   */
  struct ufid_tc_data {
  struct hmap_node ufid_to_tc_node;
@@ -200,6 +209,7 @@ struct ufid_tc_data {
  ovs_u128 ufid;
  struct tcf_id id;
  struct netdev *netdev;
+struct dpif_flow_stats adjust_stats;
  };
  
  static void

@@ -233,12 +243,36 @@ del_ufid_tc_mapping(const ovs_u128 *ufid)
  ovs_mutex_unlock(_lock);
  }
  
+static void

+netdev_tc_adjust_stats(struct dpif_flow_stats *stats,
+   struct dpif_flow_stats *adjust_stats)
+{
+/* Do not try to restore the stats->used, as in terse
+ * mode dumps we will always report them as 0. */
+stats->n_bytes += adjust_stats->n_bytes;
+stats->n_packets += adjust_stats->n_packets;

Why not also update the other members of dpif_flow_stats?

+}
+
  /* Wrapper function to delete filter and ufid tc mapping */
  static int
-del_filter_and_ufid_mapping(struct tcf_id *id, const ovs_u128 *ufid)
+del_filter_and_ufid_mapping(struct tcf_id *id, const ovs_u128 *ufid,
+struct dpif_flow_stats *stats)
  {
+struct tc_flower flower;
  int err;
  
+if (stats) {

+memset(stats, 0, sizeof *stats);
+if (!tc_get_flower(id, )) {
+struct dpif_flow_stats adjust_stats;
+
+parse_tc_flower_to_stats(, stats);
+if (!get_ufid_adjust_stats(ufid, _stats)) {
+netdev_tc_adjust_stats(stats, _stats);
+}
+}
+}
+
  err = tc_del_filter(id);
  if (!err) {
  del_ufid_tc_mapping(ufid);
@@ -249,7 +283,7 @@ del_filter_and_ufid_mapping(struct tcf_id *id, const 
ovs_u128 *ufid)
  /* Add ufid entry to ufid_to_tc hashmap. */
  static void
  add_ufid_tc_mapping(struct netdev *netdev, const ovs_u128 *ufid,
-struct tcf_id *id)
+struct tcf_id *id, struct dpif_flow_stats *stats)
  {
  struct ufid_tc_data *new_data = xzalloc(sizeof *new_data);
  size_t ufid_hash = hash_bytes(ufid, sizeof *ufid, 0);
@@ -261,6 +295,9 @@ add_ufid_tc_mapping(struct netdev *netdev, const ovs_u128 
*ufid,
  new_data->ufid = *ufid;
  new_data->id = *id;
  new_data->netdev = netdev_ref(netdev);
+if (stats) {
+new_data->adjust_stats = *stats;
+}

lgtm
  
  ovs_mutex_lock(_lock);

  hmap_insert(_to_tc, _data->ufid_to_tc_node, ufid_hash);
@@ -292,6 +329,25 @@ get_ufid_tc_mapping(const ovs_u128 *ufid, struct tcf_id 
*id)
  return ENOENT;
  }


/* Get adjust_stats from ufid_to_tc hashmap.
 *
 * Returns 0 if successful and fills stats with adjust_stats.
 * Otherwise returns the error.
 */


+static int
+get_ufid_adjust_stats(const ovs_u128 *ufid, struct dpif_flow_stats *stats)
+{
+size_t ufid_hash = hash_bytes(ufid, sizeof *ufid, 0);
+

[ovs-dev] [PATCH v2] netdev-offload-tc: Preserve tc statistics when flow gets modified.

2022-12-15 Thread Eelco Chaudron
When a flow gets modified, i.e. the actions are changes, the tc layer will
remove, and re-add the flow. This is causing all the counters to be reset.

This patch will remember the previous tc counters and adjust any requests
for statistics. This is done in a similar way as the rte_flow implementation.

It also updates the check_pkt_len tc test to purge the flows, so we do
not use updated tc flows, with their counters.

Signed-off-by: Eelco Chaudron 
---
Please note that for now two copies of the test case exists, but I will clean
this up once this gets applied by submitting a new revision of the
'tests: Add system-traffic.at tests to check-offloads' series.

v2: Do not update the stats->used, as in terse dump they should be 0.

 lib/netdev-offload-tc.c  |   92 --
 lib/tc.h |1 
 tests/system-offloads-traffic.at |   65 +--
 tests/system-traffic.at  |   64 ++
 4 files changed, 201 insertions(+), 21 deletions(-)

diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c
index ce7f8ad97..03c25fc38 100644
--- a/lib/netdev-offload-tc.c
+++ b/lib/netdev-offload-tc.c
@@ -97,6 +97,12 @@ static int netdev_tc_parse_nl_actions(struct netdev *netdev,
   bool *recirc_act, bool more_actions,
   struct tc_action **need_jump_update);
 
+static void parse_tc_flower_to_stats(struct tc_flower *flower,
+ struct dpif_flow_stats *stats);
+
+static int get_ufid_adjust_stats(const ovs_u128 *ufid,
+ struct dpif_flow_stats *stats);
+
 static bool
 is_internal_port(const char *type)
 {
@@ -193,6 +199,9 @@ static struct ovs_mutex ufid_lock = OVS_MUTEX_INITIALIZER;
  * @ufid: ufid assigned to the flow
  * @id: tc filter id (tcf_id)
  * @netdev: netdev associated with the tc rule
+ * @adjust_stats: When flow gets updated with new actions, we need to adjust
+ *the reported stats to include previous values as the hardware
+ *rule is removed and re-added. This stats copy is used for it.
  */
 struct ufid_tc_data {
 struct hmap_node ufid_to_tc_node;
@@ -200,6 +209,7 @@ struct ufid_tc_data {
 ovs_u128 ufid;
 struct tcf_id id;
 struct netdev *netdev;
+struct dpif_flow_stats adjust_stats;
 };
 
 static void
@@ -233,12 +243,36 @@ del_ufid_tc_mapping(const ovs_u128 *ufid)
 ovs_mutex_unlock(_lock);
 }
 
+static void
+netdev_tc_adjust_stats(struct dpif_flow_stats *stats,
+   struct dpif_flow_stats *adjust_stats)
+{
+/* Do not try to restore the stats->used, as in terse
+ * mode dumps we will always report them as 0. */
+stats->n_bytes += adjust_stats->n_bytes;
+stats->n_packets += adjust_stats->n_packets;
+}
+
 /* Wrapper function to delete filter and ufid tc mapping */
 static int
-del_filter_and_ufid_mapping(struct tcf_id *id, const ovs_u128 *ufid)
+del_filter_and_ufid_mapping(struct tcf_id *id, const ovs_u128 *ufid,
+struct dpif_flow_stats *stats)
 {
+struct tc_flower flower;
 int err;
 
+if (stats) {
+memset(stats, 0, sizeof *stats);
+if (!tc_get_flower(id, )) {
+struct dpif_flow_stats adjust_stats;
+
+parse_tc_flower_to_stats(, stats);
+if (!get_ufid_adjust_stats(ufid, _stats)) {
+netdev_tc_adjust_stats(stats, _stats);
+}
+}
+}
+
 err = tc_del_filter(id);
 if (!err) {
 del_ufid_tc_mapping(ufid);
@@ -249,7 +283,7 @@ del_filter_and_ufid_mapping(struct tcf_id *id, const 
ovs_u128 *ufid)
 /* Add ufid entry to ufid_to_tc hashmap. */
 static void
 add_ufid_tc_mapping(struct netdev *netdev, const ovs_u128 *ufid,
-struct tcf_id *id)
+struct tcf_id *id, struct dpif_flow_stats *stats)
 {
 struct ufid_tc_data *new_data = xzalloc(sizeof *new_data);
 size_t ufid_hash = hash_bytes(ufid, sizeof *ufid, 0);
@@ -261,6 +295,9 @@ add_ufid_tc_mapping(struct netdev *netdev, const ovs_u128 
*ufid,
 new_data->ufid = *ufid;
 new_data->id = *id;
 new_data->netdev = netdev_ref(netdev);
+if (stats) {
+new_data->adjust_stats = *stats;
+}
 
 ovs_mutex_lock(_lock);
 hmap_insert(_to_tc, _data->ufid_to_tc_node, ufid_hash);
@@ -292,6 +329,25 @@ get_ufid_tc_mapping(const ovs_u128 *ufid, struct tcf_id 
*id)
 return ENOENT;
 }
 
+static int
+get_ufid_adjust_stats(const ovs_u128 *ufid, struct dpif_flow_stats *stats)
+{
+size_t ufid_hash = hash_bytes(ufid, sizeof *ufid, 0);
+struct ufid_tc_data *data;
+
+ovs_mutex_lock(_lock);
+HMAP_FOR_EACH_WITH_HASH (data, ufid_to_tc_node, ufid_hash, _to_tc) {
+if (ovs_u128_equals(*ufid, data->ufid)) {
+*stats = data->adjust_stats;
+ovs_mutex_unlock(_lock);
+return 0;
+}
+}
+