linux/net/atm/mpoa_caches.c
<<
>>
Prefs
   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
  34static 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                        atomic_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
  54static 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                        atomic_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
  76static 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                        atomic_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
  96static 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        atomic_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        atomic_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
 132static 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
 179static void in_cache_put(in_cache_entry *entry)
 180{
 181        if (atomic_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 */
 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        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. */
 250static 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. */
 300static 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
 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                        atomic_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                        atomic_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                        atomic_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                        atomic_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 (atomic_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        atomic_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        atomic_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        do_gettimeofday(&(entry->tv));
 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        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
 528static 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
 537static struct in_cache_ops ingress_ops = {
 538        in_cache_add_entry,               /* add_entry       */
 539        in_cache_get,                     /* get             */
 540        in_cache_get_with_mask,           /* get_with_mask   */
 541        in_cache_get_by_vcc,              /* get_by_vcc      */
 542        in_cache_put,                     /* put             */
 543        in_cache_remove_entry,            /* remove_entry    */
 544        cache_hit,                        /* cache_hit       */
 545        clear_count_and_expired,          /* clear_count     */
 546        check_resolving_entries,          /* check_resolving */
 547        refresh_entries,                  /* refresh         */
 548        in_destroy_cache                  /* destroy_cache   */
 549};
 550
 551static struct eg_cache_ops egress_ops = {
 552        eg_cache_add_entry,               /* add_entry        */
 553        eg_cache_get_by_cache_id,         /* get_by_cache_id  */
 554        eg_cache_get_by_tag,              /* get_by_tag       */
 555        eg_cache_get_by_vcc,              /* get_by_vcc       */
 556        eg_cache_get_by_src_ip,           /* get_by_src_ip    */
 557        eg_cache_put,                     /* put              */
 558        eg_cache_remove_entry,            /* remove_entry     */
 559        update_eg_cache_entry,            /* update           */
 560        clear_expired,                    /* clear_expired    */
 561        eg_destroy_cache                  /* destroy_cache    */
 562};
 563
 564
 565void atm_mpoa_init_cache(struct mpoa_client *mpc)
 566{
 567        mpc->in_ops = &ingress_ops;
 568        mpc->eg_ops = &egress_ops;
 569}
 570