lan966x_fdb.c 7.1 KB

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