sch_ingress.c 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /* net/sched/sch_ingress.c - Ingress and clsact qdisc
  3. *
  4. * Authors: Jamal Hadi Salim 1999
  5. */
  6. #include <linux/module.h>
  7. #include <linux/types.h>
  8. #include <linux/list.h>
  9. #include <linux/skbuff.h>
  10. #include <linux/rtnetlink.h>
  11. #include <net/netlink.h>
  12. #include <net/pkt_sched.h>
  13. #include <net/pkt_cls.h>
  14. #include <net/tcx.h>
  15. struct ingress_sched_data {
  16. struct tcf_block *block;
  17. struct tcf_block_ext_info block_info;
  18. struct mini_Qdisc_pair miniqp;
  19. };
  20. static struct Qdisc *ingress_leaf(struct Qdisc *sch, unsigned long arg)
  21. {
  22. return NULL;
  23. }
  24. static unsigned long ingress_find(struct Qdisc *sch, u32 classid)
  25. {
  26. return TC_H_MIN(classid) + 1;
  27. }
  28. static unsigned long ingress_bind_filter(struct Qdisc *sch,
  29. unsigned long parent, u32 classid)
  30. {
  31. return ingress_find(sch, classid);
  32. }
  33. static void ingress_unbind_filter(struct Qdisc *sch, unsigned long cl)
  34. {
  35. }
  36. static void ingress_walk(struct Qdisc *sch, struct qdisc_walker *walker)
  37. {
  38. }
  39. static struct tcf_block *ingress_tcf_block(struct Qdisc *sch, unsigned long cl,
  40. struct netlink_ext_ack *extack)
  41. {
  42. struct ingress_sched_data *q = qdisc_priv(sch);
  43. return q->block;
  44. }
  45. static void clsact_chain_head_change(struct tcf_proto *tp_head, void *priv)
  46. {
  47. struct mini_Qdisc_pair *miniqp = priv;
  48. mini_qdisc_pair_swap(miniqp, tp_head);
  49. };
  50. static void ingress_ingress_block_set(struct Qdisc *sch, u32 block_index)
  51. {
  52. struct ingress_sched_data *q = qdisc_priv(sch);
  53. q->block_info.block_index = block_index;
  54. }
  55. static u32 ingress_ingress_block_get(struct Qdisc *sch)
  56. {
  57. struct ingress_sched_data *q = qdisc_priv(sch);
  58. return q->block_info.block_index;
  59. }
  60. static int ingress_init(struct Qdisc *sch, struct nlattr *opt,
  61. struct netlink_ext_ack *extack)
  62. {
  63. struct ingress_sched_data *q = qdisc_priv(sch);
  64. struct net_device *dev = qdisc_dev(sch);
  65. struct bpf_mprog_entry *entry;
  66. bool created;
  67. int err;
  68. if (sch->parent != TC_H_INGRESS)
  69. return -EOPNOTSUPP;
  70. net_inc_ingress_queue();
  71. entry = tcx_entry_fetch_or_create(dev, true, &created);
  72. if (!entry)
  73. return -ENOMEM;
  74. tcx_miniq_inc(entry);
  75. mini_qdisc_pair_init(&q->miniqp, sch, &tcx_entry(entry)->miniq);
  76. if (created)
  77. tcx_entry_update(dev, entry, true);
  78. q->block_info.binder_type = FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS;
  79. q->block_info.chain_head_change = clsact_chain_head_change;
  80. q->block_info.chain_head_change_priv = &q->miniqp;
  81. err = tcf_block_get_ext(&q->block, sch, &q->block_info, extack);
  82. if (err)
  83. return err;
  84. mini_qdisc_pair_block_init(&q->miniqp, q->block);
  85. return 0;
  86. }
  87. static void ingress_destroy(struct Qdisc *sch)
  88. {
  89. struct ingress_sched_data *q = qdisc_priv(sch);
  90. struct net_device *dev = qdisc_dev(sch);
  91. struct bpf_mprog_entry *entry;
  92. if (sch->parent != TC_H_INGRESS)
  93. return;
  94. tcf_block_put_ext(q->block, sch, &q->block_info);
  95. if (mini_qdisc_pair_inited(&q->miniqp)) {
  96. entry = rtnl_dereference(dev->tcx_ingress);
  97. tcx_miniq_dec(entry);
  98. if (!tcx_entry_is_active(entry)) {
  99. tcx_entry_update(dev, NULL, true);
  100. tcx_entry_free(entry);
  101. }
  102. }
  103. net_dec_ingress_queue();
  104. }
  105. static int ingress_dump(struct Qdisc *sch, struct sk_buff *skb)
  106. {
  107. struct nlattr *nest;
  108. nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
  109. if (nest == NULL)
  110. goto nla_put_failure;
  111. return nla_nest_end(skb, nest);
  112. nla_put_failure:
  113. nla_nest_cancel(skb, nest);
  114. return -1;
  115. }
  116. static const struct Qdisc_class_ops ingress_class_ops = {
  117. .flags = QDISC_CLASS_OPS_DOIT_UNLOCKED,
  118. .leaf = ingress_leaf,
  119. .find = ingress_find,
  120. .walk = ingress_walk,
  121. .tcf_block = ingress_tcf_block,
  122. .bind_tcf = ingress_bind_filter,
  123. .unbind_tcf = ingress_unbind_filter,
  124. };
  125. static struct Qdisc_ops ingress_qdisc_ops __read_mostly = {
  126. .cl_ops = &ingress_class_ops,
  127. .id = "ingress",
  128. .priv_size = sizeof(struct ingress_sched_data),
  129. .static_flags = TCQ_F_INGRESS | TCQ_F_CPUSTATS,
  130. .init = ingress_init,
  131. .destroy = ingress_destroy,
  132. .dump = ingress_dump,
  133. .ingress_block_set = ingress_ingress_block_set,
  134. .ingress_block_get = ingress_ingress_block_get,
  135. .owner = THIS_MODULE,
  136. };
  137. MODULE_ALIAS_NET_SCH("ingress");
  138. struct clsact_sched_data {
  139. struct tcf_block *ingress_block;
  140. struct tcf_block *egress_block;
  141. struct tcf_block_ext_info ingress_block_info;
  142. struct tcf_block_ext_info egress_block_info;
  143. struct mini_Qdisc_pair miniqp_ingress;
  144. struct mini_Qdisc_pair miniqp_egress;
  145. };
  146. static unsigned long clsact_find(struct Qdisc *sch, u32 classid)
  147. {
  148. switch (TC_H_MIN(classid)) {
  149. case TC_H_MIN(TC_H_MIN_INGRESS):
  150. case TC_H_MIN(TC_H_MIN_EGRESS):
  151. return TC_H_MIN(classid);
  152. default:
  153. return 0;
  154. }
  155. }
  156. static unsigned long clsact_bind_filter(struct Qdisc *sch,
  157. unsigned long parent, u32 classid)
  158. {
  159. return clsact_find(sch, classid);
  160. }
  161. static struct tcf_block *clsact_tcf_block(struct Qdisc *sch, unsigned long cl,
  162. struct netlink_ext_ack *extack)
  163. {
  164. struct clsact_sched_data *q = qdisc_priv(sch);
  165. switch (cl) {
  166. case TC_H_MIN(TC_H_MIN_INGRESS):
  167. return q->ingress_block;
  168. case TC_H_MIN(TC_H_MIN_EGRESS):
  169. return q->egress_block;
  170. default:
  171. return NULL;
  172. }
  173. }
  174. static void clsact_ingress_block_set(struct Qdisc *sch, u32 block_index)
  175. {
  176. struct clsact_sched_data *q = qdisc_priv(sch);
  177. q->ingress_block_info.block_index = block_index;
  178. }
  179. static void clsact_egress_block_set(struct Qdisc *sch, u32 block_index)
  180. {
  181. struct clsact_sched_data *q = qdisc_priv(sch);
  182. q->egress_block_info.block_index = block_index;
  183. }
  184. static u32 clsact_ingress_block_get(struct Qdisc *sch)
  185. {
  186. struct clsact_sched_data *q = qdisc_priv(sch);
  187. return q->ingress_block_info.block_index;
  188. }
  189. static u32 clsact_egress_block_get(struct Qdisc *sch)
  190. {
  191. struct clsact_sched_data *q = qdisc_priv(sch);
  192. return q->egress_block_info.block_index;
  193. }
  194. static int clsact_init(struct Qdisc *sch, struct nlattr *opt,
  195. struct netlink_ext_ack *extack)
  196. {
  197. struct clsact_sched_data *q = qdisc_priv(sch);
  198. struct net_device *dev = qdisc_dev(sch);
  199. struct bpf_mprog_entry *entry;
  200. bool created;
  201. int err;
  202. if (sch->parent != TC_H_CLSACT)
  203. return -EOPNOTSUPP;
  204. net_inc_ingress_queue();
  205. net_inc_egress_queue();
  206. entry = tcx_entry_fetch_or_create(dev, true, &created);
  207. if (!entry)
  208. return -ENOMEM;
  209. tcx_miniq_inc(entry);
  210. mini_qdisc_pair_init(&q->miniqp_ingress, sch, &tcx_entry(entry)->miniq);
  211. if (created)
  212. tcx_entry_update(dev, entry, true);
  213. q->ingress_block_info.binder_type = FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS;
  214. q->ingress_block_info.chain_head_change = clsact_chain_head_change;
  215. q->ingress_block_info.chain_head_change_priv = &q->miniqp_ingress;
  216. err = tcf_block_get_ext(&q->ingress_block, sch, &q->ingress_block_info,
  217. extack);
  218. if (err)
  219. return err;
  220. mini_qdisc_pair_block_init(&q->miniqp_ingress, q->ingress_block);
  221. entry = tcx_entry_fetch_or_create(dev, false, &created);
  222. if (!entry)
  223. return -ENOMEM;
  224. tcx_miniq_inc(entry);
  225. mini_qdisc_pair_init(&q->miniqp_egress, sch, &tcx_entry(entry)->miniq);
  226. if (created)
  227. tcx_entry_update(dev, entry, false);
  228. q->egress_block_info.binder_type = FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS;
  229. q->egress_block_info.chain_head_change = clsact_chain_head_change;
  230. q->egress_block_info.chain_head_change_priv = &q->miniqp_egress;
  231. return tcf_block_get_ext(&q->egress_block, sch, &q->egress_block_info, extack);
  232. }
  233. static void clsact_destroy(struct Qdisc *sch)
  234. {
  235. struct bpf_mprog_entry *ingress_entry, *egress_entry;
  236. struct clsact_sched_data *q = qdisc_priv(sch);
  237. struct net_device *dev = qdisc_dev(sch);
  238. if (sch->parent != TC_H_CLSACT)
  239. return;
  240. tcf_block_put_ext(q->ingress_block, sch, &q->ingress_block_info);
  241. tcf_block_put_ext(q->egress_block, sch, &q->egress_block_info);
  242. if (mini_qdisc_pair_inited(&q->miniqp_ingress)) {
  243. ingress_entry = rtnl_dereference(dev->tcx_ingress);
  244. tcx_miniq_dec(ingress_entry);
  245. if (!tcx_entry_is_active(ingress_entry)) {
  246. tcx_entry_update(dev, NULL, true);
  247. tcx_entry_free(ingress_entry);
  248. }
  249. }
  250. if (mini_qdisc_pair_inited(&q->miniqp_egress)) {
  251. egress_entry = rtnl_dereference(dev->tcx_egress);
  252. tcx_miniq_dec(egress_entry);
  253. if (!tcx_entry_is_active(egress_entry)) {
  254. tcx_entry_update(dev, NULL, false);
  255. tcx_entry_free(egress_entry);
  256. }
  257. }
  258. net_dec_ingress_queue();
  259. net_dec_egress_queue();
  260. }
  261. static const struct Qdisc_class_ops clsact_class_ops = {
  262. .flags = QDISC_CLASS_OPS_DOIT_UNLOCKED,
  263. .leaf = ingress_leaf,
  264. .find = clsact_find,
  265. .walk = ingress_walk,
  266. .tcf_block = clsact_tcf_block,
  267. .bind_tcf = clsact_bind_filter,
  268. .unbind_tcf = ingress_unbind_filter,
  269. };
  270. static struct Qdisc_ops clsact_qdisc_ops __read_mostly = {
  271. .cl_ops = &clsact_class_ops,
  272. .id = "clsact",
  273. .priv_size = sizeof(struct clsact_sched_data),
  274. .static_flags = TCQ_F_INGRESS | TCQ_F_CPUSTATS,
  275. .init = clsact_init,
  276. .destroy = clsact_destroy,
  277. .dump = ingress_dump,
  278. .ingress_block_set = clsact_ingress_block_set,
  279. .egress_block_set = clsact_egress_block_set,
  280. .ingress_block_get = clsact_ingress_block_get,
  281. .egress_block_get = clsact_egress_block_get,
  282. .owner = THIS_MODULE,
  283. };
  284. MODULE_ALIAS_NET_SCH("clsact");
  285. static int __init ingress_module_init(void)
  286. {
  287. int ret;
  288. ret = register_qdisc(&ingress_qdisc_ops);
  289. if (!ret) {
  290. ret = register_qdisc(&clsact_qdisc_ops);
  291. if (ret)
  292. unregister_qdisc(&ingress_qdisc_ops);
  293. }
  294. return ret;
  295. }
  296. static void __exit ingress_module_exit(void)
  297. {
  298. unregister_qdisc(&ingress_qdisc_ops);
  299. unregister_qdisc(&clsact_qdisc_ops);
  300. }
  301. module_init(ingress_module_init);
  302. module_exit(ingress_module_exit);
  303. MODULE_LICENSE("GPL");
  304. MODULE_DESCRIPTION("Ingress and clsact based ingress and egress qdiscs");