From: Markus Stockhausen Date: Wed, 24 Jun 2026 20:41:15 +0000 (+0200) Subject: realtek: l3: separate L3 coding from DSA X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b586e843dedd34b7f339375f03eb26e9b35ed5bc;p=thirdparty%2Fopenwrt.git realtek: l3: separate L3 coding from DSA DSA is layer 2 and not layer 3. Nevertheless all routing management needs a home. Separate it into its own source file. This first step is far from perfect but at least it guides the DSA driver into the right direction. No functional changes. Just relocating, renaming and proper including. Link: https://github.com/openwrt/openwrt/pull/23937 Signed-off-by: Markus Stockhausen --- diff --git a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/Makefile b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/Makefile index 1511dae8eac..21b681918fb 100644 --- a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/Makefile +++ b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_NET_DSA_RTL83XX) += rtl_otto_dsa.o -rtl_otto_dsa-objs := common.o dsa.o rtl838x.o rtl839x.o rtl930x.o rtl931x.o debugfs.o qos.o tc.o +rtl_otto_dsa-objs := common.o dsa.o rtl838x.o rtl839x.o rtl930x.o rtl931x.o debugfs.o qos.o tc.o l3.o diff --git a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/common.c b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/common.c index 88e7984b8b5..b72b401d8ec 100644 --- a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/common.c +++ b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/common.c @@ -14,6 +14,7 @@ #include #include +#include "l3.h" #include "rtl-otto.h" int rtldsa_port_get_stp_state(struct rtl838x_switch_priv *priv, int port) @@ -695,7 +696,7 @@ int rtl83xx_packet_cntr_alloc(struct rtl838x_switch_priv *priv) * Called from the L3 layer * The index in the L2 hash table is filled into nh->l2_id; */ -static int rtl83xx_l2_nexthop_add(struct rtl838x_switch_priv *priv, struct rtl83xx_nexthop *nh) +int rtl83xx_l2_nexthop_add(struct rtl838x_switch_priv *priv, struct otto_l3_nexthop *nh) { struct rtl838x_l2_entry e; u64 seed = priv->r->l2_hash_seed(nh->mac, nh->rvid); @@ -762,7 +763,7 @@ static int rtl83xx_l2_nexthop_add(struct rtl838x_switch_priv *priv, struct rtl83 * If it was static, the entire entry is removed, otherwise the nexthop bit is cleared * and we wait until the entry ages out */ -static int rtl83xx_l2_nexthop_rm(struct rtl838x_switch_priv *priv, struct rtl83xx_nexthop *nh) +int rtl83xx_l2_nexthop_rm(struct rtl838x_switch_priv *priv, struct otto_l3_nexthop *nh) { struct rtl838x_l2_entry e; u32 key = nh->l2_id >> 2; @@ -808,689 +809,6 @@ int rtl83xx_port_is_under(const struct net_device *dev, struct rtl838x_switch_pr return -EINVAL; } -static const struct rhashtable_params route_ht_params = { - .key_len = sizeof(u32), - .key_offset = offsetof(struct rtl83xx_route, gw_ip), - .head_offset = offsetof(struct rtl83xx_route, linkage), -}; - -/* Updates an L3 next hop entry in the ROUTING table */ -static int rtl83xx_l3_nexthop_update(struct rtl838x_switch_priv *priv, __be32 ip_addr, u64 mac) -{ - struct rtl83xx_route *r; - struct rhlist_head *tmp, *list; - - rcu_read_lock(); - list = rhltable_lookup(&priv->routes, &ip_addr, route_ht_params); - if (!list) { - rcu_read_unlock(); - return -ENOENT; - } - - rhl_for_each_entry_rcu(r, tmp, list, linkage) { - pr_debug("%s: Setting up fwding: ip %pI4, GW mac %016llx\n", - __func__, &ip_addr, mac); - - /* Reads the ROUTING table entry associated with the route */ - priv->r->route_read(r->id, r); - pr_debug("Route with id %d to %pI4 / %d\n", r->id, &r->dst_ip, r->prefix_len); - - r->nh.mac = r->nh.gw = mac; - r->nh.port = priv->r->port_ignore; - r->nh.id = r->id; - - /* Do we need to explicitly add a DMAC entry with the route's nh index? */ - if (priv->r->set_l3_egress_mac) - priv->r->set_l3_egress_mac(r->id, mac); - - /* Update ROUTING table: map gateway-mac and switch-mac id to route id */ - rtl83xx_l2_nexthop_add(priv, &r->nh); - - r->attr.valid = true; - r->attr.action = ROUTE_ACT_FORWARD; - r->attr.type = 0; - r->attr.hit = false; /* Reset route-used indicator */ - - /* Add PIE entry with dst_ip and prefix_len */ - r->pr.dip = r->dst_ip; - r->pr.dip_m = inet_make_mask(r->prefix_len); - - if (r->is_host_route) { - int slot = priv->r->find_l3_slot(r, false); - - pr_info("%s: Got slot for route: %d\n", __func__, slot); - priv->r->host_route_write(slot, r); - } else { - priv->r->route_write(r->id, r); - r->pr.fwd_sel = true; - r->pr.fwd_data = r->nh.l2_id; - r->pr.fwd_act = PIE_ACT_ROUTE_UC; - } - - if (priv->r->set_l3_nexthop) - priv->r->set_l3_nexthop(r->nh.id, r->nh.l2_id, r->nh.if_id); - - if (r->pr.id < 0) { - r->pr.packet_cntr = rtl83xx_packet_cntr_alloc(priv); - if (r->pr.packet_cntr >= 0) { - pr_info("Using packet counter %d\n", r->pr.packet_cntr); - r->pr.log_sel = true; - r->pr.log_data = r->pr.packet_cntr; - } - priv->r->pie_rule_add(priv, &r->pr); - } else { - int pkts = priv->r->packet_cntr_read(r->pr.packet_cntr); - - pr_debug("%s: total packets: %d\n", __func__, pkts); - - priv->r->pie_rule_write(priv, r->pr.id, &r->pr); - } - } - rcu_read_unlock(); - - return 0; -} - -static int rtl83xx_port_ipv4_resolve(struct rtl838x_switch_priv *priv, - struct net_device *dev, __be32 ip_addr) -{ - struct neighbour *n = neigh_lookup(&arp_tbl, &ip_addr, dev); - int err = 0; - u64 mac; - - if (!n) { - n = neigh_create(&arp_tbl, &ip_addr, dev); - if (IS_ERR(n)) - return PTR_ERR(n); - } - - /* If the neigh is already resolved, then go ahead and - * install the entry, otherwise start the ARP process to - * resolve the neigh. - */ - if (n->nud_state & NUD_VALID) { - mac = ether_addr_to_u64(n->ha); - pr_info("%s: resolved mac: %016llx\n", __func__, mac); - rtl83xx_l3_nexthop_update(priv, ip_addr, mac); - } else { - pr_info("%s: need to wait\n", __func__); - neigh_event_send(n, NULL); - } - - neigh_release(n); - - return err; -} - -struct rtl83xx_walk_data { - struct rtl838x_switch_priv *priv; - int port; -}; - -static int rtl83xx_port_lower_walk(struct net_device *lower, struct netdev_nested_priv *_priv) -{ - struct rtl83xx_walk_data *data = (struct rtl83xx_walk_data *)_priv->data; - struct rtl838x_switch_priv *priv = data->priv; - int ret = 0; - int index; - - index = rtl83xx_port_is_under(lower, priv); - data->port = index; - if (index >= 0) { - pr_debug("Found DSA-port, index %d\n", index); - ret = 1; - } - - return ret; -} - -static int rtl83xx_port_dev_lower_find(struct net_device *dev, struct rtl838x_switch_priv *priv) -{ - struct rtl83xx_walk_data data; - struct netdev_nested_priv _priv; - - data.priv = priv; - data.port = 0; - _priv.data = (void *)&data; - - netdev_walk_all_lower_dev(dev, rtl83xx_port_lower_walk, &_priv); - - return data.port; -} - -static struct rtl83xx_route *rtl83xx_route_alloc(struct rtl838x_switch_priv *priv, u32 ip) -{ - struct rtl83xx_route *r; - int idx = 0, err; - - mutex_lock(&priv->reg_mutex); - - idx = find_first_zero_bit(priv->route_use_bm, MAX_ROUTES); - pr_debug("%s id: %d, ip %pI4\n", __func__, idx, &ip); - - r = kzalloc(sizeof(*r), GFP_KERNEL); - if (!r) { - mutex_unlock(&priv->reg_mutex); - return r; - } - - r->id = idx; - r->gw_ip = ip; - r->pr.id = -1; /* We still need to allocate a rule in HW */ - r->is_host_route = false; - - err = rhltable_insert(&priv->routes, &r->linkage, route_ht_params); - if (err) { - pr_err("Could not insert new rule\n"); - mutex_unlock(&priv->reg_mutex); - goto out_free; - } - - set_bit(idx, priv->route_use_bm); - - mutex_unlock(&priv->reg_mutex); - - return r; - -out_free: - kfree(r); - - return NULL; -} - -static struct rtl83xx_route *rtl83xx_host_route_alloc(struct rtl838x_switch_priv *priv, u32 ip) -{ - struct rtl83xx_route *r; - int idx = 0, err; - - mutex_lock(&priv->reg_mutex); - - idx = find_first_zero_bit(priv->host_route_use_bm, MAX_HOST_ROUTES); - pr_debug("%s id: %d, ip %pI4\n", __func__, idx, &ip); - - r = kzalloc(sizeof(*r), GFP_KERNEL); - if (!r) { - mutex_unlock(&priv->reg_mutex); - return r; - } - - /* We require a unique route ID irrespective of whether it is a prefix or host - * route (on RTL93xx) as we use this ID to associate a DMAC and next-hop entry - */ - r->id = idx + MAX_ROUTES; - - r->gw_ip = ip; - r->pr.id = -1; /* We still need to allocate a rule in HW */ - r->is_host_route = true; - - err = rhltable_insert(&priv->routes, &r->linkage, route_ht_params); - if (err) { - pr_err("Could not insert new rule\n"); - mutex_unlock(&priv->reg_mutex); - goto out_free; - } - - set_bit(idx, priv->host_route_use_bm); - - mutex_unlock(&priv->reg_mutex); - - return r; - -out_free: - kfree(r); - - return NULL; -} - -static void rtl83xx_route_rm(struct rtl838x_switch_priv *priv, struct rtl83xx_route *r) -{ - int id; - - if (rhltable_remove(&priv->routes, &r->linkage, route_ht_params)) - dev_warn(priv->dev, "Could not remove route\n"); - - if (r->is_host_route) { - id = priv->r->find_l3_slot(r, false); - pr_debug("%s: Got id for host route: %d\n", __func__, id); - r->attr.valid = false; - priv->r->host_route_write(id, r); - clear_bit(r->id - MAX_ROUTES, priv->host_route_use_bm); - } else { - /* If there is a HW representation of the route, delete it */ - if (priv->r->route_lookup_hw) { - id = priv->r->route_lookup_hw(r); - pr_info("%s: Got id for prefix route: %d\n", __func__, id); - r->attr.valid = false; - priv->r->route_write(id, r); - } - clear_bit(r->id, priv->route_use_bm); - } - - kfree(r); -} - -static int rtldsa_fib4_check(struct rtl838x_switch_priv *priv, - struct fib_entry_notifier_info *info, - enum fib_event_type event) -{ - struct net_device *ndev = fib_info_nh(info->fi, 0)->fib_nh_dev; - int vlan = is_vlan_dev(ndev) ? vlan_dev_vlan_id(ndev) : 0; - struct fib_nh *nh = fib_info_nh(info->fi, 0); - char gw_message[32] = ""; - - if (nh->fib_nh_gw4) - snprintf(gw_message, sizeof(gw_message), "via %pI4 ", &nh->fib_nh_gw4); - - dev_info(priv->dev, "%s IPv4 route %pI4/%d %s(VLAN %d, MAC %pM)\n", - event == FIB_EVENT_ENTRY_ADD ? "add" : "delete", - &info->dst, info->dst_len, gw_message, vlan, ndev->dev_addr); - - if ((info->type == RTN_BROADCAST) || ipv4_is_loopback(info->dst) || !info->dst) { - dev_warn(priv->dev, "skip loopback/broadcast addresses and default routes\n"); - return -EINVAL; - } - - return 0; -} - -static int rtldsa_fib4_del(struct rtl838x_switch_priv *priv, - struct fib_entry_notifier_info *info) -{ - struct fib_nh *nh = fib_info_nh(info->fi, 0); - struct rhlist_head *tmp, *list; - struct rtl83xx_route *route; - - if (rtldsa_fib4_check(priv, info, FIB_EVENT_ENTRY_DEL)) - return 0; - - rcu_read_lock(); - list = rhltable_lookup(&priv->routes, &nh->fib_nh_gw4, route_ht_params); - if (!list) { - rcu_read_unlock(); - dev_err(priv->dev, "no such gateway: %pI4\n", &nh->fib_nh_gw4); - return -ENOENT; - } - rhl_for_each_entry_rcu(route, tmp, list, linkage) { - if (route->dst_ip == info->dst && route->prefix_len == info->dst_len) { - dev_info(priv->dev, "found a route with id %d, nh-id %d\n", - route->id, route->nh.id); - break; - } - } - rcu_read_unlock(); - - rtl83xx_l2_nexthop_rm(priv, &route->nh); - - dev_info(priv->dev, "releasing packet counter %d\n", route->pr.packet_cntr); - set_bit(route->pr.packet_cntr, priv->packet_cntr_use_bm); - priv->r->pie_rule_rm(priv, &route->pr); - - rtl83xx_route_rm(priv, route); - - nh->fib_nh_flags &= ~RTNH_F_OFFLOAD; - - return 0; -} - -/* On the RTL93xx, an L3 termination endpoint MAC address on which the router waits - * for packets to be routed needs to be allocated. - */ -static int rtl83xx_alloc_router_mac(struct rtl838x_switch_priv *priv, u64 mac) -{ - int free_mac = -1; - struct rtl93xx_rt_mac m; - - mutex_lock(&priv->reg_mutex); - for (int i = 0; i < MAX_ROUTER_MACS; i++) { - priv->r->get_l3_router_mac(i, &m); - if (free_mac < 0 && !m.valid) { - free_mac = i; - continue; - } - if (m.valid && m.mac == mac) { - free_mac = i; - break; - } - } - - if (free_mac < 0) { - pr_err("No free router MACs, cannot offload\n"); - mutex_unlock(&priv->reg_mutex); - return -1; - } - - m.valid = true; - m.mac = mac; - m.p_type = 0; /* An individual port, not a trunk port */ - m.p_id = 0x3f; /* Listen on any port */ - m.p_id_mask = 0; - m.vid = 0; /* Listen on any VLAN... */ - m.vid_mask = 0; /* ... so mask needs to be 0 */ - m.mac_mask = 0xffffffffffffULL; /* We want an exact match of the interface MAC */ - m.action = L3_FORWARD; /* Route the packet */ - priv->r->set_l3_router_mac(free_mac, &m); - - mutex_unlock(&priv->reg_mutex); - - return 0; -} - -static int rtl83xx_alloc_egress_intf(struct rtl838x_switch_priv *priv, u64 mac, int vlan) -{ - int free_mac = -1; - struct rtl838x_l3_intf intf; - u64 m; - - mutex_lock(&priv->reg_mutex); - for (int i = 0; i < MAX_SMACS; i++) { - m = priv->r->get_l3_egress_mac(L3_EGRESS_DMACS + i); - if (free_mac < 0 && !m) { - free_mac = i; - continue; - } - if (m == mac) { - mutex_unlock(&priv->reg_mutex); - return i; - } - } - - if (free_mac < 0) { - pr_err("No free egress interface, cannot offload\n"); - return -1; - } - - /* Set up default egress interface 1 */ - intf.vid = vlan; - intf.smac_idx = free_mac; - intf.ip4_mtu_id = 1; - intf.ip6_mtu_id = 1; - intf.ttl_scope = 1; /* TTL */ - intf.hl_scope = 1; /* Hop Limit */ - intf.ip4_icmp_redirect = intf.ip6_icmp_redirect = 2; /* FORWARD */ - intf.ip4_pbr_icmp_redirect = intf.ip6_pbr_icmp_redirect = 2; /* FORWARD; */ - priv->r->set_l3_egress_intf(free_mac, &intf); - - priv->r->set_l3_egress_mac(L3_EGRESS_DMACS + free_mac, mac); - - mutex_unlock(&priv->reg_mutex); - - return free_mac; -} - -static int rtldsa_fib4_add(struct rtl838x_switch_priv *priv, - struct fib_entry_notifier_info *info) -{ - struct net_device *ndev = fib_info_nh(info->fi, 0)->fib_nh_dev; - int vlan = is_vlan_dev(ndev) ? vlan_dev_vlan_id(ndev) : 0; - struct fib_nh *nh = fib_info_nh(info->fi, 0); - struct rtl83xx_route *route; - int port; - - if (rtldsa_fib4_check(priv, info, FIB_EVENT_ENTRY_ADD)) - return 0; - - port = rtl83xx_port_dev_lower_find(ndev, priv); - if (port < 0) { - dev_err(priv->dev, "lower interface %s not found\n", ndev->name); - return -ENODEV; - } - - /* Allocate route or host-route entry (if hardware supports this) */ - if (info->dst_len == 32 && priv->r->host_route_write) - route = rtl83xx_host_route_alloc(priv, nh->fib_nh_gw4); - else - route = rtl83xx_route_alloc(priv, nh->fib_nh_gw4); - - if (route) - dev_info(priv->dev, "route hashtable extended for gw %pI4\n", &nh->fib_nh_gw4); - else { - dev_err(priv->dev, "could not extend route hashtable for gw %pI4\n", &nh->fib_nh_gw4); - return -ENOSPC; - } - - route->dst_ip = info->dst; - route->prefix_len = info->dst_len; - route->nh.rvid = vlan; - - if (priv->r->set_l3_router_mac) { - u64 mac = ether_addr_to_u64(ndev->dev_addr); - - pr_debug("Local route and router MAC %pM\n", ndev->dev_addr); - if (rtl83xx_alloc_router_mac(priv, mac)) - goto out_free_rt; - - /* vid = 0: Do not care about VID */ - route->nh.if_id = rtl83xx_alloc_egress_intf(priv, mac, vlan); - if (route->nh.if_id < 0) - goto out_free_rmac; - - if (!nh->fib_nh_gw4) { - int slot; - - route->nh.mac = mac; - route->nh.port = priv->r->port_ignore; - route->attr.valid = true; - route->attr.action = ROUTE_ACT_TRAP2CPU; - route->attr.type = 0; - - slot = priv->r->find_l3_slot(route, false); - pr_debug("%s: Got slot for route: %d\n", __func__, slot); - priv->r->host_route_write(slot, route); - } - } - - /* We need to resolve the mac address of the GW */ - if (nh->fib_nh_gw4) - rtl83xx_port_ipv4_resolve(priv, ndev, nh->fib_nh_gw4); - - nh->fib_nh_flags |= RTNH_F_OFFLOAD; - - return 0; - -out_free_rmac: -out_free_rt: - return 0; -} - -static int rtl83xx_fib6_add(struct rtl838x_switch_priv *priv, - struct fib6_entry_notifier_info *info) -{ - pr_debug("In %s\n", __func__); -/* nh->fib_nh_flags |= RTNH_F_OFFLOAD; */ - - return 0; -} - -struct net_event_work { - struct work_struct work; - struct rtl838x_switch_priv *priv; - u64 mac; - u32 gw_addr; -}; - -static void rtl83xx_net_event_work_do(struct work_struct *work) -{ - struct net_event_work *net_work = - container_of(work, struct net_event_work, work); - struct rtl838x_switch_priv *priv = net_work->priv; - - rtl83xx_l3_nexthop_update(priv, net_work->gw_addr, net_work->mac); - - kfree(net_work); -} - -static int rtl83xx_netevent_event(struct notifier_block *this, - unsigned long event, void *ptr) -{ - struct rtl838x_switch_priv *priv; - struct net_device *dev; - struct neighbour *n = ptr; - int err, port; - struct net_event_work *net_work; - - priv = container_of(this, struct rtl838x_switch_priv, ne_nb); - - switch (event) { - case NETEVENT_NEIGH_UPDATE: - /* ignore events for HW with missing L3 offloading implementation */ - if (!priv->r->l3_setup) - return NOTIFY_DONE; - - if (n->tbl != &arp_tbl) - return NOTIFY_DONE; - dev = n->dev; - port = rtl83xx_port_dev_lower_find(dev, priv); - if (port < 0 || !(n->nud_state & NUD_VALID)) { - pr_debug("%s: Neigbour invalid, not updating\n", __func__); - return NOTIFY_DONE; - } - - net_work = kzalloc(sizeof(*net_work), GFP_ATOMIC); - if (!net_work) - return NOTIFY_BAD; - - INIT_WORK(&net_work->work, rtl83xx_net_event_work_do); - net_work->priv = priv; - - net_work->mac = ether_addr_to_u64(n->ha); - net_work->gw_addr = *(__be32 *)n->primary_key; - - pr_debug("%s: updating neighbour on port %d, mac %016llx\n", - __func__, port, net_work->mac); - queue_work(priv->wq, &net_work->work); - if (err) - netdev_warn(dev, "failed to handle neigh update (err %d)\n", err); - break; - } - - return NOTIFY_DONE; -} - -struct rtl83xx_fib_event_work { - struct work_struct work; - union { - struct fib_entry_notifier_info fen_info; - struct fib6_entry_notifier_info fen6_info; - struct fib_rule_notifier_info fr_info; - }; - struct rtl838x_switch_priv *priv; - bool is_fib6; - unsigned long event; -}; - -static void rtl83xx_fib_event_work_do(struct work_struct *work) -{ - struct rtl83xx_fib_event_work *fib_work = - container_of(work, struct rtl83xx_fib_event_work, work); - struct rtl838x_switch_priv *priv = fib_work->priv; - struct fib_rule *rule; - int err; - - /* Protect internal structures from changes */ - rtnl_lock(); - pr_debug("%s: doing work, event %ld\n", __func__, fib_work->event); - switch (fib_work->event) { - case FIB_EVENT_ENTRY_ADD: - case FIB_EVENT_ENTRY_REPLACE: - case FIB_EVENT_ENTRY_APPEND: - if (fib_work->is_fib6) - err = rtl83xx_fib6_add(priv, &fib_work->fen6_info); - else - err = rtldsa_fib4_add(priv, &fib_work->fen_info); - if (err) - dev_err(priv->dev, "fib_add() failed\n"); - - fib_info_put(fib_work->fen_info.fi); - break; - case FIB_EVENT_ENTRY_DEL: - err = rtldsa_fib4_del(priv, &fib_work->fen_info); - if (err) - dev_err(priv->dev, "fib_del() failed\n"); - - fib_info_put(fib_work->fen_info.fi); - break; - case FIB_EVENT_RULE_ADD: - case FIB_EVENT_RULE_DEL: - rule = fib_work->fr_info.rule; - if (!fib4_rule_default(rule)) - pr_err("%s: FIB4 default rule failed\n", __func__); - fib_rule_put(rule); - break; - } - rtnl_unlock(); - kfree(fib_work); -} - -/* Called with rcu_read_lock() */ -static int rtl83xx_fib_event(struct notifier_block *this, unsigned long event, void *ptr) -{ - struct fib_notifier_info *info = ptr; - struct rtl838x_switch_priv *priv; - struct rtl83xx_fib_event_work *fib_work; - - if ((info->family != AF_INET && info->family != AF_INET6 && - info->family != RTNL_FAMILY_IPMR && - info->family != RTNL_FAMILY_IP6MR)) - return NOTIFY_DONE; - - priv = container_of(this, struct rtl838x_switch_priv, fib_nb); - - /* ignore FIB events for HW with missing L3 offloading implementation */ - if (!priv->r->l3_setup) - return NOTIFY_DONE; - - fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC); - if (!fib_work) - return NOTIFY_BAD; - - INIT_WORK(&fib_work->work, rtl83xx_fib_event_work_do); - fib_work->priv = priv; - fib_work->event = event; - fib_work->is_fib6 = false; - - switch (event) { - case FIB_EVENT_ENTRY_ADD: - case FIB_EVENT_ENTRY_REPLACE: - case FIB_EVENT_ENTRY_APPEND: - case FIB_EVENT_ENTRY_DEL: - pr_debug("%s: FIB_ENTRY ADD/DEL, event %ld\n", __func__, event); - if (info->family == AF_INET) { - struct fib_entry_notifier_info *fen_info = ptr; - - if (fen_info->fi->fib_nh_is_v6) { - NL_SET_ERR_MSG_MOD(info->extack, - "IPv6 gateway with IPv4 route is not supported"); - kfree(fib_work); - return notifier_from_errno(-EINVAL); - } - - 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); - - } else if (info->family == AF_INET6) { - //struct fib6_entry_notifier_info *fen6_info = ptr; - pr_warn("%s: FIB_RULE ADD/DEL for IPv6 not supported\n", __func__); - kfree(fib_work); - return NOTIFY_DONE; - } - break; - - case FIB_EVENT_RULE_ADD: - case FIB_EVENT_RULE_DEL: - pr_debug("%s: FIB_RULE ADD/DEL, event: %ld\n", __func__, event); - memcpy(&fib_work->fr_info, ptr, sizeof(fib_work->fr_info)); - fib_rule_get(fib_work->fr_info.rule); - break; - } - - queue_work(priv->wq, &fib_work->work); - - return NOTIFY_DONE; -} - static irqreturn_t rtldsa_switch_irq(int irq, void *dev_id) { struct rtl838x_switch_priv *priv; @@ -1662,19 +980,19 @@ static int rtl83xx_sw_probe(struct platform_device *pdev) priv->mirror_group_ports[i] = -1; /* Initialize hash table for L3 routing */ - rhltable_init(&priv->routes, &route_ht_params); + rhltable_init(&priv->routes, &otto_l3_route_ht_params); /* Register netevent notifier callback to catch notifications about neighboring * changes to update nexthop entries for L3 routing. */ - priv->ne_nb.notifier_call = rtl83xx_netevent_event; + priv->ne_nb.notifier_call = otto_l3_netevent_notifier; if (register_netevent_notifier(&priv->ne_nb)) { priv->ne_nb.notifier_call = NULL; dev_err(dev, "Failed to register netevent notifier\n"); goto err_register_ne_nb; } - priv->fib_nb.notifier_call = rtl83xx_fib_event; + priv->fib_nb.notifier_call = otto_l3_fib_notifier; /* Register Forwarding Information Base notifier to offload routes where * possible diff --git a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/l3.c b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/l3.c new file mode 100644 index 00000000000..7c3dd28be9c --- /dev/null +++ b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/l3.c @@ -0,0 +1,692 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "l3.h" +#include "rtl-otto.h" + +struct otto_l3_net_event_work { + struct work_struct work; + struct rtl838x_switch_priv *priv; + u64 mac; + u32 gw_addr; +}; + +struct otto_l3_fib_event_work { + struct work_struct work; + union { + struct fib_entry_notifier_info fen_info; + struct fib6_entry_notifier_info fen6_info; + struct fib_rule_notifier_info fr_info; + }; + struct rtl838x_switch_priv *priv; + bool is_fib6; + unsigned long event; +}; + +struct otto_l3_walk_data { + struct rtl838x_switch_priv *priv; + int port; +}; + +static int otto_l3_port_lower_walk(struct net_device *lower, struct netdev_nested_priv *_priv) +{ + struct otto_l3_walk_data *data = (struct otto_l3_walk_data *)_priv->data; + struct rtl838x_switch_priv *priv = data->priv; + int ret = 0; + int index; + + index = rtl83xx_port_is_under(lower, priv); + data->port = index; + if (index >= 0) { + pr_debug("Found DSA-port, index %d\n", index); + ret = 1; + } + + return ret; +} + +static int otto_l3_port_dev_lower_find(struct net_device *dev, struct rtl838x_switch_priv *priv) +{ + struct otto_l3_walk_data data; + struct netdev_nested_priv _priv; + + data.priv = priv; + data.port = 0; + _priv.data = (void *)&data; + + netdev_walk_all_lower_dev(dev, otto_l3_port_lower_walk, &_priv); + + return data.port; +} + +/* On the RTL93xx, an L3 termination endpoint MAC address on which the router waits + * for packets to be routed needs to be allocated. + */ +static int otto_l3_alloc_router_mac(struct rtl838x_switch_priv *priv, u64 mac) +{ + int free_mac = -1; + struct rtl93xx_rt_mac m; + + mutex_lock(&priv->reg_mutex); + for (int i = 0; i < MAX_ROUTER_MACS; i++) { + priv->r->get_l3_router_mac(i, &m); + if (free_mac < 0 && !m.valid) { + free_mac = i; + continue; + } + if (m.valid && m.mac == mac) { + free_mac = i; + break; + } + } + + if (free_mac < 0) { + pr_err("No free router MACs, cannot offload\n"); + mutex_unlock(&priv->reg_mutex); + return -1; + } + + m.valid = true; + m.mac = mac; + m.p_type = 0; /* An individual port, not a trunk port */ + m.p_id = 0x3f; /* Listen on any port */ + m.p_id_mask = 0; + m.vid = 0; /* Listen on any VLAN... */ + m.vid_mask = 0; /* ... so mask needs to be 0 */ + m.mac_mask = 0xffffffffffffULL; /* We want an exact match of the interface MAC */ + m.action = L3_FORWARD; /* Route the packet */ + priv->r->set_l3_router_mac(free_mac, &m); + + mutex_unlock(&priv->reg_mutex); + + return 0; +} + +static int otto_l3_alloc_egress_intf(struct rtl838x_switch_priv *priv, u64 mac, int vlan) +{ + int free_mac = -1; + struct rtl838x_l3_intf intf; + u64 m; + + mutex_lock(&priv->reg_mutex); + for (int i = 0; i < MAX_SMACS; i++) { + m = priv->r->get_l3_egress_mac(L3_EGRESS_DMACS + i); + if (free_mac < 0 && !m) { + free_mac = i; + continue; + } + if (m == mac) { + mutex_unlock(&priv->reg_mutex); + return i; + } + } + + if (free_mac < 0) { + pr_err("No free egress interface, cannot offload\n"); + return -1; + } + + /* Set up default egress interface 1 */ + intf.vid = vlan; + intf.smac_idx = free_mac; + intf.ip4_mtu_id = 1; + intf.ip6_mtu_id = 1; + intf.ttl_scope = 1; /* TTL */ + intf.hl_scope = 1; /* Hop Limit */ + intf.ip4_icmp_redirect = intf.ip6_icmp_redirect = 2; /* FORWARD */ + intf.ip4_pbr_icmp_redirect = intf.ip6_pbr_icmp_redirect = 2; /* FORWARD; */ + priv->r->set_l3_egress_intf(free_mac, &intf); + + priv->r->set_l3_egress_mac(L3_EGRESS_DMACS + free_mac, mac); + + mutex_unlock(&priv->reg_mutex); + + return free_mac; +} + +/* Updates an L3 next hop entry in the ROUTING table */ +static int otto_l3_nexthop_update(struct rtl838x_switch_priv *priv, __be32 ip_addr, u64 mac) +{ + struct otto_l3_route *r; + struct rhlist_head *tmp, *list; + + rcu_read_lock(); + list = rhltable_lookup(&priv->routes, &ip_addr, otto_l3_route_ht_params); + if (!list) { + rcu_read_unlock(); + return -ENOENT; + } + + rhl_for_each_entry_rcu(r, tmp, list, linkage) { + pr_debug("%s: Setting up fwding: ip %pI4, GW mac %016llx\n", + __func__, &ip_addr, mac); + + /* Reads the ROUTING table entry associated with the route */ + priv->r->route_read(r->id, r); + pr_debug("Route with id %d to %pI4 / %d\n", r->id, &r->dst_ip, r->prefix_len); + + r->nh.mac = r->nh.gw = mac; + r->nh.port = priv->r->port_ignore; + r->nh.id = r->id; + + /* Do we need to explicitly add a DMAC entry with the route's nh index? */ + if (priv->r->set_l3_egress_mac) + priv->r->set_l3_egress_mac(r->id, mac); + + /* Update ROUTING table: map gateway-mac and switch-mac id to route id */ + rtl83xx_l2_nexthop_add(priv, &r->nh); + + r->attr.valid = true; + r->attr.action = ROUTE_ACT_FORWARD; + r->attr.type = 0; + r->attr.hit = false; /* Reset route-used indicator */ + + /* Add PIE entry with dst_ip and prefix_len */ + r->pr.dip = r->dst_ip; + r->pr.dip_m = inet_make_mask(r->prefix_len); + + if (r->is_host_route) { + int slot = priv->r->find_l3_slot(r, false); + + pr_info("%s: Got slot for route: %d\n", __func__, slot); + priv->r->host_route_write(slot, r); + } else { + priv->r->route_write(r->id, r); + r->pr.fwd_sel = true; + r->pr.fwd_data = r->nh.l2_id; + r->pr.fwd_act = PIE_ACT_ROUTE_UC; + } + + if (priv->r->set_l3_nexthop) + priv->r->set_l3_nexthop(r->nh.id, r->nh.l2_id, r->nh.if_id); + + if (r->pr.id < 0) { + r->pr.packet_cntr = rtl83xx_packet_cntr_alloc(priv); + if (r->pr.packet_cntr >= 0) { + pr_info("Using packet counter %d\n", r->pr.packet_cntr); + r->pr.log_sel = true; + r->pr.log_data = r->pr.packet_cntr; + } + priv->r->pie_rule_add(priv, &r->pr); + } else { + int pkts = priv->r->packet_cntr_read(r->pr.packet_cntr); + + pr_debug("%s: total packets: %d\n", __func__, pkts); + + priv->r->pie_rule_write(priv, r->pr.id, &r->pr); + } + } + rcu_read_unlock(); + + return 0; +} + +static int otto_l3_port_ipv4_resolve(struct rtl838x_switch_priv *priv, + struct net_device *dev, __be32 ip_addr) +{ + struct neighbour *n = neigh_lookup(&arp_tbl, &ip_addr, dev); + int err = 0; + u64 mac; + + if (!n) { + n = neigh_create(&arp_tbl, &ip_addr, dev); + if (IS_ERR(n)) + return PTR_ERR(n); + } + + /* If the neigh is already resolved, then go ahead and + * install the entry, otherwise start the ARP process to + * resolve the neigh. + */ + if (n->nud_state & NUD_VALID) { + mac = ether_addr_to_u64(n->ha); + pr_info("%s: resolved mac: %016llx\n", __func__, mac); + otto_l3_nexthop_update(priv, ip_addr, mac); + } else { + pr_info("%s: need to wait\n", __func__); + neigh_event_send(n, NULL); + } + + neigh_release(n); + + return err; +} + +static void otto_l3_route_remove(struct rtl838x_switch_priv *priv, struct otto_l3_route *r) +{ + int id; + + if (rhltable_remove(&priv->routes, &r->linkage, otto_l3_route_ht_params)) + dev_warn(priv->dev, "Could not remove route\n"); + + if (r->is_host_route) { + id = priv->r->find_l3_slot(r, false); + pr_debug("%s: Got id for host route: %d\n", __func__, id); + r->attr.valid = false; + priv->r->host_route_write(id, r); + clear_bit(r->id - MAX_ROUTES, priv->host_route_use_bm); + } else { + /* If there is a HW representation of the route, delete it */ + if (priv->r->route_lookup_hw) { + id = priv->r->route_lookup_hw(r); + pr_info("%s: Got id for prefix route: %d\n", __func__, id); + r->attr.valid = false; + priv->r->route_write(id, r); + } + clear_bit(r->id, priv->route_use_bm); + } + + kfree(r); +} + +static struct otto_l3_route *otto_l3_host_route_alloc(struct rtl838x_switch_priv *priv, u32 ip) +{ + struct otto_l3_route *r; + int idx = 0, err; + + mutex_lock(&priv->reg_mutex); + + idx = find_first_zero_bit(priv->host_route_use_bm, MAX_HOST_ROUTES); + pr_debug("%s id: %d, ip %pI4\n", __func__, idx, &ip); + + r = kzalloc(sizeof(*r), GFP_KERNEL); + if (!r) { + mutex_unlock(&priv->reg_mutex); + return r; + } + + /* We require a unique route ID irrespective of whether it is a prefix or host + * route (on RTL93xx) as we use this ID to associate a DMAC and next-hop entry + */ + r->id = idx + MAX_ROUTES; + + r->gw_ip = ip; + r->pr.id = -1; /* We still need to allocate a rule in HW */ + r->is_host_route = true; + + err = rhltable_insert(&priv->routes, &r->linkage, otto_l3_route_ht_params); + if (err) { + pr_err("Could not insert new rule\n"); + mutex_unlock(&priv->reg_mutex); + goto out_free; + } + + set_bit(idx, priv->host_route_use_bm); + + mutex_unlock(&priv->reg_mutex); + + return r; + +out_free: + kfree(r); + + return NULL; +} + +static struct otto_l3_route *otto_l3_route_alloc(struct rtl838x_switch_priv *priv, u32 ip) +{ + struct otto_l3_route *r; + int idx = 0, err; + + mutex_lock(&priv->reg_mutex); + + idx = find_first_zero_bit(priv->route_use_bm, MAX_ROUTES); + pr_debug("%s id: %d, ip %pI4\n", __func__, idx, &ip); + + r = kzalloc(sizeof(*r), GFP_KERNEL); + if (!r) { + mutex_unlock(&priv->reg_mutex); + return r; + } + + r->id = idx; + r->gw_ip = ip; + r->pr.id = -1; /* We still need to allocate a rule in HW */ + r->is_host_route = false; + + err = rhltable_insert(&priv->routes, &r->linkage, otto_l3_route_ht_params); + if (err) { + pr_err("Could not insert new rule\n"); + mutex_unlock(&priv->reg_mutex); + goto out_free; + } + + set_bit(idx, priv->route_use_bm); + + mutex_unlock(&priv->reg_mutex); + + return r; + +out_free: + kfree(r); + + return NULL; +} + +static int otto_l3_fib_check_v4(struct rtl838x_switch_priv *priv, + struct fib_entry_notifier_info *info, + enum fib_event_type event) +{ + struct net_device *ndev = fib_info_nh(info->fi, 0)->fib_nh_dev; + int vlan = is_vlan_dev(ndev) ? vlan_dev_vlan_id(ndev) : 0; + struct fib_nh *nh = fib_info_nh(info->fi, 0); + char gw_message[32] = ""; + + if (nh->fib_nh_gw4) + snprintf(gw_message, sizeof(gw_message), "via %pI4 ", &nh->fib_nh_gw4); + + dev_info(priv->dev, "%s IPv4 route %pI4/%d %s(VLAN %d, MAC %pM)\n", + event == FIB_EVENT_ENTRY_ADD ? "add" : "delete", + &info->dst, info->dst_len, gw_message, vlan, ndev->dev_addr); + + if ((info->type == RTN_BROADCAST) || ipv4_is_loopback(info->dst) || !info->dst) { + dev_warn(priv->dev, "skip loopback/broadcast addresses and default routes\n"); + return -EINVAL; + } + + return 0; +} + +static int otto_l3_fib_add_v4(struct rtl838x_switch_priv *priv, + struct fib_entry_notifier_info *info) +{ + struct net_device *ndev = fib_info_nh(info->fi, 0)->fib_nh_dev; + int vlan = is_vlan_dev(ndev) ? vlan_dev_vlan_id(ndev) : 0; + struct fib_nh *nh = fib_info_nh(info->fi, 0); + struct otto_l3_route *route; + int port; + + if (otto_l3_fib_check_v4(priv, info, FIB_EVENT_ENTRY_ADD)) + return 0; + + port = otto_l3_port_dev_lower_find(ndev, priv); + if (port < 0) { + dev_err(priv->dev, "lower interface %s not found\n", ndev->name); + return -ENODEV; + } + + /* Allocate route or host-route entry (if hardware supports this) */ + if (info->dst_len == 32 && priv->r->host_route_write) + route = otto_l3_host_route_alloc(priv, nh->fib_nh_gw4); + else + route = otto_l3_route_alloc(priv, nh->fib_nh_gw4); + + if (route) + dev_info(priv->dev, "route hashtable extended for gw %pI4\n", &nh->fib_nh_gw4); + else { + dev_err(priv->dev, "could not extend route hashtable for gw %pI4\n", &nh->fib_nh_gw4); + return -ENOSPC; + } + + route->dst_ip = info->dst; + route->prefix_len = info->dst_len; + route->nh.rvid = vlan; + + if (priv->r->set_l3_router_mac) { + u64 mac = ether_addr_to_u64(ndev->dev_addr); + + pr_debug("Local route and router MAC %pM\n", ndev->dev_addr); + if (otto_l3_alloc_router_mac(priv, mac)) + goto out_free_rt; + + /* vid = 0: Do not care about VID */ + route->nh.if_id = otto_l3_alloc_egress_intf(priv, mac, vlan); + if (route->nh.if_id < 0) + goto out_free_rmac; + + if (!nh->fib_nh_gw4) { + int slot; + + route->nh.mac = mac; + route->nh.port = priv->r->port_ignore; + route->attr.valid = true; + route->attr.action = ROUTE_ACT_TRAP2CPU; + route->attr.type = 0; + + slot = priv->r->find_l3_slot(route, false); + pr_debug("%s: Got slot for route: %d\n", __func__, slot); + priv->r->host_route_write(slot, route); + } + } + + /* We need to resolve the mac address of the GW */ + if (nh->fib_nh_gw4) + otto_l3_port_ipv4_resolve(priv, ndev, nh->fib_nh_gw4); + + nh->fib_nh_flags |= RTNH_F_OFFLOAD; + + return 0; + +out_free_rmac: +out_free_rt: + return 0; +} + +static int otto_l3_fib_del_v4(struct rtl838x_switch_priv *priv, + struct fib_entry_notifier_info *info) +{ + struct fib_nh *nh = fib_info_nh(info->fi, 0); + struct rhlist_head *tmp, *list; + struct otto_l3_route *route; + + if (otto_l3_fib_check_v4(priv, info, FIB_EVENT_ENTRY_DEL)) + return 0; + + rcu_read_lock(); + list = rhltable_lookup(&priv->routes, &nh->fib_nh_gw4, otto_l3_route_ht_params); + if (!list) { + rcu_read_unlock(); + dev_err(priv->dev, "no such gateway: %pI4\n", &nh->fib_nh_gw4); + return -ENOENT; + } + rhl_for_each_entry_rcu(route, tmp, list, linkage) { + if (route->dst_ip == info->dst && route->prefix_len == info->dst_len) { + dev_info(priv->dev, "found a route with id %d, nh-id %d\n", + route->id, route->nh.id); + break; + } + } + rcu_read_unlock(); + + rtl83xx_l2_nexthop_rm(priv, &route->nh); + + dev_info(priv->dev, "releasing packet counter %d\n", route->pr.packet_cntr); + set_bit(route->pr.packet_cntr, priv->packet_cntr_use_bm); + priv->r->pie_rule_rm(priv, &route->pr); + + otto_l3_route_remove(priv, route); + + nh->fib_nh_flags &= ~RTNH_F_OFFLOAD; + + return 0; +} + +static int otto_l3_fib_add_v6(struct rtl838x_switch_priv *priv, + struct fib6_entry_notifier_info *info) +{ + pr_debug("In %s\n", __func__); +/* nh->fib_nh_flags |= RTNH_F_OFFLOAD; */ + + return 0; +} + +static void otto_l3_fib_event_work_do(struct work_struct *work) +{ + struct otto_l3_fib_event_work *fib_work = + container_of(work, struct otto_l3_fib_event_work, work); + struct rtl838x_switch_priv *priv = fib_work->priv; + struct fib_rule *rule; + int err; + + /* Protect internal structures from changes */ + rtnl_lock(); + pr_debug("%s: doing work, event %ld\n", __func__, fib_work->event); + switch (fib_work->event) { + case FIB_EVENT_ENTRY_ADD: + case FIB_EVENT_ENTRY_REPLACE: + case FIB_EVENT_ENTRY_APPEND: + if (fib_work->is_fib6) + err = otto_l3_fib_add_v6(priv, &fib_work->fen6_info); + else + err = otto_l3_fib_add_v4(priv, &fib_work->fen_info); + if (err) + dev_err(priv->dev, "fib_add() failed\n"); + + fib_info_put(fib_work->fen_info.fi); + break; + case FIB_EVENT_ENTRY_DEL: + err = otto_l3_fib_del_v4(priv, &fib_work->fen_info); + if (err) + dev_err(priv->dev, "fib_del() failed\n"); + + fib_info_put(fib_work->fen_info.fi); + break; + case FIB_EVENT_RULE_ADD: + case FIB_EVENT_RULE_DEL: + rule = fib_work->fr_info.rule; + if (!fib4_rule_default(rule)) + pr_err("%s: FIB4 default rule failed\n", __func__); + fib_rule_put(rule); + break; + } + rtnl_unlock(); + kfree(fib_work); +} + + +/* Called with rcu_read_lock() */ +int otto_l3_fib_notifier(struct notifier_block *this, unsigned long event, void *ptr) +{ + struct fib_notifier_info *info = ptr; + struct rtl838x_switch_priv *priv; + struct otto_l3_fib_event_work *fib_work; + + if ((info->family != AF_INET && info->family != AF_INET6 && + info->family != RTNL_FAMILY_IPMR && + info->family != RTNL_FAMILY_IP6MR)) + return NOTIFY_DONE; + + priv = container_of(this, struct rtl838x_switch_priv, fib_nb); + + /* ignore FIB events for HW with missing L3 offloading implementation */ + if (!priv->r->l3_setup) + return NOTIFY_DONE; + + fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC); + if (!fib_work) + return NOTIFY_BAD; + + INIT_WORK(&fib_work->work, otto_l3_fib_event_work_do); + fib_work->priv = priv; + fib_work->event = event; + fib_work->is_fib6 = false; + + switch (event) { + case FIB_EVENT_ENTRY_ADD: + case FIB_EVENT_ENTRY_REPLACE: + case FIB_EVENT_ENTRY_APPEND: + case FIB_EVENT_ENTRY_DEL: + pr_debug("%s: FIB_ENTRY ADD/DEL, event %ld\n", __func__, event); + if (info->family == AF_INET) { + struct fib_entry_notifier_info *fen_info = ptr; + + if (fen_info->fi->fib_nh_is_v6) { + NL_SET_ERR_MSG_MOD(info->extack, + "IPv6 gateway with IPv4 route is not supported"); + kfree(fib_work); + return notifier_from_errno(-EINVAL); + } + + 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); + + } else if (info->family == AF_INET6) { + //struct fib6_entry_notifier_info *fen6_info = ptr; + pr_warn("%s: FIB_RULE ADD/DEL for IPv6 not supported\n", __func__); + kfree(fib_work); + return NOTIFY_DONE; + } + break; + + case FIB_EVENT_RULE_ADD: + case FIB_EVENT_RULE_DEL: + pr_debug("%s: FIB_RULE ADD/DEL, event: %ld\n", __func__, event); + memcpy(&fib_work->fr_info, ptr, sizeof(fib_work->fr_info)); + fib_rule_get(fib_work->fr_info.rule); + break; + } + + queue_work(priv->wq, &fib_work->work); + + return NOTIFY_DONE; +} + +static void otto_l3_net_event_work_do(struct work_struct *work) +{ + struct otto_l3_net_event_work *net_work = + container_of(work, struct otto_l3_net_event_work, work); + struct rtl838x_switch_priv *priv = net_work->priv; + + otto_l3_nexthop_update(priv, net_work->gw_addr, net_work->mac); + + kfree(net_work); +} + +int otto_l3_netevent_notifier(struct notifier_block *this, unsigned long event, void *ptr) +{ + struct rtl838x_switch_priv *priv; + struct net_device *dev; + struct neighbour *n = ptr; + int err, port; + struct otto_l3_net_event_work *net_work; + + priv = container_of(this, struct rtl838x_switch_priv, ne_nb); + + switch (event) { + case NETEVENT_NEIGH_UPDATE: + /* ignore events for HW with missing L3 offloading implementation */ + if (!priv->r->l3_setup) + return NOTIFY_DONE; + + if (n->tbl != &arp_tbl) + return NOTIFY_DONE; + dev = n->dev; + port = otto_l3_port_dev_lower_find(dev, priv); + if (port < 0 || !(n->nud_state & NUD_VALID)) { + pr_debug("%s: Neigbour invalid, not updating\n", __func__); + return NOTIFY_DONE; + } + + net_work = kzalloc(sizeof(*net_work), GFP_ATOMIC); + if (!net_work) + return NOTIFY_BAD; + + INIT_WORK(&net_work->work, otto_l3_net_event_work_do); + net_work->priv = priv; + + net_work->mac = ether_addr_to_u64(n->ha); + net_work->gw_addr = *(__be32 *)n->primary_key; + + pr_debug("%s: updating neighbour on port %d, mac %016llx\n", + __func__, port, net_work->mac); + queue_work(priv->wq, &net_work->work); + if (err) + netdev_warn(dev, "failed to handle neigh update (err %d)\n", err); + break; + } + + return NOTIFY_DONE; +} diff --git a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/l3.h b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/l3.h new file mode 100644 index 00000000000..50257984044 --- /dev/null +++ b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/l3.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _OTTO_L3_H +#define _OTTO_L3_H + +#include "rtl-otto.h" + +struct otto_l3_route_attr { + bool valid; + bool hit; + bool ttl_dec; + bool ttl_check; + bool dst_null; + bool qos_as; + u8 qos_prio; + u8 type; + u8 action; +}; + +struct otto_l3_nexthop { + u16 id; /* ID: L3_NEXT_HOP table-index or route-index set in L2_NEXT_HOP */ + u32 dev_id; + u16 port; + u16 vid; /* VLAN-ID for L2 table entry (saved from L2-UC entry) */ + u16 rvid; /* Relay VID/FID for the L2 table entry */ + u64 mac; /* The MAC address of the entry in the L2_NEXT_HOP table */ + u16 mac_id; + u16 l2_id; /* Index of this next hop forwarding entry in L2 FIB table */ + u64 gw; /* The gateway MAC address packets are forwarded to */ + int if_id; /* Interface (into L3_EGR_INTF_IDX) */ +}; + +struct otto_l3_route { + u32 gw_ip; /* IP of the route's gateway */ + u32 dst_ip; /* IP of the destination net */ + struct in6_addr dst_ip6; + int prefix_len; /* Network prefix len of the destination net */ + bool is_host_route; + int id; /* ID number of this route */ + struct rhlist_head linkage; + u16 switch_mac_id; /* Index into switch's own MACs, RTL839X only */ + struct otto_l3_nexthop nh; + struct pie_rule pr; + struct otto_l3_route_attr attr; +}; + +static const struct rhashtable_params otto_l3_route_ht_params = { + .key_len = sizeof(u32), + .key_offset = offsetof(struct otto_l3_route, gw_ip), + .head_offset = offsetof(struct otto_l3_route, linkage), +}; + +int otto_l3_fib_notifier(struct notifier_block *this, unsigned long event, void *ptr); +int otto_l3_netevent_notifier(struct notifier_block *this, unsigned long event, void *ptr); + +#endif /* _OTTO_L3_H */ diff --git a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/rtl-otto.h b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/rtl-otto.h index 6ed1ed4fc19..8a1aca7cfa6 100644 --- a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/rtl-otto.h +++ b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/rtl-otto.h @@ -898,6 +898,8 @@ typedef enum { */ #define RTLDSA_COUNTERS_FAST_POLL_INTERVAL (3 * HZ) +struct otto_l3_route; + enum pbvlan_type { PBVLAN_TYPE_INNER = 0, PBVLAN_TYPE_OUTER, @@ -1302,19 +1304,6 @@ struct rtl93xx_rt_mac { u64 mac_mask; }; -struct rtl83xx_nexthop { - u16 id; /* ID: L3_NEXT_HOP table-index or route-index set in L2_NEXT_HOP */ - u32 dev_id; - u16 port; - u16 vid; /* VLAN-ID for L2 table entry (saved from L2-UC entry) */ - u16 rvid; /* Relay VID/FID for the L2 table entry */ - u64 mac; /* The MAC address of the entry in the L2_NEXT_HOP table */ - u16 mac_id; - u16 l2_id; /* Index of this next hop forwarding entry in L2 FIB table */ - u64 gw; /* The gateway MAC address packets are forwarded to */ - int if_id; /* Interface (into L3_EGR_INTF_IDX) */ -}; - struct rtl838x_switch_priv; struct rtl83xx_flow { @@ -1326,32 +1315,6 @@ struct rtl83xx_flow { u32 flags; }; -struct rtl93xx_route_attr { - bool valid; - bool hit; - bool ttl_dec; - bool ttl_check; - bool dst_null; - bool qos_as; - u8 qos_prio; - u8 type; - u8 action; -}; - -struct rtl83xx_route { - u32 gw_ip; /* IP of the route's gateway */ - u32 dst_ip; /* IP of the destination net */ - struct in6_addr dst_ip6; - int prefix_len; /* Network prefix len of the destination net */ - bool is_host_route; - int id; /* ID number of this route */ - struct rhlist_head linkage; - u16 switch_mac_id; /* Index into switch's own MACs, RTL839X only */ - struct rtl83xx_nexthop nh; - struct pie_rule pr; - struct rtl93xx_route_attr attr; -}; - /** * struct rtldsa_mirror_config - Mirror configuration for specific group and port */ @@ -1474,16 +1437,16 @@ struct rtldsa_config { void (*l2_learning_setup)(void); u32 (*packet_cntr_read)(int counter); void (*packet_cntr_clear)(int counter); - void (*route_read)(int idx, struct rtl83xx_route *rt); - void (*route_write)(int idx, struct rtl83xx_route *rt); - void (*host_route_write)(int idx, struct rtl83xx_route *rt); + void (*route_read)(int idx, struct otto_l3_route *rt); + void (*route_write)(int idx, struct otto_l3_route *rt); + void (*host_route_write)(int idx, struct otto_l3_route *rt); int (*l3_setup)(struct rtl838x_switch_priv *priv); void (*set_l3_nexthop)(int idx, u16 dmac_id, u16 interface); void (*get_l3_nexthop)(int idx, u16 *dmac_id, u16 *interface); u64 (*get_l3_egress_mac)(u32 idx); void (*set_l3_egress_mac)(u32 idx, u64 mac); - int (*find_l3_slot)(struct rtl83xx_route *rt, bool must_exist); - int (*route_lookup_hw)(struct rtl83xx_route *rt); + int (*find_l3_slot)(struct otto_l3_route *rt, bool must_exist); + int (*route_lookup_hw)(struct otto_l3_route *rt); void (*get_l3_router_mac)(u32 idx, struct rtl93xx_rt_mac *m); void (*set_l3_router_mac)(u32 idx, struct rtl93xx_rt_mac *m); void (*set_l3_egress_intf)(int idx, struct rtl838x_l3_intf *intf); @@ -1789,6 +1752,12 @@ void rtldsa_counters_unlock_table(struct rtl838x_switch_priv *priv, int port) void rtldsa_update_counters_atomically(struct rtl838x_switch_priv *priv, int port); + +struct otto_l3_nexthop; +int rtl83xx_l2_nexthop_add(struct rtl838x_switch_priv *priv, struct otto_l3_nexthop *nh); +int rtl83xx_l2_nexthop_rm(struct rtl838x_switch_priv *priv, struct otto_l3_nexthop *nh); + + extern int rtldsa_max_available_queue[]; extern int rtldsa_default_queue_weights[]; diff --git a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/rtl838x.c b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/rtl838x.c index aa118ff7caa..ae193e25372 100644 --- a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/rtl838x.c +++ b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/rtl838x.c @@ -5,6 +5,7 @@ #include #include +#include "l3.h" #include "rtl-otto.h" #define RTL838X_VLAN_PORT_TAG_STS_UNTAG 0x0 @@ -1627,7 +1628,7 @@ static void rtl838x_packet_cntr_clear(int counter) rtl_table_release(r); } -static void rtl838x_route_read(int idx, struct rtl83xx_route *rt) +static void rtl838x_route_read(int idx, struct otto_l3_route *rt) { /* Read ROUTING table (2) via register RTL8380_TBL_1 */ struct table_reg *r = rtl_table_get(RTL8380_TBL_1, 2); @@ -1643,7 +1644,7 @@ static void rtl838x_route_read(int idx, struct rtl83xx_route *rt) rtl_table_release(r); } -static void rtl838x_route_write(int idx, struct rtl83xx_route *rt) +static void rtl838x_route_write(int idx, struct otto_l3_route *rt) { /* Access ROUTING table (2) via register RTL8380_TBL_1 */ struct table_reg *r = rtl_table_get(RTL8380_TBL_1, 2); diff --git a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/rtl839x.c b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/rtl839x.c index b932cee3b1f..9e9d11a544b 100644 --- a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/rtl839x.c +++ b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/rtl839x.c @@ -3,6 +3,7 @@ #include #include +#include "l3.h" #include "rtl-otto.h" #define RTL839X_VLAN_PORT_TAG_STS_UNTAG 0x0 @@ -1532,7 +1533,7 @@ static void rtl839x_packet_cntr_clear(int counter) rtl_table_release(r); } -static void rtl839x_route_read(int idx, struct rtl83xx_route *rt) +static void rtl839x_route_read(int idx, struct otto_l3_route *rt) { u64 v; /* Read ROUTING table (2) via register RTL8390_TBL_1 */ @@ -1551,7 +1552,7 @@ static void rtl839x_route_read(int idx, struct rtl83xx_route *rt) rtl_table_release(r); } -static void rtl839x_route_write(int idx, struct rtl83xx_route *rt) +static void rtl839x_route_write(int idx, struct otto_l3_route *rt) { u32 v; diff --git a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/rtl930x.c b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/rtl930x.c index 3e406824f57..1c83510d9ef 100644 --- a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/rtl930x.c +++ b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/rtl930x.c @@ -4,6 +4,7 @@ #include #include +#include "l3.h" #include "rtl-otto.h" #define RTL930X_VLAN_PORT_TAG_STS_INTERNAL 0x0 @@ -1262,7 +1263,7 @@ static u32 rtl930x_l3_hash4(u32 ip, int algorithm, bool move_dip) /* Read a prefix route entry from the L3_PREFIX_ROUTE_IPUC table * We currently only support IPv4 and IPv6 unicast route */ -static void rtl930x_route_read(int idx, struct rtl83xx_route *rt) +static void rtl930x_route_read(int idx, struct otto_l3_route *rt) { u32 v, ip4_m; bool host_route, default_route; @@ -1344,7 +1345,7 @@ static void rtl930x_net6_mask(int prefix_len, struct in6_addr *ip6_m) /* Read a host route entry from the table using its index * We currently only support IPv4 and IPv6 unicast route */ -static void rtl930x_host_route_read(int idx, struct rtl83xx_route *rt) +static void rtl930x_host_route_read(int idx, struct otto_l3_route *rt) { u32 v; /* Read L3_HOST_ROUTE_IPUC table (1) via register RTL9300_TBL_1 */ @@ -1396,7 +1397,7 @@ out: /* Write a host route entry from the table using its index * We currently only support IPv4 and IPv6 unicast route */ -static void rtl930x_host_route_write(int idx, struct rtl83xx_route *rt) +static void rtl930x_host_route_write(int idx, struct otto_l3_route *rt) { u32 v; /* Access L3_HOST_ROUTE_IPUC table (1) via register RTL9300_TBL_1 */ @@ -1451,7 +1452,7 @@ out: /* Look up the index of a prefix route in the routing table CAM for unicast IPv4/6 routes * using hardware offload. */ -static int rtl930x_route_lookup_hw(struct rtl83xx_route *rt) +static int rtl930x_route_lookup_hw(struct otto_l3_route *rt) { u32 ip4_m, v; struct in6_addr ip6_m; @@ -1492,11 +1493,11 @@ static int rtl930x_route_lookup_hw(struct rtl83xx_route *rt) return -1; } -static int rtl930x_find_l3_slot(struct rtl83xx_route *rt, bool must_exist) +static int rtl930x_find_l3_slot(struct otto_l3_route *rt, bool must_exist) { int slot_width, algorithm, addr, idx; u32 hash; - struct rtl83xx_route route_entry; + struct otto_l3_route route_entry; /* IPv6 entries take up 3 slots */ slot_width = (rt->attr.type == 0) || (rt->attr.type == 2) ? 1 : 3; @@ -1529,7 +1530,7 @@ static int rtl930x_find_l3_slot(struct rtl83xx_route *rt, bool must_exist) /* Write a prefix route into the routing table CAM at position idx * Currently only IPv4 and IPv6 unicast routes are supported */ -static void rtl930x_route_write(int idx, struct rtl83xx_route *rt) +static void rtl930x_route_write(int idx, struct otto_l3_route *rt) { u32 v, ip4_m; struct in6_addr ip6_m;