#include <linux/bitfield.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
+#include <linux/workqueue.h>
 #include <net/dst_metadata.h>
 
 #include "main.h"
 #include "../nfpcore/nfp_cpp.h"
+#include "../nfp_net.h"
 #include "../nfp_net_repr.h"
 #include "./cmsg.h"
 
        rcu_read_unlock();
 }
 
-void nfp_flower_cmsg_rx(struct nfp_app *app, struct sk_buff *skb)
+static void
+nfp_flower_cmsg_process_one_rx(struct nfp_app *app, struct sk_buff *skb)
 {
        struct nfp_flower_cmsg_hdr *cmsg_hdr;
        enum nfp_flower_cmsg_type_port type;
 out:
        dev_kfree_skb_any(skb);
 }
+
+void nfp_flower_cmsg_process_rx(struct work_struct *work)
+{
+       struct nfp_flower_priv *priv;
+       struct sk_buff *skb;
+
+       priv = container_of(work, struct nfp_flower_priv, cmsg_work);
+
+       while ((skb = skb_dequeue(&priv->cmsg_skbs)))
+               nfp_flower_cmsg_process_one_rx(priv->nn->app, skb);
+}
+
+void nfp_flower_cmsg_rx(struct nfp_app *app, struct sk_buff *skb)
+{
+       struct nfp_flower_priv *priv = app->priv;
+
+       skb_queue_tail(&priv->cmsg_skbs, skb);
+       schedule_work(&priv->cmsg_work);
+}
 
                             unsigned int nbi, unsigned int nbi_port,
                             unsigned int phys_port);
 int nfp_flower_cmsg_portmod(struct nfp_repr *repr, bool carrier_ok);
+void nfp_flower_cmsg_process_rx(struct work_struct *work);
 void nfp_flower_cmsg_rx(struct nfp_app *app, struct sk_buff *skb);
 struct sk_buff *
 nfp_flower_cmsg_alloc(struct nfp_app *app, unsigned int size,
 
 static int nfp_flower_init(struct nfp_app *app)
 {
        const struct nfp_pf *pf = app->pf;
+       struct nfp_flower_priv *app_priv;
        u64 version;
        int err;
 
                return -EINVAL;
        }
 
-       app->priv = vzalloc(sizeof(struct nfp_flower_priv));
-       if (!app->priv)
+       app_priv = vzalloc(sizeof(struct nfp_flower_priv));
+       if (!app_priv)
                return -ENOMEM;
 
+       app->priv = app_priv;
+       skb_queue_head_init(&app_priv->cmsg_skbs);
+       INIT_WORK(&app_priv->cmsg_work, nfp_flower_cmsg_process_rx);
+
        err = nfp_flower_metadata_init(app);
        if (err)
                goto err_free_app_priv;
 
 static void nfp_flower_clean(struct nfp_app *app)
 {
+       struct nfp_flower_priv *app_priv = app->priv;
+
+       skb_queue_purge(&app_priv->cmsg_skbs);
+       flush_work(&app_priv->cmsg_work);
+
        nfp_flower_metadata_cleanup(app);
        vfree(app->priv);
        app->priv = NULL;
 
 #include <linux/time64.h>
 #include <linux/types.h>
 #include <net/pkt_cls.h>
+#include <linux/workqueue.h>
 
 struct net_device;
 struct nfp_app;
  * @mask_ids:          List of free mask ids
  * @mask_table:                Hash table used to store masks
  * @flow_table:                Hash table used to store flower rules
+ * @cmsg_work:         Workqueue for control messages processing
+ * @cmsg_skbs:         List of skbs for control message processing
  */
 struct nfp_flower_priv {
        struct nfp_net *nn;
        struct nfp_fl_mask_id mask_ids;
        DECLARE_HASHTABLE(mask_table, NFP_FLOWER_MASK_HASH_BITS);
        DECLARE_HASHTABLE(flow_table, NFP_FLOWER_HASH_BITS);
+       struct work_struct cmsg_work;
+       struct sk_buff_head cmsg_skbs;
 };
 
 struct nfp_fl_key_ls {