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