linux/net/atm/mpoa_caches.c
<<
>>
Prefs
   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
  35static 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
  55static 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
  77static 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
  97static 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
 133static 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
 180static 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 */
 190static 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... */
 226static 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. */
 249static 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. */
 299static 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
 324static 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
 332static 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 */
 353static 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 */
 374static 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
 395static 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
 415static 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 */
 425static 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
 456static 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
 493static 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
 500static 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
 525static 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
 534static 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
 548static 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
 561void atm_mpoa_init_cache(struct mpoa_client *mpc)
 562{
 563        mpc->in_ops = &ingress_ops;
 564        mpc->eg_ops = &egress_ops;
 565}
 566