1#include <linux/types.h> 2#include <linux/atmmpc.h> 3#include <linux/slab.h> 4#include <linux/time.h> 5 6#include "mpoa_caches.h" 7#include "mpc.h" 8 9/* 10 * mpoa_caches.c: Implementation of ingress and egress cache 11 * handling functions 12 */ 13 14#if 0 15#define dprintk(format, args...) \ 16 printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args) /* debug */ 17#else 18#define dprintk(format, args...) \ 19 do { if (0) \ 20 printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args);\ 21 } while (0) 22#endif 23 24#if 0 25#define ddprintk(format, args...) \ 26 printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args) /* debug */ 27#else 28#define ddprintk(format, args...) \ 29 do { if (0) \ 30 printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args);\ 31 } while (0) 32#endif 33 34static in_cache_entry *in_cache_get(__be32 dst_ip, 35 struct mpoa_client *client) 36{ 37 in_cache_entry *entry; 38 39 read_lock_bh(&client->ingress_lock); 40 entry = client->in_cache; 41 while (entry != NULL) { 42 if (entry->ctrl_info.in_dst_ip == dst_ip) { 43 atomic_inc(&entry->use); 44 read_unlock_bh(&client->ingress_lock); 45 return entry; 46 } 47 entry = entry->next; 48 } 49 read_unlock_bh(&client->ingress_lock); 50 51 return NULL; 52} 53 54static in_cache_entry *in_cache_get_with_mask(__be32 dst_ip, 55 struct mpoa_client *client, 56 __be32 mask) 57{ 58 in_cache_entry *entry; 59 60 read_lock_bh(&client->ingress_lock); 61 entry = client->in_cache; 62 while (entry != NULL) { 63 if ((entry->ctrl_info.in_dst_ip & mask) == (dst_ip & mask)) { 64 atomic_inc(&entry->use); 65 read_unlock_bh(&client->ingress_lock); 66 return entry; 67 } 68 entry = entry->next; 69 } 70 read_unlock_bh(&client->ingress_lock); 71 72 return NULL; 73 74} 75 76static in_cache_entry *in_cache_get_by_vcc(struct atm_vcc *vcc, 77 struct mpoa_client *client) 78{ 79 in_cache_entry *entry; 80 81 read_lock_bh(&client->ingress_lock); 82 entry = client->in_cache; 83 while (entry != NULL) { 84 if (entry->shortcut == vcc) { 85 atomic_inc(&entry->use); 86 read_unlock_bh(&client->ingress_lock); 87 return entry; 88 } 89 entry = entry->next; 90 } 91 read_unlock_bh(&client->ingress_lock); 92 93 return NULL; 94} 95 96static in_cache_entry *in_cache_add_entry(__be32 dst_ip, 97 struct mpoa_client *client) 98{ 99 in_cache_entry *entry = kzalloc(sizeof(in_cache_entry), GFP_KERNEL); 100 101 if (entry == NULL) { 102 pr_info("mpoa: mpoa_caches.c: new_in_cache_entry: out of memory\n"); 103 return NULL; 104 } 105 106 dprintk("adding an ingress entry, ip = %pI4\n", &dst_ip); 107 108 atomic_set(&entry->use, 1); 109 dprintk("new_in_cache_entry: about to lock\n"); 110 write_lock_bh(&client->ingress_lock); 111 entry->next = client->in_cache; 112 entry->prev = NULL; 113 if (client->in_cache != NULL) 114 client->in_cache->prev = entry; 115 client->in_cache = entry; 116 117 memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN); 118 entry->ctrl_info.in_dst_ip = dst_ip; 119 do_gettimeofday(&(entry->tv)); 120 entry->retry_time = client->parameters.mpc_p4; 121 entry->count = 1; 122 entry->entry_state = INGRESS_INVALID; 123 entry->ctrl_info.holding_time = HOLDING_TIME_DEFAULT; 124 atomic_inc(&entry->use); 125 126 write_unlock_bh(&client->ingress_lock); 127 dprintk("new_in_cache_entry: unlocked\n"); 128 129 return entry; 130} 131 132static int cache_hit(in_cache_entry *entry, struct mpoa_client *mpc) 133{ 134 struct atm_mpoa_qos *qos; 135 struct k_message msg; 136 137 entry->count++; 138 if (entry->entry_state == INGRESS_RESOLVED && entry->shortcut != NULL) 139 return OPEN; 140 141 if (entry->entry_state == INGRESS_REFRESHING) { 142 if (entry->count > mpc->parameters.mpc_p1) { 143 msg.type = SND_MPOA_RES_RQST; 144 msg.content.in_info = entry->ctrl_info; 145 memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN); 146 qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip); 147 if (qos != NULL) 148 msg.qos = qos->qos; 149 msg_to_mpoad(&msg, mpc); 150 do_gettimeofday(&(entry->reply_wait)); 151 entry->entry_state = INGRESS_RESOLVING; 152 } 153 if (entry->shortcut != NULL) 154 return OPEN; 155 return CLOSED; 156 } 157 158 if (entry->entry_state == INGRESS_RESOLVING && entry->shortcut != NULL) 159 return OPEN; 160 161 if (entry->count > mpc->parameters.mpc_p1 && 162 entry->entry_state == INGRESS_INVALID) { 163 dprintk("(%s) threshold exceeded for ip %pI4, sending MPOA res req\n", 164 mpc->dev->name, &entry->ctrl_info.in_dst_ip); 165 entry->entry_state = INGRESS_RESOLVING; 166 msg.type = SND_MPOA_RES_RQST; 167 memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN); 168 msg.content.in_info = entry->ctrl_info; 169 qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip); 170 if (qos != NULL) 171 msg.qos = qos->qos; 172 msg_to_mpoad(&msg, mpc); 173 do_gettimeofday(&(entry->reply_wait)); 174 } 175 176 return CLOSED; 177} 178 179static void in_cache_put(in_cache_entry *entry) 180{ 181 if (atomic_dec_and_test(&entry->use)) { 182 memset(entry, 0, sizeof(in_cache_entry)); 183 kfree(entry); 184 } 185} 186 187/* 188 * This should be called with write lock on 189 */ 190static void in_cache_remove_entry(in_cache_entry *entry, 191 struct mpoa_client *client) 192{ 193 struct atm_vcc *vcc; 194 struct k_message msg; 195 196 vcc = entry->shortcut; 197 dprintk("removing an ingress entry, ip = %pI4\n", 198 &entry->ctrl_info.in_dst_ip); 199 200 if (entry->prev != NULL) 201 entry->prev->next = entry->next; 202 else 203 client->in_cache = entry->next; 204 if (entry->next != NULL) 205 entry->next->prev = entry->prev; 206 client->in_ops->put(entry); 207 if (client->in_cache == NULL && client->eg_cache == NULL) { 208 msg.type = STOP_KEEP_ALIVE_SM; 209 msg_to_mpoad(&msg, client); 210 } 211 212 /* Check if the egress side still uses this VCC */ 213 if (vcc != NULL) { 214 eg_cache_entry *eg_entry = client->eg_ops->get_by_vcc(vcc, 215 client); 216 if (eg_entry != NULL) { 217 client->eg_ops->put(eg_entry); 218 return; 219 } 220 vcc_release_async(vcc, -EPIPE); 221 } 222} 223 224/* Call this every MPC-p2 seconds... Not exactly correct solution, 225 but an easy one... */ 226static void clear_count_and_expired(struct mpoa_client *client) 227{ 228 in_cache_entry *entry, *next_entry; 229 struct timeval now; 230 231 do_gettimeofday(&now); 232 233 write_lock_bh(&client->ingress_lock); 234 entry = client->in_cache; 235 while (entry != NULL) { 236 entry->count = 0; 237 next_entry = entry->next; 238 if ((now.tv_sec - entry->tv.tv_sec) 239 > entry->ctrl_info.holding_time) { 240 dprintk("holding time expired, ip = %pI4\n", 241 &entry->ctrl_info.in_dst_ip); 242 client->in_ops->remove_entry(entry, client); 243 } 244 entry = next_entry; 245 } 246 write_unlock_bh(&client->ingress_lock); 247} 248 249/* Call this every MPC-p4 seconds. */ 250static void check_resolving_entries(struct mpoa_client *client) 251{ 252 253 struct atm_mpoa_qos *qos; 254 in_cache_entry *entry; 255 struct timeval now; 256 struct k_message msg; 257 258 do_gettimeofday(&now); 259 260 read_lock_bh(&client->ingress_lock); 261 entry = client->in_cache; 262 while (entry != NULL) { 263 if (entry->entry_state == INGRESS_RESOLVING) { 264 if ((now.tv_sec - entry->hold_down.tv_sec) < 265 client->parameters.mpc_p6) { 266 entry = entry->next; /* Entry in hold down */ 267 continue; 268 } 269 if ((now.tv_sec - entry->reply_wait.tv_sec) > 270 entry->retry_time) { 271 entry->retry_time = MPC_C1 * (entry->retry_time); 272 /* 273 * Retry time maximum exceeded, 274 * put entry in hold down. 275 */ 276 if (entry->retry_time > client->parameters.mpc_p5) { 277 do_gettimeofday(&(entry->hold_down)); 278 entry->retry_time = client->parameters.mpc_p4; 279 entry = entry->next; 280 continue; 281 } 282 /* Ask daemon to send a resolution request. */ 283 memset(&(entry->hold_down), 0, sizeof(struct timeval)); 284 msg.type = SND_MPOA_RES_RTRY; 285 memcpy(msg.MPS_ctrl, client->mps_ctrl_addr, ATM_ESA_LEN); 286 msg.content.in_info = entry->ctrl_info; 287 qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip); 288 if (qos != NULL) 289 msg.qos = qos->qos; 290 msg_to_mpoad(&msg, client); 291 do_gettimeofday(&(entry->reply_wait)); 292 } 293 } 294 entry = entry->next; 295 } 296 read_unlock_bh(&client->ingress_lock); 297} 298 299/* Call this every MPC-p5 seconds. */ 300static void refresh_entries(struct mpoa_client *client) 301{ 302 struct timeval now; 303 struct in_cache_entry *entry = client->in_cache; 304 305 ddprintk("refresh_entries\n"); 306 do_gettimeofday(&now); 307 308 read_lock_bh(&client->ingress_lock); 309 while (entry != NULL) { 310 if (entry->entry_state == INGRESS_RESOLVED) { 311 if (!(entry->refresh_time)) 312 entry->refresh_time = (2 * (entry->ctrl_info.holding_time))/3; 313 if ((now.tv_sec - entry->reply_wait.tv_sec) > 314 entry->refresh_time) { 315 dprintk("refreshing an entry.\n"); 316 entry->entry_state = INGRESS_REFRESHING; 317 318 } 319 } 320 entry = entry->next; 321 } 322 read_unlock_bh(&client->ingress_lock); 323} 324 325static void in_destroy_cache(struct mpoa_client *mpc) 326{ 327 write_lock_irq(&mpc->ingress_lock); 328 while (mpc->in_cache != NULL) 329 mpc->in_ops->remove_entry(mpc->in_cache, mpc); 330 write_unlock_irq(&mpc->ingress_lock); 331} 332 333static eg_cache_entry *eg_cache_get_by_cache_id(__be32 cache_id, 334 struct mpoa_client *mpc) 335{ 336 eg_cache_entry *entry; 337 338 read_lock_irq(&mpc->egress_lock); 339 entry = mpc->eg_cache; 340 while (entry != NULL) { 341 if (entry->ctrl_info.cache_id == cache_id) { 342 atomic_inc(&entry->use); 343 read_unlock_irq(&mpc->egress_lock); 344 return entry; 345 } 346 entry = entry->next; 347 } 348 read_unlock_irq(&mpc->egress_lock); 349 350 return NULL; 351} 352 353/* This can be called from any context since it saves CPU flags */ 354static eg_cache_entry *eg_cache_get_by_tag(__be32 tag, struct mpoa_client *mpc) 355{ 356 unsigned long flags; 357 eg_cache_entry *entry; 358 359 read_lock_irqsave(&mpc->egress_lock, flags); 360 entry = mpc->eg_cache; 361 while (entry != NULL) { 362 if (entry->ctrl_info.tag == tag) { 363 atomic_inc(&entry->use); 364 read_unlock_irqrestore(&mpc->egress_lock, flags); 365 return entry; 366 } 367 entry = entry->next; 368 } 369 read_unlock_irqrestore(&mpc->egress_lock, flags); 370 371 return NULL; 372} 373 374/* This can be called from any context since it saves CPU flags */ 375static eg_cache_entry *eg_cache_get_by_vcc(struct atm_vcc *vcc, 376 struct mpoa_client *mpc) 377{ 378 unsigned long flags; 379 eg_cache_entry *entry; 380 381 read_lock_irqsave(&mpc->egress_lock, flags); 382 entry = mpc->eg_cache; 383 while (entry != NULL) { 384 if (entry->shortcut == vcc) { 385 atomic_inc(&entry->use); 386 read_unlock_irqrestore(&mpc->egress_lock, flags); 387 return entry; 388 } 389 entry = entry->next; 390 } 391 read_unlock_irqrestore(&mpc->egress_lock, flags); 392 393 return NULL; 394} 395 396static eg_cache_entry *eg_cache_get_by_src_ip(__be32 ipaddr, 397 struct mpoa_client *mpc) 398{ 399 eg_cache_entry *entry; 400 401 read_lock_irq(&mpc->egress_lock); 402 entry = mpc->eg_cache; 403 while (entry != NULL) { 404 if (entry->latest_ip_addr == ipaddr) { 405 atomic_inc(&entry->use); 406 read_unlock_irq(&mpc->egress_lock); 407 return entry; 408 } 409 entry = entry->next; 410 } 411 read_unlock_irq(&mpc->egress_lock); 412 413 return NULL; 414} 415 416static void eg_cache_put(eg_cache_entry *entry) 417{ 418 if (atomic_dec_and_test(&entry->use)) { 419 memset(entry, 0, sizeof(eg_cache_entry)); 420 kfree(entry); 421 } 422} 423 424/* 425 * This should be called with write lock on 426 */ 427static void eg_cache_remove_entry(eg_cache_entry *entry, 428 struct mpoa_client *client) 429{ 430 struct atm_vcc *vcc; 431 struct k_message msg; 432 433 vcc = entry->shortcut; 434 dprintk("removing an egress entry.\n"); 435 if (entry->prev != NULL) 436 entry->prev->next = entry->next; 437 else 438 client->eg_cache = entry->next; 439 if (entry->next != NULL) 440 entry->next->prev = entry->prev; 441 client->eg_ops->put(entry); 442 if (client->in_cache == NULL && client->eg_cache == NULL) { 443 msg.type = STOP_KEEP_ALIVE_SM; 444 msg_to_mpoad(&msg, client); 445 } 446 447 /* Check if the ingress side still uses this VCC */ 448 if (vcc != NULL) { 449 in_cache_entry *in_entry = client->in_ops->get_by_vcc(vcc, client); 450 if (in_entry != NULL) { 451 client->in_ops->put(in_entry); 452 return; 453 } 454 vcc_release_async(vcc, -EPIPE); 455 } 456} 457 458static eg_cache_entry *eg_cache_add_entry(struct k_message *msg, 459 struct mpoa_client *client) 460{ 461 eg_cache_entry *entry = kzalloc(sizeof(eg_cache_entry), GFP_KERNEL); 462 463 if (entry == NULL) { 464 pr_info("out of memory\n"); 465 return NULL; 466 } 467 468 dprintk("adding an egress entry, ip = %pI4, this should be our IP\n", 469 &msg->content.eg_info.eg_dst_ip); 470 471 atomic_set(&entry->use, 1); 472 dprintk("new_eg_cache_entry: about to lock\n"); 473 write_lock_irq(&client->egress_lock); 474 entry->next = client->eg_cache; 475 entry->prev = NULL; 476 if (client->eg_cache != NULL) 477 client->eg_cache->prev = entry; 478 client->eg_cache = entry; 479 480 memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN); 481 entry->ctrl_info = msg->content.eg_info; 482 do_gettimeofday(&(entry->tv)); 483 entry->entry_state = EGRESS_RESOLVED; 484 dprintk("new_eg_cache_entry cache_id %u\n", 485 ntohl(entry->ctrl_info.cache_id)); 486 dprintk("mps_ip = %pI4\n", &entry->ctrl_info.mps_ip); 487 atomic_inc(&entry->use); 488 489 write_unlock_irq(&client->egress_lock); 490 dprintk("new_eg_cache_entry: unlocked\n"); 491 492 return entry; 493} 494 495static void update_eg_cache_entry(eg_cache_entry *entry, uint16_t holding_time) 496{ 497 do_gettimeofday(&(entry->tv)); 498 entry->entry_state = EGRESS_RESOLVED; 499 entry->ctrl_info.holding_time = holding_time; 500} 501 502static void clear_expired(struct mpoa_client *client) 503{ 504 eg_cache_entry *entry, *next_entry; 505 struct timeval now; 506 struct k_message msg; 507 508 do_gettimeofday(&now); 509 510 write_lock_irq(&client->egress_lock); 511 entry = client->eg_cache; 512 while (entry != NULL) { 513 next_entry = entry->next; 514 if ((now.tv_sec - entry->tv.tv_sec) 515 > entry->ctrl_info.holding_time) { 516 msg.type = SND_EGRESS_PURGE; 517 msg.content.eg_info = entry->ctrl_info; 518 dprintk("egress_cache: holding time expired, cache_id = %u.\n", 519 ntohl(entry->ctrl_info.cache_id)); 520 msg_to_mpoad(&msg, client); 521 client->eg_ops->remove_entry(entry, client); 522 } 523 entry = next_entry; 524 } 525 write_unlock_irq(&client->egress_lock); 526} 527 528static void eg_destroy_cache(struct mpoa_client *mpc) 529{ 530 write_lock_irq(&mpc->egress_lock); 531 while (mpc->eg_cache != NULL) 532 mpc->eg_ops->remove_entry(mpc->eg_cache, mpc); 533 write_unlock_irq(&mpc->egress_lock); 534} 535 536 537static struct in_cache_ops ingress_ops = { 538 in_cache_add_entry, /* add_entry */ 539 in_cache_get, /* get */ 540 in_cache_get_with_mask, /* get_with_mask */ 541 in_cache_get_by_vcc, /* get_by_vcc */ 542 in_cache_put, /* put */ 543 in_cache_remove_entry, /* remove_entry */ 544 cache_hit, /* cache_hit */ 545 clear_count_and_expired, /* clear_count */ 546 check_resolving_entries, /* check_resolving */ 547 refresh_entries, /* refresh */ 548 in_destroy_cache /* destroy_cache */ 549}; 550 551static struct eg_cache_ops egress_ops = { 552 eg_cache_add_entry, /* add_entry */ 553 eg_cache_get_by_cache_id, /* get_by_cache_id */ 554 eg_cache_get_by_tag, /* get_by_tag */ 555 eg_cache_get_by_vcc, /* get_by_vcc */ 556 eg_cache_get_by_src_ip, /* get_by_src_ip */ 557 eg_cache_put, /* put */ 558 eg_cache_remove_entry, /* remove_entry */ 559 update_eg_cache_entry, /* update */ 560 clear_expired, /* clear_expired */ 561 eg_destroy_cache /* destroy_cache */ 562}; 563 564 565void atm_mpoa_init_cache(struct mpoa_client *mpc) 566{ 567 mpc->in_ops = &ingress_ops; 568 mpc->eg_ops = &egress_ops; 569} 570