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 entry->time = ktime_get_seconds(); 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 entry->reply_wait = ktime_get_seconds(); 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 entry->reply_wait = ktime_get_seconds(); 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 kfree_sensitive(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 time64_t now; 230 231 now = ktime_get_seconds(); 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 - entry->time) > entry->ctrl_info.holding_time) { 239 dprintk("holding time expired, ip = %pI4\n", 240 &entry->ctrl_info.in_dst_ip); 241 client->in_ops->remove_entry(entry, client); 242 } 243 entry = next_entry; 244 } 245 write_unlock_bh(&client->ingress_lock); 246 } 247 248 /* Call this every MPC-p4 seconds. */ 249 static void check_resolving_entries(struct mpoa_client *client) 250 { 251 252 struct atm_mpoa_qos *qos; 253 in_cache_entry *entry; 254 time64_t now; 255 struct k_message msg; 256 257 now = ktime_get_seconds(); 258 259 read_lock_bh(&client->ingress_lock); 260 entry = client->in_cache; 261 while (entry != NULL) { 262 if (entry->entry_state == INGRESS_RESOLVING) { 263 264 if ((now - entry->hold_down) 265 < client->parameters.mpc_p6) { 266 entry = entry->next; /* Entry in hold down */ 267 continue; 268 } 269 if ((now - entry->reply_wait) > entry->retry_time) { 270 entry->retry_time = MPC_C1 * (entry->retry_time); 271 /* 272 * Retry time maximum exceeded, 273 * put entry in hold down. 274 */ 275 if (entry->retry_time > client->parameters.mpc_p5) { 276 entry->hold_down = ktime_get_seconds(); 277 entry->retry_time = client->parameters.mpc_p4; 278 entry = entry->next; 279 continue; 280 } 281 /* Ask daemon to send a resolution request. */ 282 memset(&entry->hold_down, 0, sizeof(time64_t)); 283 msg.type = SND_MPOA_RES_RTRY; 284 memcpy(msg.MPS_ctrl, client->mps_ctrl_addr, ATM_ESA_LEN); 285 msg.content.in_info = entry->ctrl_info; 286 qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip); 287 if (qos != NULL) 288 msg.qos = qos->qos; 289 msg_to_mpoad(&msg, client); 290 entry->reply_wait = ktime_get_seconds(); 291 } 292 } 293 entry = entry->next; 294 } 295 read_unlock_bh(&client->ingress_lock); 296 } 297 298 /* Call this every MPC-p5 seconds. */ 299 static void refresh_entries(struct mpoa_client *client) 300 { 301 time64_t now; 302 struct in_cache_entry *entry = client->in_cache; 303 304 ddprintk("refresh_entries\n"); 305 now = ktime_get_seconds(); 306 307 read_lock_bh(&client->ingress_lock); 308 while (entry != NULL) { 309 if (entry->entry_state == INGRESS_RESOLVED) { 310 if (!(entry->refresh_time)) 311 entry->refresh_time = (2 * (entry->ctrl_info.holding_time))/3; 312 if ((now - entry->reply_wait) > 313 entry->refresh_time) { 314 dprintk("refreshing an entry.\n"); 315 entry->entry_state = INGRESS_REFRESHING; 316 317 } 318 } 319 entry = entry->next; 320 } 321 read_unlock_bh(&client->ingress_lock); 322 } 323 324 static void in_destroy_cache(struct mpoa_client *mpc) 325 { 326 write_lock_irq(&mpc->ingress_lock); 327 while (mpc->in_cache != NULL) 328 mpc->in_ops->remove_entry(mpc->in_cache, mpc); 329 write_unlock_irq(&mpc->ingress_lock); 330 } 331 332 static eg_cache_entry *eg_cache_get_by_cache_id(__be32 cache_id, 333 struct mpoa_client *mpc) 334 { 335 eg_cache_entry *entry; 336 337 read_lock_irq(&mpc->egress_lock); 338 entry = mpc->eg_cache; 339 while (entry != NULL) { 340 if (entry->ctrl_info.cache_id == cache_id) { 341 refcount_inc(&entry->use); 342 read_unlock_irq(&mpc->egress_lock); 343 return entry; 344 } 345 entry = entry->next; 346 } 347 read_unlock_irq(&mpc->egress_lock); 348 349 return NULL; 350 } 351 352 /* This can be called from any context since it saves CPU flags */ 353 static eg_cache_entry *eg_cache_get_by_tag(__be32 tag, struct mpoa_client *mpc) 354 { 355 unsigned long flags; 356 eg_cache_entry *entry; 357 358 read_lock_irqsave(&mpc->egress_lock, flags); 359 entry = mpc->eg_cache; 360 while (entry != NULL) { 361 if (entry->ctrl_info.tag == tag) { 362 refcount_inc(&entry->use); 363 read_unlock_irqrestore(&mpc->egress_lock, flags); 364 return entry; 365 } 366 entry = entry->next; 367 } 368 read_unlock_irqrestore(&mpc->egress_lock, flags); 369 370 return NULL; 371 } 372 373 /* This can be called from any context since it saves CPU flags */ 374 static eg_cache_entry *eg_cache_get_by_vcc(struct atm_vcc *vcc, 375 struct mpoa_client *mpc) 376 { 377 unsigned long flags; 378 eg_cache_entry *entry; 379 380 read_lock_irqsave(&mpc->egress_lock, flags); 381 entry = mpc->eg_cache; 382 while (entry != NULL) { 383 if (entry->shortcut == vcc) { 384 refcount_inc(&entry->use); 385 read_unlock_irqrestore(&mpc->egress_lock, flags); 386 return entry; 387 } 388 entry = entry->next; 389 } 390 read_unlock_irqrestore(&mpc->egress_lock, flags); 391 392 return NULL; 393 } 394 395 static eg_cache_entry *eg_cache_get_by_src_ip(__be32 ipaddr, 396 struct mpoa_client *mpc) 397 { 398 eg_cache_entry *entry; 399 400 read_lock_irq(&mpc->egress_lock); 401 entry = mpc->eg_cache; 402 while (entry != NULL) { 403 if (entry->latest_ip_addr == ipaddr) { 404 refcount_inc(&entry->use); 405 read_unlock_irq(&mpc->egress_lock); 406 return entry; 407 } 408 entry = entry->next; 409 } 410 read_unlock_irq(&mpc->egress_lock); 411 412 return NULL; 413 } 414 415 static void eg_cache_put(eg_cache_entry *entry) 416 { 417 if (refcount_dec_and_test(&entry->use)) { 418 kfree_sensitive(entry); 419 } 420 } 421 422 /* 423 * This should be called with write lock on 424 */ 425 static void eg_cache_remove_entry(eg_cache_entry *entry, 426 struct mpoa_client *client) 427 { 428 struct atm_vcc *vcc; 429 struct k_message msg; 430 431 vcc = entry->shortcut; 432 dprintk("removing an egress entry.\n"); 433 if (entry->prev != NULL) 434 entry->prev->next = entry->next; 435 else 436 client->eg_cache = entry->next; 437 if (entry->next != NULL) 438 entry->next->prev = entry->prev; 439 client->eg_ops->put(entry); 440 if (client->in_cache == NULL && client->eg_cache == NULL) { 441 msg.type = STOP_KEEP_ALIVE_SM; 442 msg_to_mpoad(&msg, client); 443 } 444 445 /* Check if the ingress side still uses this VCC */ 446 if (vcc != NULL) { 447 in_cache_entry *in_entry = client->in_ops->get_by_vcc(vcc, client); 448 if (in_entry != NULL) { 449 client->in_ops->put(in_entry); 450 return; 451 } 452 vcc_release_async(vcc, -EPIPE); 453 } 454 } 455 456 static eg_cache_entry *eg_cache_add_entry(struct k_message *msg, 457 struct mpoa_client *client) 458 { 459 eg_cache_entry *entry = kzalloc(sizeof(eg_cache_entry), GFP_KERNEL); 460 461 if (entry == NULL) { 462 pr_info("out of memory\n"); 463 return NULL; 464 } 465 466 dprintk("adding an egress entry, ip = %pI4, this should be our IP\n", 467 &msg->content.eg_info.eg_dst_ip); 468 469 refcount_set(&entry->use, 1); 470 dprintk("new_eg_cache_entry: about to lock\n"); 471 write_lock_irq(&client->egress_lock); 472 entry->next = client->eg_cache; 473 entry->prev = NULL; 474 if (client->eg_cache != NULL) 475 client->eg_cache->prev = entry; 476 client->eg_cache = entry; 477 478 memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN); 479 entry->ctrl_info = msg->content.eg_info; 480 entry->time = ktime_get_seconds(); 481 entry->entry_state = EGRESS_RESOLVED; 482 dprintk("new_eg_cache_entry cache_id %u\n", 483 ntohl(entry->ctrl_info.cache_id)); 484 dprintk("mps_ip = %pI4\n", &entry->ctrl_info.mps_ip); 485 refcount_inc(&entry->use); 486 487 write_unlock_irq(&client->egress_lock); 488 dprintk("new_eg_cache_entry: unlocked\n"); 489 490 return entry; 491 } 492 493 static void update_eg_cache_entry(eg_cache_entry *entry, uint16_t holding_time) 494 { 495 entry->time = ktime_get_seconds(); 496 entry->entry_state = EGRESS_RESOLVED; 497 entry->ctrl_info.holding_time = holding_time; 498 } 499 500 static void clear_expired(struct mpoa_client *client) 501 { 502 eg_cache_entry *entry, *next_entry; 503 time64_t now; 504 struct k_message msg; 505 506 now = ktime_get_seconds(); 507 508 write_lock_irq(&client->egress_lock); 509 entry = client->eg_cache; 510 while (entry != NULL) { 511 next_entry = entry->next; 512 if ((now - entry->time) > entry->ctrl_info.holding_time) { 513 msg.type = SND_EGRESS_PURGE; 514 msg.content.eg_info = entry->ctrl_info; 515 dprintk("egress_cache: holding time expired, cache_id = %u.\n", 516 ntohl(entry->ctrl_info.cache_id)); 517 msg_to_mpoad(&msg, client); 518 client->eg_ops->remove_entry(entry, client); 519 } 520 entry = next_entry; 521 } 522 write_unlock_irq(&client->egress_lock); 523 } 524 525 static void eg_destroy_cache(struct mpoa_client *mpc) 526 { 527 write_lock_irq(&mpc->egress_lock); 528 while (mpc->eg_cache != NULL) 529 mpc->eg_ops->remove_entry(mpc->eg_cache, mpc); 530 write_unlock_irq(&mpc->egress_lock); 531 } 532 533 534 static const struct in_cache_ops ingress_ops = { 535 .add_entry = in_cache_add_entry, 536 .get = in_cache_get, 537 .get_with_mask = in_cache_get_with_mask, 538 .get_by_vcc = in_cache_get_by_vcc, 539 .put = in_cache_put, 540 .remove_entry = in_cache_remove_entry, 541 .cache_hit = cache_hit, 542 .clear_count = clear_count_and_expired, 543 .check_resolving = check_resolving_entries, 544 .refresh = refresh_entries, 545 .destroy_cache = in_destroy_cache 546 }; 547 548 static const struct eg_cache_ops egress_ops = { 549 .add_entry = eg_cache_add_entry, 550 .get_by_cache_id = eg_cache_get_by_cache_id, 551 .get_by_tag = eg_cache_get_by_tag, 552 .get_by_vcc = eg_cache_get_by_vcc, 553 .get_by_src_ip = eg_cache_get_by_src_ip, 554 .put = eg_cache_put, 555 .remove_entry = eg_cache_remove_entry, 556 .update = update_eg_cache_entry, 557 .clear_expired = clear_expired, 558 .destroy_cache = eg_destroy_cache 559 }; 560 561 void atm_mpoa_init_cache(struct mpoa_client *mpc) 562 { 563 mpc->in_ops = &ingress_ops; 564 mpc->eg_ops = &egress_ops; 565 } 566
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.