struct nsim_fib_entry nexthops;
        struct rhashtable fib_rt_ht;
        struct list_head fib_rt_list;
-       spinlock_t fib_lock;    /* Protects hashtable and list */
+       struct mutex fib_lock; /* Protects hashtable and list */
        struct notifier_block nexthop_nb;
        struct rhashtable nexthop_ht;
        struct devlink *devlink;
+       struct work_struct fib_event_work;
+       struct list_head fib_event_queue;
+       spinlock_t fib_event_queue_lock; /* Protects fib event queue list */
 };
 
 struct nsim_fib_rt_key {
        struct fib6_info *rt;
 };
 
+struct nsim_fib6_event {
+       struct fib6_info **rt_arr;
+       unsigned int nrt6;
+};
+
+struct nsim_fib_event {
+       struct list_head list; /* node in fib queue */
+       union {
+               struct fib_entry_notifier_info fen_info;
+               struct nsim_fib6_event fib6_event;
+       };
+       struct nsim_fib_data *data;
+       unsigned long event;
+       int family;
+};
+
 static const struct rhashtable_params nsim_fib_rt_ht_params = {
        .key_offset = offsetof(struct nsim_fib_rt, key),
        .head_offset = offsetof(struct nsim_fib_rt, ht_node),
        return err;
 }
 
