Blame view

net/core/dst.c 9.82 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
  /*
   * net/core/dst.c	Protocol independent destination cache.
   *
   * Authors:		Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   *
   */
  
  #include <linux/bitops.h>
  #include <linux/errno.h>
  #include <linux/init.h>
  #include <linux/kernel.h>
86bba269d   Eric Dumazet   [PATCH] NET : con...
12
  #include <linux/workqueue.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
  #include <linux/mm.h>
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
15
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
  #include <linux/netdevice.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
  #include <linux/skbuff.h>
  #include <linux/string.h>
  #include <linux/types.h>
e9dc86534   Eric W. Biederman   [NET]: Make devic...
20
  #include <net/net_namespace.h>
2fc1b5dd9   Eric Dumazet   dst: call cond_re...
21
  #include <linux/sched.h>
268bb0ce3   Linus Torvalds   sanitize <linux/p...
22
  #include <linux/prefetch.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
  
  #include <net/dst.h>
86bba269d   Eric Dumazet   [PATCH] NET : con...
25
26
27
28
29
30
31
32
33
34
  /*
   * Theory of operations:
   * 1) We use a list, protected by a spinlock, to add
   *    new entries from both BH and non-BH context.
   * 2) In order to keep spinlock held for a small delay,
   *    we use a second list where are stored long lived
   *    entries, that are handled by the garbage collect thread
   *    fired by a workqueue.
   * 3) This list is guarded by a mutex,
   *    so that the gc_task and dst_dev_event() can be synchronized.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36

86bba269d   Eric Dumazet   [PATCH] NET : con...
37
38
39
40
41
42
43
  /*
   * We want to keep lock & list close together
   * to dirty as few cache lines as possible in __dst_free().
   * As this is not a very strong hint, we dont force an alignment on SMP.
   */
  static struct {
  	spinlock_t		lock;
598ed9367   laurent chavey   fix net/core/dst....
44
  	struct dst_entry	*list;
86bba269d   Eric Dumazet   [PATCH] NET : con...
45
46
47
48
49
50
51
  	unsigned long		timer_inc;
  	unsigned long		timer_expires;
  } dst_garbage = {
  	.lock = __SPIN_LOCK_UNLOCKED(dst_garbage.lock),
  	.timer_inc = DST_GC_MAX,
  };
  static void dst_gc_task(struct work_struct *work);
