icssm_switchdev.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. // SPDX-License-Identifier: GPL-2.0
  2. /* Texas Instruments ICSSM Ethernet Driver
  3. *
  4. * Copyright (C) 2018-2022 Texas Instruments Incorporated - https://www.ti.com/
  5. *
  6. */
  7. #include <linux/etherdevice.h>
  8. #include <linux/kernel.h>
  9. #include <linux/remoteproc.h>
  10. #include <net/switchdev.h>
  11. #include "icssm_prueth.h"
  12. #include "icssm_prueth_switch.h"
  13. #include "icssm_prueth_fdb_tbl.h"
  14. /* switchev event work */
  15. struct icssm_sw_event_work {
  16. netdevice_tracker ndev_tracker;
  17. struct work_struct work;
  18. struct switchdev_notifier_fdb_info fdb_info;
  19. struct prueth_emac *emac;
  20. unsigned long event;
  21. };
  22. void icssm_prueth_sw_set_stp_state(struct prueth *prueth,
  23. enum prueth_port port, u8 state)
  24. {
  25. struct fdb_tbl *t = prueth->fdb_tbl;
  26. writeb(state, port - 1 ? (void __iomem *)&t->port2_stp_cfg->state :
  27. (void __iomem *)&t->port1_stp_cfg->state);
  28. }
  29. u8 icssm_prueth_sw_get_stp_state(struct prueth *prueth, enum prueth_port port)
  30. {
  31. struct fdb_tbl *t = prueth->fdb_tbl;
  32. u8 state;
  33. state = readb(port - 1 ? (void __iomem *)&t->port2_stp_cfg->state :
  34. (void __iomem *)&t->port1_stp_cfg->state);
  35. return state;
  36. }
  37. static int icssm_prueth_sw_attr_set(struct net_device *ndev, const void *ctx,
  38. const struct switchdev_attr *attr,
  39. struct netlink_ext_ack *extack)
  40. {
  41. struct prueth_emac *emac = netdev_priv(ndev);
  42. struct prueth *prueth = emac->prueth;
  43. int err = 0;
  44. u8 o_state;
  45. /* Interface is not up */
  46. if (!prueth->fdb_tbl)
  47. return 0;
  48. switch (attr->id) {
  49. case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
  50. o_state = icssm_prueth_sw_get_stp_state(prueth, emac->port_id);
  51. icssm_prueth_sw_set_stp_state(prueth, emac->port_id,
  52. attr->u.stp_state);
  53. if (o_state != attr->u.stp_state)
  54. icssm_prueth_sw_purge_fdb(emac);
  55. dev_dbg(prueth->dev, "attr set: stp state:%u port:%u\n",
  56. attr->u.stp_state, emac->port_id);
  57. break;
  58. default:
  59. err = -EOPNOTSUPP;
  60. break;
  61. }
  62. return err;
  63. }
  64. static void icssm_prueth_sw_fdb_offload(struct net_device *ndev,
  65. struct switchdev_notifier_fdb_info *rcv)
  66. {
  67. struct switchdev_notifier_fdb_info info;
  68. info.addr = rcv->addr;
  69. info.vid = rcv->vid;
  70. call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, ndev, &info.info,
  71. NULL);
  72. }
  73. /**
  74. * icssm_sw_event_work - insert/delete fdb entry
  75. *
  76. * @work: work structure
  77. *
  78. */
  79. static void icssm_sw_event_work(struct work_struct *work)
  80. {
  81. struct icssm_sw_event_work *switchdev_work =
  82. container_of(work, struct icssm_sw_event_work, work);
  83. struct prueth_emac *emac = switchdev_work->emac;
  84. struct switchdev_notifier_fdb_info *fdb;
  85. struct prueth *prueth = emac->prueth;
  86. int port = emac->port_id;
  87. rtnl_lock();
  88. /* Interface is not up */
  89. if (!emac->prueth->fdb_tbl)
  90. goto free;
  91. switch (switchdev_work->event) {
  92. case SWITCHDEV_FDB_ADD_TO_DEVICE:
  93. fdb = &switchdev_work->fdb_info;
  94. dev_dbg(prueth->dev,
  95. "prueth fdb add: MACID = %pM vid = %u flags = %u -- port %d\n",
  96. fdb->addr, fdb->vid, fdb->added_by_user, port);
  97. if (!fdb->added_by_user)
  98. break;
  99. if (fdb->is_local)
  100. break;
  101. icssm_prueth_sw_fdb_add(emac, fdb);
  102. icssm_prueth_sw_fdb_offload(emac->ndev, fdb);
  103. break;
  104. case SWITCHDEV_FDB_DEL_TO_DEVICE:
  105. fdb = &switchdev_work->fdb_info;
  106. dev_dbg(prueth->dev,
  107. "prueth fdb del: MACID = %pM vid = %u flags = %u -- port %d\n",
  108. fdb->addr, fdb->vid, fdb->added_by_user, port);
  109. if (fdb->is_local)
  110. break;
  111. icssm_prueth_sw_fdb_del(emac, fdb);
  112. break;
  113. default:
  114. break;
  115. }
  116. free:
  117. rtnl_unlock();
  118. netdev_put(emac->ndev, &switchdev_work->ndev_tracker);
  119. kfree(switchdev_work->fdb_info.addr);
  120. kfree(switchdev_work);
  121. }
  122. /* called under rcu_read_lock() */
  123. static int icssm_prueth_sw_switchdev_event(struct notifier_block *unused,
  124. unsigned long event, void *ptr)
  125. {
  126. struct net_device *ndev = switchdev_notifier_info_to_dev(ptr);
  127. struct switchdev_notifier_fdb_info *fdb_info = ptr;
  128. struct prueth_emac *emac = netdev_priv(ndev);
  129. struct icssm_sw_event_work *switchdev_work;
  130. int err;
  131. if (!icssm_prueth_sw_port_dev_check(ndev))
  132. return NOTIFY_DONE;
  133. if (event == SWITCHDEV_PORT_ATTR_SET) {
  134. err = switchdev_handle_port_attr_set
  135. (ndev, ptr, icssm_prueth_sw_port_dev_check,
  136. icssm_prueth_sw_attr_set);
  137. return notifier_from_errno(err);
  138. }
  139. switchdev_work = kzalloc_obj(*switchdev_work, GFP_ATOMIC);
  140. if (WARN_ON(!switchdev_work))
  141. return NOTIFY_BAD;
  142. INIT_WORK(&switchdev_work->work, icssm_sw_event_work);
  143. switchdev_work->emac = emac;
  144. switchdev_work->event = event;
  145. switch (event) {
  146. case SWITCHDEV_FDB_ADD_TO_DEVICE:
  147. case SWITCHDEV_FDB_DEL_TO_DEVICE:
  148. memcpy(&switchdev_work->fdb_info, ptr,
  149. sizeof(switchdev_work->fdb_info));
  150. switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
  151. if (!switchdev_work->fdb_info.addr)
  152. goto err_addr_alloc;
  153. ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
  154. fdb_info->addr);
  155. netdev_hold(ndev, &switchdev_work->ndev_tracker, GFP_ATOMIC);
  156. break;
  157. default:
  158. kfree(switchdev_work);
  159. return NOTIFY_DONE;
  160. }
  161. queue_work(system_long_wq, &switchdev_work->work);
  162. return NOTIFY_DONE;
  163. err_addr_alloc:
  164. kfree(switchdev_work);
  165. return NOTIFY_BAD;
  166. }
  167. static int icssm_prueth_switchdev_obj_add(struct net_device *ndev,
  168. const void *ctx,
  169. const struct switchdev_obj *obj,
  170. struct netlink_ext_ack *extack)
  171. {
  172. struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
  173. struct prueth_emac *emac = netdev_priv(ndev);
  174. struct prueth *prueth = emac->prueth;
  175. int ret = 0;
  176. u8 hash;
  177. switch (obj->id) {
  178. case SWITCHDEV_OBJ_ID_HOST_MDB:
  179. dev_dbg(prueth->dev, "MDB add: %s: vid %u:%pM port: %x\n",
  180. ndev->name, mdb->vid, mdb->addr, emac->port_id);
  181. hash = icssm_emac_get_mc_hash(mdb->addr, emac->mc_filter_mask);
  182. icssm_emac_mc_filter_bin_allow(emac, hash);
  183. break;
  184. default:
  185. ret = -EOPNOTSUPP;
  186. break;
  187. }
  188. return ret;
  189. }
  190. static int icssm_prueth_switchdev_obj_del(struct net_device *ndev,
  191. const void *ctx,
  192. const struct switchdev_obj *obj)
  193. {
  194. struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
  195. struct prueth_emac *emac = netdev_priv(ndev);
  196. struct prueth *prueth = emac->prueth;
  197. struct netdev_hw_addr *ha;
  198. u8 hash, tmp_hash;
  199. int ret = 0;
  200. u8 *mask;
  201. switch (obj->id) {
  202. case SWITCHDEV_OBJ_ID_HOST_MDB:
  203. dev_dbg(prueth->dev, "MDB del: %s: vid %u:%pM port: %x\n",
  204. ndev->name, mdb->vid, mdb->addr, emac->port_id);
  205. if (prueth->hw_bridge_dev) {
  206. mask = emac->mc_filter_mask;
  207. hash = icssm_emac_get_mc_hash(mdb->addr, mask);
  208. netdev_for_each_mc_addr(ha, prueth->hw_bridge_dev) {
  209. tmp_hash = icssm_emac_get_mc_hash(ha->addr,
  210. mask);
  211. /* Another MC address is in the bin.
  212. * Don't disable.
  213. */
  214. if (tmp_hash == hash)
  215. return 0;
  216. }
  217. icssm_emac_mc_filter_bin_disallow(emac, hash);
  218. }
  219. break;
  220. default:
  221. ret = -EOPNOTSUPP;
  222. break;
  223. }
  224. return ret;
  225. }
  226. /* switchdev notifiers */
  227. static int icssm_prueth_sw_blocking_event(struct notifier_block *unused,
  228. unsigned long event, void *ptr)
  229. {
  230. struct net_device *ndev = switchdev_notifier_info_to_dev(ptr);
  231. int err;
  232. switch (event) {
  233. case SWITCHDEV_PORT_OBJ_ADD:
  234. err = switchdev_handle_port_obj_add
  235. (ndev, ptr, icssm_prueth_sw_port_dev_check,
  236. icssm_prueth_switchdev_obj_add);
  237. return notifier_from_errno(err);
  238. case SWITCHDEV_PORT_OBJ_DEL:
  239. err = switchdev_handle_port_obj_del
  240. (ndev, ptr, icssm_prueth_sw_port_dev_check,
  241. icssm_prueth_switchdev_obj_del);
  242. return notifier_from_errno(err);
  243. case SWITCHDEV_PORT_ATTR_SET:
  244. err = switchdev_handle_port_attr_set
  245. (ndev, ptr, icssm_prueth_sw_port_dev_check,
  246. icssm_prueth_sw_attr_set);
  247. return notifier_from_errno(err);
  248. default:
  249. break;
  250. }
  251. return NOTIFY_DONE;
  252. }
  253. int icssm_prueth_sw_register_notifiers(struct prueth *prueth)
  254. {
  255. int ret = 0;
  256. prueth->prueth_switchdev_nb.notifier_call =
  257. &icssm_prueth_sw_switchdev_event;
  258. ret = register_switchdev_notifier(&prueth->prueth_switchdev_nb);
  259. if (ret) {
  260. dev_err(prueth->dev,
  261. "register switchdev notifier failed ret:%d\n", ret);
  262. return ret;
  263. }
  264. prueth->prueth_switchdev_bl_nb.notifier_call =
  265. &icssm_prueth_sw_blocking_event;
  266. ret = register_switchdev_blocking_notifier
  267. (&prueth->prueth_switchdev_bl_nb);
  268. if (ret) {
  269. dev_err(prueth->dev,
  270. "register switchdev blocking notifier failed ret:%d\n",
  271. ret);
  272. unregister_switchdev_notifier(&prueth->prueth_switchdev_nb);
  273. }
  274. return ret;
  275. }
  276. void icssm_prueth_sw_unregister_notifiers(struct prueth *prueth)
  277. {
  278. unregister_switchdev_blocking_notifier(&prueth->prueth_switchdev_bl_nb);
  279. unregister_switchdev_notifier(&prueth->prueth_switchdev_nb);
  280. }