1 // SPDX-License-Identifier: GPL-2.0+ 2 3 #include <net/switchdev.h> 4 5 #include "lan966x_main.h" 6 7 struct lan966x_fdb_event_work { 8 struct work_struct work; 9 struct switchdev_notifier_fdb_info fdb_info; 10 struct net_device *dev; 11 struct net_device *orig_dev; 12 struct lan966x *lan966x; 13 unsigned long event; 14 }; 15 16 struct lan966x_fdb_entry { 17 struct list_head list; 18 unsigned char mac[ETH_ALEN] __aligned(2); 19 u16 vid; 20 u32 references; 21 }; 22 23 static struct lan966x_fdb_entry * 24 lan966x_fdb_find_entry(struct lan966x *lan966x, 25 struct switchdev_notifier_fdb_info *fdb_info) 26 { 27 struct lan966x_fdb_entry *fdb_entry; 28 29 list_for_each_entry(fdb_entry, &lan966x->fdb_entries, list) { 30 if (fdb_entry->vid == fdb_info->vid && 31 ether_addr_equal(fdb_entry->mac, fdb_info->addr)) 32 return fdb_entry; 33 } 34 35 return NULL; 36 } 37 38 static void lan966x_fdb_add_entry(struct lan966x *lan966x, 39 struct switchdev_notifier_fdb_info *fdb_info) 40 { 41 struct lan966x_fdb_entry *fdb_entry; 42 43 fdb_entry = lan966x_fdb_find_entry(lan966x, fdb_info); 44 if (fdb_entry) { 45 fdb_entry->references++; 46 return; 47 } 48 49 fdb_entry = kzalloc(sizeof(*fdb_entry), GFP_KERNEL); 50 if (!fdb_entry) 51 return; 52 53 ether_addr_copy(fdb_entry->mac, fdb_info->addr); 54 fdb_entry->vid = fdb_info->vid; 55 fdb_entry->references = 1; 56 list_add_tail(&fdb_entry->list, &lan966x->fdb_entries); 57 } 58 59 static bool lan966x_fdb_del_entry(struct lan966x *lan966x, 60 struct switchdev_notifier_fdb_info *fdb_info) 61 { 62 struct lan966x_fdb_entry *fdb_entry, *tmp; 63 64 list_for_each_entry_safe(fdb_entry, tmp, &lan966x->fdb_entries, 65 list) { 66 if (fdb_entry->vid == fdb_info->vid && 67 ether_addr_equal(fdb_entry->mac, fdb_info->addr)) { 68 fdb_entry->references--; 69 if (!fdb_entry->references) { 70 list_del(&fdb_entry->list); 71 kfree(fdb_entry); 72 return true; 73 } 74 break; 75 } 76 } 77 78 return false; 79 } 80 81 void lan966x_fdb_write_entries(struct lan966x *lan966x, u16 vid) 82 { 83 struct lan966x_fdb_entry *fdb_entry; 84 85 list_for_each_entry(fdb_entry, &lan966x->fdb_entries, list) { 86 if (fdb_entry->vid != vid) 87 continue; 88 89 lan966x_mac_cpu_learn(lan966x, fdb_entry->mac, fdb_entry->vid); 90 } 91 } 92 93 void lan966x_fdb_erase_entries(struct lan966x *lan966x, u16 vid) 94 { 95 struct lan966x_fdb_entry *fdb_entry; 96 97 list_for_each_entry(fdb_entry, &lan966x->fdb_entries, list) { 98 if (fdb_entry->vid != vid) 99 continue; 100 101 lan966x_mac_cpu_forget(lan966x, fdb_entry->mac, fdb_entry->vid); 102 } 103 } 104 105 static void lan966x_fdb_purge_entries(struct lan966x *lan966x) 106 { 107 struct lan966x_fdb_entry *fdb_entry, *tmp; 108 109 list_for_each_entry_safe(fdb_entry, tmp, &lan966x->fdb_entries, list) { 110 list_del(&fdb_entry->list); 111 kfree(fdb_entry); 112 } 113 } 114 115 int lan966x_fdb_init(struct lan966x *lan966x) 116 { 117 INIT_LIST_HEAD(&lan966x->fdb_entries); 118 lan966x->fdb_work = alloc_ordered_workqueue("lan966x_order", 0); 119 if (!lan966x->fdb_work) 120 return -ENOMEM; 121 122 return 0; 123 } 124 125 void lan966x_fdb_deinit(struct lan966x *lan966x) 126 { 127 destroy_workqueue(lan966x->fdb_work); 128 lan966x_fdb_purge_entries(lan966x); 129 } 130 131 void lan966x_fdb_flush_workqueue(struct lan966x *lan966x) 132 { 133 flush_workqueue(lan966x->fdb_work); 134 } 135 136 static void lan966x_fdb_port_event_work(struct lan966x_fdb_event_work *fdb_work) 137 { 138 struct switchdev_notifier_fdb_info *fdb_info; 139 struct lan966x_port *port; 140 struct lan966x *lan966x; 141 142 lan966x = fdb_work->lan966x; 143 port = netdev_priv(fdb_work->orig_dev); 144 fdb_info = &fdb_work->fdb_info; 145 146 switch (fdb_work->event) { 147 case SWITCHDEV_FDB_ADD_TO_DEVICE: 148 if (!fdb_info->added_by_user) 149 break; 150 lan966x_mac_add_entry(lan966x, port, fdb_info->addr, 151 fdb_info->vid); 152 break; 153 case SWITCHDEV_FDB_DEL_TO_DEVICE: 154 if (!fdb_info->added_by_user) 155 break; 156 lan966x_mac_del_entry(lan966x, fdb_info->addr, 157 fdb_info->vid); 158 break; 159 } 160 } 161 162 static void lan966x_fdb_bridge_event_work(struct lan966x_fdb_event_work *fdb_work) 163 { 164 struct switchdev_notifier_fdb_info *fdb_info; 165 struct lan966x *lan966x; 166 int ret; 167 168 lan966x = fdb_work->lan966x; 169 fdb_info = &fdb_work->fdb_info; 170 171 /* In case the bridge is called */ 172 switch (fdb_work->event) { 173 case SWITCHDEV_FDB_ADD_TO_DEVICE: 174 /* If there is no front port in this vlan, there is no 175 * point to copy the frame to CPU because it would be 176 * just dropped at later point. So add it only if 177 * there is a port but it is required to store the fdb 178 * entry for later point when a port actually gets in 179 * the vlan. 180 */ 181 lan966x_fdb_add_entry(lan966x, fdb_info); 182 if (!lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, 183 fdb_info->vid)) 184 break; 185 186 lan966x_mac_cpu_learn(lan966x, fdb_info->addr, 187 fdb_info->vid); 188 break; 189 case SWITCHDEV_FDB_DEL_TO_DEVICE: 190 ret = lan966x_fdb_del_entry(lan966x, fdb_info); 191 if (!lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, 192 fdb_info->vid)) 193 break; 194 195 if (ret) 196 lan966x_mac_cpu_forget(lan966x, fdb_info->addr, 197 fdb_info->vid); 198 break; 199 } 200 } 201 202 static void lan966x_fdb_lag_event_work(struct lan966x_fdb_event_work *fdb_work) 203 { 204 struct switchdev_notifier_fdb_info *fdb_info; 205 struct lan966x_port *port; 206 struct lan966x *lan966x; 207 208 if (!lan966x_lag_first_port(fdb_work->orig_dev, fdb_work->dev)) 209 return; 210 211 lan966x = fdb_work->lan966x; 212 port = netdev_priv(fdb_work->dev); 213 fdb_info = &fdb_work->fdb_info; 214 215 switch (fdb_work->event) { 216 case SWITCHDEV_FDB_ADD_TO_DEVICE: 217 if (!fdb_info->added_by_user) 218 break; 219 lan966x_mac_add_entry(lan966x, port, fdb_info->addr, 220 fdb_info->vid); 221 break; 222 case SWITCHDEV_FDB_DEL_TO_DEVICE: 223 if (!fdb_info->added_by_user) 224 break; 225 lan966x_mac_del_entry(lan966x, fdb_info->addr, fdb_info->vid); 226 break; 227 } 228 } 229 230 static void lan966x_fdb_event_work(struct work_struct *work) 231 { 232 struct lan966x_fdb_event_work *fdb_work = 233 container_of(work, struct lan966x_fdb_event_work, work); 234 235 if (lan966x_netdevice_check(fdb_work->orig_dev)) 236 lan966x_fdb_port_event_work(fdb_work); 237 else if (netif_is_bridge_master(fdb_work->orig_dev)) 238 lan966x_fdb_bridge_event_work(fdb_work); 239 else if (netif_is_lag_master(fdb_work->orig_dev)) 240 lan966x_fdb_lag_event_work(fdb_work); 241 242 kfree(fdb_work->fdb_info.addr); 243 kfree(fdb_work); 244 } 245 246 int lan966x_handle_fdb(struct net_device *dev, 247 struct net_device *orig_dev, 248 unsigned long event, const void *ctx, 249 const struct switchdev_notifier_fdb_info *fdb_info) 250 { 251 struct lan966x_port *port = netdev_priv(dev); 252 struct lan966x *lan966x = port->lan966x; 253 struct lan966x_fdb_event_work *fdb_work; 254 255 if (ctx && ctx != port) 256 return 0; 257 258 switch (event) { 259 case SWITCHDEV_FDB_ADD_TO_DEVICE: 260 case SWITCHDEV_FDB_DEL_TO_DEVICE: 261 if (lan966x_netdevice_check(orig_dev) && 262 !fdb_info->added_by_user) 263 break; 264 265 fdb_work = kzalloc(sizeof(*fdb_work), GFP_ATOMIC); 266 if (!fdb_work) 267 return -ENOMEM; 268 269 fdb_work->dev = dev; 270 fdb_work->orig_dev = orig_dev; 271 fdb_work->lan966x = lan966x; 272 fdb_work->event = event; 273 INIT_WORK(&fdb_work->work, lan966x_fdb_event_work); 274 memcpy(&fdb_work->fdb_info, fdb_info, sizeof(fdb_work->fdb_info)); 275 fdb_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); 276 if (!fdb_work->fdb_info.addr) 277 goto err_addr_alloc; 278 279 ether_addr_copy((u8 *)fdb_work->fdb_info.addr, fdb_info->addr); 280 281 queue_work(lan966x->fdb_work, &fdb_work->work); 282 break; 283 } 284 285 return 0; 286 err_addr_alloc: 287 kfree(fdb_work); 288 return -ENOMEM; 289 } 290