-static int nsim_fib_account(struct nsim_fib_entry *entry, bool add,
-                           struct netlink_ext_ack *extack)
+static int nsim_fib_account(struct nsim_fib_entry *entry, bool add)
 {
        int err = 0;
 
        if (add) {
-               if (!atomic64_add_unless(&entry->num, 1, entry->max)) {
+               if (!atomic64_add_unless(&entry->num, 1, entry->max))
                        err = -ENOSPC;
-                       NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
-               }
        } else {
                atomic64_dec_if_positive(&entry->num);
        }
 {
        struct nsim_fib4_rt *fib4_rt;
 
-       fib4_rt = kzalloc(sizeof(*fib4_rt), GFP_ATOMIC);
+       fib4_rt = kzalloc(sizeof(*fib4_rt), GFP_KERNEL);
        if (!fib4_rt)
                return NULL;
 
 }
 
 static int nsim_fib4_rt_add(struct nsim_fib_data *data,
-                           struct nsim_fib4_rt *fib4_rt,
-                           struct netlink_ext_ack *extack)
+                           struct nsim_fib4_rt *fib4_rt)
 {
        struct net *net = devlink_net(data->devlink);
        int err;
 
-       err = nsim_fib_account(&data->ipv4.fib, true, extack);
-       if (err)
-               return err;
-
        err = rhashtable_insert_fast(&data->fib_rt_ht,
                                     &fib4_rt->common.ht_node,
                                     nsim_fib_rt_ht_params);
-       if (err) {
-               NL_SET_ERR_MSG_MOD(extack, "Failed to insert IPv4 route");
+       if (err)
                goto err_fib_dismiss;
-       }
 
+       /* Simulate hardware programming latency. */
+       msleep(1);
        nsim_fib4_rt_hw_flags_set(net, fib4_rt, true);
 
        return 0;
 
 err_fib_dismiss:
-       nsim_fib_account(&data->ipv4.fib, false, extack);
+       /* Drop the accounting that was increased from the notification
+        * context when FIB_EVENT_ENTRY_REPLACE was triggered.
+        */
+       nsim_fib_account(&data->ipv4.fib, false);
        return err;
 }
 
 static int nsim_fib4_rt_replace(struct nsim_fib_data *data,
                                struct nsim_fib4_rt *fib4_rt,
-                               struct nsim_fib4_rt *fib4_rt_old,
-                               struct netlink_ext_ack *extack)
+                               struct nsim_fib4_rt *fib4_rt_old)
 {
        struct net *net = devlink_net(data->devlink);
        int err;
 
-       /* We are replacing a route, so no need to change the accounting. */
+       /* We are replacing a route, so need to remove the accounting which
+        * was increased when FIB_EVENT_ENTRY_REPLACE was triggered.
+        */
+       err = nsim_fib_account(&data->ipv4.fib, false);
+       if (err)
+               return err;
        err = rhashtable_replace_fast(&data->fib_rt_ht,
                                      &fib4_rt_old->common.ht_node,
                                      &fib4_rt->common.ht_node,
                                      nsim_fib_rt_ht_params);
-       if (err) {
-               NL_SET_ERR_MSG_MOD(extack, "Failed to replace IPv4 route");
+       if (err)
                return err;
-       }
 
+       msleep(1);
        nsim_fib4_rt_hw_flags_set(net, fib4_rt, true);
 
        nsim_fib4_rt_hw_flags_set(net, fib4_rt_old, false);
 static int nsim_fib4_rt_insert(struct nsim_fib_data *data,
                               struct fib_entry_notifier_info *fen_info)
 {
-       struct netlink_ext_ack *extack = fen_info->info.extack;
        struct nsim_fib4_rt *fib4_rt, *fib4_rt_old;
        int err;
 
 
        fib4_rt_old = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info);
        if (!fib4_rt_old)
-               err = nsim_fib4_rt_add(data, fib4_rt, extack);
+               err = nsim_fib4_rt_add(data, fib4_rt);
        else
-               err = nsim_fib4_rt_replace(data, fib4_rt, fib4_rt_old, extack);
+               err = nsim_fib4_rt_replace(data, fib4_rt, fib4_rt_old);
 
        if (err)
                nsim_fib4_rt_destroy(fib4_rt);
 static void nsim_fib4_rt_remove(struct nsim_fib_data *data,
                                const struct fib_entry_notifier_info *fen_info)
 {
-       struct netlink_ext_ack *extack = fen_info->info.extack;
        struct nsim_fib4_rt *fib4_rt;
 
        fib4_rt = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info);
 
        rhashtable_remove_fast(&data->fib_rt_ht, &fib4_rt->common.ht_node,
                               nsim_fib_rt_ht_params);
-       nsim_fib_account(&data->ipv4.fib, false, extack);
        nsim_fib4_rt_destroy(fib4_rt);
 }
 
 static int nsim_fib4_event(struct nsim_fib_data *data,
-                          struct fib_notifier_info *info,
+                          struct fib_entry_notifier_info *fen_info,
                           unsigned long event)
 {
-       struct fib_entry_notifier_info *fen_info;
        int err = 0;
 
-       fen_info = container_of(info, struct fib_entry_notifier_info, info);
-
        switch (event) {
        case FIB_EVENT_ENTRY_REPLACE:
                err = nsim_fib4_rt_insert(data, fen_info);
 {
        struct nsim_fib6_rt_nh *fib6_rt_nh;
 
-       fib6_rt_nh = kzalloc(sizeof(*fib6_rt_nh), GFP_ATOMIC);
+       fib6_rt_nh = kzalloc(sizeof(*fib6_rt_nh), GFP_KERNEL);
        if (!fib6_rt_nh)
                return -ENOMEM;
 
        return 0;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static void nsim_rt6_release(struct fib6_info *rt)
+{
+       fib6_info_release(rt);
+}
+#else
+static void nsim_rt6_release(struct fib6_info *rt)
+{
+}
+#endif
+
 static void nsim_fib6_rt_nh_del(struct nsim_fib6_rt *fib6_rt,
                                const struct fib6_info *rt)
 {
 
        fib6_rt->nhs--;
        list_del(&fib6_rt_nh->list);
-#if IS_ENABLED(CONFIG_IPV6)
-       fib6_info_release(fib6_rt_nh->rt);
-#endif
+       nsim_rt6_release(fib6_rt_nh->rt);
        kfree(fib6_rt_nh);
 }
 
 static struct nsim_fib6_rt *
 nsim_fib6_rt_create(struct nsim_fib_data *data,
-                   struct fib6_entry_notifier_info *fen6_info)
+                   struct fib6_info **rt_arr, unsigned int nrt6)
 {
-       struct fib6_info *iter, *rt = fen6_info->rt;
+       struct fib6_info *rt = rt_arr[0];
        struct nsim_fib6_rt *fib6_rt;
        int i = 0;
        int err;
 
-       fib6_rt = kzalloc(sizeof(*fib6_rt), GFP_ATOMIC);
+       fib6_rt = kzalloc(sizeof(*fib6_rt), GFP_KERNEL);
        if (!fib6_rt)
                return ERR_PTR(-ENOMEM);
 
         */
        INIT_LIST_HEAD(&fib6_rt->nh_list);
 
-       err = nsim_fib6_rt_nh_add(fib6_rt, rt);
-       if (err)
-               goto err_fib_rt_fini;
-
-       if (!fen6_info->nsiblings)
-               return fib6_rt;
-
-       list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
-               if (i == fen6_info->nsiblings)
-                       break;
-
-               err = nsim_fib6_rt_nh_add(fib6_rt, iter);
+       for (i = 0; i < nrt6; i++) {
+               err = nsim_fib6_rt_nh_add(fib6_rt, rt_arr[i]);
                if (err)
                        goto err_fib6_rt_nh_del;
-               i++;
        }
-       WARN_ON_ONCE(i != fen6_info->nsiblings);
 
        return fib6_rt;
 
 err_fib6_rt_nh_del:
-       list_for_each_entry_continue_reverse(iter, &rt->fib6_siblings,
-                                            fib6_siblings)
-               nsim_fib6_rt_nh_del(fib6_rt, iter);
-       nsim_fib6_rt_nh_del(fib6_rt, rt);
-err_fib_rt_fini:
+       for (i--; i >= 0; i--) {
+               nsim_fib6_rt_nh_del(fib6_rt, rt_arr[i]);
+       };
        nsim_fib_rt_fini(&fib6_rt->common);
        kfree(fib6_rt);
        return ERR_PTR(err);
 }
 
 static int nsim_fib6_rt_append(struct nsim_fib_data *data,
-                              struct fib6_entry_notifier_info *fen6_info)
+                              struct nsim_fib6_event *fib6_event)
 {
-       struct fib6_info *iter, *rt = fen6_info->rt;
+       struct fib6_info *rt = fib6_event->rt_arr[0];
        struct nsim_fib6_rt *fib6_rt;
-       int i = 0;
-       int err;
+       int i, err;
 
        fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt);
        if (WARN_ON_ONCE(!fib6_rt))
                return -EINVAL;
 
-       err = nsim_fib6_rt_nh_add(fib6_rt, rt);
-       if (err)
-               return err;
-       rt->trap = true;
-
-       if (!fen6_info->nsiblings)
-               return 0;
-
-       list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
-               if (i == fen6_info->nsiblings)
-                       break;
-
-               err = nsim_fib6_rt_nh_add(fib6_rt, iter);
+       for (i = 0; i < fib6_event->nrt6; i++) {
+               err = nsim_fib6_rt_nh_add(fib6_rt, fib6_event->rt_arr[i]);
                if (err)
                        goto err_fib6_rt_nh_del;
-               iter->trap = true;
-               i++;
+
+               fib6_event->rt_arr[i]->trap = true;
        }
-       WARN_ON_ONCE(i != fen6_info->nsiblings);
 
        return 0;
 
 err_fib6_rt_nh_del:
-       list_for_each_entry_continue_reverse(iter, &rt->fib6_siblings,
-                                            fib6_siblings) {
-               iter->trap = false;
-               nsim_fib6_rt_nh_del(fib6_rt, iter);
+       for (i--; i >= 0; i--) {
+               fib6_event->rt_arr[i]->trap = false;
+               nsim_fib6_rt_nh_del(fib6_rt, fib6_event->rt_arr[i]);
        }
-       rt->trap = false;
-       nsim_fib6_rt_nh_del(fib6_rt, rt);
        return err;
 }
 
 }
 
 static int nsim_fib6_rt_add(struct nsim_fib_data *data,
-                           struct nsim_fib6_rt *fib6_rt,
-                           struct netlink_ext_ack *extack)
+                           struct nsim_fib6_rt *fib6_rt)
 {
        int err;
 
-       err = nsim_fib_account(&data->ipv6.fib, true, extack);
-       if (err)
-               return err;
-
        err = rhashtable_insert_fast(&data->fib_rt_ht,
                                     &fib6_rt->common.ht_node,
                                     nsim_fib_rt_ht_params);
-       if (err) {
-               NL_SET_ERR_MSG_MOD(extack, "Failed to insert IPv6 route");
+
+       if (err)
                goto err_fib_dismiss;
-       }
 
+       msleep(1);
        nsim_fib6_rt_hw_flags_set(fib6_rt, true);
 
        return 0;
 
 err_fib_dismiss:
-       nsim_fib_account(&data->ipv6.fib, false, extack);
+       /* Drop the accounting that was increased from the notification
+        * context when FIB_EVENT_ENTRY_REPLACE was triggered.
+        */
+       nsim_fib_account(&data->ipv6.fib, false);
        return err;
 }
 
 static int nsim_fib6_rt_replace(struct nsim_fib_data *data,
                                struct nsim_fib6_rt *fib6_rt,
-                               struct nsim_fib6_rt *fib6_rt_old,
-                               struct netlink_ext_ack *extack)
+                               struct nsim_fib6_rt *fib6_rt_old)
 {
        int err;
 
-       /* We are replacing a route, so no need to change the accounting. */
+       /* We are replacing a route, so need to remove the accounting which
+        * was increased when FIB_EVENT_ENTRY_REPLACE was triggered.
+        */
+       err = nsim_fib_account(&data->ipv6.fib, false);
+       if (err)
+               return err;
+
        err = rhashtable_replace_fast(&data->fib_rt_ht,
                                      &fib6_rt_old->common.ht_node,
                                      &fib6_rt->common.ht_node,
                                      nsim_fib_rt_ht_params);
-       if (err) {
-               NL_SET_ERR_MSG_MOD(extack, "Failed to replace IPv6 route");
+
+       if (err)
                return err;
-       }
 
+       msleep(1);
        nsim_fib6_rt_hw_flags_set(fib6_rt, true);
 
        nsim_fib6_rt_hw_flags_set(fib6_rt_old, false);
 }
 
 static int nsim_fib6_rt_insert(struct nsim_fib_data *data,
-                              struct fib6_entry_notifier_info *fen6_info)
+                              struct nsim_fib6_event *fib6_event)
 {
-       struct netlink_ext_ack *extack = fen6_info->info.extack;
+       struct fib6_info *rt = fib6_event->rt_arr[0];
        struct nsim_fib6_rt *fib6_rt, *fib6_rt_old;
        int err;
 
-       fib6_rt = nsim_fib6_rt_create(data, fen6_info);
+       fib6_rt = nsim_fib6_rt_create(data, fib6_event->rt_arr,
+                                     fib6_event->nrt6);
        if (IS_ERR(fib6_rt))
                return PTR_ERR(fib6_rt);
 
-       fib6_rt_old = nsim_fib6_rt_lookup(&data->fib_rt_ht, fen6_info->rt);
+       fib6_rt_old = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt);
        if (!fib6_rt_old)
