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                memset(entry, 0, sizeof(in_cache_entry));
 184                kfree(entry);
 185        }
 186}
 187
 188/*
 189 * This should be called with write lock on
 190 */
 191static 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... */
 227static void clear_count_and_expired(struct mpoa_client *client)
 228{
 229        in_cache_entry *entry, *next_entry;
 230        time64_t now;
 231
 232        now = ktime_get_seconds();
 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 - entry->time) > 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. */
 250static void check_resolving_entries(struct mpoa_client *client)
 251{
 252
 253        struct atm_mpoa_qos *qos;
 254        in_cache_entry *entry;
 255        time64_t now;
 256        struct k_message msg;
 257
 258        now = ktime_get_seconds();
 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
 265                        if ((now - entry->hold_down)
 266                                        < client->parameters.mpc_p6) {
 267                                entry = entry->next;    /* Entry in hold down */
 268                                continue;
 269                        }
 270                        if ((now - entry->reply_wait) > 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                                        entry->hold_down = ktime_get_seconds();
 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(time64_t));
 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                                entry->reply_wait = ktime_get_seconds();
 292                        }
 293                }
 294                entry = entry->next;
 295        }
 296        read_unlock_bh(&client->ingress_lock);
 297}
 298
 299/* Call this every MPC-p5 seconds. */
 300static void refresh_entries(struct mpoa_client *client)
 301{
 302        time64_t now;
 303        struct in_cache_entry *entry = client->in_cache;
 304
 305        ddprintk("refresh_entries\n");
 306        now = ktime_get_seconds();
 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 - entry->reply_wait) >
 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
 325static 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
 333static 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 */
 354static 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 */
 375static 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
 396static 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
 416static 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 */
 427static 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
 458static 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        entry->time = ktime_get_seconds();
 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
 495static void update_eg_cache_entry(eg_cache_entry *entry, uint16_t holding_time)
 496{
 497        entry->time = ktime_get_seconds();
 498        entry->entry_state = EGRESS_RESOLVED;
 499        entry->ctrl_info.holding_time = holding_time;
 500}
 501
 502static void clear_expired(struct mpoa_client *client)
 503{
 504        eg_cache_entry *entry, *next_entry;
 505        time64_t now;
 506        struct k_message msg;
 507
 508        now = ktime_get_seconds();
 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 - entry->time) > entry->ctrl_info.holding_time) {
 515                        msg.type = SND_EGRESS_PURGE;
 516                        msg.content.eg_info = entry->ctrl_info;
 517                        dprintk("egress_cache: holding time expired, cache_id = %u.\n",
 518                                ntohl(entry->ctrl_info.cache_id));
 519                        msg_to_mpoad(&msg, client);
 520                        client->eg_ops->remove_entry(entry, client);
 521                }
 522                entry = next_entry;
 523        }
 524        write_unlock_irq(&client->egress_lock);
 525}
 526
 527static void eg_destroy_cache(struct mpoa_client *mpc)
 528{
 529        write_lock_irq(&mpc->egress_lock);
 530        while (mpc->eg_cache != NULL)
 531                mpc->eg_ops->remove_entry(mpc->eg_cache, mpc);
 532        write_unlock_irq(&mpc->egress_lock);
 533}
 534
 535
 536static const struct in_cache_ops ingress_ops = {
 537        .add_entry = in_cache_add_entry,
 538        .get = in_cache_get,
 539        .get_with_mask = in_cache_get_with_mask,
 540        .get_by_vcc = in_cache_get_by_vcc,
 541        .put = in_cache_put,
 542        .remove_entry = in_cache_remove_entry,
 543        .cache_hit = cache_hit,
 544        .clear_count = clear_count_and_expired,
 545        .check_resolving = check_resolving_entries,
 546        .refresh = refresh_entries,
 547        .destroy_cache = in_destroy_cache
 548};
 549
 550static const struct eg_cache_ops egress_ops = {
 551        .add_entry = eg_cache_add_entry,
 552        .get_by_cache_id = eg_cache_get_by_cache_id,
 553        .get_by_tag = eg_cache_get_by_tag,
 554        .get_by_vcc = eg_cache_get_by_vcc,
 555        .get_by_src_ip = eg_cache_get_by_src_ip,
 556        .put = eg_cache_put,
 557        .remove_entry = eg_cache_remove_entry,
 558        .update = update_eg_cache_entry,
 559        .clear_expired = clear_expired,
 560        .destroy_cache = eg_destroy_cache
 561};
 562
 563void atm_mpoa_init_cache(struct mpoa_client *mpc)
 564{
 565        mpc->in_ops = &ingress_ops;
 566        mpc->eg_ops = &egress_ops;
 567}
 568