1#include <linux/types.h> 2#include <linux/atmmpc.h> 3#include <linux/slab.h> 4#include <linux/time.h> 5 6#include "mpoa_caches.h" 7#include "mpc.h" 8 9/* 10 * mpoa_caches.c: Implementation of ingress and egress cache 11 * handling functions 12 */ 13 14#define dprintk(format, args...) \ 15 do { if (0) \ 16 printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args);\ 17 } while (0) 18 19#define ddprintk(format, args...) \ 20 do { if (0) \ 21 printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args);\ 22 } while (0) 23 24static in_cache_entry *in_cache_get(__be32 dst_ip, 25 struct mpoa_client *client) 26{ 27 in_cache_entry *entry; 28 29 read_lock_bh(&client->ingress_lock); 30 entry = client->in_cache; 31 while (entry != NULL) { 32 if (entry->ctrl_info.in_dst_ip == dst_ip) { 33 atomic_inc(&entry->use); 34 read_unlock_bh(&client->ingress_lock); 35 return entry; 36 } 37 entry = entry->next; 38 } 39 read_unlock_bh(&client->ingress_lock); 40 41 return NULL; 42} 43 44static in_cache_entry *in_cache_get_with_mask(__be32 dst_ip, 45 struct mpoa_client *client, 46 __be32 mask) 47{ 48 in_cache_entry *entry; 49 50 read_lock_bh(&client->ingress_lock); 51 entry = client->in_cache; 52 while (entry != NULL) { 53 if ((entry->ctrl_info.in_dst_ip & mask) == (dst_ip & mask)) { 54 atomic_inc(&entry->use); 55 read_unlock_bh(&client->ingress_lock); 56 return entry; 57 } 58 entry = entry->next; 59 } 60 read_unlock_bh(&client->ingress_lock); 61 62 return NULL; 63 64} 65 66static in_cache_entry *in_cache_get_by_vcc(struct atm_vcc *vcc, 67 struct mpoa_client *client) 68{ 69 in_cache_entry *entry; 70 71 read_lock_bh(&client->ingress_lock); 72 entry = client->in_cache; 73 while (entry != NULL) { 74 if (entry->shortcut == vcc) { 75 atomic_inc(&entry->use); 76 read_unlock_bh(&client->ingress_lock); 77 return entry; 78 } 79 entry = entry->next; 80 } 81 read_unlock_bh(&client->ingress_lock); 82 83 return NULL; 84} 85 86static in_cache_entry *in_cache_add_entry(__be32 dst_ip, 87 struct mpoa_client *client) 88{ 89 in_cache_entry *entry = kzalloc(sizeof(in_cache_entry), GFP_KERNEL); 90 91 if (entry == NULL) { 92 pr_info("mpoa: mpoa_caches.c: new_in_cache_entry: out of memory\n"); 93 return NULL; 94 } 95 96 dprintk("adding an ingress entry, ip = %pI4\n", &dst_ip); 97 98 atomic_set(&entry->use, 1); 99 dprintk("new_in_cache_entry: about to lock\n"); 100 write_lock_bh(&client->ingress_lock); 101 entry->next = client->in_cache; 102 entry->prev = NULL; 103 if (client->in_cache != NULL) 104 client->in_cache->prev = entry; 105 client->in_cache = entry; 106 107 memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN); 108 entry->ctrl_info.in_dst_ip = dst_ip; 109 do_gettimeofday(&(entry->tv)); 110 entry->retry_time = client->parameters.mpc_p4; 111 entry->count = 1; 112 entry->entry_state = INGRESS_INVALID; 113 entry->ctrl_info.holding_time = HOLDING_TIME_DEFAULT; 114 atomic_inc(&entry->use); 115 116 write_unlock_bh(&client->ingress_lock); 117 dprintk("new_in_cache_entry: unlocked\n"); 118 119 return entry; 120} 121 122static int cache_hit(in_cache_entry *entry, struct mpoa_client *mpc) 123{ 124 struct atm_mpoa_qos *qos; 125 struct k_message msg; 126 127 entry->count++; 128 if (entry->entry_state == INGRESS_RESOLVED && entry->shortcut != NULL) 129 return OPEN; 130 131 if (entry->entry_state == INGRESS_REFRESHING) { 132 if (entry->count > mpc->parameters.mpc_p1) { 133 msg.type = SND_MPOA_RES_RQST; 134 msg.content.in_info = entry->ctrl_info; 135 memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN); 136 qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip); 137 if (qos != NULL) 138 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("(%s) 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) 161 msg.qos = qos->qos; 162 msg_to_mpoad(&msg, mpc); 163 do_gettimeofday(&(entry->reply_wait)); 164 } 165 166 return CLOSED; 167} 168 169static void in_cache_put(in_cache_entry *entry) 170{ 171 if (atomic_dec_and_test(&entry->use)) { 172 memset(entry, 0, sizeof(in_cache_entry)); 173 kfree(entry); 174 } 175} 176 177/* 178 * This should be called with write lock on 179 */ 180static 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("removing an ingress entry, ip = %pI4\n", 188 &entry->ctrl_info.in_dst_ip); 189 190 if (entry->prev != NULL) 191 entry->prev->next = entry->next; 192 else 193 client->in_cache = entry->next; 194 if (entry->next != NULL) 195 entry->next->prev = entry->prev; 196 client->in_ops->put(entry); 197 if (client->in_cache == NULL && client->eg_cache == NULL) { 198 msg.type = STOP_KEEP_ALIVE_SM; 199 msg_to_mpoad(&msg, client); 200 } 201 202 /* Check if the egress side still uses this VCC */ 203 if (vcc != NULL) { 204 eg_cache_entry *eg_entry = client->eg_ops->get_by_vcc(vcc, 205 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 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 in_cache_entry *entry, *next_entry; 219 struct timeval now; 220 221 do_gettimeofday(&now); 222 223 write_lock_bh(&client->ingress_lock); 224 entry = client->in_cache; 225 while (entry != NULL) { 226 entry->count = 0; 227 next_entry = entry->next; 228 if ((now.tv_sec - entry->tv.tv_sec) 229 > entry->ctrl_info.holding_time) { 230 dprintk("holding time expired, ip = %pI4\n", 231 &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 239/* Call this every MPC-p4 seconds. */ 240static void check_resolving_entries(struct mpoa_client *client) 241{ 242 243 struct atm_mpoa_qos *qos; 244 in_cache_entry *entry; 245 struct timeval now; 246 struct k_message msg; 247 248 do_gettimeofday(&now); 249 250 read_lock_bh(&client->ingress_lock); 251 entry = client->in_cache; 252 while (entry != NULL) { 253 if (entry->entry_state == INGRESS_RESOLVING) { 254 if ((now.tv_sec - entry->hold_down.tv_sec) < 255 client->parameters.mpc_p6) { 256 entry = entry->next; /* Entry in hold down */ 257 continue; 258 } 259 if ((now.tv_sec - entry->reply_wait.tv_sec) > 260 entry->retry_time) { 261 entry->retry_time = MPC_C1 * (entry->retry_time); 262 /* 263 * Retry time maximum exceeded, 264 * put entry in hold down. 265 */ 266 if (entry->retry_time > client->parameters.mpc_p5) { 267 do_gettimeofday(&(entry->hold_down)); 268 entry->retry_time = client->parameters.mpc_p4; 269 entry = entry->next; 270 continue; 271 } 272 /* Ask daemon to send a resolution request. */ 273 memset(&(entry->hold_down), 0, sizeof(struct timeval)); 274 msg.type = SND_MPOA_RES_RTRY; 275 memcpy(msg.MPS_ctrl, client->mps_ctrl_addr, ATM_ESA_LEN); 276 msg.content.in_info = entry->ctrl_info; 277 qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip); 278 if (qos != NULL) 279 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. */ 290static void refresh_entries(struct mpoa_client *client) 291{ 292 struct timeval now; 293 struct in_cache_entry *entry = client->in_cache; 294 295 ddprintk("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) > 304 entry->refresh_time) { 305 dprintk("refreshing an entry.\n"); 306 entry->entry_state = INGRESS_REFRESHING; 307 308 } 309 } 310 entry = entry->next; 311 } 312 read_unlock_bh(&client->ingress_lock); 313} 314 315static void in_destroy_cache(struct mpoa_client *mpc) 316{ 317 write_lock_irq(&mpc->ingress_lock); 318 while (mpc->in_cache != NULL) 319 mpc->in_ops->remove_entry(mpc->in_cache, mpc); 320 write_unlock_irq(&mpc->ingress_lock); 321} 322 323static eg_cache_entry *eg_cache_get_by_cache_id(__be32 cache_id, 324 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 */ 344static 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 */ 365static eg_cache_entry *eg_cache_get_by_vcc(struct atm_vcc *vcc, 366 struct mpoa_client *mpc) 367{ 368 unsigned long flags; 369 eg_cache_entry *entry; 370 371 read_lock_irqsave(&mpc->egress_lock, flags); 372 entry = mpc->eg_cache; 373 while (entry != NULL) { 374 if (entry->shortcut == vcc) { 375 atomic_inc(&entry->use); 376 read_unlock_irqrestore(&mpc->egress_lock, flags); 377 return entry; 378 } 379 entry = entry->next; 380 } 381 read_unlock_irqrestore(&mpc->egress_lock, flags); 382 383 return NULL; 384} 385 386static eg_cache_entry *eg_cache_get_by_src_ip(__be32 ipaddr, 387 struct mpoa_client *mpc) 388{ 389 eg_cache_entry *entry; 390 391 read_lock_irq(&mpc->egress_lock); 392 entry = mpc->eg_cache; 393 while (entry != NULL) { 394 if (entry->latest_ip_addr == ipaddr) { 395 atomic_inc(&entry->use); 396 read_unlock_irq(&mpc->egress_lock); 397 return entry; 398 } 399 entry = entry->next; 400 } 401 read_unlock_irq(&mpc->egress_lock); 402 403 return NULL; 404} 405 406static void eg_cache_put(eg_cache_entry *entry) 407{ 408 if (atomic_dec_and_test(&entry->use)) { 409 memset(entry, 0, sizeof(eg_cache_entry)); 410 kfree(entry); 411 } 412} 413 414/* 415 * This should be called with write lock on 416 */ 417static 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("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 448static eg_cache_entry *eg_cache_add_entry(struct k_message *msg, 449 struct mpoa_client *client) 450{ 451 eg_cache_entry *entry = kzalloc(sizeof(eg_cache_entry), GFP_KERNEL); 452 453 if (entry == NULL) { 454 pr_info("out of memory\n"); 455 return NULL; 456 } 457 458 dprintk("adding an egress entry, ip = %pI4, this should be our IP\n", 459 &msg->content.eg_info.eg_dst_ip); 460 461 atomic_set(&entry->use, 1); 462 dprintk("new_eg_cache_entry: about to lock\n"); 463 write_lock_irq(&client->egress_lock); 464 entry->next = client->eg_cache; 465 entry->prev = NULL; 466 if (client->eg_cache != NULL) 467 client->eg_cache->prev = entry; 468 client->eg_cache = entry; 469 470 memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN); 471 entry->ctrl_info = msg->content.eg_info; 472 do_gettimeofday(&(entry->tv)); 473 entry->entry_state = EGRESS_RESOLVED; 474 dprintk("new_eg_cache_entry cache_id %u\n", 475 ntohl(entry->ctrl_info.cache_id)); 476 dprintk("mps_ip = %pI4\n", &entry->ctrl_info.mps_ip); 477 atomic_inc(&entry->use); 478 479 write_unlock_irq(&client->egress_lock); 480 dprintk("new_eg_cache_entry: unlocked\n"); 481 482 return entry; 483} 484 485static void update_eg_cache_entry(eg_cache_entry *entry, uint16_t holding_time) 486{ 487 do_gettimeofday(&(entry->tv)); 488 entry->entry_state = EGRESS_RESOLVED; 489 entry->ctrl_info.holding_time = holding_time; 490} 491 492static void clear_expired(struct mpoa_client *client) 493{ 494 eg_cache_entry *entry, *next_entry; 495 struct timeval now; 496 struct k_message msg; 497 498 do_gettimeofday(&now); 499 500 write_lock_irq(&client->egress_lock); 501 entry = client->eg_cache; 502 while (entry != NULL) { 503 next_entry = entry->next; 504 if ((now.tv_sec - entry->tv.tv_sec) 505 > entry->ctrl_info.holding_time) { 506 msg.type = SND_EGRESS_PURGE; 507 msg.content.eg_info = entry->ctrl_info; 508 dprintk("egress_cache: holding time expired, cache_id = %u.\n", 509 ntohl(entry->ctrl_info.cache_id)); 510 msg_to_mpoad(&msg, client); 511 client->eg_ops->remove_entry(entry, client); 512 } 513 entry = next_entry; 514 } 515 write_unlock_irq(&client->egress_lock); 516} 517 518static 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 526 527static struct in_cache_ops ingress_ops = { 528 in_cache_add_entry, /* add_entry */ 529 in_cache_get, /* get */ 530 in_cache_get_with_mask, /* get_with_mask */ 531 in_cache_get_by_vcc, /* get_by_vcc */ 532 in_cache_put, /* put */ 533 in_cache_remove_entry, /* remove_entry */ 534 cache_hit, /* cache_hit */ 535 clear_count_and_expired, /* clear_count */ 536 check_resolving_entries, /* check_resolving */ 537 refresh_entries, /* refresh */ 538 in_destroy_cache /* destroy_cache */ 539}; 540 541static struct eg_cache_ops egress_ops = { 542 eg_cache_add_entry, /* add_entry */ 543 eg_cache_get_by_cache_id, /* get_by_cache_id */ 544 eg_cache_get_by_tag, /* get_by_tag */ 545 eg_cache_get_by_vcc, /* get_by_vcc */ 546 eg_cache_get_by_src_ip, /* get_by_src_ip */ 547 eg_cache_put, /* put */ 548 eg_cache_remove_entry, /* remove_entry */ 549 update_eg_cache_entry, /* update */ 550 clear_expired, /* clear_expired */ 551 eg_destroy_cache /* destroy_cache */ 552}; 553 554 555void atm_mpoa_init_cache(struct mpoa_client *mpc) 556{ 557 mpc->in_ops = &ingress_ops; 558 mpc->eg_ops = &egress_ops; 559} 560