-               err = nsim_fib6_rt_add(data, fib6_rt, extack);
+               err = nsim_fib6_rt_add(data, fib6_rt);
        else
-               err = nsim_fib6_rt_replace(data, fib6_rt, fib6_rt_old, extack);
+               err = nsim_fib6_rt_replace(data, fib6_rt, fib6_rt_old);
 
        if (err)
                nsim_fib6_rt_destroy(fib6_rt);
        return err;
 }
 
-static void
-nsim_fib6_rt_remove(struct nsim_fib_data *data,
-                   const struct fib6_entry_notifier_info *fen6_info)
+static void nsim_fib6_rt_remove(struct nsim_fib_data *data,
+                               struct nsim_fib6_event *fib6_event)
 {
-       struct netlink_ext_ack *extack = fen6_info->info.extack;
+       struct fib6_info *rt = fib6_event->rt_arr[0];
        struct nsim_fib6_rt *fib6_rt;
+       int i;
 
        /* Multipath routes are first added to the FIB trie and only then
         * notified. If we vetoed the addition, we will get a delete
         * notification for a route we do not have. Therefore, do not warn if
         * route was not found.
         */
-       fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, fen6_info->rt);
+       fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt);
        if (!fib6_rt)
                return;
 
        /* If not all the nexthops are deleted, then only reduce the nexthop
         * group.
         */
