Main Page | Class List | File List | Class Members | File Members

dst.c

Go to the documentation of this file.
00001 /* 00002 * net/dst.c Protocol independent destination cache. 00003 * 00004 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 00005 * 00006 */ 00007 00008 #include <linux/bitops.h> 00009 #include <linux/types.h> 00010 #include <linux/kernel.h> 00011 #include <linux/sched.h> 00012 #include <linux/mm.h> 00013 #include <linux/string.h> 00014 #include <linux/errno.h> 00015 #include <linux/netdevice.h> 00016 #include <linux/skbuff.h> 00017 #include <linux/init.h> 00018 00019 #include <net/dst.h> 00020 00021 /* Locking strategy: 00022 * 1) Garbage collection state of dead destination cache 00023 * entries is protected by dst_lock. 00024 * 2) GC is run only from BH context, and is the only remover 00025 * of entries. 00026 * 3) Entries are added to the garbage list from both BH 00027 * and non-BH context, so local BH disabling is needed. 00028 * 4) All operations modify state, so a spinlock is used. 00029 */ 00030 static struct dst_entry *dst_garbage_list; 00031 #if RT_CACHE_DEBUG >= 2 00032 static atomic_t dst_total = ATOMIC_INIT(0); 00033 #endif 00034 static spinlock_t dst_lock = SPIN_LOCK_UNLOCKED; 00035 00036 static unsigned long dst_gc_timer_expires; 00037 static unsigned long dst_gc_timer_inc = DST_GC_MAX; 00038 static void dst_run_gc(unsigned long); 00039 00040 static struct timer_list dst_gc_timer = 00041 { data: DST_GC_MIN, function: dst_run_gc }; 00042 00043 00044 static void dst_run_gc(unsigned long dummy) 00045 { 00046 int delayed = 0; 00047 struct dst_entry * dst, **dstp; 00048 00049 if (!spin_trylock(&dst_lock)) { 00050 mod_timer(&dst_gc_timer, jiffies + HZ/10); 00051 return; 00052 } 00053 00054 00055 del_timer(&dst_gc_timer); 00056 dstp = &dst_garbage_list; 00057 while ((dst = *dstp) != NULL) { 00058 if (atomic_read(&dst->__refcnt)) { 00059 dstp = &dst->next; 00060 delayed++; 00061 continue; 00062 } 00063 *dstp = dst->next; 00064 dst_destroy(dst); 00065 } 00066 if (!dst_garbage_list) { 00067 dst_gc_timer_inc = DST_GC_MAX; 00068 goto out; 00069 } 00070 if ((dst_gc_timer_expires += dst_gc_timer_inc) > DST_GC_MAX) 00071 dst_gc_timer_expires = DST_GC_MAX; 00072 dst_gc_timer_inc += DST_GC_INC; 00073 dst_gc_timer.expires = jiffies + dst_gc_timer_expires; 00074 #if RT_CACHE_DEBUG >= 2 00075 printk("dst_total: %d/%d %ld\n", 00076 atomic_read(&dst_total), delayed, dst_gc_timer_expires); 00077 #endif 00078 add_timer(&dst_gc_timer); 00079 00080 out: 00081 spin_unlock(&dst_lock); 00082 } 00083 00084 static int dst_discard(struct sk_buff *skb) 00085 { 00086 kfree_skb(skb); 00087 return 0; 00088 } 00089 00090 static int dst_blackhole(struct sk_buff *skb) 00091 { 00092 kfree_skb(skb); 00093 return 0; 00094 } 00095 00096 void * dst_alloc(struct dst_ops * ops) 00097 { 00098 struct dst_entry * dst; 00099 00100 if (ops->gc && atomic_read(&ops->entries) > ops->gc_thresh) { 00101 if (ops->gc()) 00102 return NULL; 00103 } 00104 dst = kmem_cache_alloc(ops->kmem_cachep, SLAB_ATOMIC); 00105 if (!dst) 00106 return NULL; 00107 memset(dst, 0, ops->entry_size); 00108 atomic_set(&dst->__refcnt, 0); 00109 dst->ops = ops; 00110 dst->lastuse = jiffies; 00111 dst->input = dst_discard; 00112 dst->output = dst_blackhole; 00113 #if RT_CACHE_DEBUG >= 2 00114 atomic_inc(&dst_total); 00115 #endif 00116 atomic_inc(&ops->entries); 00117 return dst; 00118 } 00119 00120 void __dst_free(struct dst_entry * dst) 00121 { 00122 spin_lock_bh(&dst_lock); 00123 00124 /* The first case (dev==NULL) is required, when 00125 protocol module is unloaded. 00126 */ 00127 if (dst->dev == NULL || !(dst->dev->flags&IFF_UP)) { 00128 dst->input = dst_discard; 00129 dst->output = dst_blackhole; 00130 } 00131 dst->obsolete = 2; 00132 dst->next = dst_garbage_list; 00133 dst_garbage_list = dst; 00134 if (dst_gc_timer_inc > DST_GC_INC) { 00135 dst_gc_timer_inc = DST_GC_INC; 00136 dst_gc_timer_expires = DST_GC_MIN; 00137 mod_timer(&dst_gc_timer, jiffies + dst_gc_timer_expires); 00138 } 00139 00140 spin_unlock_bh(&dst_lock); 00141 } 00142 00143 void dst_destroy(struct dst_entry * dst) 00144 { 00145 struct neighbour *neigh = dst->neighbour; 00146 struct hh_cache *hh = dst->hh; 00147 00148 dst->hh = NULL; 00149 if (hh && atomic_dec_and_test(&hh->hh_refcnt)) 00150 kfree(hh); 00151 00152 if (neigh) { 00153 dst->neighbour = NULL; 00154 neigh_release(neigh); 00155 } 00156 00157 atomic_dec(&dst->ops->entries); 00158 00159 if (dst->ops->destroy) 00160 dst->ops->destroy(dst); 00161 if (dst->dev) 00162 dev_put(dst->dev); 00163 #if RT_CACHE_DEBUG >= 2 00164 atomic_dec(&dst_total); 00165 #endif 00166 kmem_cache_free(dst->ops->kmem_cachep, dst); 00167 } 00168 00169 static int dst_dev_event(struct notifier_block *this, unsigned long event, void *ptr) 00170 { 00171 struct net_device *dev = ptr; 00172 struct dst_entry *dst; 00173 00174 switch (event) { 00175 case NETDEV_UNREGISTER: 00176 case NETDEV_DOWN: 00177 spin_lock_bh(&dst_lock); 00178 for (dst = dst_garbage_list; dst; dst = dst->next) { 00179 if (dst->dev == dev) { 00180 /* Dirty hack. We did it in 2.2 (in __dst_free), 00181 we have _very_ good reasons not to repeat 00182 this mistake in 2.3, but we have no choice 00183 now. _It_ _is_ _explicit_ _deliberate_ 00184 _race_ _condition_. 00185 */ 00186 if (event!=NETDEV_DOWN && 00187 !(dev->features & NETIF_F_DYNALLOC) && 00188 dst->output == dst_blackhole) { 00189 dst->dev = &loopback_dev; 00190 dev_put(dev); 00191 dev_hold(&loopback_dev); 00192 dst->output = dst_discard; 00193 if (dst->neighbour && dst->neighbour->dev == dev) { 00194 dst->neighbour->dev = &loopback_dev; 00195 dev_put(dev); 00196 dev_hold(&loopback_dev); 00197 } 00198 } else { 00199 dst->input = dst_discard; 00200 dst->output = dst_blackhole; 00201 } 00202 } 00203 } 00204 spin_unlock_bh(&dst_lock); 00205 break; 00206 } 00207 return NOTIFY_DONE; 00208 } 00209 00210 struct notifier_block dst_dev_notifier = { 00211 dst_dev_event, 00212 NULL, 00213 0 00214 }; 00215 00216 void __init dst_init(void) 00217 { 00218 register_netdevice_notifier(&dst_dev_notifier); 00219 }

Generated on Wed Dec 1 21:25:30 2004 for Linux 2.4.23 Networking by doxygen 1.3.8