aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/sched/sch_teql.c47
1 files changed, 22 insertions, 25 deletions
diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c
index ca0c29695d51..474167162947 100644
--- a/net/sched/sch_teql.c
+++ b/net/sched/sch_teql.c
@@ -67,7 +67,6 @@ struct teql_master {
67struct teql_sched_data { 67struct teql_sched_data {
68 struct Qdisc *next; 68 struct Qdisc *next;
69 struct teql_master *m; 69 struct teql_master *m;
70 struct neighbour *ncache;
71 struct sk_buff_head q; 70 struct sk_buff_head q;
72}; 71};
73 72
@@ -134,7 +133,6 @@ teql_reset(struct Qdisc *sch)
134 133
135 skb_queue_purge(&dat->q); 134 skb_queue_purge(&dat->q);
136 sch->q.qlen = 0; 135 sch->q.qlen = 0;
137 teql_neigh_release(xchg(&dat->ncache, NULL));
138} 136}
139 137
140static void 138static void
@@ -166,7 +164,6 @@ teql_destroy(struct Qdisc *sch)
166 } 164 }
167 } 165 }
168 skb_queue_purge(&dat->q); 166 skb_queue_purge(&dat->q);
169 teql_neigh_release(xchg(&dat->ncache, NULL));
170 break; 167 break;
171 } 168 }
172 169
@@ -225,21 +222,25 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt)
225static int 222static int
226__teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, 223__teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res,
227 struct net_device *dev, struct netdev_queue *txq, 224 struct net_device *dev, struct netdev_queue *txq,
228 struct neighbour *mn) 225 struct dst_entry *dst)
229{ 226{
230 struct teql_sched_data *q = qdisc_priv(txq->qdisc); 227 struct neighbour *n;
231 struct neighbour *n = q->ncache; 228 int err = 0;
232 229
233 if (mn->tbl == NULL) 230 n = dst_neigh_lookup_skb(dst, skb);
234 return -EINVAL; 231 if (!n)
235 if (n && n->tbl == mn->tbl && 232 return -ENOENT;
236 memcmp(n->primary_key, mn->primary_key, mn->tbl->key_len) == 0) { 233
237 atomic_inc(&n->refcnt); 234 if (dst->dev != dev) {
238 } else { 235 struct neighbour *mn;
239 n = __neigh_lookup_errno(mn->tbl, mn->primary_key, dev); 236
240 if (IS_ERR(n)) 237 mn = __neigh_lookup_errno(n->tbl, n->primary_key, dev);
241 return PTR_ERR(n); 238 neigh_release(n);
239 if (IS_ERR(mn))
240 return PTR_ERR(mn);
241 n = mn;
242 } 242 }
243
243 if (neigh_event_send(n, skb_res) == 0) { 244 if (neigh_event_send(n, skb_res) == 0) {
244 int err; 245 int err;
245 char haddr[MAX_ADDR_LEN]; 246 char haddr[MAX_ADDR_LEN];
@@ -248,15 +249,13 @@ __teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res,
248 err = dev_hard_header(skb, dev, ntohs(skb->protocol), haddr, 249 err = dev_hard_header(skb, dev, ntohs(skb->protocol), haddr,
249 NULL, skb->len); 250 NULL, skb->len);
250 251
251 if (err < 0) { 252 if (err < 0)
252 neigh_release(n); 253 err = -EINVAL;
253 return -EINVAL; 254 } else {
254 } 255 err = (skb_res == NULL) ? -EAGAIN : 1;
255 teql_neigh_release(xchg(&q->ncache, n));
256 return 0;
257 } 256 }
258 neigh_release(n); 257 neigh_release(n);
259 return (skb_res == NULL) ? -EAGAIN : 1; 258 return err;
260} 259}
261 260
262static inline int teql_resolve(struct sk_buff *skb, 261static inline int teql_resolve(struct sk_buff *skb,
@@ -265,7 +264,6 @@ static inline int teql_resolve(struct sk_buff *skb,
265 struct netdev_queue *txq) 264 struct netdev_queue *txq)
266{ 265{
267 struct dst_entry *dst = skb_dst(skb); 266 struct dst_entry *dst = skb_dst(skb);
268 struct neighbour *mn;
269 int res; 267 int res;
270 268
271 if (txq->qdisc == &noop_qdisc) 269 if (txq->qdisc == &noop_qdisc)
@@ -275,8 +273,7 @@ static inline int teql_resolve(struct sk_buff *skb,
275 return 0; 273 return 0;
276 274
277 rcu_read_lock(); 275 rcu_read_lock();
278 mn = dst_get_neighbour_noref(dst); 276 res = __teql_resolve(skb, skb_res, dev, txq, dst);
279 res = mn ? __teql_resolve(skb, skb_res, dev, txq, mn) : 0;
280 rcu_read_unlock(); 277 rcu_read_unlock();
281 278
282 return res; 279 return res;