diff options
Diffstat (limited to 'net/core/dst.c')
-rw-r--r-- | net/core/dst.c | 23 |
1 files changed, 14 insertions, 9 deletions
diff --git a/net/core/dst.c b/net/core/dst.c index 960e503b5a52..6192f11beec9 100644 --- a/net/core/dst.c +++ b/net/core/dst.c | |||
@@ -151,13 +151,13 @@ int dst_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb) | |||
151 | } | 151 | } |
152 | EXPORT_SYMBOL(dst_discard_out); | 152 | EXPORT_SYMBOL(dst_discard_out); |
153 | 153 | ||
154 | const u32 dst_default_metrics[RTAX_MAX + 1] = { | 154 | const struct dst_metrics dst_default_metrics = { |
155 | /* This initializer is needed to force linker to place this variable | 155 | /* This initializer is needed to force linker to place this variable |
156 | * into const section. Otherwise it might end into bss section. | 156 | * into const section. Otherwise it might end into bss section. |
157 | * We really want to avoid false sharing on this variable, and catch | 157 | * We really want to avoid false sharing on this variable, and catch |
158 | * any writes on it. | 158 | * any writes on it. |
159 | */ | 159 | */ |
160 | [RTAX_MAX] = 0xdeadbeef, | 160 | .refcnt = ATOMIC_INIT(1), |
161 | }; | 161 | }; |
162 | 162 | ||
163 | void dst_init(struct dst_entry *dst, struct dst_ops *ops, | 163 | void dst_init(struct dst_entry *dst, struct dst_ops *ops, |
@@ -169,7 +169,7 @@ void dst_init(struct dst_entry *dst, struct dst_ops *ops, | |||
169 | if (dev) | 169 | if (dev) |
170 | dev_hold(dev); | 170 | dev_hold(dev); |
171 | dst->ops = ops; | 171 | dst->ops = ops; |
172 | dst_init_metrics(dst, dst_default_metrics, true); | 172 | dst_init_metrics(dst, dst_default_metrics.metrics, true); |
173 | dst->expires = 0UL; | 173 | dst->expires = 0UL; |
174 | dst->path = dst; | 174 | dst->path = dst; |
175 | dst->from = NULL; | 175 | dst->from = NULL; |
@@ -314,25 +314,30 @@ EXPORT_SYMBOL(dst_release); | |||
314 | 314 | ||
315 | u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old) | 315 | u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old) |
316 | { | 316 | { |
317 | u32 *p = kmalloc(sizeof(u32) * RTAX_MAX, GFP_ATOMIC); | 317 | struct dst_metrics *p = kmalloc(sizeof(*p), GFP_ATOMIC); |
318 | 318 | ||
319 | if (p) { | 319 | if (p) { |
320 | u32 *old_p = __DST_METRICS_PTR(old); | 320 | struct dst_metrics *old_p = (struct dst_metrics *)__DST_METRICS_PTR(old); |
321 | unsigned long prev, new; | 321 | unsigned long prev, new; |
322 | 322 | ||
323 | memcpy(p, old_p, sizeof(u32) * RTAX_MAX); | 323 | atomic_set(&p->refcnt, 1); |
324 | memcpy(p->metrics, old_p->metrics, sizeof(p->metrics)); | ||
324 | 325 | ||
325 | new = (unsigned long) p; | 326 | new = (unsigned long) p; |
326 | prev = cmpxchg(&dst->_metrics, old, new); | 327 | prev = cmpxchg(&dst->_metrics, old, new); |
327 | 328 | ||
328 | if (prev != old) { | 329 | if (prev != old) { |
329 | kfree(p); | 330 | kfree(p); |
330 | p = __DST_METRICS_PTR(prev); | 331 | p = (struct dst_metrics *)__DST_METRICS_PTR(prev); |
331 | if (prev & DST_METRICS_READ_ONLY) | 332 | if (prev & DST_METRICS_READ_ONLY) |
332 | p = NULL; | 333 | p = NULL; |
334 | } else if (prev & DST_METRICS_REFCOUNTED) { | ||
335 | if (atomic_dec_and_test(&old_p->refcnt)) | ||
336 | kfree(old_p); | ||
333 | } | 337 | } |
334 | } | 338 | } |
335 | return p; | 339 | BUILD_BUG_ON(offsetof(struct dst_metrics, metrics) != 0); |
340 | return (u32 *)p; | ||
336 | } | 341 | } |
337 | EXPORT_SYMBOL(dst_cow_metrics_generic); | 342 | EXPORT_SYMBOL(dst_cow_metrics_generic); |
338 | 343 | ||
@@ -341,7 +346,7 @@ void __dst_destroy_metrics_generic(struct dst_entry *dst, unsigned long old) | |||
341 | { | 346 | { |
342 | unsigned long prev, new; | 347 | unsigned long prev, new; |
343 | 348 | ||
344 | new = ((unsigned long) dst_default_metrics) | DST_METRICS_READ_ONLY; | 349 | new = ((unsigned long) &dst_default_metrics) | DST_METRICS_READ_ONLY; |
345 | prev = cmpxchg(&dst->_metrics, old, new); | 350 | prev = cmpxchg(&dst->_metrics, old, new); |
346 | if (prev == old) | 351 | if (prev == old) |
347 | kfree(__DST_METRICS_PTR(old)); | 352 | kfree(__DST_METRICS_PTR(old)); |