18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Handling of a single switch chip, part of a switch fabric 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2017 Savoir-faire Linux Inc. 68c2ecf20Sopenharmony_ci * Vivien Didelot <vivien.didelot@savoirfairelinux.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/if_bridge.h> 108c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 118c2ecf20Sopenharmony_ci#include <linux/notifier.h> 128c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 138c2ecf20Sopenharmony_ci#include <net/switchdev.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "dsa_priv.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic unsigned int dsa_switch_fastest_ageing_time(struct dsa_switch *ds, 188c2ecf20Sopenharmony_ci unsigned int ageing_time) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci int i; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci for (i = 0; i < ds->num_ports; ++i) { 238c2ecf20Sopenharmony_ci struct dsa_port *dp = dsa_to_port(ds, i); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci if (dp->ageing_time && dp->ageing_time < ageing_time) 268c2ecf20Sopenharmony_ci ageing_time = dp->ageing_time; 278c2ecf20Sopenharmony_ci } 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci return ageing_time; 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int dsa_switch_ageing_time(struct dsa_switch *ds, 338c2ecf20Sopenharmony_ci struct dsa_notifier_ageing_time_info *info) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci unsigned int ageing_time = info->ageing_time; 368c2ecf20Sopenharmony_ci struct switchdev_trans *trans = info->trans; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci if (switchdev_trans_ph_prepare(trans)) { 398c2ecf20Sopenharmony_ci if (ds->ageing_time_min && ageing_time < ds->ageing_time_min) 408c2ecf20Sopenharmony_ci return -ERANGE; 418c2ecf20Sopenharmony_ci if (ds->ageing_time_max && ageing_time > ds->ageing_time_max) 428c2ecf20Sopenharmony_ci return -ERANGE; 438c2ecf20Sopenharmony_ci return 0; 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci /* Program the fastest ageing time in case of multiple bridges */ 478c2ecf20Sopenharmony_ci ageing_time = dsa_switch_fastest_ageing_time(ds, ageing_time); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if (ds->ops->set_ageing_time) 508c2ecf20Sopenharmony_ci return ds->ops->set_ageing_time(ds, ageing_time); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return 0; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic bool dsa_switch_mtu_match(struct dsa_switch *ds, int port, 568c2ecf20Sopenharmony_ci struct dsa_notifier_mtu_info *info) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci if (ds->index == info->sw_index) 598c2ecf20Sopenharmony_ci return (port == info->port) || dsa_is_dsa_port(ds, port); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (!info->propagate_upstream) 628c2ecf20Sopenharmony_ci return false; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port)) 658c2ecf20Sopenharmony_ci return true; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci return false; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int dsa_switch_mtu(struct dsa_switch *ds, 718c2ecf20Sopenharmony_ci struct dsa_notifier_mtu_info *info) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci int port, ret; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (!ds->ops->port_change_mtu) 768c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci for (port = 0; port < ds->num_ports; port++) { 798c2ecf20Sopenharmony_ci if (dsa_switch_mtu_match(ds, port, info)) { 808c2ecf20Sopenharmony_ci ret = ds->ops->port_change_mtu(ds, port, info->mtu); 818c2ecf20Sopenharmony_ci if (ret) 828c2ecf20Sopenharmony_ci return ret; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return 0; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic int dsa_switch_bridge_join(struct dsa_switch *ds, 908c2ecf20Sopenharmony_ci struct dsa_notifier_bridge_info *info) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct dsa_switch_tree *dst = ds->dst; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (dst->index == info->tree_index && ds->index == info->sw_index && 958c2ecf20Sopenharmony_ci ds->ops->port_bridge_join) 968c2ecf20Sopenharmony_ci return ds->ops->port_bridge_join(ds, info->port, info->br); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if ((dst->index != info->tree_index || ds->index != info->sw_index) && 998c2ecf20Sopenharmony_ci ds->ops->crosschip_bridge_join) 1008c2ecf20Sopenharmony_ci return ds->ops->crosschip_bridge_join(ds, info->tree_index, 1018c2ecf20Sopenharmony_ci info->sw_index, 1028c2ecf20Sopenharmony_ci info->port, info->br); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return 0; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int dsa_switch_bridge_leave(struct dsa_switch *ds, 1088c2ecf20Sopenharmony_ci struct dsa_notifier_bridge_info *info) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci bool unset_vlan_filtering = br_vlan_enabled(info->br); 1118c2ecf20Sopenharmony_ci struct dsa_switch_tree *dst = ds->dst; 1128c2ecf20Sopenharmony_ci int err, i; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (dst->index == info->tree_index && ds->index == info->sw_index && 1158c2ecf20Sopenharmony_ci ds->ops->port_bridge_leave) 1168c2ecf20Sopenharmony_ci ds->ops->port_bridge_leave(ds, info->port, info->br); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if ((dst->index != info->tree_index || ds->index != info->sw_index) && 1198c2ecf20Sopenharmony_ci ds->ops->crosschip_bridge_leave) 1208c2ecf20Sopenharmony_ci ds->ops->crosschip_bridge_leave(ds, info->tree_index, 1218c2ecf20Sopenharmony_ci info->sw_index, info->port, 1228c2ecf20Sopenharmony_ci info->br); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* If the bridge was vlan_filtering, the bridge core doesn't trigger an 1258c2ecf20Sopenharmony_ci * event for changing vlan_filtering setting upon slave ports leaving 1268c2ecf20Sopenharmony_ci * it. That is a good thing, because that lets us handle it and also 1278c2ecf20Sopenharmony_ci * handle the case where the switch's vlan_filtering setting is global 1288c2ecf20Sopenharmony_ci * (not per port). When that happens, the correct moment to trigger the 1298c2ecf20Sopenharmony_ci * vlan_filtering callback is only when the last port left this bridge. 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_ci if (unset_vlan_filtering && ds->vlan_filtering_is_global) { 1328c2ecf20Sopenharmony_ci for (i = 0; i < ds->num_ports; i++) { 1338c2ecf20Sopenharmony_ci if (i == info->port) 1348c2ecf20Sopenharmony_ci continue; 1358c2ecf20Sopenharmony_ci if (dsa_to_port(ds, i)->bridge_dev == info->br) { 1368c2ecf20Sopenharmony_ci unset_vlan_filtering = false; 1378c2ecf20Sopenharmony_ci break; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci if (unset_vlan_filtering) { 1428c2ecf20Sopenharmony_ci struct switchdev_trans trans; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci trans.ph_prepare = true; 1458c2ecf20Sopenharmony_ci err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port), 1468c2ecf20Sopenharmony_ci false, &trans); 1478c2ecf20Sopenharmony_ci if (err && err != EOPNOTSUPP) 1488c2ecf20Sopenharmony_ci return err; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci trans.ph_prepare = false; 1518c2ecf20Sopenharmony_ci err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port), 1528c2ecf20Sopenharmony_ci false, &trans); 1538c2ecf20Sopenharmony_ci if (err && err != EOPNOTSUPP) 1548c2ecf20Sopenharmony_ci return err; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int dsa_switch_fdb_add(struct dsa_switch *ds, 1608c2ecf20Sopenharmony_ci struct dsa_notifier_fdb_info *info) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci int port = dsa_towards_port(ds, info->sw_index, info->port); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (!ds->ops->port_fdb_add) 1658c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return ds->ops->port_fdb_add(ds, port, info->addr, info->vid); 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic int dsa_switch_fdb_del(struct dsa_switch *ds, 1718c2ecf20Sopenharmony_ci struct dsa_notifier_fdb_info *info) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci int port = dsa_towards_port(ds, info->sw_index, info->port); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (!ds->ops->port_fdb_del) 1768c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return ds->ops->port_fdb_del(ds, port, info->addr, info->vid); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic bool dsa_switch_mdb_match(struct dsa_switch *ds, int port, 1828c2ecf20Sopenharmony_ci struct dsa_notifier_mdb_info *info) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci if (ds->index == info->sw_index && port == info->port) 1858c2ecf20Sopenharmony_ci return true; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (dsa_is_dsa_port(ds, port)) 1888c2ecf20Sopenharmony_ci return true; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return false; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic int dsa_switch_mdb_prepare(struct dsa_switch *ds, 1948c2ecf20Sopenharmony_ci struct dsa_notifier_mdb_info *info) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci int port, err; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (!ds->ops->port_mdb_prepare || !ds->ops->port_mdb_add) 1998c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci for (port = 0; port < ds->num_ports; port++) { 2028c2ecf20Sopenharmony_ci if (dsa_switch_mdb_match(ds, port, info)) { 2038c2ecf20Sopenharmony_ci err = ds->ops->port_mdb_prepare(ds, port, info->mdb); 2048c2ecf20Sopenharmony_ci if (err) 2058c2ecf20Sopenharmony_ci return err; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return 0; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int dsa_switch_mdb_add(struct dsa_switch *ds, 2138c2ecf20Sopenharmony_ci struct dsa_notifier_mdb_info *info) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci int port; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (switchdev_trans_ph_prepare(info->trans)) 2188c2ecf20Sopenharmony_ci return dsa_switch_mdb_prepare(ds, info); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (!ds->ops->port_mdb_add) 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci for (port = 0; port < ds->num_ports; port++) 2248c2ecf20Sopenharmony_ci if (dsa_switch_mdb_match(ds, port, info)) 2258c2ecf20Sopenharmony_ci ds->ops->port_mdb_add(ds, port, info->mdb); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci return 0; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic int dsa_switch_mdb_del(struct dsa_switch *ds, 2318c2ecf20Sopenharmony_ci struct dsa_notifier_mdb_info *info) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci if (!ds->ops->port_mdb_del) 2348c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (ds->index == info->sw_index) 2378c2ecf20Sopenharmony_ci return ds->ops->port_mdb_del(ds, info->port, info->mdb); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic bool dsa_switch_vlan_match(struct dsa_switch *ds, int port, 2438c2ecf20Sopenharmony_ci struct dsa_notifier_vlan_info *info) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci if (ds->index == info->sw_index && port == info->port) 2468c2ecf20Sopenharmony_ci return true; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (dsa_is_dsa_port(ds, port)) 2498c2ecf20Sopenharmony_ci return true; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return false; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic int dsa_switch_vlan_prepare(struct dsa_switch *ds, 2558c2ecf20Sopenharmony_ci struct dsa_notifier_vlan_info *info) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci int port, err; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (!ds->ops->port_vlan_prepare || !ds->ops->port_vlan_add) 2608c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci for (port = 0; port < ds->num_ports; port++) { 2638c2ecf20Sopenharmony_ci if (dsa_switch_vlan_match(ds, port, info)) { 2648c2ecf20Sopenharmony_ci err = ds->ops->port_vlan_prepare(ds, port, info->vlan); 2658c2ecf20Sopenharmony_ci if (err) 2668c2ecf20Sopenharmony_ci return err; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic int dsa_switch_vlan_add(struct dsa_switch *ds, 2748c2ecf20Sopenharmony_ci struct dsa_notifier_vlan_info *info) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci int port; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (switchdev_trans_ph_prepare(info->trans)) 2798c2ecf20Sopenharmony_ci return dsa_switch_vlan_prepare(ds, info); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (!ds->ops->port_vlan_add) 2828c2ecf20Sopenharmony_ci return 0; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci for (port = 0; port < ds->num_ports; port++) 2858c2ecf20Sopenharmony_ci if (dsa_switch_vlan_match(ds, port, info)) 2868c2ecf20Sopenharmony_ci ds->ops->port_vlan_add(ds, port, info->vlan); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return 0; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic int dsa_switch_vlan_del(struct dsa_switch *ds, 2928c2ecf20Sopenharmony_ci struct dsa_notifier_vlan_info *info) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci if (!ds->ops->port_vlan_del) 2958c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (ds->index == info->sw_index) 2988c2ecf20Sopenharmony_ci return ds->ops->port_vlan_del(ds, info->port, info->vlan); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* Do not deprogram the DSA links as they may be used as conduit 3018c2ecf20Sopenharmony_ci * for other VLAN members in the fabric. 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_ci return 0; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic int dsa_switch_event(struct notifier_block *nb, 3078c2ecf20Sopenharmony_ci unsigned long event, void *info) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct dsa_switch *ds = container_of(nb, struct dsa_switch, nb); 3108c2ecf20Sopenharmony_ci int err; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci switch (event) { 3138c2ecf20Sopenharmony_ci case DSA_NOTIFIER_AGEING_TIME: 3148c2ecf20Sopenharmony_ci err = dsa_switch_ageing_time(ds, info); 3158c2ecf20Sopenharmony_ci break; 3168c2ecf20Sopenharmony_ci case DSA_NOTIFIER_BRIDGE_JOIN: 3178c2ecf20Sopenharmony_ci err = dsa_switch_bridge_join(ds, info); 3188c2ecf20Sopenharmony_ci break; 3198c2ecf20Sopenharmony_ci case DSA_NOTIFIER_BRIDGE_LEAVE: 3208c2ecf20Sopenharmony_ci err = dsa_switch_bridge_leave(ds, info); 3218c2ecf20Sopenharmony_ci break; 3228c2ecf20Sopenharmony_ci case DSA_NOTIFIER_FDB_ADD: 3238c2ecf20Sopenharmony_ci err = dsa_switch_fdb_add(ds, info); 3248c2ecf20Sopenharmony_ci break; 3258c2ecf20Sopenharmony_ci case DSA_NOTIFIER_FDB_DEL: 3268c2ecf20Sopenharmony_ci err = dsa_switch_fdb_del(ds, info); 3278c2ecf20Sopenharmony_ci break; 3288c2ecf20Sopenharmony_ci case DSA_NOTIFIER_MDB_ADD: 3298c2ecf20Sopenharmony_ci err = dsa_switch_mdb_add(ds, info); 3308c2ecf20Sopenharmony_ci break; 3318c2ecf20Sopenharmony_ci case DSA_NOTIFIER_MDB_DEL: 3328c2ecf20Sopenharmony_ci err = dsa_switch_mdb_del(ds, info); 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci case DSA_NOTIFIER_VLAN_ADD: 3358c2ecf20Sopenharmony_ci err = dsa_switch_vlan_add(ds, info); 3368c2ecf20Sopenharmony_ci break; 3378c2ecf20Sopenharmony_ci case DSA_NOTIFIER_VLAN_DEL: 3388c2ecf20Sopenharmony_ci err = dsa_switch_vlan_del(ds, info); 3398c2ecf20Sopenharmony_ci break; 3408c2ecf20Sopenharmony_ci case DSA_NOTIFIER_MTU: 3418c2ecf20Sopenharmony_ci err = dsa_switch_mtu(ds, info); 3428c2ecf20Sopenharmony_ci break; 3438c2ecf20Sopenharmony_ci default: 3448c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 3458c2ecf20Sopenharmony_ci break; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* Non-switchdev operations cannot be rolled back. If a DSA driver 3498c2ecf20Sopenharmony_ci * returns an error during the chained call, switch chips may be in an 3508c2ecf20Sopenharmony_ci * inconsistent state. 3518c2ecf20Sopenharmony_ci */ 3528c2ecf20Sopenharmony_ci if (err) 3538c2ecf20Sopenharmony_ci dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n", 3548c2ecf20Sopenharmony_ci event, err); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci return notifier_from_errno(err); 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ciint dsa_switch_register_notifier(struct dsa_switch *ds) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci ds->nb.notifier_call = dsa_switch_event; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return raw_notifier_chain_register(&ds->dst->nh, &ds->nb); 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_civoid dsa_switch_unregister_notifier(struct dsa_switch *ds) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci int err; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci err = raw_notifier_chain_unregister(&ds->dst->nh, &ds->nb); 3718c2ecf20Sopenharmony_ci if (err) 3728c2ecf20Sopenharmony_ci dev_err(ds->dev, "failed to unregister notifier (%d)\n", err); 3738c2ecf20Sopenharmony_ci} 374