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 22struct fifo_sched_data 23{ 24 u32 limit; 25}; 26 27static 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 37static 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 47static 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 67static 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 90static 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 98nla_put_failure: 99 return -1; 100} 101 102struct 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}; 115EXPORT_SYMBOL(pfifo_qdisc_ops); 116 117struct 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}; 130EXPORT_SYMBOL(bfifo_qdisc_ops); 131 132struct 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 */ 147int 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} 167EXPORT_SYMBOL(fifo_set_limit); 168 169struct 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} 187EXPORT_SYMBOL(fifo_create_dflt); 188