-       if (fen6_info->nsiblings + 1 != fib6_rt->nhs) {
-               nsim_fib6_rt_nh_del(fib6_rt, fen6_info->rt);
+       if (fib6_event->nrt6 != fib6_rt->nhs) {
+               for (i = 0; i < fib6_event->nrt6; i++)
+                       nsim_fib6_rt_nh_del(fib6_rt, fib6_event->rt_arr[i]);
                return;
        }
 
        rhashtable_remove_fast(&data->fib_rt_ht, &fib6_rt->common.ht_node,
                               nsim_fib_rt_ht_params);
-       nsim_fib_account(&data->ipv6.fib, false, extack);
        nsim_fib6_rt_destroy(fib6_rt);
 }
 
+static int nsim_fib6_event_init(struct nsim_fib6_event *fib6_event,
+                               struct fib6_entry_notifier_info *fen6_info)
+{
+       struct fib6_info *rt = fen6_info->rt;
+       struct fib6_info **rt_arr;
+       struct fib6_info *iter;
+       unsigned int nrt6;
+       int i = 0;
+
+       nrt6 = fen6_info->nsiblings + 1;
+
+       rt_arr = kcalloc(nrt6, sizeof(struct fib6_info *), GFP_ATOMIC);
+       if (!rt_arr)
+               return -ENOMEM;
+
+       fib6_event->rt_arr = rt_arr;
+       fib6_event->nrt6 = nrt6;
+
+       rt_arr[0] = rt;
+       fib6_info_hold(rt);
+
+       if (!fen6_info->nsiblings)
+               return 0;
+
+       list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
+               if (i == fen6_info->nsiblings)
+                       break;
+
+               rt_arr[i + 1] = iter;
+               fib6_info_hold(iter);
+               i++;
+       }
+       WARN_ON_ONCE(i != fen6_info->nsiblings);
+
+       return 0;
+}
+
+static void nsim_fib6_event_fini(struct nsim_fib6_event *fib6_event)
+{
+       int i;
+
+       for (i = 0; i < fib6_event->nrt6; i++)
+               nsim_rt6_release(fib6_event->rt_arr[i]);
+       kfree(fib6_event->rt_arr);
+}
+
 static int nsim_fib6_event(struct nsim_fib_data *data,
-                          struct fib_notifier_info *info,
+                          struct nsim_fib6_event *fib6_event,
                           unsigned long event)
 {
-       struct fib6_entry_notifier_info *fen6_info;
        int err = 0;
 
-       fen6_info = container_of(info, struct fib6_entry_notifier_info, info);
-
-       if (fen6_info->rt->fib6_src.plen) {
-               NL_SET_ERR_MSG_MOD(info->extack, "IPv6 source-specific route is not supported");
+       if (fib6_event->rt_arr[0]->fib6_src.plen)
                return 0;
-       }
 
        switch (event) {
        case FIB_EVENT_ENTRY_REPLACE:
-               err = nsim_fib6_rt_insert(data, fen6_info);
+               err = nsim_fib6_rt_insert(data, fib6_event);
                break;
        case FIB_EVENT_ENTRY_APPEND:
-               err = nsim_fib6_rt_append(data, fen6_info);
+               err = nsim_fib6_rt_append(data, fib6_event);
                break;
        case FIB_EVENT_ENTRY_DEL:
-               nsim_fib6_rt_remove(data, fen6_info);
+               nsim_fib6_rt_remove(data, fib6_event);
                break;
        default:
                break;
        return err;
 }
 
-static int nsim_fib_event(struct nsim_fib_data *data,
-                         struct fib_notifier_info *info, unsigned long event)
+static int nsim_fib_event(struct nsim_fib_event *fib_event)
 {
        int err = 0;
 
-       switch (info->family) {
+       switch (fib_event->family) {
        case AF_INET:
-               err = nsim_fib4_event(data, info, event);
+               nsim_fib4_event(fib_event->data, &fib_event->fen_info,
+                               fib_event->event);
+               fib_info_put(fib_event->fen_info.fi);
                break;
        case AF_INET6:
-               err = nsim_fib6_event(data, info, event);
+               nsim_fib6_event(fib_event->data, &fib_event->fib6_event,
+                               fib_event->event);
+               nsim_fib6_event_fini(&fib_event->fib6_event);
                break;
        }
 
        return err;
 }
 
+static int nsim_fib4_prepare_event(struct fib_notifier_info *info,
+                                  struct nsim_fib_event *fib_event,
+                                  unsigned long event)
+{
+       struct nsim_fib_data *data = fib_event->data;
+       struct fib_entry_notifier_info *fen_info;
+       struct netlink_ext_ack *extack;
+       int err = 0;
+
+       fen_info = container_of(info, struct fib_entry_notifier_info,
+                               info);
+       fib_event->fen_info = *fen_info;
+       extack = info->extack;
+
+       switch (event) {
+       case FIB_EVENT_ENTRY_REPLACE:
+               err = nsim_fib_account(&data->ipv4.fib, true);
+               if (err) {
+                       NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
+                       return err;
+               }
+               break;
+       case FIB_EVENT_ENTRY_DEL:
+               nsim_fib_account(&data->ipv4.fib, false);
+               break;
+       }
+
+       /* Take reference on fib_info to prevent it from being
+        * freed while event is queued. Release it afterwards.
+        */
+       fib_info_hold(fib_event->fen_info.fi);
+
+       return 0;
+}
+
+static int nsim_fib6_prepare_event(struct fib_notifier_info *info,
+                                  struct nsim_fib_event *fib_event,
+                                  unsigned long event)
+{
+       struct nsim_fib_data *data = fib_event->data;
+       struct fib6_entry_notifier_info *fen6_info;
+       struct netlink_ext_ack *extack;
+       int err = 0;
+
+       fen6_info = container_of(info, struct fib6_entry_notifier_info,
+                                info);
+
+       err = nsim_fib6_event_init(&fib_event->fib6_event, fen6_info);
+       if (err)
+               return err;
+
+       extack = info->extack;
+       switch (event) {
+       case FIB_EVENT_ENTRY_REPLACE:
+               err = nsim_fib_account(&data->ipv6.fib, true);
+               if (err) {
+                       NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
+                       goto err_fib6_event_fini;
+               }
+               break;
+       case FIB_EVENT_ENTRY_DEL:
+               nsim_fib_account(&data->ipv6.fib, false);
+               break;
+       }
+
+       return 0;
+
+err_fib6_event_fini:
+       nsim_fib6_event_fini(&fib_event->fib6_event);
+       return err;
+}
+
+static int nsim_fib_event_schedule_work(struct nsim_fib_data *data,
+                                       struct fib_notifier_info *info,
+                                       unsigned long event)
+{
+       struct nsim_fib_event *fib_event;
+       int err;
+
+       if (info->family != AF_INET && info->family != AF_INET6)
+               /* netdevsim does not support 'RTNL_FAMILY_IP6MR' and
+                * 'RTNL_FAMILY_IPMR' and should ignore them.
+                */
+               return NOTIFY_DONE;
+
+       fib_event = kzalloc(sizeof(*fib_event), GFP_ATOMIC);
+       if (!fib_event)
+               return NOTIFY_BAD;
+
+       fib_event->data = data;
+       fib_event->event = event;
+       fib_event->family = info->family;
+
+       switch (info->family) {
+       case AF_INET:
+               err = nsim_fib4_prepare_event(info, fib_event, event);
+               break;
+       case AF_INET6:
+               err = nsim_fib6_prepare_event(info, fib_event, event);
+               break;
+       }
+
+       if (err)
+               goto err_fib_prepare_event;
+
+       /* Enqueue the event and trigger the work */
+       spin_lock_bh(&data->fib_event_queue_lock);
+       list_add_tail(&fib_event->list, &data->fib_event_queue);
+       spin_unlock_bh(&data->fib_event_queue_lock);
+       schedule_work(&data->fib_event_work);
+
+       return NOTIFY_DONE;
+
+err_fib_prepare_event:
+       kfree(fib_event);
+       return NOTIFY_BAD;
+}
+
 static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
                             void *ptr)
 {
        struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
                                                  fib_nb);
        struct fib_notifier_info *info = ptr;
-       int err = 0;
+       int err;
 
        switch (event) {
        case FIB_EVENT_RULE_ADD:
        case FIB_EVENT_RULE_DEL:
                err = nsim_fib_rule_event(data, info,
                                          event == FIB_EVENT_RULE_ADD);
-               break;
+               return notifier_from_errno(err);
        case FIB_EVENT_ENTRY_REPLACE:
        case FIB_EVENT_ENTRY_APPEND:
        case FIB_EVENT_ENTRY_DEL:
-               /* IPv6 routes can be added via RAs from softIRQ. */
-               spin_lock_bh(&data->fib_lock);
-               err = nsim_fib_event(data, info, event);
-               spin_unlock_bh(&data->fib_lock);
-               break;
+               return nsim_fib_event_schedule_work(data, info, event);
        }
 
-       return notifier_from_errno(err);
+       return NOTIFY_DONE;
 }
 
 static void nsim_fib4_rt_free(struct nsim_fib_rt *fib_rt,
 
        fib4_rt = container_of(fib_rt, struct nsim_fib4_rt, common);
        nsim_fib4_rt_hw_flags_set(devlink_net(devlink), fib4_rt, false);
-       nsim_fib_account(&data->ipv4.fib, false, NULL);
+       nsim_fib_account(&data->ipv4.fib, false);
        nsim_fib4_rt_destroy(fib4_rt);
 }
 
 
        fib6_rt = container_of(fib_rt, struct nsim_fib6_rt, common);
        nsim_fib6_rt_hw_flags_set(fib6_rt, false);
