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#define dprintk(format,args...) 14 15#define ddprintk(format,args...) 16 17static in_cache_entry *in_cache_get(uint32_t dst_ip, 18 struct mpoa_client *client) 19{ 20 in_cache_entry *entry; 21 22 read_lock_bh(&client->ingress_lock); 23 entry = client->in_cache; 24 while(entry != NULL){ 25 if( entry->ctrl_info.in_dst_ip == dst_ip ){ 26 atomic_inc(&entry->use); 27 read_unlock_bh(&client->ingress_lock); 28 return entry; 29 } 30 entry = entry->next; 31 } 32 read_unlock_bh(&client->ingress_lock); 33 34 return NULL; 35} 36 37static in_cache_entry *in_cache_get_with_mask(uint32_t dst_ip, 38 struct mpoa_client *client, 39 uint32_t mask) 40{ 41 in_cache_entry *entry; 42 43 read_lock_bh(&client->ingress_lock); 44 entry = client->in_cache; 45 while(entry != NULL){ 46 if((entry->ctrl_info.in_dst_ip & mask) == (dst_ip & mask )){ 47 atomic_inc(&entry->use); 48 read_unlock_bh(&client->ingress_lock); 49 return entry; 50 } 51 entry = entry->next; 52 } 53 read_unlock_bh(&client->ingress_lock); 54 55 return NULL; 56 57} 58 59static in_cache_entry *in_cache_get_by_vcc(struct atm_vcc *vcc, 60 struct mpoa_client *client ) 61{ 62 in_cache_entry *entry; 63 64 read_lock_bh(&client->ingress_lock); 65 entry = client->in_cache; 66 while(entry != NULL){ 67 if(entry->shortcut == vcc) { 68 atomic_inc(&entry->use); 69 read_unlock_bh(&client->ingress_lock); 70 return entry; 71 } 72 entry = entry->next; 73 } 74 read_unlock_bh(&client->ingress_lock); 75 76 return NULL; 77} 78 79static in_cache_entry *in_cache_add_entry(uint32_t dst_ip, 80 struct mpoa_client *client) 81{ 82 unsigned char *ip __attribute__ ((unused)) = (unsigned char *)&dst_ip; 83 in_cache_entry* entry = kmalloc(sizeof(in_cache_entry), GFP_KERNEL); 84 85 if (entry == NULL) { 86 printk("mpoa: mpoa_caches.c: new_in_cache_entry: out of memory\n"); 87 return NULL; 88 } 89 90 dprintk("mpoa: mpoa_caches.c: adding an ingress entry, ip = %u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]); 91 memset(entry,0,sizeof(in_cache_entry)); 92 93 atomic_set(&entry->use, 1); 94 dprintk("mpoa: mpoa_caches.c: new_in_cache_entry: about to lock\n"); 95 write_lock_bh(&client->ingress_lock); 96 entry->next = client->in_cache; 97 entry->prev = NULL; 98 if (client->in_cache != NULL) 99 client->in_cache->prev = entry; 100 client->in_cache = entry; 101 102 memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN); 103 entry->ctrl_info.in_dst_ip = dst_ip; 104 do_gettimeofday(&(entry->tv)); 105 entry->retry_time = client->parameters.mpc_p4; 106 entry->count = 1; 107 entry->entry_state = INGRESS_INVALID; 108 entry->ctrl_info.holding_time = HOLDING_TIME_DEFAULT; 109 atomic_inc(&entry->use); 110 111 write_unlock_bh(&client->ingress_lock); 112 dprintk("mpoa: mpoa_caches.c: new_in_cache_entry: unlocked\n"); 113 114 return entry; 115} 116 117static int cache_hit(in_cache_entry *entry, struct mpoa_client *mpc) 118{ 119 struct atm_mpoa_qos *qos; 120 struct k_message msg; 121 122 entry->count++; 123 if(entry->entry_state == INGRESS_RESOLVED && entry->shortcut != NULL) 124 return OPEN; 125 126 if(entry->entry_state == INGRESS_REFRESHING){ 127 if(entry->count > mpc->parameters.mpc_p1){ 128 msg.type = SND_MPOA_RES_RQST; 129 msg.content.in_info = entry->ctrl_info; 130 memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN); 131 qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip); 132 if (qos != NULL) msg.qos = qos->qos; 133 msg_to_mpoad(&msg, mpc); 134 do_gettimeofday(&(entry->reply_wait)); 135 entry->entry_state = INGRESS_RESOLVING; 136 } 137 if(entry->shortcut != NULL) 138 return OPEN; 139 return CLOSED; 140 } 141 142 if(entry->entry_state == INGRESS_RESOLVING && entry->shortcut != NULL) 143 return OPEN; 144 145 if( entry->count > mpc->parameters.mpc_p1 && 146 entry->entry_state == INGRESS_INVALID){ 147 unsigned char *ip __attribute__ ((unused)) = 148 (unsigned char *)&entry->ctrl_info.in_dst_ip; 149 150 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]); 151 entry->entry_state = INGRESS_RESOLVING; 152 msg.type = SND_MPOA_RES_RQST; 153 memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN ); 154 msg.content.in_info = entry->ctrl_info; 155 qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip); 156 if (qos != NULL) msg.qos = qos->qos; 157 msg_to_mpoad( &msg, mpc); 158 do_gettimeofday(&(entry->reply_wait)); 159 } 160 161 return CLOSED; 162} 163 164static void in_cache_put(in_cache_entry *entry) 165{ 166 if (atomic_dec_and_test(&entry->use)) { 167 memset(entry, 0, sizeof(in_cache_entry)); 168 kfree(entry); 169 } 170 171 return; 172} 173 174/* 175 * This should be called with write lock on 176 */ 177static void in_cache_remove_entry(in_cache_entry *entry, 178 struct mpoa_client *client) 179{ 180 struct atm_vcc *vcc; 181 struct k_message msg; 182 unsigned char *ip; 183 184 vcc = entry->shortcut; 185 ip = (unsigned char *)&entry->ctrl_info.in_dst_ip; 186 dprintk("mpoa: mpoa_caches.c: removing an ingress entry, ip = %u.%u.%u.%u\n",ip[0], ip[1], ip[2], ip[3]); 187 188 if (entry->prev != NULL) 189 entry->prev->next = entry->next; 190 else 191 client->in_cache = entry->next; 192 if (entry->next != NULL) 193 entry->next->prev = entry->prev; 194 client->in_ops->put(entry); 195 if(client->in_cache == NULL && client->eg_cache == NULL){ 196 msg.type = STOP_KEEP_ALIVE_SM; 197 msg_to_mpoad(&msg,client); 198 } 199 200 /* Check if the egress side still uses this VCC */ 201 if (vcc != NULL) { 202 eg_cache_entry *eg_entry = client->eg_ops->get_by_vcc(vcc, client); 203 if (eg_entry != NULL) { 204 client->eg_ops->put(eg_entry); 205 return; 206 } 207 atm_async_release_vcc(vcc, -EPIPE); 208 } 209 210 return; 211} 212 213 214/* Call this every MPC-p2 seconds... Not exactly correct solution, 215 but an easy one... */ 216static void clear_count_and_expired(struct mpoa_client *client) 217{ 218 unsigned char *ip; 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 ip = (unsigned char*)&entry->ctrl_info.in_dst_ip; 232 dprintk("mpoa: mpoa_caches.c: holding time expired, ip = %u.%u.%u.%u\n", NIPQUAD(ip)); 233 client->in_ops->remove_entry(entry, client); 234 } 235 entry = next_entry; 236 } 237 write_unlock_bh(&client->ingress_lock); 238 239 return; 240} 241 242/* Call this every MPC-p4 seconds. */ 243static void check_resolving_entries(struct mpoa_client *client) 244{ 245 246 struct atm_mpoa_qos *qos; 247 in_cache_entry *entry; 248 struct timeval now; 249 struct k_message msg; 250 251 do_gettimeofday( &now ); 252 253 read_lock_bh(&client->ingress_lock); 254 entry = client->in_cache; 255 while( entry != NULL ){ 256 if(entry->entry_state == INGRESS_RESOLVING){ 257 if(now.tv_sec - entry->hold_down.tv_sec < client->parameters.mpc_p6){ 258 entry = entry->next; /* Entry in hold down */ 259 continue; 260 } 261 if( (now.tv_sec - entry->reply_wait.tv_sec) > 262 entry->retry_time ){ 263 entry->retry_time = MPC_C1*( entry->retry_time ); 264 if(entry->retry_time > client->parameters.mpc_p5){ 265 /* Retry time maximum exceeded, put entry in hold down. */ 266 do_gettimeofday(&(entry->hold_down)); 267 entry->retry_time = client->parameters.mpc_p4; 268 entry = entry->next; 269 continue; 270 } 271 /* Ask daemon to send a resolution request. */ 272 memset(&(entry->hold_down),0,sizeof(struct timeval)); 273 msg.type = SND_MPOA_RES_RTRY; 274 memcpy(msg.MPS_ctrl, client->mps_ctrl_addr, ATM_ESA_LEN); 275 msg.content.in_info = entry->ctrl_info; 276 qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip); 277 if (qos != NULL) msg.qos = qos->qos; 278 msg_to_mpoad(&msg, client); 279 do_gettimeofday(&(entry->reply_wait)); 280 } 281 } 282 entry = entry->next; 283 } 284 read_unlock_bh(&client->ingress_lock); 285} 286 287/* Call this every MPC-p5 seconds. */ 288static void refresh_entries(struct mpoa_client *client) 289{ 290 struct timeval now; 291 struct in_cache_entry *entry = client->in_cache; 292 293 ddprintk("mpoa: mpoa_caches.c: refresh_entries\n"); 294 do_gettimeofday(&now); 295 296 read_lock_bh(&client->ingress_lock); 297 while( entry != NULL ){ 298 if( entry->entry_state == INGRESS_RESOLVED ){ 299 if(!(entry->refresh_time)) 300 entry->refresh_time = (2*(entry->ctrl_info.holding_time))/3; 301 if( (now.tv_sec - entry->reply_wait.tv_sec) > entry->refresh_time ){ 302 dprintk("mpoa: mpoa_caches.c: refreshing an entry.\n"); 303 entry->entry_state = INGRESS_REFRESHING; 304 305 } 306 } 307 entry = entry->next; 308 } 309 read_unlock_bh(&client->ingress_lock); 310} 311 312static void in_destroy_cache(struct mpoa_client *mpc) 313{ 314 write_lock_irq(&mpc->ingress_lock); 315 while(mpc->in_cache != NULL) 316 mpc->in_ops->remove_entry(mpc->in_cache, mpc); 317 write_unlock_irq(&mpc->ingress_lock); 318 319 return; 320} 321 322static eg_cache_entry *eg_cache_get_by_cache_id(uint32_t cache_id, struct mpoa_client *mpc) 323{ 324 eg_cache_entry *entry; 325 326 read_lock_irq(&mpc->egress_lock); 327 entry = mpc->eg_cache; 328 while(entry != NULL){ 329 if(entry->ctrl_info.cache_id == cache_id){ 330 atomic_inc(&entry->use); 331 read_unlock_irq(&mpc->egress_lock); 332 return entry; 333 } 334 entry = entry->next; 335 } 336 read_unlock_irq(&mpc->egress_lock); 337 338 return NULL; 339} 340 341/* This can be called from any context since it saves CPU flags */ 342static eg_cache_entry *eg_cache_get_by_tag(uint32_t tag, struct mpoa_client *mpc) 343{ 344 unsigned long flags; 345 eg_cache_entry *entry; 346 347 read_lock_irqsave(&mpc->egress_lock, flags); 348 entry = mpc->eg_cache; 349 while (entry != NULL){ 350 if (entry->ctrl_info.tag == tag) { 351 atomic_inc(&entry->use); 352 read_unlock_irqrestore(&mpc->egress_lock, flags); 353 return entry; 354 } 355 entry = entry->next; 356 } 357 read_unlock_irqrestore(&mpc->egress_lock, flags); 358 359 return NULL; 360} 361 362/* This can be called from any context since it saves CPU flags */ 363static eg_cache_entry *eg_cache_get_by_vcc(struct atm_vcc *vcc, struct mpoa_client *mpc) 364{ 365 unsigned long flags; 366 eg_cache_entry *entry; 367 368 read_lock_irqsave(&mpc->egress_lock, flags); 369 entry = mpc->eg_cache; 370 while (entry != NULL){ 371 if (entry->shortcut == vcc) { 372 atomic_inc(&entry->use); 373 read_unlock_irqrestore(&mpc->egress_lock, flags); 374 return entry; 375 } 376 entry = entry->next; 377 } 378 read_unlock_irqrestore(&mpc->egress_lock, flags); 379 380 return NULL; 381} 382 383static eg_cache_entry *eg_cache_get_by_src_ip(uint32_t ipaddr, struct mpoa_client *mpc) 384{ 385 eg_cache_entry *entry; 386 387 read_lock_irq(&mpc->egress_lock); 388 entry = mpc->eg_cache; 389 while(entry != NULL){ 390 if(entry->latest_ip_addr == ipaddr) { 391 atomic_inc(&entry->use); 392 read_unlock_irq(&mpc->egress_lock); 393 return entry; 394 } 395 entry = entry->next; 396 } 397 read_unlock_irq(&mpc->egress_lock); 398 399 return NULL; 400} 401 402static void eg_cache_put(eg_cache_entry *entry) 403{ 404 if (atomic_dec_and_test(&entry->use)) { 405 memset(entry, 0, sizeof(eg_cache_entry)); 406 kfree(entry); 407 } 408 409 return; 410} 411 412/* 413 * This should be called with write lock on 414 */ 415static void eg_cache_remove_entry(eg_cache_entry *entry, 416 struct mpoa_client *client) 417{ 418 struct atm_vcc *vcc; 419 struct k_message msg; 420 421 vcc = entry->shortcut; 422 dprintk("mpoa: mpoa_caches.c: removing an egress entry.\n"); 423 if (entry->prev != NULL) 424 entry->prev->next = entry->next; 425 else 426 client->eg_cache = entry->next; 427 if (entry->next != NULL) 428 entry->next->prev = entry->prev; 429 client->eg_ops->put(entry); 430 if(client->in_cache == NULL && client->eg_cache == NULL){ 431 msg.type = STOP_KEEP_ALIVE_SM; 432 msg_to_mpoad(&msg,client); 433 } 434 435 /* Check if the ingress side still uses this VCC */ 436 if (vcc != NULL) { 437 in_cache_entry *in_entry = client->in_ops->get_by_vcc(vcc, client); 438 if (in_entry != NULL) { 439 client->in_ops->put(in_entry); 440 return; 441 } 442 atm_async_release_vcc(vcc, -EPIPE); 443 } 444 445 return; 446} 447 448static eg_cache_entry *eg_cache_add_entry(struct k_message *msg, struct mpoa_client *client) 449{ 450 unsigned char *ip; 451 eg_cache_entry *entry = kmalloc(sizeof(eg_cache_entry), GFP_KERNEL); 452 453 if (entry == NULL) { 454 printk("mpoa: mpoa_caches.c: new_eg_cache_entry: out of memory\n"); 455 return NULL; 456 } 457 458 ip = (unsigned char *)&msg->content.eg_info.eg_dst_ip; 459 dprintk("mpoa: mpoa_caches.c: adding an egress entry, ip = %u.%u.%u.%u, this should be our IP\n", NIPQUAD(ip)); 460 memset(entry, 0, sizeof(eg_cache_entry)); 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 ip = (unsigned char *)&entry->ctrl_info.mps_ip; 477 dprintk("mpoa: mpoa_caches.c: mps_ip = %u.%u.%u.%u\n", NIPQUAD(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 486static 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 495static 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 522static 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 534static 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 548static 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 562void 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