net: dsa: felix: check the 32-bit PSFP stats against overflow
The Felix PSFP counters suffer from the same problem as the ocelot ndo_get_stats64 ones - they are 32-bit, so they can easily overflow and this can easily go undetected. Add a custom hook in ocelot_check_stats_work() through which driver specific actions can be taken, and update the stats for the existing PSFP filters from that hook. Previously, vsc9959_psfp_filter_add() and vsc9959_psfp_filter_del() were serialized with respect to each other via rtnl_lock(). However, with the new entry point into &psfp->sfi_list coming from the periodic worker, we now need an explicit mutex to serialize access to these lists. We used to keep a struct felix_stream_filter_counters on stack, through which vsc9959_psfp_stats_get() - a FLOW_CLS_STATS callback - would retrieve data from vsc9959_psfp_counters_get(). We need to become smarter about that in 3 ways: - we need to keep a persistent set of counters for each stream instead of keeping them on stack - we need to promote those counters from u32 to u64, and create a procedure that properly keeps 64-bit counters. Since we clear the hardware counters anyway, and we poll every 2 seconds, a simple increment of a u64 counter with a u32 value will perfectly do the job. - FLOW_CLS_STATS also expect incremental counters, so we also need to zeroize our u64 counters every time sch_flower calls us Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
96980ff7c2
commit
25027c8409
|
@ -2046,7 +2046,15 @@ struct felix_stream {
|
||||||
u32 ssid;
|
u32 ssid;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct felix_stream_filter_counters {
|
||||||
|
u64 match;
|
||||||
|
u64 not_pass_gate;
|
||||||
|
u64 not_pass_sdu;
|
||||||
|
u64 red;
|
||||||
|
};
|
||||||
|
|
||||||
struct felix_stream_filter {
|
struct felix_stream_filter {
|
||||||
|
struct felix_stream_filter_counters stats;
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
refcount_t refcount;
|
refcount_t refcount;
|
||||||
u32 index;
|
u32 index;
|
||||||
|
@ -2061,13 +2069,6 @@ struct felix_stream_filter {
|
||||||
u32 maxsdu;
|
u32 maxsdu;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct felix_stream_filter_counters {
|
|
||||||
u32 match;
|
|
||||||
u32 not_pass_gate;
|
|
||||||
u32 not_pass_sdu;
|
|
||||||
u32 red;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct felix_stream_gate {
|
struct felix_stream_gate {
|
||||||
u32 index;
|
u32 index;
|
||||||
u8 enable;
|
u8 enable;
|
||||||
|
@ -2571,31 +2572,6 @@ static void vsc9959_psfp_sgi_table_del(struct ocelot *ocelot,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vsc9959_psfp_counters_get(struct ocelot *ocelot, u32 index,
|
|
||||||
struct felix_stream_filter_counters *counters)
|
|
||||||
{
|
|
||||||
mutex_lock(&ocelot->stat_view_lock);
|
|
||||||
|
|
||||||
ocelot_rmw(ocelot, SYS_STAT_CFG_STAT_VIEW(index),
|
|
||||||
SYS_STAT_CFG_STAT_VIEW_M,
|
|
||||||
SYS_STAT_CFG);
|
|
||||||
|
|
||||||
counters->match = ocelot_read(ocelot, SYS_COUNT_SF_MATCHING_FRAMES);
|
|
||||||
counters->not_pass_gate = ocelot_read(ocelot,
|
|
||||||
SYS_COUNT_SF_NOT_PASSING_FRAMES);
|
|
||||||
counters->not_pass_sdu = ocelot_read(ocelot,
|
|
||||||
SYS_COUNT_SF_NOT_PASSING_SDU);
|
|
||||||
counters->red = ocelot_read(ocelot, SYS_COUNT_SF_RED_FRAMES);
|
|
||||||
|
|
||||||
/* Clear the PSFP counter. */
|
|
||||||
ocelot_write(ocelot,
|
|
||||||
SYS_STAT_CFG_STAT_VIEW(index) |
|
|
||||||
SYS_STAT_CFG_STAT_CLEAR_SHOT(0x10),
|
|
||||||
SYS_STAT_CFG);
|
|
||||||
|
|
||||||
mutex_unlock(&ocelot->stat_view_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int vsc9959_psfp_filter_add(struct ocelot *ocelot, int port,
|
static int vsc9959_psfp_filter_add(struct ocelot *ocelot, int port,
|
||||||
struct flow_cls_offload *f)
|
struct flow_cls_offload *f)
|
||||||
{
|
{
|
||||||
|
@ -2620,6 +2596,8 @@ static int vsc9959_psfp_filter_add(struct ocelot *ocelot, int port,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutex_lock(&psfp->lock);
|
||||||
|
|
||||||
flow_action_for_each(i, a, &f->rule->action) {
|
flow_action_for_each(i, a, &f->rule->action) {
|
||||||
switch (a->id) {
|
switch (a->id) {
|
||||||
case FLOW_ACTION_GATE:
|
case FLOW_ACTION_GATE:
|
||||||
|
@ -2661,6 +2639,7 @@ static int vsc9959_psfp_filter_add(struct ocelot *ocelot, int port,
|
||||||
sfi.maxsdu = a->police.mtu;
|
sfi.maxsdu = a->police.mtu;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
mutex_unlock(&psfp->lock);
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2730,6 +2709,8 @@ static int vsc9959_psfp_filter_add(struct ocelot *ocelot, int port,
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&psfp->lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
@ -2739,6 +2720,8 @@ err:
|
||||||
if (sfi.fm_valid)
|
if (sfi.fm_valid)
|
||||||
ocelot_vcap_policer_del(ocelot, sfi.fmid);
|
ocelot_vcap_policer_del(ocelot, sfi.fmid);
|
||||||
|
|
||||||
|
mutex_unlock(&psfp->lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2746,18 +2729,22 @@ static int vsc9959_psfp_filter_del(struct ocelot *ocelot,
|
||||||
struct flow_cls_offload *f)
|
struct flow_cls_offload *f)
|
||||||
{
|
{
|
||||||
struct felix_stream *stream, tmp, *stream_entry;
|
struct felix_stream *stream, tmp, *stream_entry;
|
||||||
|
struct ocelot_psfp_list *psfp = &ocelot->psfp;
|
||||||
static struct felix_stream_filter *sfi;
|
static struct felix_stream_filter *sfi;
|
||||||
struct ocelot_psfp_list *psfp;
|
|
||||||
|
|
||||||
psfp = &ocelot->psfp;
|
mutex_lock(&psfp->lock);
|
||||||
|
|
||||||
stream = vsc9959_stream_table_get(&psfp->stream_list, f->cookie);
|
stream = vsc9959_stream_table_get(&psfp->stream_list, f->cookie);
|
||||||
if (!stream)
|
if (!stream) {
|
||||||
|
mutex_unlock(&psfp->lock);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
sfi = vsc9959_psfp_sfi_table_get(&psfp->sfi_list, stream->sfid);
|
sfi = vsc9959_psfp_sfi_table_get(&psfp->sfi_list, stream->sfid);
|
||||||
if (!sfi)
|
if (!sfi) {
|
||||||
|
mutex_unlock(&psfp->lock);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
if (sfi->sg_valid)
|
if (sfi->sg_valid)
|
||||||
vsc9959_psfp_sgi_table_del(ocelot, sfi->sgid);
|
vsc9959_psfp_sgi_table_del(ocelot, sfi->sgid);
|
||||||
|
@ -2783,27 +2770,83 @@ static int vsc9959_psfp_filter_del(struct ocelot *ocelot,
|
||||||
stream_entry->ports);
|
stream_entry->ports);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&psfp->lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void vsc9959_update_sfid_stats(struct ocelot *ocelot,
|
||||||
|
struct felix_stream_filter *sfi)
|
||||||
|
{
|
||||||
|
struct felix_stream_filter_counters *s = &sfi->stats;
|
||||||
|
u32 match, not_pass_gate, not_pass_sdu, red;
|
||||||
|
u32 sfid = sfi->index;
|
||||||
|
|
||||||
|
lockdep_assert_held(&ocelot->stat_view_lock);
|
||||||
|
|
||||||
|
ocelot_rmw(ocelot, SYS_STAT_CFG_STAT_VIEW(sfid),
|
||||||
|
SYS_STAT_CFG_STAT_VIEW_M,
|
||||||
|
SYS_STAT_CFG);
|
||||||
|
|
||||||
|
match = ocelot_read(ocelot, SYS_COUNT_SF_MATCHING_FRAMES);
|
||||||
|
not_pass_gate = ocelot_read(ocelot, SYS_COUNT_SF_NOT_PASSING_FRAMES);
|
||||||
|
not_pass_sdu = ocelot_read(ocelot, SYS_COUNT_SF_NOT_PASSING_SDU);
|
||||||
|
red = ocelot_read(ocelot, SYS_COUNT_SF_RED_FRAMES);
|
||||||
|
|
||||||
|
/* Clear the PSFP counter. */
|
||||||
|
ocelot_write(ocelot,
|
||||||
|
SYS_STAT_CFG_STAT_VIEW(sfid) |
|
||||||
|
SYS_STAT_CFG_STAT_CLEAR_SHOT(0x10),
|
||||||
|
SYS_STAT_CFG);
|
||||||
|
|
||||||
|
s->match += match;
|
||||||
|
s->not_pass_gate += not_pass_gate;
|
||||||
|
s->not_pass_sdu += not_pass_sdu;
|
||||||
|
s->red += red;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Caller must hold &ocelot->stat_view_lock */
|
||||||
|
static void vsc9959_update_stats(struct ocelot *ocelot)
|
||||||
|
{
|
||||||
|
struct ocelot_psfp_list *psfp = &ocelot->psfp;
|
||||||
|
struct felix_stream_filter *sfi;
|
||||||
|
|
||||||
|
mutex_lock(&psfp->lock);
|
||||||
|
|
||||||
|
list_for_each_entry(sfi, &psfp->sfi_list, list)
|
||||||
|
vsc9959_update_sfid_stats(ocelot, sfi);
|
||||||
|
|
||||||
|
mutex_unlock(&psfp->lock);
|
||||||
|
}
|
||||||
|
|
||||||
static int vsc9959_psfp_stats_get(struct ocelot *ocelot,
|
static int vsc9959_psfp_stats_get(struct ocelot *ocelot,
|
||||||
struct flow_cls_offload *f,
|
struct flow_cls_offload *f,
|
||||||
struct flow_stats *stats)
|
struct flow_stats *stats)
|
||||||
{
|
{
|
||||||
struct felix_stream_filter_counters counters;
|
struct ocelot_psfp_list *psfp = &ocelot->psfp;
|
||||||
struct ocelot_psfp_list *psfp;
|
struct felix_stream_filter_counters *s;
|
||||||
|
static struct felix_stream_filter *sfi;
|
||||||
struct felix_stream *stream;
|
struct felix_stream *stream;
|
||||||
|
|
||||||
psfp = &ocelot->psfp;
|
|
||||||
stream = vsc9959_stream_table_get(&psfp->stream_list, f->cookie);
|
stream = vsc9959_stream_table_get(&psfp->stream_list, f->cookie);
|
||||||
if (!stream)
|
if (!stream)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
vsc9959_psfp_counters_get(ocelot, stream->sfid, &counters);
|
sfi = vsc9959_psfp_sfi_table_get(&psfp->sfi_list, stream->sfid);
|
||||||
|
if (!sfi)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
stats->pkts = counters.match;
|
mutex_lock(&ocelot->stat_view_lock);
|
||||||
stats->drops = counters.not_pass_gate + counters.not_pass_sdu +
|
|
||||||
counters.red;
|
vsc9959_update_sfid_stats(ocelot, sfi);
|
||||||
|
|
||||||
|
s = &sfi->stats;
|
||||||
|
stats->pkts = s->match;
|
||||||
|
stats->drops = s->not_pass_gate + s->not_pass_sdu + s->red;
|
||||||
|
|
||||||
|
memset(s, 0, sizeof(*s));
|
||||||
|
|
||||||
|
mutex_unlock(&ocelot->stat_view_lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2815,6 +2858,7 @@ static void vsc9959_psfp_init(struct ocelot *ocelot)
|
||||||
INIT_LIST_HEAD(&psfp->stream_list);
|
INIT_LIST_HEAD(&psfp->stream_list);
|
||||||
INIT_LIST_HEAD(&psfp->sfi_list);
|
INIT_LIST_HEAD(&psfp->sfi_list);
|
||||||
INIT_LIST_HEAD(&psfp->sgi_list);
|
INIT_LIST_HEAD(&psfp->sgi_list);
|
||||||
|
mutex_init(&psfp->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* When using cut-through forwarding and the egress port runs at a higher data
|
/* When using cut-through forwarding and the egress port runs at a higher data
|
||||||
|
@ -2913,6 +2957,7 @@ static const struct ocelot_ops vsc9959_ops = {
|
||||||
.psfp_stats_get = vsc9959_psfp_stats_get,
|
.psfp_stats_get = vsc9959_psfp_stats_get,
|
||||||
.cut_through_fwd = vsc9959_cut_through_fwd,
|
.cut_through_fwd = vsc9959_cut_through_fwd,
|
||||||
.tas_clock_adjust = vsc9959_tas_clock_adjust,
|
.tas_clock_adjust = vsc9959_tas_clock_adjust,
|
||||||
|
.update_stats = vsc9959_update_stats,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct felix_info felix_info_vsc9959 = {
|
static const struct felix_info felix_info_vsc9959 = {
|
||||||
|
|
|
@ -1934,6 +1934,9 @@ static void ocelot_check_stats_work(struct work_struct *work)
|
||||||
spin_unlock(&ocelot->stats_lock);
|
spin_unlock(&ocelot->stats_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!err && ocelot->ops->update_stats)
|
||||||
|
ocelot->ops->update_stats(ocelot);
|
||||||
|
|
||||||
mutex_unlock(&ocelot->stat_view_lock);
|
mutex_unlock(&ocelot->stat_view_lock);
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
|
|
|
@ -729,6 +729,7 @@ struct ocelot_ops {
|
||||||
struct flow_stats *stats);
|
struct flow_stats *stats);
|
||||||
void (*cut_through_fwd)(struct ocelot *ocelot);
|
void (*cut_through_fwd)(struct ocelot *ocelot);
|
||||||
void (*tas_clock_adjust)(struct ocelot *ocelot);
|
void (*tas_clock_adjust)(struct ocelot *ocelot);
|
||||||
|
void (*update_stats)(struct ocelot *ocelot);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ocelot_vcap_policer {
|
struct ocelot_vcap_policer {
|
||||||
|
@ -766,6 +767,8 @@ struct ocelot_psfp_list {
|
||||||
struct list_head stream_list;
|
struct list_head stream_list;
|
||||||
struct list_head sfi_list;
|
struct list_head sfi_list;
|
||||||
struct list_head sgi_list;
|
struct list_head sgi_list;
|
||||||
|
/* Serialize access to the lists */
|
||||||
|
struct mutex lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ocelot_sb {
|
enum ocelot_sb {
|
||||||
|
|
Loading…
Reference in New Issue