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