598ed9367   laurent chavey   fix net/core/dst....
52
  static void ___dst_free(struct dst_entry *dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53

86bba269d   Eric Dumazet   [PATCH] NET : con...
54
  static DECLARE_DELAYED_WORK(dst_gc_work, dst_gc_task);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
55

86bba269d   Eric Dumazet   [PATCH] NET : con...
56
57
58
59
60
61
62
  static DEFINE_MUTEX(dst_gc_mutex);
  /*
   * long lived entries are maintained in this list, guarded by dst_gc_mutex
   */
  static struct dst_entry         *dst_busy_list;
  
  static void dst_gc_task(struct work_struct *work)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
64
  {
  	int    delayed = 0;
86bba269d   Eric Dumazet   [PATCH] NET : con...
65
66
67
68
  	int    work_performed = 0;
  	unsigned long expires = ~0L;
  	struct dst_entry *dst, *next, head;
  	struct dst_entry *last = &head;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69

86bba269d   Eric Dumazet   [PATCH] NET : con...
70
71
  	mutex_lock(&dst_gc_mutex);
  	next = dst_busy_list;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72

86bba269d   Eric Dumazet   [PATCH] NET : con...
73
74
75
76
  loop:
  	while ((dst = next) != NULL) {
  		next = dst->next;
  		prefetch(&next->next);
2fc1b5dd9   Eric Dumazet   dst: call cond_re...
77
  		cond_resched();
86bba269d   Eric Dumazet   [PATCH] NET : con...
78
79
80
  		if (likely(atomic_read(&dst->__refcnt))) {
  			last->next = dst;
  			last = dst;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
82
83
  			delayed++;
  			continue;
  		}
86bba269d   Eric Dumazet   [PATCH] NET : con...
84
  		work_performed++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
86
87
88
89
90
91
92
93
94
95
  
  		dst = dst_destroy(dst);
  		if (dst) {
  			/* NOHASH and still referenced. Unless it is already
  			 * on gc list, invalidate it and add to gc list.
  			 *
  			 * Note: this is temporary. Actually, NOHASH dst's
  			 * must be obsoleted when parent is obsoleted.
  			 * But we do not have state "obsoleted, but
  			 * referenced by parent", so it is right.
  			 */
f5b0a8743   David S. Miller   net: Document dst...
96
  			if (dst->obsolete > 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
98
99
  				continue;
  
  			___dst_free(dst);
86bba269d   Eric Dumazet   [PATCH] NET : con...
100
101
  			dst->next = next;
  			next = dst;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
103
  		}
  	}
86bba269d   Eric Dumazet   [PATCH] NET : con...
104
105
106
107
108
109
110
  
  	spin_lock_bh(&dst_garbage.lock);
  	next = dst_garbage.list;
  	if (next) {
  		dst_garbage.list = NULL;
  		spin_unlock_bh(&dst_garbage.lock);
  		goto loop;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
  	}
86bba269d   Eric Dumazet   [PATCH] NET : con...
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
  	last->next = NULL;
  	dst_busy_list = head.next;
  	if (!dst_busy_list)
  		dst_garbage.timer_inc = DST_GC_MAX;
  	else {
  		/*
  		 * if we freed less than 1/10 of delayed entries,
  		 * we can sleep longer.
  		 */
  		if (work_performed <= delayed/10) {
  			dst_garbage.timer_expires += dst_garbage.timer_inc;
  			if (dst_garbage.timer_expires > DST_GC_MAX)
  				dst_garbage.timer_expires = DST_GC_MAX;
  			dst_garbage.timer_inc += DST_GC_INC;
  		} else {
  			dst_garbage.timer_inc = DST_GC_INC;
  			dst_garbage.timer_expires = DST_GC_MIN;
  		}
  		expires = dst_garbage.timer_expires;
  		/*
598ed9367   laurent chavey   fix net/core/dst....
132
133
  		 * if the next desired timer is more than 4 seconds in the
  		 * future then round the timer to whole seconds
86bba269d   Eric Dumazet   [PATCH] NET : con...
134
135
136
137
  		 */
  		if (expires > 4*HZ)
  			expires = round_jiffies_relative(expires);
  		schedule_delayed_work(&dst_gc_work, expires);
f0098f786   Denis Lunev   [NET] Fix too agg...
138
  	}
86bba269d   Eric Dumazet   [PATCH] NET : con...
139
140
141
  
  	spin_unlock_bh(&dst_garbage.lock);
  	mutex_unlock(&dst_gc_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
  }
352e512c3   Herbert Xu   [NET]: Eliminate ...
143
  int dst_discard(struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
145
146
147
  {
  	kfree_skb(skb);
  	return 0;
  }
352e512c3   Herbert Xu   [NET]: Eliminate ...
148
  EXPORT_SYMBOL(dst_discard);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149

a37e6e344   Eric Dumazet   net: force dst_de...
150
151
152
153
154
155
156
157
  const u32 dst_default_metrics[RTAX_MAX + 1] = {
  	/* This initializer is needed to force linker to place this variable
  	 * into const section. Otherwise it might end into bss section.
  	 * We really want to avoid false sharing on this variable, and catch
  	 * any writes on it.
  	 */
  	[RTAX_MAX] = 0xdeadbeef,
  };
62fa8a846   David S. Miller   net: Implement re...
158

5c1e6aa30   David S. Miller   net: Make dst_all...
159
  void *dst_alloc(struct dst_ops *ops, struct net_device *dev,
5110effee   David S. Miller   net: Do delayed n...
160
  		int initial_ref, int initial_obsolete, unsigned short flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
  {
598ed9367   laurent chavey   fix net/core/dst....
162
  	struct dst_entry *dst;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163

fc66f95c6   Eric Dumazet   net dst: use a pe...
164
  	if (ops->gc && dst_entries_get_fast(ops) > ops->gc_thresh) {
569d36452   Daniel Lezcano   [NETNS][DST] dst:...
165
  		if (ops->gc(ops))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
167
  			return NULL;
  	}
cf9116622   David S. Miller   net: Use non-zero...
168
  	dst = kmem_cache_alloc(ops->kmem_cachep, GFP_ATOMIC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169
170
  	if (!dst)
  		return NULL;
cf9116622   David S. Miller   net: Use non-zero...
171
  	dst->child = NULL;
5c1e6aa30   David S. Miller   net: Make dst_all...
172
173
174
  	dst->dev = dev;
  	if (dev)
  		dev_hold(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
  	dst->ops = ops;
62fa8a846   David S. Miller   net: Implement re...
176
  	dst_init_metrics(dst, dst_default_metrics, true);
cf9116622   David S. Miller   net: Use non-zero...
177
  	dst->expires = 0UL;
5c1e6aa30   David S. Miller   net: Make dst_all...
178
  	dst->path = dst;
ecd988372   YOSHIFUJI Hideaki / 吉藤英明   ipv6: fix race co...
179
  	dst->from = NULL;
cf9116622   David S. Miller   net: Use non-zero...
180
181
182
  #ifdef CONFIG_XFRM
  	dst->xfrm = NULL;
  #endif
5c1e6aa30   David S. Miller   net: Make dst_all...
183
184
  	dst->input = dst_discard;
  	dst->output = dst_discard;
cf9116622   David S. Miller   net: Use non-zero...
185
  	dst->error = 0;
5c1e6aa30   David S. Miller   net: Make dst_all...
186
  	dst->obsolete = initial_obsolete;
cf9116622   David S. Miller   net: Use non-zero...
187
188
189
190
  	dst->header_len = 0;
  	dst->trailer_len = 0;
  #ifdef CONFIG_IP_ROUTE_CLASSID
  	dst->tclassid = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
  #endif
5c1e6aa30   David S. Miller   net: Make dst_all...
192
  	atomic_set(&dst->__refcnt, initial_ref);
cf9116622   David S. Miller   net: Use non-zero...
193
  	dst->__use = 0;
5c1e6aa30   David S. Miller   net: Make dst_all...
194
195
  	dst->lastuse = jiffies;
  	dst->flags = flags;
5110effee   David S. Miller   net: Do delayed n...
196
  	dst->pending_confirm = 0;
cf9116622   David S. Miller   net: Use non-zero...
197
  	dst->next = NULL;
957c665f3   David S. Miller   ipv6: Don't put a...
198
199
  	if (!(flags & DST_NOCOUNT))
  		dst_entries_add(ops, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
201
  	return dst;
  }
598ed9367   laurent chavey   fix net/core/dst....
202
  EXPORT_SYMBOL(dst_alloc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203

598ed9367   laurent chavey   fix net/core/dst....
204
  static void ___dst_free(struct dst_entry *dst)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
206
207
208
  {
  	/* The first case (dev==NULL) is required, when
  	   protocol module is unloaded.
  	 */
598ed9367   laurent chavey   fix net/core/dst....
209
  	if (dst->dev == NULL || !(dst->dev->flags&IFF_UP))
c4b1010f4   Denis Cheng   [NET]: Merge dst_...
210
  		dst->input = dst->output = dst_discard;
f5b0a8743   David S. Miller   net: Document dst...
211
  	dst->obsolete = DST_OBSOLETE_DEAD;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
212
  }
598ed9367   laurent chavey   fix net/core/dst....
213
  void __dst_free(struct dst_entry *dst)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
  {
86bba269d   Eric Dumazet   [PATCH] NET : con...
215
  	spin_lock_bh(&dst_garbage.lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216
  	___dst_free(dst);
86bba269d   Eric Dumazet   [PATCH] NET : con...
217
218
219
220
221
  	dst->next = dst_garbage.list;
  	dst_garbage.list = dst;
  	if (dst_garbage.timer_inc > DST_GC_INC) {
  		dst_garbage.timer_inc = DST_GC_INC;
  		dst_garbage.timer_expires = DST_GC_MIN;
41f63c535   Tejun Heo   workqueue: use mo...
222
223
  		mod_delayed_work(system_wq, &dst_gc_work,
  				 dst_garbage.timer_expires);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
  	}
86bba269d   Eric Dumazet   [PATCH] NET : con...
225
  	spin_unlock_bh(&dst_garbage.lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
  }
d79d99137   Nicolas Dichtel   __dst_free(): put...
227
  EXPORT_SYMBOL(__dst_free);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
229
230
231
  
  struct dst_entry *dst_destroy(struct dst_entry * dst)
  {
  	struct dst_entry *child;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
233
234
235
  
  	smp_rmb();
  
  again:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
  	child = dst->child;
957c665f3   David S. Miller   ipv6: Don't put a...
237
238
  	if (!(dst->flags & DST_NOCOUNT))
  		dst_entries_add(dst->ops, -1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
240
241
242
243
  
  	if (dst->ops->destroy)
  		dst->ops->destroy(dst);
  	if (dst->dev)
  		dev_put(dst->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
245
246
247
  	kmem_cache_free(dst->ops->kmem_cachep, dst);
  
  	dst = child;
  	if (dst) {
6775cab98   Herbert Xu   [PATCH] Fix dst_d...
248
  		int nohash = dst->flags & DST_NOHASH;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
250
  		if (atomic_dec_and_test(&dst->__refcnt)) {
  			/* We were real parent of this dst, so kill child. */
6775cab98   Herbert Xu   [PATCH] Fix dst_d...
251
  			if (nohash)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
254
  				goto again;
  		} else {
  			/* Child is still referenced, return it for freeing. */
6775cab98   Herbert Xu   [PATCH] Fix dst_d...
255
  			if (nohash)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
257
258
259
260
261
  				return dst;
  			/* Child is still in his hash table */
  		}
  	}
  	return NULL;
  }
598ed9367   laurent chavey   fix net/core/dst....
262
  EXPORT_SYMBOL(dst_destroy);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263

8d3308687   Ilpo Järvinen   [NET]: uninline d...
264
265
266
  void dst_release(struct dst_entry *dst)
  {
  	if (dst) {
598ed9367   laurent chavey   fix net/core/dst....
267
  		int newrefcnt;
ef711cf1d   Eric Dumazet   net: speedup dst_...
268

598ed9367   laurent chavey   fix net/core/dst....
269
270
  		newrefcnt = atomic_dec_return(&dst->__refcnt);
  		WARN_ON(newrefcnt < 0);
27b75c95f   Eric Dumazet   net: avoid RCU fo...
271
272
273
274
275
  		if (unlikely(dst->flags & DST_NOCACHE) && !newrefcnt) {
  			dst = dst_destroy(dst);
  			if (dst)
  				__dst_free(dst);
  		}
8d3308687   Ilpo Järvinen   [NET]: uninline d...
276
277
278
  	}
  }
  EXPORT_SYMBOL(dst_release);
62fa8a846   David S. Miller   net: Implement re...
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
  u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old)
  {
  	u32 *p = kmalloc(sizeof(u32) * RTAX_MAX, GFP_ATOMIC);
  
  	if (p) {
  		u32 *old_p = __DST_METRICS_PTR(old);
  		unsigned long prev, new;
  
  		memcpy(p, old_p, sizeof(u32) * RTAX_MAX);
  
  		new = (unsigned long) p;
  		prev = cmpxchg(&dst->_metrics, old, new);
  
  		if (prev != old) {
  			kfree(p);
  			p = __DST_METRICS_PTR(prev);
  			if (prev & DST_METRICS_READ_ONLY)
  				p = NULL;
  		}
  	}
  	return p;
  }
  EXPORT_SYMBOL(dst_cow_metrics_generic);
  
  /* Caller asserts that dst_metrics_read_only(dst) is false.  */
  void __dst_destroy_metrics_generic(struct dst_entry *dst, unsigned long old)
  {
  	unsigned long prev, new;
b30c516f8   Eric Dumazet   net: fix __dst_de...
307
  	new = ((unsigned long) dst_default_metrics) | DST_METRICS_READ_ONLY;
62fa8a846   David S. Miller   net: Implement re...
308
309
310
311
312
  	prev = cmpxchg(&dst->_metrics, old, new);
  	if (prev == old)
  		kfree(__DST_METRICS_PTR(old));
  }
  EXPORT_SYMBOL(__dst_destroy_metrics_generic);
27b75c95f   Eric Dumazet   net: avoid RCU fo...
313
  /**
932bc4d7a   Julian Anastasov   net: add skb_dst_...
314
   * __skb_dst_set_noref - sets skb dst, without a reference
27b75c95f   Eric Dumazet   net: avoid RCU fo...
315
316
   * @skb: buffer
   * @dst: dst entry
932bc4d7a   Julian Anastasov   net: add skb_dst_...
317
   * @force: if force is set, use noref version even for DST_NOCACHE entries
27b75c95f   Eric Dumazet   net: avoid RCU fo...
318
319
320
321
   *
   * Sets skb dst, assuming a reference was not taken on dst
   * skb_dst_drop() should not dst_release() this dst
   */
932bc4d7a   Julian Anastasov   net: add skb_dst_...
322
  void __skb_dst_set_noref(struct sk_buff *skb, struct dst_entry *dst, bool force)
27b75c95f   Eric Dumazet   net: avoid RCU fo...
323
324
325
326
327
  {
  	WARN_ON(!rcu_read_lock_held() && !rcu_read_lock_bh_held());
  	/* If dst not in cache, we must take a reference, because
  	 * dst_release() will destroy dst as soon as its refcount becomes zero
  	 */
932bc4d7a   Julian Anastasov   net: add skb_dst_...
328
  	if (unlikely((dst->flags & DST_NOCACHE) && !force)) {
27b75c95f   Eric Dumazet   net: avoid RCU fo...
329
330
331
332
333
334
  		dst_hold(dst);
  		skb_dst_set(skb, dst);
  	} else {
  		skb->_skb_refdst = (unsigned long)dst | SKB_DST_NOREF;
  	}
  }
932bc4d7a   Julian Anastasov   net: add skb_dst_...
335
  EXPORT_SYMBOL(__skb_dst_set_noref);
27b75c95f   Eric Dumazet   net: avoid RCU fo...
336

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
337
338
339
340
341
342
343
344
  /* Dirty hack. We did it in 2.2 (in __dst_free),
   * we have _very_ good reasons not to repeat
   * this mistake in 2.3, but we have no choice
   * now. _It_ _is_ _explicit_ _deliberate_
   * _race_ _condition_.
   *
   * Commented and originally written by Alexey.
   */
561155110   stephen hemminger   dst: don't inline...
345
346
  static void dst_ifdown(struct dst_entry *dst, struct net_device *dev,
  		       int unregister)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
348
349
350
351
352
353
354
  {
  	if (dst->ops->ifdown)
  		dst->ops->ifdown(dst, dev, unregister);
  
  	if (dev != dst->dev)
  		return;
  
  	if (!unregister) {
c4b1010f4   Denis Cheng   [NET]: Merge dst_...
355
  		dst->input = dst->output = dst_discard;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
356
  	} else {
c346dca10   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
357
  		dst->dev = dev_net(dst->dev)->loopback_dev;
de3cb747f   Daniel Lezcano   [NET]: Dynamicall...
358
  		dev_hold(dst->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
  		dev_put(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
361
  	}
  }
598ed9367   laurent chavey   fix net/core/dst....
362
363
  static int dst_dev_event(struct notifier_block *this, unsigned long event,
  			 void *ptr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
364
365
  {
  	struct net_device *dev = ptr;
86bba269d   Eric Dumazet   [PATCH] NET : con...
366
  	struct dst_entry *dst, *last = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367
368
  
  	switch (event) {
0115e8e30   Eric Dumazet   net: remove delay...
369
  	case NETDEV_UNREGISTER_FINAL:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
370
  	case NETDEV_DOWN:
86bba269d   Eric Dumazet   [PATCH] NET : con...
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
  		mutex_lock(&dst_gc_mutex);
  		for (dst = dst_busy_list; dst; dst = dst->next) {
  			last = dst;
  			dst_ifdown(dst, dev, event != NETDEV_DOWN);
  		}
  
  		spin_lock_bh(&dst_garbage.lock);
  		dst = dst_garbage.list;
  		dst_garbage.list = NULL;
  		spin_unlock_bh(&dst_garbage.lock);
  
  		if (last)
  			last->next = dst;
  		else
  			dst_busy_list = dst;
598ed9367   laurent chavey   fix net/core/dst....
386
  		for (; dst; dst = dst->next)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
  			dst_ifdown(dst, dev, event != NETDEV_DOWN);
86bba269d   Eric Dumazet   [PATCH] NET : con...
388
  		mutex_unlock(&dst_gc_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389
390
391
392
393
394
395
  		break;
  	}
  	return NOTIFY_DONE;
  }
  
  static struct notifier_block dst_dev_notifier = {
  	.notifier_call	= dst_dev_event,
332dd96f7   Eric Dumazet   net/dst: dst_dev_...
396
  	.priority = -10, /* must be called after other network notifiers */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
397
398
399
400
401
402
  };
  
  void __init dst_init(void)
  {
  	register_netdevice_notifier(&dst_dev_notifier);
  }