|
@@ -2166,28 +2166,70 @@ static const struct switchdev_ops rocker_port_switchdev_ops = {
|
|
|
.switchdev_port_obj_dump = rocker_port_obj_dump,
|
|
|
};
|
|
|
|
|
|
-static int rocker_router_fib_event(struct notifier_block *nb,
|
|
|
- unsigned long event, void *ptr)
|
|
|
+struct rocker_fib_event_work {
|
|
|
+ struct work_struct work;
|
|
|
+ struct fib_entry_notifier_info fen_info;
|
|
|
+ struct rocker *rocker;
|
|
|
+ unsigned long event;
|
|
|
+};
|
|
|
+
|
|
|
+static void rocker_router_fib_event_work(struct work_struct *work)
|
|
|
{
|
|
|
- struct rocker *rocker = container_of(nb, struct rocker, fib_nb);
|
|
|
- struct fib_entry_notifier_info *fen_info = ptr;
|
|
|
+ struct rocker_fib_event_work *fib_work =
|
|
|
+ container_of(work, struct rocker_fib_event_work, work);
|
|
|
+ struct rocker *rocker = fib_work->rocker;
|
|
|
int err;
|
|
|
|
|
|
- switch (event) {
|
|
|
+ /* Protect internal structures from changes */
|
|
|
+ rtnl_lock();
|
|
|
+ switch (fib_work->event) {
|
|
|
case FIB_EVENT_ENTRY_ADD:
|
|
|
- err = rocker_world_fib4_add(rocker, fen_info);
|
|
|
+ err = rocker_world_fib4_add(rocker, &fib_work->fen_info);
|
|
|
if (err)
|
|
|
rocker_world_fib4_abort(rocker);
|
|
|
- else
|
|
|
+ fib_info_put(fib_work->fen_info.fi);
|
|
|
break;
|
|
|
case FIB_EVENT_ENTRY_DEL:
|
|
|
- rocker_world_fib4_del(rocker, fen_info);
|
|
|
+ rocker_world_fib4_del(rocker, &fib_work->fen_info);
|
|
|
+ fib_info_put(fib_work->fen_info.fi);
|
|
|
break;
|
|
|
case FIB_EVENT_RULE_ADD: /* fall through */
|
|
|
case FIB_EVENT_RULE_DEL:
|
|
|
rocker_world_fib4_abort(rocker);
|
|
|
break;
|
|
|
}
|
|
|
+ rtnl_unlock();
|
|
|
+ kfree(fib_work);
|
|
|
+}
|
|
|
+
|
|
|
+/* Called with rcu_read_lock() */
|
|
|
+static int rocker_router_fib_event(struct notifier_block *nb,
|
|
|
+ unsigned long event, void *ptr)
|
|
|
+{
|
|
|
+ struct rocker *rocker = container_of(nb, struct rocker, fib_nb);
|
|
|
+ struct rocker_fib_event_work *fib_work;
|
|
|
+
|
|
|
+ fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
|
|
|
+ if (WARN_ON(!fib_work))
|
|
|
+ return NOTIFY_BAD;
|
|
|
+
|
|
|
+ INIT_WORK(&fib_work->work, rocker_router_fib_event_work);
|
|
|
+ fib_work->rocker = rocker;
|
|
|
+ fib_work->event = event;
|
|
|
+
|
|
|
+ switch (event) {
|
|
|
+ case FIB_EVENT_ENTRY_ADD: /* fall through */
|
|
|
+ case FIB_EVENT_ENTRY_DEL:
|
|
|
+ memcpy(&fib_work->fen_info, ptr, sizeof(fib_work->fen_info));
|
|
|
+ /* Take referece on fib_info to prevent it from being
|
|
|
+ * freed while work is queued. Release it afterwards.
|
|
|
+ */
|
|
|
+ fib_info_hold(fib_work->fen_info.fi);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ queue_work(rocker->rocker_owq, &fib_work->work);
|
|
|
+
|
|
|
return NOTIFY_DONE;
|
|
|
}
|
|
|
|