Message ID | 20220217105652.14451-4-simon.horman@corigine.com (mailing list archive) |
---|---|
State | Superseded |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | nfp: flow-independent tc action hardware offload | expand |
On Thu, 17 Feb 2022 11:56:49 +0100 Simon Horman wrote: > From: Baowen Zheng <baowen.zheng@corigine.com> > > Add a hash table to store meter table. > > This meter table will also be used by flower action. > +static struct nfp_meter_entry * > +nfp_flower_add_meter_entry(struct nfp_app *app, u32 meter_id) > +{ > + struct nfp_meter_entry *meter_entry = NULL; > + struct nfp_flower_priv *priv = app->priv; > + > + meter_entry = rhashtable_lookup_fast(&priv->meter_table, > + &meter_id, > + stats_meter_table_params); > + unnecessary new line > + if (meter_entry) > + return meter_entry; > + > + meter_entry = kzalloc(sizeof(*meter_entry), GFP_ATOMIC); why is this ATOMIC? > + if (!meter_entry) > + goto err; > + > + meter_entry->meter_id = meter_id; > + meter_entry->used = jiffies; > + if (rhashtable_insert_fast(&priv->meter_table, &meter_entry->ht_node, > + stats_meter_table_params)) { > + goto err_free_meter_entry; > + } unnecessary brackets > + priv->qos_rate_limiters++; > + if (priv->qos_rate_limiters == 1) > + schedule_delayed_work(&priv->qos_stats_work, > + NFP_FL_QOS_UPDATE); > + return meter_entry; > + > +err_free_meter_entry: > + kfree(meter_entry); > +err: don't jump to return, just return directly instead of a goto > + return NULL; > +} > + > +static void nfp_flower_del_meter_entry(struct nfp_app *app, u32 meter_id) > +{ > + struct nfp_meter_entry *meter_entry = NULL; > + struct nfp_flower_priv *priv = app->priv; > + > + meter_entry = rhashtable_lookup_fast(&priv->meter_table, &meter_id, > + stats_meter_table_params); > + unnecessary nl > + if (meter_entry) { flip condition and return early > + rhashtable_remove_fast(&priv->meter_table, > + &meter_entry->ht_node, > + stats_meter_table_params); > + kfree(meter_entry); > + priv->qos_rate_limiters--; > + if (!priv->qos_rate_limiters) > + cancel_delayed_work_sync(&priv->qos_stats_work); > + } > +} > + > +int nfp_flower_setup_meter_entry(struct nfp_app *app, > + const struct flow_action_entry *action, > + enum nfp_meter_op op, > + u32 meter_id) > +{ > + struct nfp_flower_priv *fl_priv = app->priv; > + struct nfp_meter_entry *meter_entry = NULL; > + int err = 0; > + > + mutex_lock(&fl_priv->meter_stats_lock); > + > + switch (op) { > + case NFP_METER_DEL: > + nfp_flower_del_meter_entry(app, meter_id); > + goto ret; try to avoid naming labels with common variable names, exit_unlock would be most in line with the style of the driver here. > + case NFP_METER_ADD: > + meter_entry = nfp_flower_add_meter_entry(app, meter_id); > + break; > + default: why default and not use _SET? > + meter_entry = nfp_flower_search_meter_entry(app, meter_id); > + break; > + } > + > + if (!meter_entry) { > + err = -ENOMEM; > + goto ret; > + } > + > + if (!action) { > + err = -EINVAL; > + goto ret; > + } defensive programming is discouraged in the kernel, please drop the action check if it can't happen in practice > + if (action->police.rate_bytes_ps > 0) { > + meter_entry->bps = true; > + meter_entry->rate = action->police.rate_bytes_ps; > + meter_entry->burst = action->police.burst; > + } else { > + meter_entry->bps = false; > + meter_entry->rate = action->police.rate_pkt_ps; > + meter_entry->burst = action->police.burst_pkt; > + } > +ret: > + mutex_unlock(&fl_priv->meter_stats_lock); > + return err; > +} > + > +int nfp_init_meter_table(struct nfp_app *app) > +{ > + struct nfp_flower_priv *priv = app->priv; > + > + return rhashtable_init(&priv->meter_table, &stats_meter_table_params); > +} missing nl > static int > nfp_act_install_actions(struct nfp_app *app, struct flow_offload_action *fl_act, > struct netlink_ext_ack *extack)
On February 18, 2022 12:47 PM, Jakub wrote: >On Thu, 17 Feb 2022 11:56:49 +0100 Simon Horman wrote: >> From: Baowen Zheng <baowen.zheng@corigine.com> >> >> Add a hash table to store meter table. >> >> This meter table will also be used by flower action. > >> +static struct nfp_meter_entry * >> +nfp_flower_add_meter_entry(struct nfp_app *app, u32 meter_id) { >> + struct nfp_meter_entry *meter_entry = NULL; >> + struct nfp_flower_priv *priv = app->priv; >> + >> + meter_entry = rhashtable_lookup_fast(&priv->meter_table, >> + &meter_id, >> + stats_meter_table_params); >> + > >unnecessary new line Thanks, will make the change in V2 patch. > >> + if (meter_entry) >> + return meter_entry; >> + >> + meter_entry = kzalloc(sizeof(*meter_entry), GFP_ATOMIC); > >why is this ATOMIC? Previously, we use spin_lock to protect the meter entry, so we use the But now we change the mutex so it will make sense to use GFP_KERNEL memory alloc. We will make the change in V2 patch. Thanks > >> + if (!meter_entry) >> + goto err; >> + >> + meter_entry->meter_id = meter_id; >> + meter_entry->used = jiffies; >> + if (rhashtable_insert_fast(&priv->meter_table, &meter_entry- >>ht_node, >> + stats_meter_table_params)) { >> + goto err_free_meter_entry; >> + } > >unnecessary brackets Thanks, will make the change in V2 patch. > >> + priv->qos_rate_limiters++; >> + if (priv->qos_rate_limiters == 1) >> + schedule_delayed_work(&priv->qos_stats_work, >> + NFP_FL_QOS_UPDATE); >> + return meter_entry; >> + >> +err_free_meter_entry: >> + kfree(meter_entry); >> +err: > >don't jump to return, just return directly instead of a goto > >> + return NULL; >> +} >> + >> +static void nfp_flower_del_meter_entry(struct nfp_app *app, u32 >> +meter_id) { >> + struct nfp_meter_entry *meter_entry = NULL; >> + struct nfp_flower_priv *priv = app->priv; >> + >> + meter_entry = rhashtable_lookup_fast(&priv->meter_table, >&meter_id, >> + stats_meter_table_params); >> + > >unnecessary nl Thanks, will make the change in V2 patch. > >> + if (meter_entry) { > >flip condition and return early Thanks, will make the change in V2 patch. > >> + rhashtable_remove_fast(&priv->meter_table, >> + &meter_entry->ht_node, >> + stats_meter_table_params); >> + kfree(meter_entry); >> + priv->qos_rate_limiters--; >> + if (!priv->qos_rate_limiters) >> + cancel_delayed_work_sync(&priv->qos_stats_work); >> + } >> +} >> + >> +int nfp_flower_setup_meter_entry(struct nfp_app *app, >> + const struct flow_action_entry *action, >> + enum nfp_meter_op op, >> + u32 meter_id) >> +{ >> + struct nfp_flower_priv *fl_priv = app->priv; >> + struct nfp_meter_entry *meter_entry = NULL; >> + int err = 0; >> + >> + mutex_lock(&fl_priv->meter_stats_lock); >> + >> + switch (op) { >> + case NFP_METER_DEL: >> + nfp_flower_del_meter_entry(app, meter_id); >> + goto ret; > >try to avoid naming labels with common variable names, exit_unlock would be >most in line with the style of the driver here. Thanks, will make the change in V2 patch. > >> + case NFP_METER_ADD: >> + meter_entry = nfp_flower_add_meter_entry(app, meter_id); >> + break; >> + default: > >why default and not use _SET? Actually, we will check if the meter entry exists and add in NFP_METER_ADD, so we do not use the NFP_METER_SET. Maybe we will need to delete the NFP_METER_SET definition to omit the confusion. Thanks > >> + meter_entry = nfp_flower_search_meter_entry(app, >meter_id); >> + break; >> + } >> + >> + if (!meter_entry) { >> + err = -ENOMEM; >> + goto ret; >> + } >> + >> + if (!action) { >> + err = -EINVAL; >> + goto ret; >> + } > >defensive programming is discouraged in the kernel, please drop the action >check if it can't happen in practice Thanks, will make the change in V2 patch. > >> + if (action->police.rate_bytes_ps > 0) { >> + meter_entry->bps = true; >> + meter_entry->rate = action->police.rate_bytes_ps; >> + meter_entry->burst = action->police.burst; >> + } else { >> + meter_entry->bps = false; >> + meter_entry->rate = action->police.rate_pkt_ps; >> + meter_entry->burst = action->police.burst_pkt; >> + } >> +ret: >> + mutex_unlock(&fl_priv->meter_stats_lock); >> + return err; >> +} >> + >> +int nfp_init_meter_table(struct nfp_app *app) { >> + struct nfp_flower_priv *priv = app->priv; >> + >> + return rhashtable_init(&priv->meter_table, >> +&stats_meter_table_params); } > >missing nl Thanks, will make the change in V2 patch. > >> static int >> nfp_act_install_actions(struct nfp_app *app, struct flow_offload_action >*fl_act, >> struct netlink_ext_ack *extack)
diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h index a880f7684600..0c28e3414b7f 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.h +++ b/drivers/net/ethernet/netronome/nfp/flower/main.h @@ -194,6 +194,8 @@ struct nfp_fl_internal_ports { * @qos_stats_work: Workqueue for qos stats processing * @qos_rate_limiters: Current active qos rate limiters * @qos_stats_lock: Lock on qos stats updates + * @meter_stats_lock: Lock on meter stats updates + * @meter_table: Hash table used to store the meter table * @pre_tun_rule_cnt: Number of pre-tunnel rules offloaded * @merge_table: Hash table to store merged flows * @ct_zone_table: Hash table used to store the different zones @@ -231,6 +233,8 @@ struct nfp_flower_priv { struct delayed_work qos_stats_work; unsigned int qos_rate_limiters; spinlock_t qos_stats_lock; /* Protect the qos stats */ + struct mutex meter_stats_lock; /* Protect the meter stats */ + struct rhashtable meter_table; int pre_tun_rule_cnt; struct rhashtable merge_table; struct rhashtable ct_zone_table; @@ -377,6 +381,32 @@ struct nfp_fl_stats_frame { __be64 stats_cookie; }; +struct nfp_meter_stats_entry { + u64 pkts; + u64 bytes; + u64 drops; +}; + +struct nfp_meter_entry { + struct rhash_head ht_node; + u32 meter_id; + bool bps; + u32 rate; + u32 burst; + u64 used; + struct nfp_meter_stats { + u64 update; + struct nfp_meter_stats_entry curr; + struct nfp_meter_stats_entry prev; + } stats; +}; + +enum nfp_meter_op { + NFP_METER_ADD, + NFP_METER_SET, + NFP_METER_DEL, +}; + static inline bool nfp_flower_internal_port_can_offload(struct nfp_app *app, struct net_device *netdev) @@ -575,6 +605,12 @@ nfp_flower_update_merge_stats(struct nfp_app *app, int nfp_setup_tc_act_offload(struct nfp_app *app, struct flow_offload_action *fl_act); +int nfp_init_meter_table(struct nfp_app *app); + int nfp_flower_offload_one_police(struct nfp_app *app, bool ingress, bool pps, u32 id, u32 rate, u32 burst); +int nfp_flower_setup_meter_entry(struct nfp_app *app, + const struct flow_action_entry *action, + enum nfp_meter_op op, + u32 meter_id); #endif diff --git a/drivers/net/ethernet/netronome/nfp/flower/qos_conf.c b/drivers/net/ethernet/netronome/nfp/flower/qos_conf.c index 3ec63217fb19..f9f9e506b303 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/qos_conf.c +++ b/drivers/net/ethernet/netronome/nfp/flower/qos_conf.c @@ -1,7 +1,11 @@ // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) /* Copyright (C) 2019 Netronome Systems, Inc. */ +#include <linux/hash.h> +#include <linux/hashtable.h> +#include <linux/jhash.h> #include <linux/math64.h> +#include <linux/vmalloc.h> #include <net/pkt_cls.h> #include <net/pkt_sched.h> @@ -440,6 +444,9 @@ void nfp_flower_qos_init(struct nfp_app *app) struct nfp_flower_priv *fl_priv = app->priv; spin_lock_init(&fl_priv->qos_stats_lock); + mutex_init(&fl_priv->meter_stats_lock); + nfp_init_meter_table(app); + INIT_DELAYED_WORK(&fl_priv->qos_stats_work, &update_stats_cache); } @@ -478,6 +485,128 @@ int nfp_flower_setup_qos_offload(struct nfp_app *app, struct net_device *netdev, /* offload tc action, currently only for tc police */ +static const struct rhashtable_params stats_meter_table_params = { + .key_offset = offsetof(struct nfp_meter_entry, meter_id), + .head_offset = offsetof(struct nfp_meter_entry, ht_node), + .key_len = sizeof(u32), +}; + +static struct nfp_meter_entry * +nfp_flower_search_meter_entry(struct nfp_app *app, u32 meter_id) +{ + struct nfp_flower_priv *priv = app->priv; + + return rhashtable_lookup_fast(&priv->meter_table, &meter_id, + stats_meter_table_params); +} + +static struct nfp_meter_entry * +nfp_flower_add_meter_entry(struct nfp_app *app, u32 meter_id) +{ + struct nfp_meter_entry *meter_entry = NULL; + struct nfp_flower_priv *priv = app->priv; + + meter_entry = rhashtable_lookup_fast(&priv->meter_table, + &meter_id, + stats_meter_table_params); + + if (meter_entry) + return meter_entry; + + meter_entry = kzalloc(sizeof(*meter_entry), GFP_ATOMIC); + if (!meter_entry) + goto err; + + meter_entry->meter_id = meter_id; + meter_entry->used = jiffies; + if (rhashtable_insert_fast(&priv->meter_table, &meter_entry->ht_node, + stats_meter_table_params)) { + goto err_free_meter_entry; + } + priv->qos_rate_limiters++; + if (priv->qos_rate_limiters == 1) + schedule_delayed_work(&priv->qos_stats_work, + NFP_FL_QOS_UPDATE); + return meter_entry; + +err_free_meter_entry: + kfree(meter_entry); +err: + return NULL; +} + +static void nfp_flower_del_meter_entry(struct nfp_app *app, u32 meter_id) +{ + struct nfp_meter_entry *meter_entry = NULL; + struct nfp_flower_priv *priv = app->priv; + + meter_entry = rhashtable_lookup_fast(&priv->meter_table, &meter_id, + stats_meter_table_params); + + if (meter_entry) { + rhashtable_remove_fast(&priv->meter_table, + &meter_entry->ht_node, + stats_meter_table_params); + kfree(meter_entry); + priv->qos_rate_limiters--; + if (!priv->qos_rate_limiters) + cancel_delayed_work_sync(&priv->qos_stats_work); + } +} + +int nfp_flower_setup_meter_entry(struct nfp_app *app, + const struct flow_action_entry *action, + enum nfp_meter_op op, + u32 meter_id) +{ + struct nfp_flower_priv *fl_priv = app->priv; + struct nfp_meter_entry *meter_entry = NULL; + int err = 0; + + mutex_lock(&fl_priv->meter_stats_lock); + + switch (op) { + case NFP_METER_DEL: + nfp_flower_del_meter_entry(app, meter_id); + goto ret; + case NFP_METER_ADD: + meter_entry = nfp_flower_add_meter_entry(app, meter_id); + break; + default: + meter_entry = nfp_flower_search_meter_entry(app, meter_id); + break; + } + + if (!meter_entry) { + err = -ENOMEM; + goto ret; + } + + if (!action) { + err = -EINVAL; + goto ret; + } + + if (action->police.rate_bytes_ps > 0) { + meter_entry->bps = true; + meter_entry->rate = action->police.rate_bytes_ps; + meter_entry->burst = action->police.burst; + } else { + meter_entry->bps = false; + meter_entry->rate = action->police.rate_pkt_ps; + meter_entry->burst = action->police.burst_pkt; + } +ret: + mutex_unlock(&fl_priv->meter_stats_lock); + return err; +} + +int nfp_init_meter_table(struct nfp_app *app) +{ + struct nfp_flower_priv *priv = app->priv; + + return rhashtable_init(&priv->meter_table, &stats_meter_table_params); +} static int nfp_act_install_actions(struct nfp_app *app, struct flow_offload_action *fl_act, struct netlink_ext_ack *extack) @@ -514,10 +643,13 @@ nfp_act_install_actions(struct nfp_app *app, struct flow_offload_action *fl_act, } if (rate != 0) { + meter_id = action->hw_index; + if (nfp_flower_setup_meter_entry(app, action, NFP_METER_ADD, meter_id)) + continue; + pps = false; if (action->police.rate_pkt_ps > 0) pps = true; - meter_id = action->hw_index; nfp_flower_offload_one_police(app, false, pps, meter_id, rate, burst); add = true; @@ -534,9 +666,11 @@ static int nfp_act_remove_actions(struct nfp_app *app, struct flow_offload_action *fl_act, struct netlink_ext_ack *extack) { + struct nfp_meter_entry *meter_entry = NULL; struct nfp_police_config *config; struct sk_buff *skb; u32 meter_id; + bool pps; /*delete qos associate data for this interface */ if (fl_act->id != FLOW_ACTION_POLICE) { @@ -546,6 +680,14 @@ nfp_act_remove_actions(struct nfp_app *app, struct flow_offload_action *fl_act, } meter_id = fl_act->index; + meter_entry = nfp_flower_search_meter_entry(app, meter_id); + if (!meter_entry) { + NL_SET_ERR_MSG_MOD(extack, + "no meter entry when delete the action index.\n"); + return -ENOENT; + } + pps = !meter_entry->bps; + skb = nfp_flower_cmsg_alloc(app, sizeof(struct nfp_police_config), NFP_FLOWER_CMSG_TYPE_QOS_DEL, GFP_KERNEL); if (!skb) @@ -555,7 +697,11 @@ nfp_act_remove_actions(struct nfp_app *app, struct flow_offload_action *fl_act, memset(config, 0, sizeof(struct nfp_police_config)); config->head.flags_opts = cpu_to_be32(NFP_FL_QOS_METER); config->head.meter_id = cpu_to_be32(meter_id); + if (pps) + config->head.flags_opts |= cpu_to_be32(NFP_FL_QOS_PPS); + nfp_ctrl_tx(app->ctrl, skb); + nfp_flower_setup_meter_entry(app, NULL, NFP_METER_DEL, meter_id); return 0; }