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