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