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