-       nsim_fib_account(&data->ipv6.fib, false, NULL);
+       nsim_fib_account(&data->ipv6.fib, false);
        nsim_fib6_rt_destroy(fib6_rt);
 }
 
                                                  fib_nb);
        struct nsim_fib_rt *fib_rt, *fib_rt_tmp;
 
+       /* Flush the work to make sure there is no race with notifications. */
+       flush_work(&data->fib_event_work);
+
        /* The notifier block is still not registered, so we do not need to
         * take any locks here.
         */
        }
 }
 
+static void nsim_fib_event_work(struct work_struct *work)
+{
+       struct nsim_fib_data *data = container_of(work, struct nsim_fib_data,
+                                                 fib_event_work);
+       struct nsim_fib_event *fib_event, *next_fib_event;
+
+       LIST_HEAD(fib_event_queue);
+
+       spin_lock_bh(&data->fib_event_queue_lock);
+       list_splice_init(&data->fib_event_queue, &fib_event_queue);
+       spin_unlock_bh(&data->fib_event_queue_lock);
+
+       mutex_lock(&data->fib_lock);
+       list_for_each_entry_safe(fib_event, next_fib_event, &fib_event_queue,
+                                list) {
+               nsim_fib_event(fib_event);
+               list_del(&fib_event->list);
+               kfree(fib_event);
+               cond_resched();
+       }
+       mutex_unlock(&data->fib_lock);
+}
+
 struct nsim_fib_data *nsim_fib_create(struct devlink *devlink,
                                      struct netlink_ext_ack *extack)
 {
        if (err)
                goto err_data_free;
 
-       spin_lock_init(&data->fib_lock);
+       mutex_init(&data->fib_lock);
        INIT_LIST_HEAD(&data->fib_rt_list);
        err = rhashtable_init(&data->fib_rt_ht, &nsim_fib_rt_ht_params);
        if (err)
                goto err_rhashtable_nexthop_destroy;
 
+       INIT_WORK(&data->fib_event_work, nsim_fib_event_work);
+       INIT_LIST_HEAD(&data->fib_event_queue);
+       spin_lock_init(&data->fib_event_queue_lock);
+
        nsim_fib_set_max_all(data, devlink);
 
        data->nexthop_nb.notifier_call = nsim_nexthop_event_nb;
 err_nexthop_nb_unregister:
        unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb);
 err_rhashtable_fib_destroy:
+       flush_work(&data->fib_event_work);
        rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
                                    data);
 err_rhashtable_nexthop_destroy:
        rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free,
                                    data);
+       mutex_destroy(&data->fib_lock);
 err_data_free:
        kfree(data);
        return ERR_PTR(err);
                                            NSIM_RESOURCE_IPV4_FIB);
        unregister_fib_notifier(devlink_net(devlink), &data->fib_nb);
        unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb);
+       flush_work(&data->fib_event_work);
        rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
                                    data);
        rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free,
                                    data);
+       WARN_ON_ONCE(!list_empty(&data->fib_event_queue));
        WARN_ON_ONCE(!list_empty(&data->fib_rt_list));
+       mutex_destroy(&data->fib_lock);
        kfree(data);
 }