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 unsigned char *ip; 227 in_cache_entry *entry, *next_entry; 228 struct timeval now; 229 230 do_gettimeofday(&now); 231 232 write_lock_bh(&client->ingress_lock); 233 entry = client->in_cache; 234 while(entry != NULL){ 235 entry->count=0; 236 next_entry = entry->next; 237 if((now.tv_sec - entry->tv.tv_sec) 238 > entry->ctrl_info.holding_time){ 239 ip = (unsigned char*)&entry->ctrl_info.in_dst_ip; 240 dprintk("mpoa: mpoa_caches.c: holding time expired, ip = %u.%u.%u.%u\n", NIPQUAD(ip)); 241 client->in_ops->remove_entry(entry, client); 242 } 243 entry = next_entry; 244 } 245 write_unlock_bh(&client->ingress_lock); 246 247 return; 248 } 249 250 /* Call this every MPC-p4 seconds. */ 251 static void check_resolving_entries(struct mpoa_client *client) 252 { 253 254 struct atm_mpoa_qos *qos; 255 in_cache_entry *entry; 256 struct timeval now; 257 struct k_message msg; 258 259 do_gettimeofday( &now ); 260 261 read_lock_bh(&client->ingress_lock); 262 entry = client->in_cache; 263 while( entry != NULL ){ 264 if(entry->entry_state == INGRESS_RESOLVING){ 265 if(now.tv_sec - entry->hold_down.tv_sec < 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 if(entry->retry_time > client->parameters.mpc_p5){ 273 /* Retry time maximum exceeded, put entry in hold down. */ 274 do_gettimeofday(&(entry->hold_down)); 275 entry->retry_time = client->parameters.mpc_p4; 276 entry = entry->next; 277 continue; 278 } 279 /* Ask daemon to send a resolution request. */ 280 memset(&(entry->hold_down),0,sizeof(struct timeval)); 281 msg.type = SND_MPOA_RES_RTRY; 282 memcpy(msg.MPS_ctrl, client->mps_ctrl_addr, ATM_ESA_LEN); 283 msg.content.in_info = entry->ctrl_info; 284 qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip); 285 if (qos != NULL) msg.qos = qos->qos; 286 msg_to_mpoad(&msg, client); 287 do_gettimeofday(&(entry->reply_wait)); 288 } 289 } 290 entry = entry->next; 291 } 292 read_unlock_bh(&client->ingress_lock); 293 } 294 295 /* Call this every MPC-p5 seconds. */ 296 static void refresh_entries(struct mpoa_client *client) 297 { 298 struct timeval now; 299 struct in_cache_entry *entry = client->in_cache; 300 301 ddprintk("mpoa: mpoa_caches.c: refresh_entries\n"); 302 do_gettimeofday(&now); 303 304 read_lock_bh(&client->ingress_lock); 305 while( entry != NULL ){ 306 if( entry->entry_state == INGRESS_RESOLVED ){ 307 if(!(entry->refresh_time)) 308 entry->refresh_time = (2*(entry->ctrl_info.holding_time))/3; 309 if( (now.tv_sec - entry->reply_wait.tv_sec) > entry->refresh_time ){ 310 dprintk("mpoa: mpoa_caches.c: refreshing an entry.\n"); 311 entry->entry_state = INGRESS_REFRESHING; 312 313 } 314 } 315 entry = entry->next; 316 } 317 read_unlock_bh(&client->ingress_lock); 318 } 319 320 static void in_destroy_cache(struct mpoa_client *mpc) 321 { 322 write_lock_irq(&mpc->ingress_lock); 323 while(mpc->in_cache != NULL) 324 mpc->in_ops->remove_entry(mpc->in_cache, mpc); 325 write_unlock_irq(&mpc->ingress_lock); 326 327 return; 328 } 329 330 static eg_cache_entry *eg_cache_get_by_cache_id(uint32_t cache_id, struct mpoa_client *mpc) 331 { 332 eg_cache_entry *entry; 333 334 read_lock_irq(&mpc->egress_lock); 335 entry = mpc->eg_cache; 336 while(entry != NULL){ 337 if(entry->ctrl_info.cache_id == cache_id){ 338 atomic_inc(&entry->use); 339 read_unlock_irq(&mpc->egress_lock); 340 return entry; 341 } 342 entry = entry->next; 343 } 344 read_unlock_irq(&mpc->egress_lock); 345 346 return NULL; 347 } 348 349 /* This can be called from any context since it saves CPU flags */ 350 static eg_cache_entry *eg_cache_get_by_tag(uint32_t tag, struct mpoa_client *mpc) 351 { 352 unsigned long flags; 353 eg_cache_entry *entry; 354 355 read_lock_irqsave(&mpc->egress_lock, flags); 356 entry = mpc->eg_cache; 357 while (entry != NULL){ 358 if (entry->ctrl_info.tag == tag) { 359 atomic_inc(&entry->use); 360 read_unlock_irqrestore(&mpc->egress_lock, flags); 361 return entry; 362 } 363 entry = entry->next; 364 } 365 read_unlock_irqrestore(&mpc->egress_lock, flags); 366 367 return NULL; 368 } 369 370 /* This can be called from any context since it saves CPU flags */ 371 static eg_cache_entry *eg_cache_get_by_vcc(struct atm_vcc *vcc, struct mpoa_client *mpc) 372 { 373 unsigned long flags; 374 eg_cache_entry *entry; 375 376 read_lock_irqsave(&mpc->egress_lock, flags); 377 entry = mpc->eg_cache; 378 while (entry != NULL){ 379 if (entry->shortcut == vcc) { 380 atomic_inc(&entry->use); 381 read_unlock_irqrestore(&mpc->egress_lock, flags); 382 return entry; 383 } 384 entry = entry->next; 385 } 386 read_unlock_irqrestore(&mpc->egress_lock, flags); 387 388 return NULL; 389 } 390 391 static eg_cache_entry *eg_cache_get_by_src_ip(uint32_t ipaddr, struct mpoa_client *mpc) 392 { 393 eg_cache_entry *entry; 394 395 read_lock_irq(&mpc->egress_lock); 396 entry = mpc->eg_cache; 397 while(entry != NULL){ 398 if(entry->latest_ip_addr == ipaddr) { 399 atomic_inc(&entry->use); 400 read_unlock_irq(&mpc->egress_lock); 401 return entry; 402 } 403 entry = entry->next; 404 } 405 read_unlock_irq(&mpc->egress_lock); 406 407 return NULL; 408 } 409 410 static void eg_cache_put(eg_cache_entry *entry) 411 { 412 if (atomic_dec_and_test(&entry->use)) { 413 memset(entry, 0, sizeof(eg_cache_entry)); 414 kfree(entry); 415 } 416 417 return; 418 } 419 420 /* 421 * This should be called with write lock on 422 */ 423 static void eg_cache_remove_entry(eg_cache_entry *entry, 424 struct mpoa_client *client) 425 { 426 struct atm_vcc *vcc; 427 struct k_message msg; 428 429 vcc = entry->shortcut; 430 dprintk("mpoa: mpoa_caches.c: removing an egress entry.\n"); 431 if (entry->prev != NULL) 432 entry->prev->next = entry->next; 433 else 434 client->eg_cache = entry->next; 435 if (entry->next != NULL) 436 entry->next->prev = entry->prev; 437 client->eg_ops->put(entry); 438 if(client->in_cache == NULL && client->eg_cache == NULL){ 439 msg.type = STOP_KEEP_ALIVE_SM; 440 msg_to_mpoad(&msg,client); 441 } 442 443 /* Check if the ingress side still uses this VCC */ 444 if (vcc != NULL) { 445 in_cache_entry *in_entry = client->in_ops->get_by_vcc(vcc, client); 446 if (in_entry != NULL) { 447 client->in_ops->put(in_entry); 448 return; 449 } 450 vcc_release_async(vcc, -EPIPE); 451 } 452 453 return; 454 } 455 456 static eg_cache_entry *eg_cache_add_entry(struct k_message *msg, struct mpoa_client *client) 457 { 458 unsigned char *ip; 459 eg_cache_entry *entry = kmalloc(sizeof(eg_cache_entry), GFP_KERNEL); 460 461 if (entry == NULL) { 462 printk("mpoa: mpoa_caches.c: new_eg_cache_entry: out of memory\n"); 463 return NULL; 464 } 465 466 ip = (unsigned char *)&msg->content.eg_info.eg_dst_ip; 467 dprintk("mpoa: mpoa_caches.c: adding an egress entry, ip = %u.%u.%u.%u, this should be our IP\n", NIPQUAD(ip)); 468 memset(entry, 0, sizeof(eg_cache_entry)); 469 470 atomic_set(&entry->use, 1); 471 dprintk("mpoa: mpoa_caches.c: new_eg_cache_entry: about to lock\n"); 472 write_lock_irq(&client->egress_lock); 473 entry->next = client->eg_cache; 474 entry->prev = NULL; 475 if (client->eg_cache != NULL) 476 client->eg_cache->prev = entry; 477 client->eg_cache = entry; 478 479 memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN); 480 entry->ctrl_info = msg->content.eg_info; 481 do_gettimeofday(&(entry->tv)); 482 entry->entry_state = EGRESS_RESOLVED; 483 dprintk("mpoa: mpoa_caches.c: new_eg_cache_entry cache_id %lu\n", ntohl(entry->ctrl_info.cache_id)); 484 ip = (unsigned char *)&entry->ctrl_info.mps_ip; 485 dprintk("mpoa: mpoa_caches.c: mps_ip = %u.%u.%u.%u\n", NIPQUAD(ip)); 486 atomic_inc(&entry->use); 487 488 write_unlock_irq(&client->egress_lock); 489 dprintk("mpoa: mpoa_caches.c: new_eg_cache_entry: unlocked\n"); 490 491 return entry; 492 } 493 494 static void update_eg_cache_entry(eg_cache_entry * entry, uint16_t holding_time) 495 { 496 do_gettimeofday(&(entry->tv)); 497 entry->entry_state = EGRESS_RESOLVED; 498 entry->ctrl_info.holding_time = holding_time; 499 500 return; 501 } 502 503 static void clear_expired(struct mpoa_client *client) 504 { 505 eg_cache_entry *entry, *next_entry; 506 struct timeval now; 507 struct k_message msg; 508 509 do_gettimeofday(&now); 510 511 write_lock_irq(&client->egress_lock); 512 entry = client->eg_cache; 513 while(entry != NULL){ 514 next_entry = entry->next; 515 if((now.tv_sec - entry->tv.tv_sec) 516 > entry->ctrl_info.holding_time){ 517 msg.type = SND_EGRESS_PURGE; 518 msg.content.eg_info = entry->ctrl_info; 519 dprintk("mpoa: mpoa_caches.c: egress_cache: holding time expired, cache_id = %lu.\n",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 return; 528 } 529 530 static void eg_destroy_cache(struct mpoa_client *mpc) 531 { 532 write_lock_irq(&mpc->egress_lock); 533 while(mpc->eg_cache != NULL) 534 mpc->eg_ops->remove_entry(mpc->eg_cache, mpc); 535 write_unlock_irq(&mpc->egress_lock); 536 537 return; 538 } 539 540 541 542 static struct in_cache_ops ingress_ops = { 543 in_cache_add_entry, /* add_entry */ 544 in_cache_get, /* get */ 545 in_cache_get_with_mask, /* get_with_mask */ 546 in_cache_get_by_vcc, /* get_by_vcc */ 547 in_cache_put, /* put */ 548 in_cache_remove_entry, /* remove_entry */ 549 cache_hit, /* cache_hit */ 550 clear_count_and_expired, /* clear_count */ 551 check_resolving_entries, /* check_resolving */ 552 refresh_entries, /* refresh */ 553 in_destroy_cache /* destroy_cache */ 554 }; 555 556 static struct eg_cache_ops egress_ops = { 557 eg_cache_add_entry, /* add_entry */ 558 eg_cache_get_by_cache_id, /* get_by_cache_id */ 559 eg_cache_get_by_tag, /* get_by_tag */ 560 eg_cache_get_by_vcc, /* get_by_vcc */ 561 eg_cache_get_by_src_ip, /* get_by_src_ip */ 562 eg_cache_put, /* put */ 563 eg_cache_remove_entry, /* remove_entry */ 564 update_eg_cache_entry, /* update */ 565 clear_expired, /* clear_expired */ 566 eg_destroy_cache /* destroy_cache */ 567 }; 568 569 570 void atm_mpoa_init_cache(struct mpoa_client *mpc) 571 { 572 mpc->in_ops = &ingress_ops; 573 mpc->eg_ops = &egress_ops; 574 575 return; 576 } 577