ocelot_tc.c 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // SPDX-License-Identifier: (GPL-2.0 OR MIT)
  2. /* Microsemi Ocelot Switch TC driver
  3. *
  4. * Copyright (c) 2019 Microsemi Corporation
  5. */
  6. #include "ocelot_tc.h"
  7. #include "ocelot_police.h"
  8. #include "ocelot_ace.h"
  9. #include <net/pkt_cls.h>
  10. static int ocelot_setup_tc_cls_matchall(struct ocelot_port *port,
  11. struct tc_cls_matchall_offload *f,
  12. bool ingress)
  13. {
  14. struct netlink_ext_ack *extack = f->common.extack;
  15. struct ocelot_policer pol = { 0 };
  16. struct flow_action_entry *action;
  17. int err;
  18. netdev_dbg(port->dev, "%s: port %u command %d cookie %lu\n",
  19. __func__, port->chip_port, f->command, f->cookie);
  20. if (!ingress) {
  21. NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported");
  22. return -EOPNOTSUPP;
  23. }
  24. switch (f->command) {
  25. case TC_CLSMATCHALL_REPLACE:
  26. if (!flow_offload_has_one_action(&f->rule->action)) {
  27. NL_SET_ERR_MSG_MOD(extack,
  28. "Only one action is supported");
  29. return -EOPNOTSUPP;
  30. }
  31. if (port->tc.block_shared) {
  32. NL_SET_ERR_MSG_MOD(extack,
  33. "Rate limit is not supported on shared blocks");
  34. return -EOPNOTSUPP;
  35. }
  36. action = &f->rule->action.entries[0];
  37. if (action->id != FLOW_ACTION_POLICE) {
  38. NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
  39. return -EOPNOTSUPP;
  40. }
  41. if (port->tc.police_id && port->tc.police_id != f->cookie) {
  42. NL_SET_ERR_MSG_MOD(extack,
  43. "Only one policer per port is supported\n");
  44. return -EEXIST;
  45. }
  46. pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8;
  47. pol.burst = (u32)div_u64(action->police.rate_bytes_ps *
  48. PSCHED_NS2TICKS(action->police.burst),
  49. PSCHED_TICKS_PER_SEC);
  50. err = ocelot_port_policer_add(port, &pol);
  51. if (err) {
  52. NL_SET_ERR_MSG_MOD(extack, "Could not add policer\n");
  53. return err;
  54. }
  55. port->tc.police_id = f->cookie;
  56. port->tc.offload_cnt++;
  57. return 0;
  58. case TC_CLSMATCHALL_DESTROY:
  59. if (port->tc.police_id != f->cookie)
  60. return -ENOENT;
  61. err = ocelot_port_policer_del(port);
  62. if (err) {
  63. NL_SET_ERR_MSG_MOD(extack,
  64. "Could not delete policer\n");
  65. return err;
  66. }
  67. port->tc.police_id = 0;
  68. port->tc.offload_cnt--;
  69. return 0;
  70. case TC_CLSMATCHALL_STATS: /* fall through */
  71. default:
  72. return -EOPNOTSUPP;
  73. }
  74. }
  75. static int ocelot_setup_tc_block_cb(enum tc_setup_type type,
  76. void *type_data,
  77. void *cb_priv, bool ingress)
  78. {
  79. struct ocelot_port *port = cb_priv;
  80. if (!tc_cls_can_offload_and_chain0(port->dev, type_data))
  81. return -EOPNOTSUPP;
  82. switch (type) {
  83. case TC_SETUP_CLSMATCHALL:
  84. netdev_dbg(port->dev, "tc_block_cb: TC_SETUP_CLSMATCHALL %s\n",
  85. ingress ? "ingress" : "egress");
  86. return ocelot_setup_tc_cls_matchall(port, type_data, ingress);
  87. case TC_SETUP_CLSFLOWER:
  88. return 0;
  89. default:
  90. netdev_dbg(port->dev, "tc_block_cb: type %d %s\n",
  91. type,
  92. ingress ? "ingress" : "egress");
  93. return -EOPNOTSUPP;
  94. }
  95. }
  96. static int ocelot_setup_tc_block_cb_ig(enum tc_setup_type type,
  97. void *type_data,
  98. void *cb_priv)
  99. {
  100. return ocelot_setup_tc_block_cb(type, type_data,
  101. cb_priv, true);
  102. }
  103. static int ocelot_setup_tc_block_cb_eg(enum tc_setup_type type,
  104. void *type_data,
  105. void *cb_priv)
  106. {
  107. return ocelot_setup_tc_block_cb(type, type_data,
  108. cb_priv, false);
  109. }
  110. static LIST_HEAD(ocelot_block_cb_list);
  111. static int ocelot_setup_tc_block(struct ocelot_port *port,
  112. struct flow_block_offload *f)
  113. {
  114. struct flow_block_cb *block_cb;
  115. flow_setup_cb_t *cb;
  116. int err;
  117. netdev_dbg(port->dev, "tc_block command %d, binder_type %d\n",
  118. f->command, f->binder_type);
  119. if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) {
  120. cb = ocelot_setup_tc_block_cb_ig;
  121. port->tc.block_shared = f->block_shared;
  122. } else if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) {
  123. cb = ocelot_setup_tc_block_cb_eg;
  124. } else {
  125. return -EOPNOTSUPP;
  126. }
  127. f->driver_block_list = &ocelot_block_cb_list;
  128. switch (f->command) {
  129. case FLOW_BLOCK_BIND:
  130. if (flow_block_cb_is_busy(cb, port, &ocelot_block_cb_list))
  131. return -EBUSY;
  132. block_cb = flow_block_cb_alloc(cb, port, port, NULL);
  133. if (IS_ERR(block_cb))
  134. return PTR_ERR(block_cb);
  135. err = ocelot_setup_tc_block_flower_bind(port, f);
  136. if (err < 0) {
  137. flow_block_cb_free(block_cb);
  138. return err;
  139. }
  140. flow_block_cb_add(block_cb, f);
  141. list_add_tail(&block_cb->driver_list, f->driver_block_list);
  142. return 0;
  143. case FLOW_BLOCK_UNBIND:
  144. block_cb = flow_block_cb_lookup(f->block, cb, port);
  145. if (!block_cb)
  146. return -ENOENT;
  147. ocelot_setup_tc_block_flower_unbind(port, f);
  148. flow_block_cb_remove(block_cb, f);
  149. list_del(&block_cb->driver_list);
  150. return 0;
  151. default:
  152. return -EOPNOTSUPP;
  153. }
  154. }
  155. int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type,
  156. void *type_data)
  157. {
  158. struct ocelot_port *port = netdev_priv(dev);
  159. switch (type) {
  160. case TC_SETUP_BLOCK:
  161. return ocelot_setup_tc_block(port, type_data);
  162. default:
  163. return -EOPNOTSUPP;
  164. }
  165. return 0;
  166. }