1 /* 2 * net/sched/sch_fifo.c The simplest FIFO queue. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 7 * 2 of the License, or (at your option) any later version. 8 * 9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 10 */ 11 12 #include <linux/module.h> 13 #include <linux/slab.h> 14 #include <linux/types.h> 15 #include <linux/kernel.h> 16 #include <linux/errno.h> 17 #include <linux/skbuff.h> 18 #include <net/pkt_sched.h> 19 20 /* 1 band FIFO pseudo-"scheduler" */ 21 22 struct fifo_sched_data 23 { 24 u32 limit; 25 }; 26 27 static int bfifo_enqueue(struct sk_buff *skb, struct Qdisc* sch) 28 { 29 struct fifo_sched_data *q = qdisc_priv(sch); 30 31 if (likely(sch->qstats.backlog + qdisc_pkt_len(skb) <= q->limit)) 32 return qdisc_enqueue_tail(skb, sch); 33 34 return qdisc_reshape_fail(skb, sch); 35 } 36 37 static int pfifo_enqueue(struct sk_buff *skb, struct Qdisc* sch) 38 { 39 struct fifo_sched_data *q = qdisc_priv(sch); 40 41 if (likely(skb_queue_len(&sch->q) < q->limit)) 42 return qdisc_enqueue_tail(skb, sch); 43 44 return qdisc_reshape_fail(skb, sch); 45 } 46 47 static int pfifo_tail_enqueue(struct sk_buff *skb, struct Qdisc* sch) 48 { 49 struct sk_buff *skb_head; 50 struct fifo_sched_data *q = qdisc_priv(sch); 51 52 if (likely(skb_queue_len(&sch->q) < q->limit)) 53 return qdisc_enqueue_tail(skb, sch); 54 55 /* queue full, remove one skb to fulfill the limit */ 56 skb_head = qdisc_dequeue_head(sch); 57 sch->bstats.bytes -= qdisc_pkt_len(skb_head); 58 sch->bstats.packets--; 59 sch->qstats.drops++; 60 kfree_skb(skb_head); 61 62 qdisc_enqueue_tail(skb, sch); 63 64 return NET_XMIT_CN; 65 } 66 67 static int fifo_init(struct Qdisc *sch, struct nlattr *opt) 68 { 69 struct fifo_sched_data *q = qdisc_priv(sch); 70 71 if (opt == NULL) { 72 u32 limit = qdisc_dev(sch)->tx_queue_len ? : 1; 73 74 if (sch->ops == &bfifo_qdisc_ops) 75 limit *= psched_mtu(qdisc_dev(sch)); 76 77 q->limit = limit; 78 } else { 79 struct tc_fifo_qopt *ctl = nla_data(opt); 80 81 if (nla_len(opt) < sizeof(*ctl)) 82 return -EINVAL; 83 84 q->limit = ctl->limit; 85 } 86 87 return 0; 88 } 89 90 static int fifo_dump(struct Qdisc *sch, struct sk_buff *skb) 91 { 92 struct fifo_sched_data *q = qdisc_priv(sch); 93 struct tc_fifo_qopt opt = { .limit = q->limit }; 94 95 NLA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); 96 return skb->len; 97 98 nla_put_failure: 99 return -1; 100 } 101 102 struct Qdisc_ops pfifo_qdisc_ops __read_mostly = { 103 .id = "pfifo", 104 .priv_size = sizeof(struct fifo_sched_data), 105 .enqueue = pfifo_enqueue, 106 .dequeue = qdisc_dequeue_head, 107 .peek = qdisc_peek_head, 108 .drop = qdisc_queue_drop, 109 .init = fifo_init, 110 .reset = qdisc_reset_queue, 111 .change = fifo_init, 112 .dump = fifo_dump, 113 .owner = THIS_MODULE, 114 }; 115 EXPORT_SYMBOL(pfifo_qdisc_ops); 116 117 struct Qdisc_ops bfifo_qdisc_ops __read_mostly = { 118 .id = "bfifo", 119 .priv_size = sizeof(struct fifo_sched_data), 120 .enqueue = bfifo_enqueue, 121 .dequeue = qdisc_dequeue_head, 122 .peek = qdisc_peek_head, 123 .drop = qdisc_queue_drop, 124 .init = fifo_init, 125 .reset = qdisc_reset_queue, 126 .change = fifo_init, 127 .dump = fifo_dump, 128 .owner = THIS_MODULE, 129 }; 130 EXPORT_SYMBOL(bfifo_qdisc_ops); 131 132 struct Qdisc_ops pfifo_head_drop_qdisc_ops __read_mostly = { 133 .id = "pfifo_head_drop", 134 .priv_size = sizeof(struct fifo_sched_data), 135 .enqueue = pfifo_tail_enqueue, 136 .dequeue = qdisc_dequeue_head, 137 .peek = qdisc_peek_head, 138 .drop = qdisc_queue_drop_head, 139 .init = fifo_init, 140 .reset = qdisc_reset_queue, 141 .change = fifo_init, 142 .dump = fifo_dump, 143 .owner = THIS_MODULE, 144 }; 145 146 /* Pass size change message down to embedded FIFO */ 147 int fifo_set_limit(struct Qdisc *q, unsigned int limit) 148 { 149 struct nlattr *nla; 150 int ret = -ENOMEM; 151 152 /* Hack to avoid sending change message to non-FIFO */ 153 if (strncmp(q->ops->id + 1, "fifo", 4) != 0) 154 return 0; 155 156 nla = kmalloc(nla_attr_size(sizeof(struct tc_fifo_qopt)), GFP_KERNEL); 157 if (nla) { 158 nla->nla_type = RTM_NEWQDISC; 159 nla->nla_len = nla_attr_size(sizeof(struct tc_fifo_qopt)); 160 ((struct tc_fifo_qopt *)nla_data(nla))->limit = limit; 161 162 ret = q->ops->change(q, nla); 163 kfree(nla); 164 } 165 return ret; 166 } 167 EXPORT_SYMBOL(fifo_set_limit); 168 169 struct Qdisc *fifo_create_dflt(struct Qdisc *sch, struct Qdisc_ops *ops, 170 unsigned int limit) 171 { 172 struct Qdisc *q; 173 int err = -ENOMEM; 174 175 q = qdisc_create_dflt(qdisc_dev(sch), sch->dev_queue, 176 ops, TC_H_MAKE(sch->handle, 1)); 177 if (q) { 178 err = fifo_set_limit(q, limit); 179 if (err < 0) { 180 qdisc_destroy(q); 181 q = NULL; 182 } 183 } 184 185 return q ? : ERR_PTR(err); 186 } 187 EXPORT_SYMBOL(fifo_create_dflt); 188