static int
mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib_node *fib_node,
struct mlxsw_sp_fib_entry *new_entry,
- bool append)
+ bool replace, bool append)
{
struct mlxsw_sp_fib_entry *fib_entry;
if (append)
return mlxsw_sp_fib4_node_list_append(fib_entry, new_entry);
+ if (replace && WARN_ON(!fib_entry))
+ return -EINVAL;
+ /* Insert new entry before replaced one, so that we can later
+ * remove the second.
+ */
if (fib_entry) {
list_add_tail(&new_entry->list, &fib_entry->list);
} else {
static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_fib_entry *fib_entry,
- bool append)
+ bool replace, bool append)
{
struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
int err;
- err = mlxsw_sp_fib4_node_list_insert(fib_node, fib_entry, append);
+ err = mlxsw_sp_fib4_node_list_insert(fib_node, fib_entry, replace,
+ append);
if (err)
return err;
mlxsw_sp_fib4_node_list_remove(fib_entry);
}
+static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_fib_entry *fib_entry,
+ bool replace)
+{
+ struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
+ struct mlxsw_sp_fib_entry *replaced;
+
+ if (!replace)
+ return;
+
+ /* We inserted the new entry before replaced one */
+ replaced = list_next_entry(fib_entry, list);
+
+ mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced);
+ mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced);
+ mlxsw_sp_fib4_node_put(mlxsw_sp, fib_node);
+}
+
static int
mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
const struct fib_entry_notifier_info *fen_info,
- bool append)
+ bool replace, bool append)
{
struct mlxsw_sp_fib_entry *fib_entry;
struct mlxsw_sp_fib_node *fib_node;
goto err_fib4_entry_create;
}
- err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib_entry, append);
+ err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib_entry, replace,
+ append);
if (err) {
dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
goto err_fib4_node_entry_link;
}
+ mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib_entry, replace);
+
return 0;
err_fib4_node_entry_link:
struct mlxsw_sp_fib_event_work *fib_work =
container_of(work, struct mlxsw_sp_fib_event_work, work);
struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
- bool append;
+ bool replace, append;
int err;
/* Protect internal structures from changes */
rtnl_lock();
switch (fib_work->event) {
+ case FIB_EVENT_ENTRY_REPLACE: /* fall through */
case FIB_EVENT_ENTRY_APPEND: /* fall through */
case FIB_EVENT_ENTRY_ADD:
+ replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info,
- append);
+ replace, append);
if (err)
mlxsw_sp_router_fib4_abort(mlxsw_sp);
fib_info_put(fib_work->fen_info.fi);
fib_work->event = event;
switch (event) {
+ case FIB_EVENT_ENTRY_REPLACE: /* fall through */
case FIB_EVENT_ENTRY_APPEND: /* fall through */
case FIB_EVENT_ENTRY_ADD: /* fall through */
case FIB_EVENT_ENTRY_DEL: