On Sat, 5 Dec 2020 02:43:15 +0200 Vladimir Oltean wrote: > Currently ocelot_set_rx_mode calls ocelot_mact_learn directly, which has > a very nice ocelot_mact_wait_for_completion at the end. Introduced in > commit 639c1b2625af ("net: mscc: ocelot: Register poll timeout should be > wall time not attempts"), this function uses readx_poll_timeout which > triggers a lot of lockdep warnings and is also dangerous to use from > atomic context, potentially leading to lockups and panics. > > Steen Hegelund added a poll timeout of 100 ms for checking the MAC > table, a duration which is clearly absurd to poll in atomic context. > So we need to defer the MAC table access to process context, which we do > via a dynamically allocated workqueue which contains all there is to > know about the MAC table operation it has to do. > > Signed-off-by: Vladimir Oltean <vladimir.olt...@nxp.com> > Reviewed-by: Florian Fainelli <f.faine...@gmail.com> > --- > Changes in v3: > - Dropped Fixes tag and retargeted to net-next > - Dropped get_device/put_device since they don't offer real protection > - Now allocating a private ordered workqueue which is drained on unbind > to avoid accessing freed memory > > Changes in v2: > - Added Fixes tag (it won't backport that far, but anyway) > - Using get_device and put_device to avoid racing with unbind > > drivers/net/ethernet/mscc/ocelot.c | 5 ++ > drivers/net/ethernet/mscc/ocelot_net.c | 81 +++++++++++++++++++++++++- > include/soc/mscc/ocelot.h | 2 + > 3 files changed, 85 insertions(+), 3 deletions(-) > > diff --git a/drivers/net/ethernet/mscc/ocelot.c > b/drivers/net/ethernet/mscc/ocelot.c > index abea8dd2b0cb..b9626eec8db6 100644 > --- a/drivers/net/ethernet/mscc/ocelot.c > +++ b/drivers/net/ethernet/mscc/ocelot.c > @@ -1513,6 +1513,10 @@ int ocelot_init(struct ocelot *ocelot) > if (!ocelot->stats_queue) > return -ENOMEM; > > + ocelot->owq = alloc_ordered_workqueue("ocelot-owq", WQ_MEM_RECLAIM);
Why MEM_RECLAIM ? > + if (!ocelot->owq) > + return -ENOMEM; I don't think you can pass NULL to destroy_workqueue() so IDK how this code does error handling (freeing of ocelot->stats_queue if owq fails). > INIT_LIST_HEAD(&ocelot->multicast); > INIT_LIST_HEAD(&ocelot->pgids); > ocelot_mact_init(ocelot); > @@ -1619,6 +1623,7 @@ void ocelot_deinit(struct ocelot *ocelot) > { > cancel_delayed_work(&ocelot->stats_work); > destroy_workqueue(ocelot->stats_queue); > + destroy_workqueue(ocelot->owq); > mutex_destroy(&ocelot->stats_lock); > } > +static int ocelot_enqueue_mact_action(struct ocelot *ocelot, > + const struct ocelot_mact_work_ctx *ctx) > +{ > + struct ocelot_mact_work_ctx *w = kmalloc(sizeof(*w), GFP_ATOMIC); > + > + if (!w) > + return -ENOMEM; > + > + memcpy(w, ctx, sizeof(*w)); kmemdup()?