diff options
Diffstat (limited to 'net/ipv4/inetpeer.c')
-rw-r--r-- | net/ipv4/inetpeer.c | 167 |
1 files changed, 108 insertions, 59 deletions
diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index 9e94d7cf4f8a..d9bc85751c74 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c | |||
@@ -63,7 +63,7 @@ | |||
63 | * refcnt: atomically against modifications on other CPU; | 63 | * refcnt: atomically against modifications on other CPU; |
64 | * usually under some other lock to prevent node disappearing | 64 | * usually under some other lock to prevent node disappearing |
65 | * dtime: unused node list lock | 65 | * dtime: unused node list lock |
66 | * v4daddr: unchangeable | 66 | * daddr: unchangeable |
67 | * ip_id_count: atomic value (no lock needed) | 67 | * ip_id_count: atomic value (no lock needed) |
68 | */ | 68 | */ |
69 | 69 | ||
@@ -79,15 +79,24 @@ static const struct inet_peer peer_fake_node = { | |||
79 | .avl_height = 0 | 79 | .avl_height = 0 |
80 | }; | 80 | }; |
81 | 81 | ||
82 | static struct { | 82 | struct inet_peer_base { |
83 | struct inet_peer __rcu *root; | 83 | struct inet_peer __rcu *root; |
84 | spinlock_t lock; | 84 | spinlock_t lock; |
85 | int total; | 85 | int total; |
86 | } peers = { | 86 | }; |
87 | |||
88 | static struct inet_peer_base v4_peers = { | ||
89 | .root = peer_avl_empty_rcu, | ||
90 | .lock = __SPIN_LOCK_UNLOCKED(v4_peers.lock), | ||
91 | .total = 0, | ||
92 | }; | ||
93 | |||
94 | static struct inet_peer_base v6_peers = { | ||
87 | .root = peer_avl_empty_rcu, | 95 | .root = peer_avl_empty_rcu, |
88 | .lock = __SPIN_LOCK_UNLOCKED(peers.lock), | 96 | .lock = __SPIN_LOCK_UNLOCKED(v6_peers.lock), |
89 | .total = 0, | 97 | .total = 0, |
90 | }; | 98 | }; |
99 | |||
91 | #define PEER_MAXDEPTH 40 /* sufficient for about 2^27 nodes */ | 100 | #define PEER_MAXDEPTH 40 /* sufficient for about 2^27 nodes */ |
92 | 101 | ||
93 | /* Exported for sysctl_net_ipv4. */ | 102 | /* Exported for sysctl_net_ipv4. */ |
@@ -152,28 +161,45 @@ static void unlink_from_unused(struct inet_peer *p) | |||
152 | } | 161 | } |
153 | } | 162 | } |
154 | 163 | ||
164 | static int addr_compare(const struct inetpeer_addr *a, | ||
165 | const struct inetpeer_addr *b) | ||
166 | { | ||
167 | int i, n = (a->family == AF_INET ? 1 : 4); | ||
168 | |||
169 | for (i = 0; i < n; i++) { | ||
170 | if (a->a6[i] == b->a6[i]) | ||
171 | continue; | ||
172 | if (a->a6[i] < b->a6[i]) | ||
173 | return -1; | ||
174 | return 1; | ||
175 | } | ||
176 | |||
177 | return 0; | ||
178 | } | ||
179 | |||
155 | /* | 180 | /* |
156 | * Called with local BH disabled and the pool lock held. | 181 | * Called with local BH disabled and the pool lock held. |
157 | */ | 182 | */ |
158 | #define lookup(_daddr, _stack) \ | 183 | #define lookup(_daddr, _stack, _base) \ |
159 | ({ \ | 184 | ({ \ |
160 | struct inet_peer *u; \ | 185 | struct inet_peer *u; \ |
161 | struct inet_peer __rcu **v; \ | 186 | struct inet_peer __rcu **v; \ |
162 | \ | 187 | \ |
163 | stackptr = _stack; \ | 188 | stackptr = _stack; \ |
164 | *stackptr++ = &peers.root; \ | 189 | *stackptr++ = &_base->root; \ |
165 | for (u = rcu_dereference_protected(peers.root, \ | 190 | for (u = rcu_dereference_protected(_base->root, \ |
166 | lockdep_is_held(&peers.lock)); \ | 191 | lockdep_is_held(&_base->lock)); \ |
167 | u != peer_avl_empty; ) { \ | 192 | u != peer_avl_empty; ) { \ |
168 | if (_daddr == u->v4daddr) \ | 193 | int cmp = addr_compare(_daddr, &u->daddr); \ |
194 | if (cmp == 0) \ | ||
169 | break; \ | 195 | break; \ |
170 | if ((__force __u32)_daddr < (__force __u32)u->v4daddr) \ | 196 | if (cmp == -1) \ |
171 | v = &u->avl_left; \ | 197 | v = &u->avl_left; \ |
172 | else \ | 198 | else \ |
173 | v = &u->avl_right; \ | 199 | v = &u->avl_right; \ |
174 | *stackptr++ = v; \ | 200 | *stackptr++ = v; \ |
175 | u = rcu_dereference_protected(*v, \ | 201 | u = rcu_dereference_protected(*v, \ |
176 | lockdep_is_held(&peers.lock)); \ | 202 | lockdep_is_held(&_base->lock)); \ |
177 | } \ | 203 | } \ |
178 | u; \ | 204 | u; \ |
179 | }) | 205 | }) |
@@ -185,13 +211,15 @@ static void unlink_from_unused(struct inet_peer *p) | |||
185 | * But every pointer we follow is guaranteed to be valid thanks to RCU. | 211 | * But every pointer we follow is guaranteed to be valid thanks to RCU. |
186 | * We exit from this function if number of links exceeds PEER_MAXDEPTH | 212 | * We exit from this function if number of links exceeds PEER_MAXDEPTH |
187 | */ | 213 | */ |
188 | static struct inet_peer *lookup_rcu_bh(__be32 daddr) | 214 | static struct inet_peer *lookup_rcu_bh(const struct inetpeer_addr *daddr, |
215 | struct inet_peer_base *base) | ||
189 | { | 216 | { |
190 | struct inet_peer *u = rcu_dereference_bh(peers.root); | 217 | struct inet_peer *u = rcu_dereference_bh(base->root); |
191 | int count = 0; | 218 | int count = 0; |
192 | 219 | ||
193 | while (u != peer_avl_empty) { | 220 | while (u != peer_avl_empty) { |
194 | if (daddr == u->v4daddr) { | 221 | int cmp = addr_compare(daddr, &u->daddr); |
222 | if (cmp == 0) { | ||
195 | /* Before taking a reference, check if this entry was | 223 | /* Before taking a reference, check if this entry was |
196 | * deleted, unlink_from_pool() sets refcnt=-1 to make | 224 | * deleted, unlink_from_pool() sets refcnt=-1 to make |
197 | * distinction between an unused entry (refcnt=0) and | 225 | * distinction between an unused entry (refcnt=0) and |
@@ -201,7 +229,7 @@ static struct inet_peer *lookup_rcu_bh(__be32 daddr) | |||
201 | u = NULL; | 229 | u = NULL; |
202 | return u; | 230 | return u; |
203 | } | 231 | } |
204 | if ((__force __u32)daddr < (__force __u32)u->v4daddr) | 232 | if (cmp == -1) |
205 | u = rcu_dereference_bh(u->avl_left); | 233 | u = rcu_dereference_bh(u->avl_left); |
206 | else | 234 | else |
207 | u = rcu_dereference_bh(u->avl_right); | 235 | u = rcu_dereference_bh(u->avl_right); |
@@ -212,19 +240,19 @@ static struct inet_peer *lookup_rcu_bh(__be32 daddr) | |||
212 | } | 240 | } |
213 | 241 | ||
214 | /* Called with local BH disabled and the pool lock held. */ | 242 | /* Called with local BH disabled and the pool lock held. */ |
215 | #define lookup_rightempty(start) \ | 243 | #define lookup_rightempty(start, base) \ |
216 | ({ \ | 244 | ({ \ |
217 | struct inet_peer *u; \ | 245 | struct inet_peer *u; \ |
218 | struct inet_peer __rcu **v; \ | 246 | struct inet_peer __rcu **v; \ |
219 | *stackptr++ = &start->avl_left; \ | 247 | *stackptr++ = &start->avl_left; \ |
220 | v = &start->avl_left; \ | 248 | v = &start->avl_left; \ |
221 | for (u = rcu_dereference_protected(*v, \ | 249 | for (u = rcu_dereference_protected(*v, \ |
222 | lockdep_is_held(&peers.lock)); \ | 250 | lockdep_is_held(&base->lock)); \ |
223 | u->avl_right != peer_avl_empty_rcu; ) { \ | 251 | u->avl_right != peer_avl_empty_rcu; ) { \ |
224 | v = &u->avl_right; \ | 252 | v = &u->avl_right; \ |
225 | *stackptr++ = v; \ | 253 | *stackptr++ = v; \ |
226 | u = rcu_dereference_protected(*v, \ | 254 | u = rcu_dereference_protected(*v, \ |
227 | lockdep_is_held(&peers.lock)); \ | 255 | lockdep_is_held(&base->lock)); \ |
228 | } \ | 256 | } \ |
229 | u; \ | 257 | u; \ |
230 | }) | 258 | }) |
@@ -234,7 +262,8 @@ static struct inet_peer *lookup_rcu_bh(__be32 daddr) | |||
234 | * Look into mm/map_avl.c for more detail description of the ideas. | 262 | * Look into mm/map_avl.c for more detail description of the ideas. |
235 | */ | 263 | */ |
236 | static void peer_avl_rebalance(struct inet_peer __rcu **stack[], | 264 | static void peer_avl_rebalance(struct inet_peer __rcu **stack[], |
237 | struct inet_peer __rcu ***stackend) | 265 | struct inet_peer __rcu ***stackend, |
266 | struct inet_peer_base *base) | ||
238 | { | 267 | { |
239 | struct inet_peer __rcu **nodep; | 268 | struct inet_peer __rcu **nodep; |
240 | struct inet_peer *node, *l, *r; | 269 | struct inet_peer *node, *l, *r; |
@@ -243,20 +272,20 @@ static void peer_avl_rebalance(struct inet_peer __rcu **stack[], | |||
243 | while (stackend > stack) { | 272 | while (stackend > stack) { |
244 | nodep = *--stackend; | 273 | nodep = *--stackend; |
245 | node = rcu_dereference_protected(*nodep, | 274 | node = rcu_dereference_protected(*nodep, |
246 | lockdep_is_held(&peers.lock)); | 275 | lockdep_is_held(&base->lock)); |
247 | l = rcu_dereference_protected(node->avl_left, | 276 | l = rcu_dereference_protected(node->avl_left, |
248 | lockdep_is_held(&peers.lock)); | 277 | lockdep_is_held(&base->lock)); |
249 | r = rcu_dereference_protected(node->avl_right, | 278 | r = rcu_dereference_protected(node->avl_right, |
250 | lockdep_is_held(&peers.lock)); | 279 | lockdep_is_held(&base->lock)); |
251 | lh = node_height(l); | 280 | lh = node_height(l); |
252 | rh = node_height(r); | 281 | rh = node_height(r); |
253 | if (lh > rh + 1) { /* l: RH+2 */ | 282 | if (lh > rh + 1) { /* l: RH+2 */ |
254 | struct inet_peer *ll, *lr, *lrl, *lrr; | 283 | struct inet_peer *ll, *lr, *lrl, *lrr; |
255 | int lrh; | 284 | int lrh; |
256 | ll = rcu_dereference_protected(l->avl_left, | 285 | ll = rcu_dereference_protected(l->avl_left, |
257 | lockdep_is_held(&peers.lock)); | 286 | lockdep_is_held(&base->lock)); |
258 | lr = rcu_dereference_protected(l->avl_right, | 287 | lr = rcu_dereference_protected(l->avl_right, |
259 | lockdep_is_held(&peers.lock)); | 288 | lockdep_is_held(&base->lock)); |
260 | lrh = node_height(lr); | 289 | lrh = node_height(lr); |
261 | if (lrh <= node_height(ll)) { /* ll: RH+1 */ | 290 | if (lrh <= node_height(ll)) { /* ll: RH+1 */ |
262 | RCU_INIT_POINTER(node->avl_left, lr); /* lr: RH or RH+1 */ | 291 | RCU_INIT_POINTER(node->avl_left, lr); /* lr: RH or RH+1 */ |
@@ -268,9 +297,9 @@ static void peer_avl_rebalance(struct inet_peer __rcu **stack[], | |||
268 | RCU_INIT_POINTER(*nodep, l); | 297 | RCU_INIT_POINTER(*nodep, l); |
269 | } else { /* ll: RH, lr: RH+1 */ | 298 | } else { /* ll: RH, lr: RH+1 */ |
270 | lrl = rcu_dereference_protected(lr->avl_left, | 299 | lrl = rcu_dereference_protected(lr->avl_left, |
271 | lockdep_is_held(&peers.lock)); /* lrl: RH or RH-1 */ | 300 | lockdep_is_held(&base->lock)); /* lrl: RH or RH-1 */ |
272 | lrr = rcu_dereference_protected(lr->avl_right, | 301 | lrr = rcu_dereference_protected(lr->avl_right, |
273 | lockdep_is_held(&peers.lock)); /* lrr: RH or RH-1 */ | 302 | lockdep_is_held(&base->lock)); /* lrr: RH or RH-1 */ |
274 | RCU_INIT_POINTER(node->avl_left, lrr); /* lrr: RH or RH-1 */ | 303 | RCU_INIT_POINTER(node->avl_left, lrr); /* lrr: RH or RH-1 */ |
275 | RCU_INIT_POINTER(node->avl_right, r); /* r: RH */ | 304 | RCU_INIT_POINTER(node->avl_right, r); /* r: RH */ |
276 | node->avl_height = rh + 1; /* node: RH+1 */ | 305 | node->avl_height = rh + 1; /* node: RH+1 */ |
@@ -286,9 +315,9 @@ static void peer_avl_rebalance(struct inet_peer __rcu **stack[], | |||
286 | struct inet_peer *rr, *rl, *rlr, *rll; | 315 | struct inet_peer *rr, *rl, *rlr, *rll; |
287 | int rlh; | 316 | int rlh; |
288 | rr = rcu_dereference_protected(r->avl_right, | 317 | rr = rcu_dereference_protected(r->avl_right, |
289 | lockdep_is_held(&peers.lock)); | 318 | lockdep_is_held(&base->lock)); |
290 | rl = rcu_dereference_protected(r->avl_left, | 319 | rl = rcu_dereference_protected(r->avl_left, |
291 | lockdep_is_held(&peers.lock)); | 320 | lockdep_is_held(&base->lock)); |
292 | rlh = node_height(rl); | 321 | rlh = node_height(rl); |
293 | if (rlh <= node_height(rr)) { /* rr: LH+1 */ | 322 | if (rlh <= node_height(rr)) { /* rr: LH+1 */ |
294 | RCU_INIT_POINTER(node->avl_right, rl); /* rl: LH or LH+1 */ | 323 | RCU_INIT_POINTER(node->avl_right, rl); /* rl: LH or LH+1 */ |
@@ -300,9 +329,9 @@ static void peer_avl_rebalance(struct inet_peer __rcu **stack[], | |||
300 | RCU_INIT_POINTER(*nodep, r); | 329 | RCU_INIT_POINTER(*nodep, r); |
301 | } else { /* rr: RH, rl: RH+1 */ | 330 | } else { /* rr: RH, rl: RH+1 */ |
302 | rlr = rcu_dereference_protected(rl->avl_right, | 331 | rlr = rcu_dereference_protected(rl->avl_right, |
303 | lockdep_is_held(&peers.lock)); /* rlr: LH or LH-1 */ | 332 | lockdep_is_held(&base->lock)); /* rlr: LH or LH-1 */ |
304 | rll = rcu_dereference_protected(rl->avl_left, | 333 | rll = rcu_dereference_protected(rl->avl_left, |
305 | lockdep_is_held(&peers.lock)); /* rll: LH or LH-1 */ | 334 | lockdep_is_held(&base->lock)); /* rll: LH or LH-1 */ |
306 | RCU_INIT_POINTER(node->avl_right, rll); /* rll: LH or LH-1 */ | 335 | RCU_INIT_POINTER(node->avl_right, rll); /* rll: LH or LH-1 */ |
307 | RCU_INIT_POINTER(node->avl_left, l); /* l: LH */ | 336 | RCU_INIT_POINTER(node->avl_left, l); /* l: LH */ |
308 | node->avl_height = lh + 1; /* node: LH+1 */ | 337 | node->avl_height = lh + 1; /* node: LH+1 */ |
@@ -321,14 +350,14 @@ static void peer_avl_rebalance(struct inet_peer __rcu **stack[], | |||
321 | } | 350 | } |
322 | 351 | ||
323 | /* Called with local BH disabled and the pool lock held. */ | 352 | /* Called with local BH disabled and the pool lock held. */ |
324 | #define link_to_pool(n) \ | 353 | #define link_to_pool(n, base) \ |
325 | do { \ | 354 | do { \ |
326 | n->avl_height = 1; \ | 355 | n->avl_height = 1; \ |
327 | n->avl_left = peer_avl_empty_rcu; \ | 356 | n->avl_left = peer_avl_empty_rcu; \ |
328 | n->avl_right = peer_avl_empty_rcu; \ | 357 | n->avl_right = peer_avl_empty_rcu; \ |
329 | /* lockless readers can catch us now */ \ | 358 | /* lockless readers can catch us now */ \ |
330 | rcu_assign_pointer(**--stackptr, n); \ | 359 | rcu_assign_pointer(**--stackptr, n); \ |
331 | peer_avl_rebalance(stack, stackptr); \ | 360 | peer_avl_rebalance(stack, stackptr, base); \ |
332 | } while (0) | 361 | } while (0) |
333 | 362 | ||
334 | static void inetpeer_free_rcu(struct rcu_head *head) | 363 | static void inetpeer_free_rcu(struct rcu_head *head) |
@@ -337,13 +366,13 @@ static void inetpeer_free_rcu(struct rcu_head *head) | |||
337 | } | 366 | } |
338 | 367 | ||
339 | /* May be called with local BH enabled. */ | 368 | /* May be called with local BH enabled. */ |
340 | static void unlink_from_pool(struct inet_peer *p) | 369 | static void unlink_from_pool(struct inet_peer *p, struct inet_peer_base *base) |
341 | { | 370 | { |
342 | int do_free; | 371 | int do_free; |
343 | 372 | ||
344 | do_free = 0; | 373 | do_free = 0; |
345 | 374 | ||
346 | spin_lock_bh(&peers.lock); | 375 | spin_lock_bh(&base->lock); |
347 | /* Check the reference counter. It was artificially incremented by 1 | 376 | /* Check the reference counter. It was artificially incremented by 1 |
348 | * in cleanup() function to prevent sudden disappearing. If we can | 377 | * in cleanup() function to prevent sudden disappearing. If we can |
349 | * atomically (because of lockless readers) take this last reference, | 378 | * atomically (because of lockless readers) take this last reference, |
@@ -353,7 +382,7 @@ static void unlink_from_pool(struct inet_peer *p) | |||
353 | if (atomic_cmpxchg(&p->refcnt, 1, -1) == 1) { | 382 | if (atomic_cmpxchg(&p->refcnt, 1, -1) == 1) { |
354 | struct inet_peer __rcu **stack[PEER_MAXDEPTH]; | 383 | struct inet_peer __rcu **stack[PEER_MAXDEPTH]; |
355 | struct inet_peer __rcu ***stackptr, ***delp; | 384 | struct inet_peer __rcu ***stackptr, ***delp; |
356 | if (lookup(p->v4daddr, stack) != p) | 385 | if (lookup(&p->daddr, stack, base) != p) |
357 | BUG(); | 386 | BUG(); |
358 | delp = stackptr - 1; /* *delp[0] == p */ | 387 | delp = stackptr - 1; /* *delp[0] == p */ |
359 | if (p->avl_left == peer_avl_empty_rcu) { | 388 | if (p->avl_left == peer_avl_empty_rcu) { |
@@ -362,11 +391,11 @@ static void unlink_from_pool(struct inet_peer *p) | |||
362 | } else { | 391 | } else { |
363 | /* look for a node to insert instead of p */ | 392 | /* look for a node to insert instead of p */ |
364 | struct inet_peer *t; | 393 | struct inet_peer *t; |
365 | t = lookup_rightempty(p); | 394 | t = lookup_rightempty(p, base); |
366 | BUG_ON(rcu_dereference_protected(*stackptr[-1], | 395 | BUG_ON(rcu_dereference_protected(*stackptr[-1], |
367 | lockdep_is_held(&peers.lock)) != t); | 396 | lockdep_is_held(&base->lock)) != t); |
368 | **--stackptr = t->avl_left; | 397 | **--stackptr = t->avl_left; |
369 | /* t is removed, t->v4daddr > x->v4daddr for any | 398 | /* t is removed, t->daddr > x->daddr for any |
370 | * x in p->avl_left subtree. | 399 | * x in p->avl_left subtree. |
371 | * Put t in the old place of p. */ | 400 | * Put t in the old place of p. */ |
372 | RCU_INIT_POINTER(*delp[0], t); | 401 | RCU_INIT_POINTER(*delp[0], t); |
@@ -376,11 +405,11 @@ static void unlink_from_pool(struct inet_peer *p) | |||
376 | BUG_ON(delp[1] != &p->avl_left); | 405 | BUG_ON(delp[1] != &p->avl_left); |
377 | delp[1] = &t->avl_left; /* was &p->avl_left */ | 406 | delp[1] = &t->avl_left; /* was &p->avl_left */ |
378 | } | 407 | } |
379 | peer_avl_rebalance(stack, stackptr); | 408 | peer_avl_rebalance(stack, stackptr, base); |
380 | peers.total--; | 409 | base->total--; |
381 | do_free = 1; | 410 | do_free = 1; |
382 | } | 411 | } |
383 | spin_unlock_bh(&peers.lock); | 412 | spin_unlock_bh(&base->lock); |
384 | 413 | ||
385 | if (do_free) | 414 | if (do_free) |
386 | call_rcu_bh(&p->rcu, inetpeer_free_rcu); | 415 | call_rcu_bh(&p->rcu, inetpeer_free_rcu); |
@@ -395,6 +424,16 @@ static void unlink_from_pool(struct inet_peer *p) | |||
395 | inet_putpeer(p); | 424 | inet_putpeer(p); |
396 | } | 425 | } |
397 | 426 | ||
427 | static struct inet_peer_base *family_to_base(int family) | ||
428 | { | ||
429 | return (family == AF_INET ? &v4_peers : &v6_peers); | ||
430 | } | ||
431 | |||
432 | static struct inet_peer_base *peer_to_base(struct inet_peer *p) | ||
433 | { | ||
434 | return family_to_base(p->daddr.family); | ||
435 | } | ||
436 | |||
398 | /* May be called with local BH enabled. */ | 437 | /* May be called with local BH enabled. */ |
399 | static int cleanup_once(unsigned long ttl) | 438 | static int cleanup_once(unsigned long ttl) |
400 | { | 439 | { |
@@ -428,21 +467,22 @@ static int cleanup_once(unsigned long ttl) | |||
428 | * happen because of entry limits in route cache. */ | 467 | * happen because of entry limits in route cache. */ |
429 | return -1; | 468 | return -1; |
430 | 469 | ||
431 | unlink_from_pool(p); | 470 | unlink_from_pool(p, peer_to_base(p)); |
432 | return 0; | 471 | return 0; |
433 | } | 472 | } |
434 | 473 | ||
435 | /* Called with or without local BH being disabled. */ | 474 | /* Called with or without local BH being disabled. */ |
436 | struct inet_peer *inet_getpeer(__be32 daddr, int create) | 475 | struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create) |
437 | { | 476 | { |
438 | struct inet_peer *p; | ||
439 | struct inet_peer __rcu **stack[PEER_MAXDEPTH], ***stackptr; | 477 | struct inet_peer __rcu **stack[PEER_MAXDEPTH], ***stackptr; |
478 | struct inet_peer_base *base = family_to_base(AF_INET); | ||
479 | struct inet_peer *p; | ||
440 | 480 | ||
441 | /* Look up for the address quickly, lockless. | 481 | /* Look up for the address quickly, lockless. |
442 | * Because of a concurrent writer, we might not find an existing entry. | 482 | * Because of a concurrent writer, we might not find an existing entry. |
443 | */ | 483 | */ |
444 | rcu_read_lock_bh(); | 484 | rcu_read_lock_bh(); |
445 | p = lookup_rcu_bh(daddr); | 485 | p = lookup_rcu_bh(daddr, base); |
446 | rcu_read_unlock_bh(); | 486 | rcu_read_unlock_bh(); |
447 | 487 | ||
448 | if (p) { | 488 | if (p) { |
@@ -456,50 +496,57 @@ struct inet_peer *inet_getpeer(__be32 daddr, int create) | |||
456 | /* retry an exact lookup, taking the lock before. | 496 | /* retry an exact lookup, taking the lock before. |
457 | * At least, nodes should be hot in our cache. | 497 | * At least, nodes should be hot in our cache. |
458 | */ | 498 | */ |
459 | spin_lock_bh(&peers.lock); | 499 | spin_lock_bh(&base->lock); |
460 | p = lookup(daddr, stack); | 500 | p = lookup(daddr, stack, base); |
461 | if (p != peer_avl_empty) { | 501 | if (p != peer_avl_empty) { |
462 | atomic_inc(&p->refcnt); | 502 | atomic_inc(&p->refcnt); |
463 | spin_unlock_bh(&peers.lock); | 503 | spin_unlock_bh(&base->lock); |
464 | /* Remove the entry from unused list if it was there. */ | 504 | /* Remove the entry from unused list if it was there. */ |
465 | unlink_from_unused(p); | 505 | unlink_from_unused(p); |
466 | return p; | 506 | return p; |
467 | } | 507 | } |
468 | p = create ? kmem_cache_alloc(peer_cachep, GFP_ATOMIC) : NULL; | 508 | p = create ? kmem_cache_alloc(peer_cachep, GFP_ATOMIC) : NULL; |
469 | if (p) { | 509 | if (p) { |
470 | p->v4daddr = daddr; | 510 | p->daddr = *daddr; |
471 | atomic_set(&p->refcnt, 1); | 511 | atomic_set(&p->refcnt, 1); |
472 | atomic_set(&p->rid, 0); | 512 | atomic_set(&p->rid, 0); |
473 | atomic_set(&p->ip_id_count, secure_ip_id(daddr)); | 513 | atomic_set(&p->ip_id_count, secure_ip_id(daddr->a4)); |
474 | p->tcp_ts_stamp = 0; | 514 | p->tcp_ts_stamp = 0; |
475 | INIT_LIST_HEAD(&p->unused); | 515 | INIT_LIST_HEAD(&p->unused); |
476 | 516 | ||
477 | 517 | ||
478 | /* Link the node. */ | 518 | /* Link the node. */ |
479 | link_to_pool(p); | 519 | link_to_pool(p, base); |
480 | peers.total++; | 520 | base->total++; |
481 | } | 521 | } |
482 | spin_unlock_bh(&peers.lock); | 522 | spin_unlock_bh(&base->lock); |
483 | 523 | ||
484 | if (peers.total >= inet_peer_threshold) | 524 | if (base->total >= inet_peer_threshold) |
485 | /* Remove one less-recently-used entry. */ | 525 | /* Remove one less-recently-used entry. */ |
486 | cleanup_once(0); | 526 | cleanup_once(0); |
487 | 527 | ||
488 | return p; | 528 | return p; |
489 | } | 529 | } |
490 | 530 | ||
531 | static int compute_total(void) | ||
532 | { | ||
533 | return v4_peers.total + v6_peers.total; | ||
534 | } | ||
535 | EXPORT_SYMBOL_GPL(inet_getpeer); | ||
536 | |||
491 | /* Called with local BH disabled. */ | 537 | /* Called with local BH disabled. */ |
492 | static void peer_check_expire(unsigned long dummy) | 538 | static void peer_check_expire(unsigned long dummy) |
493 | { | 539 | { |
494 | unsigned long now = jiffies; | 540 | unsigned long now = jiffies; |
495 | int ttl; | 541 | int ttl, total; |
496 | 542 | ||
497 | if (peers.total >= inet_peer_threshold) | 543 | total = compute_total(); |
544 | if (total >= inet_peer_threshold) | ||
498 | ttl = inet_peer_minttl; | 545 | ttl = inet_peer_minttl; |
499 | else | 546 | else |
500 | ttl = inet_peer_maxttl | 547 | ttl = inet_peer_maxttl |
501 | - (inet_peer_maxttl - inet_peer_minttl) / HZ * | 548 | - (inet_peer_maxttl - inet_peer_minttl) / HZ * |
502 | peers.total / inet_peer_threshold * HZ; | 549 | total / inet_peer_threshold * HZ; |
503 | while (!cleanup_once(ttl)) { | 550 | while (!cleanup_once(ttl)) { |
504 | if (jiffies != now) | 551 | if (jiffies != now) |
505 | break; | 552 | break; |
@@ -508,13 +555,14 @@ static void peer_check_expire(unsigned long dummy) | |||
508 | /* Trigger the timer after inet_peer_gc_mintime .. inet_peer_gc_maxtime | 555 | /* Trigger the timer after inet_peer_gc_mintime .. inet_peer_gc_maxtime |
509 | * interval depending on the total number of entries (more entries, | 556 | * interval depending on the total number of entries (more entries, |
510 | * less interval). */ | 557 | * less interval). */ |
511 | if (peers.total >= inet_peer_threshold) | 558 | total = compute_total(); |
559 | if (total >= inet_peer_threshold) | ||
512 | peer_periodic_timer.expires = jiffies + inet_peer_gc_mintime; | 560 | peer_periodic_timer.expires = jiffies + inet_peer_gc_mintime; |
513 | else | 561 | else |
514 | peer_periodic_timer.expires = jiffies | 562 | peer_periodic_timer.expires = jiffies |
515 | + inet_peer_gc_maxtime | 563 | + inet_peer_gc_maxtime |
516 | - (inet_peer_gc_maxtime - inet_peer_gc_mintime) / HZ * | 564 | - (inet_peer_gc_maxtime - inet_peer_gc_mintime) / HZ * |
517 | peers.total / inet_peer_threshold * HZ; | 565 | total / inet_peer_threshold * HZ; |
518 | add_timer(&peer_periodic_timer); | 566 | add_timer(&peer_periodic_timer); |
519 | } | 567 | } |
520 | 568 | ||
@@ -530,3 +578,4 @@ void inet_putpeer(struct inet_peer *p) | |||
530 | 578 | ||
531 | local_bh_enable(); | 579 | local_bh_enable(); |
532 | } | 580 | } |
581 | EXPORT_SYMBOL_GPL(inet_putpeer); | ||