diff options
Diffstat (limited to 'net/sched')
-rw-r--r-- | net/sched/sch_tbf.c | 47 |
1 files changed, 45 insertions, 2 deletions
diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index c8388f3c3426..38008b0980d9 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c | |||
@@ -116,14 +116,57 @@ struct tbf_sched_data { | |||
116 | struct qdisc_watchdog watchdog; /* Watchdog timer */ | 116 | struct qdisc_watchdog watchdog; /* Watchdog timer */ |
117 | }; | 117 | }; |
118 | 118 | ||
119 | |||
120 | /* GSO packet is too big, segment it so that tbf can transmit | ||
121 | * each segment in time | ||
122 | */ | ||
123 | static int tbf_segment(struct sk_buff *skb, struct Qdisc *sch) | ||
124 | { | ||
125 | struct tbf_sched_data *q = qdisc_priv(sch); | ||
126 | struct sk_buff *segs, *nskb; | ||
127 | netdev_features_t features = netif_skb_features(skb); | ||
128 | int ret, nb; | ||
129 | |||
130 | segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK); | ||
131 | |||
132 | if (IS_ERR_OR_NULL(segs)) | ||
133 | return qdisc_reshape_fail(skb, sch); | ||
134 | |||
135 | nb = 0; | ||
136 | while (segs) { | ||
137 | nskb = segs->next; | ||
138 | segs->next = NULL; | ||
139 | if (likely(segs->len <= q->max_size)) { | ||
140 | qdisc_skb_cb(segs)->pkt_len = segs->len; | ||
141 | ret = qdisc_enqueue(segs, q->qdisc); | ||
142 | } else { | ||
143 | ret = qdisc_reshape_fail(skb, sch); | ||
144 | } | ||
145 | if (ret != NET_XMIT_SUCCESS) { | ||
146 | if (net_xmit_drop_count(ret)) | ||
147 | sch->qstats.drops++; | ||
148 | } else { | ||
149 | nb++; | ||
150 | } | ||
151 | segs = nskb; | ||
152 | } | ||
153 | sch->q.qlen += nb; | ||
154 | if (nb > 1) | ||
155 | qdisc_tree_decrease_qlen(sch, 1 - nb); | ||
156 | consume_skb(skb); | ||
157 | return nb > 0 ? NET_XMIT_SUCCESS : NET_XMIT_DROP; | ||
158 | } | ||
159 | |||
119 | static int tbf_enqueue(struct sk_buff *skb, struct Qdisc *sch) | 160 | static int tbf_enqueue(struct sk_buff *skb, struct Qdisc *sch) |
120 | { | 161 | { |
121 | struct tbf_sched_data *q = qdisc_priv(sch); | 162 | struct tbf_sched_data *q = qdisc_priv(sch); |
122 | int ret; | 163 | int ret; |
123 | 164 | ||
124 | if (qdisc_pkt_len(skb) > q->max_size) | 165 | if (qdisc_pkt_len(skb) > q->max_size) { |
166 | if (skb_is_gso(skb)) | ||
167 | return tbf_segment(skb, sch); | ||
125 | return qdisc_reshape_fail(skb, sch); | 168 | return qdisc_reshape_fail(skb, sch); |
126 | 169 | } | |
127 | ret = qdisc_enqueue(skb, q->qdisc); | 170 | ret = qdisc_enqueue(skb, q->qdisc); |
128 | if (ret != NET_XMIT_SUCCESS) { | 171 | if (ret != NET_XMIT_SUCCESS) { |
129 | if (net_xmit_drop_count(ret)) | 172 | if (net_xmit_drop_count(ret)) |