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