Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <linux/types.h>
0003 #include <linux/atmmpc.h>
0004 #include <linux/slab.h>
0005 #include <linux/time.h>
0006 
0007 #include "mpoa_caches.h"
0008 #include "mpc.h"
0009 
0010 /*
0011  * mpoa_caches.c: Implementation of ingress and egress cache
0012  * handling functions
0013  */
0014 
0015 #if 0
0016 #define dprintk(format, args...)                    \
0017     printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args)  /* debug */
0018 #else
0019 #define dprintk(format, args...)                    \
0020     do { if (0)                         \
0021         printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args);\
0022     } while (0)
0023 #endif
0024 
0025 #if 0
0026 #define ddprintk(format, args...)                   \
0027     printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args)  /* debug */
0028 #else
0029 #define ddprintk(format, args...)                   \
0030     do { if (0)                         \
0031         printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args);\
0032     } while (0)
0033 #endif
0034 
0035 static in_cache_entry *in_cache_get(__be32 dst_ip,
0036                     struct mpoa_client *client)
0037 {
0038     in_cache_entry *entry;
0039 
0040     read_lock_bh(&client->ingress_lock);
0041     entry = client->in_cache;
0042     while (entry != NULL) {
0043         if (entry->ctrl_info.in_dst_ip == dst_ip) {
0044             refcount_inc(&entry->use);
0045             read_unlock_bh(&client->ingress_lock);
0046             return entry;
0047         }
0048         entry = entry->next;
0049     }
0050     read_unlock_bh(&client->ingress_lock);
0051 
0052     return NULL;
0053 }
0054 
0055 static in_cache_entry *in_cache_get_with_mask(__be32 dst_ip,
0056                           struct mpoa_client *client,
0057                           __be32 mask)
0058 {
0059     in_cache_entry *entry;
0060 
0061     read_lock_bh(&client->ingress_lock);
0062     entry = client->in_cache;
0063     while (entry != NULL) {
0064         if ((entry->ctrl_info.in_dst_ip & mask) == (dst_ip & mask)) {
0065             refcount_inc(&entry->use);
0066             read_unlock_bh(&client->ingress_lock);
0067             return entry;
0068         }
0069         entry = entry->next;
0070     }
0071     read_unlock_bh(&client->ingress_lock);
0072 
0073     return NULL;
0074 
0075 }
0076 
0077 static in_cache_entry *in_cache_get_by_vcc(struct atm_vcc *vcc,
0078                        struct mpoa_client *client)
0079 {
0080     in_cache_entry *entry;
0081 
0082     read_lock_bh(&client->ingress_lock);
0083     entry = client->in_cache;
0084     while (entry != NULL) {
0085         if (entry->shortcut == vcc) {
0086             refcount_inc(&entry->use);
0087             read_unlock_bh(&client->ingress_lock);
0088             return entry;
0089         }
0090         entry = entry->next;
0091     }
0092     read_unlock_bh(&client->ingress_lock);
0093 
0094     return NULL;
0095 }
0096 
0097 static in_cache_entry *in_cache_add_entry(__be32 dst_ip,
0098                       struct mpoa_client *client)
0099 {
0100     in_cache_entry *entry = kzalloc(sizeof(in_cache_entry), GFP_KERNEL);
0101 
0102     if (entry == NULL) {
0103         pr_info("mpoa: mpoa_caches.c: new_in_cache_entry: out of memory\n");
0104         return NULL;
0105     }
0106 
0107     dprintk("adding an ingress entry, ip = %pI4\n", &dst_ip);
0108 
0109     refcount_set(&entry->use, 1);
0110     dprintk("new_in_cache_entry: about to lock\n");
0111     write_lock_bh(&client->ingress_lock);
0112     entry->next = client->in_cache;
0113     entry->prev = NULL;
0114     if (client->in_cache != NULL)
0115         client->in_cache->prev = entry;
0116     client->in_cache = entry;
0117 
0118     memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN);
0119     entry->ctrl_info.in_dst_ip = dst_ip;
0120     entry->time = ktime_get_seconds();
0121     entry->retry_time = client->parameters.mpc_p4;
0122     entry->count = 1;
0123     entry->entry_state = INGRESS_INVALID;
0124     entry->ctrl_info.holding_time = HOLDING_TIME_DEFAULT;
0125     refcount_inc(&entry->use);
0126 
0127     write_unlock_bh(&client->ingress_lock);
0128     dprintk("new_in_cache_entry: unlocked\n");
0129 
0130     return entry;
0131 }
0132 
0133 static int cache_hit(in_cache_entry *entry, struct mpoa_client *mpc)
0134 {
0135     struct atm_mpoa_qos *qos;
0136     struct k_message msg;
0137 
0138     entry->count++;
0139     if (entry->entry_state == INGRESS_RESOLVED && entry->shortcut != NULL)
0140         return OPEN;
0141 
0142     if (entry->entry_state == INGRESS_REFRESHING) {
0143         if (entry->count > mpc->parameters.mpc_p1) {
0144             msg.type = SND_MPOA_RES_RQST;
0145             msg.content.in_info = entry->ctrl_info;
0146             memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN);
0147             qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip);
0148             if (qos != NULL)
0149                 msg.qos = qos->qos;
0150             msg_to_mpoad(&msg, mpc);
0151             entry->reply_wait = ktime_get_seconds();
0152             entry->entry_state = INGRESS_RESOLVING;
0153         }
0154         if (entry->shortcut != NULL)
0155             return OPEN;
0156         return CLOSED;
0157     }
0158 
0159     if (entry->entry_state == INGRESS_RESOLVING && entry->shortcut != NULL)
0160         return OPEN;
0161 
0162     if (entry->count > mpc->parameters.mpc_p1 &&
0163         entry->entry_state == INGRESS_INVALID) {
0164         dprintk("(%s) threshold exceeded for ip %pI4, sending MPOA res req\n",
0165             mpc->dev->name, &entry->ctrl_info.in_dst_ip);
0166         entry->entry_state = INGRESS_RESOLVING;
0167         msg.type = SND_MPOA_RES_RQST;
0168         memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN);
0169         msg.content.in_info = entry->ctrl_info;
0170         qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip);
0171         if (qos != NULL)
0172             msg.qos = qos->qos;
0173         msg_to_mpoad(&msg, mpc);
0174         entry->reply_wait = ktime_get_seconds();
0175     }
0176 
0177     return CLOSED;
0178 }
0179 
0180 static void in_cache_put(in_cache_entry *entry)
0181 {
0182     if (refcount_dec_and_test(&entry->use)) {
0183         kfree_sensitive(entry);
0184     }
0185 }
0186 
0187 /*
0188  * This should be called with write lock on
0189  */
0190 static void in_cache_remove_entry(in_cache_entry *entry,
0191                   struct mpoa_client *client)
0192 {
0193     struct atm_vcc *vcc;
0194     struct k_message msg;
0195 
0196     vcc = entry->shortcut;
0197     dprintk("removing an ingress entry, ip = %pI4\n",
0198         &entry->ctrl_info.in_dst_ip);
0199 
0200     if (entry->prev != NULL)
0201         entry->prev->next = entry->next;
0202     else
0203         client->in_cache = entry->next;
0204     if (entry->next != NULL)
0205         entry->next->prev = entry->prev;
0206     client->in_ops->put(entry);
0207     if (client->in_cache == NULL && client->eg_cache == NULL) {
0208         msg.type = STOP_KEEP_ALIVE_SM;
0209         msg_to_mpoad(&msg, client);
0210     }
0211 
0212     /* Check if the egress side still uses this VCC */
0213     if (vcc != NULL) {
0214         eg_cache_entry *eg_entry = client->eg_ops->get_by_vcc(vcc,
0215                                       client);
0216         if (eg_entry != NULL) {
0217             client->eg_ops->put(eg_entry);
0218             return;
0219         }
0220         vcc_release_async(vcc, -EPIPE);
0221     }
0222 }
0223 
0224 /* Call this every MPC-p2 seconds... Not exactly correct solution,
0225    but an easy one... */
0226 static void clear_count_and_expired(struct mpoa_client *client)
0227 {
0228     in_cache_entry *entry, *next_entry;
0229     time64_t now;
0230 
0231     now = ktime_get_seconds();
0232 
0233     write_lock_bh(&client->ingress_lock);
0234     entry = client->in_cache;
0235     while (entry != NULL) {
0236         entry->count = 0;
0237         next_entry = entry->next;
0238         if ((now - entry->time) > entry->ctrl_info.holding_time) {
0239             dprintk("holding time expired, ip = %pI4\n",
0240                 &entry->ctrl_info.in_dst_ip);
0241             client->in_ops->remove_entry(entry, client);
0242         }
0243         entry = next_entry;
0244     }
0245     write_unlock_bh(&client->ingress_lock);
0246 }
0247 
0248 /* Call this every MPC-p4 seconds. */
0249 static void check_resolving_entries(struct mpoa_client *client)
0250 {
0251 
0252     struct atm_mpoa_qos *qos;
0253     in_cache_entry *entry;
0254     time64_t now;
0255     struct k_message msg;
0256 
0257     now = ktime_get_seconds();
0258 
0259     read_lock_bh(&client->ingress_lock);
0260     entry = client->in_cache;
0261     while (entry != NULL) {
0262         if (entry->entry_state == INGRESS_RESOLVING) {
0263 
0264             if ((now - entry->hold_down)
0265                     < client->parameters.mpc_p6) {
0266                 entry = entry->next;    /* Entry in hold down */
0267                 continue;
0268             }
0269             if ((now - entry->reply_wait) > entry->retry_time) {
0270                 entry->retry_time = MPC_C1 * (entry->retry_time);
0271                 /*
0272                  * Retry time maximum exceeded,
0273                  * put entry in hold down.
0274                  */
0275                 if (entry->retry_time > client->parameters.mpc_p5) {
0276                     entry->hold_down = ktime_get_seconds();
0277                     entry->retry_time = client->parameters.mpc_p4;
0278                     entry = entry->next;
0279                     continue;
0280                 }
0281                 /* Ask daemon to send a resolution request. */
0282                 memset(&entry->hold_down, 0, sizeof(time64_t));
0283                 msg.type = SND_MPOA_RES_RTRY;
0284                 memcpy(msg.MPS_ctrl, client->mps_ctrl_addr, ATM_ESA_LEN);
0285                 msg.content.in_info = entry->ctrl_info;
0286                 qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip);
0287                 if (qos != NULL)
0288                     msg.qos = qos->qos;
0289                 msg_to_mpoad(&msg, client);
0290                 entry->reply_wait = ktime_get_seconds();
0291             }
0292         }
0293         entry = entry->next;
0294     }
0295     read_unlock_bh(&client->ingress_lock);
0296 }
0297 
0298 /* Call this every MPC-p5 seconds. */
0299 static void refresh_entries(struct mpoa_client *client)
0300 {
0301     time64_t now;
0302     struct in_cache_entry *entry = client->in_cache;
0303 
0304     ddprintk("refresh_entries\n");
0305     now = ktime_get_seconds();
0306 
0307     read_lock_bh(&client->ingress_lock);
0308     while (entry != NULL) {
0309         if (entry->entry_state == INGRESS_RESOLVED) {
0310             if (!(entry->refresh_time))
0311                 entry->refresh_time = (2 * (entry->ctrl_info.holding_time))/3;
0312             if ((now - entry->reply_wait) >
0313                 entry->refresh_time) {
0314                 dprintk("refreshing an entry.\n");
0315                 entry->entry_state = INGRESS_REFRESHING;
0316 
0317             }
0318         }
0319         entry = entry->next;
0320     }
0321     read_unlock_bh(&client->ingress_lock);
0322 }
0323 
0324 static void in_destroy_cache(struct mpoa_client *mpc)
0325 {
0326     write_lock_irq(&mpc->ingress_lock);
0327     while (mpc->in_cache != NULL)
0328         mpc->in_ops->remove_entry(mpc->in_cache, mpc);
0329     write_unlock_irq(&mpc->ingress_lock);
0330 }
0331 
0332 static eg_cache_entry *eg_cache_get_by_cache_id(__be32 cache_id,
0333                         struct mpoa_client *mpc)
0334 {
0335     eg_cache_entry *entry;
0336 
0337     read_lock_irq(&mpc->egress_lock);
0338     entry = mpc->eg_cache;
0339     while (entry != NULL) {
0340         if (entry->ctrl_info.cache_id == cache_id) {
0341             refcount_inc(&entry->use);
0342             read_unlock_irq(&mpc->egress_lock);
0343             return entry;
0344         }
0345         entry = entry->next;
0346     }
0347     read_unlock_irq(&mpc->egress_lock);
0348 
0349     return NULL;
0350 }
0351 
0352 /* This can be called from any context since it saves CPU flags */
0353 static eg_cache_entry *eg_cache_get_by_tag(__be32 tag, struct mpoa_client *mpc)
0354 {
0355     unsigned long flags;
0356     eg_cache_entry *entry;
0357 
0358     read_lock_irqsave(&mpc->egress_lock, flags);
0359     entry = mpc->eg_cache;
0360     while (entry != NULL) {
0361         if (entry->ctrl_info.tag == tag) {
0362             refcount_inc(&entry->use);
0363             read_unlock_irqrestore(&mpc->egress_lock, flags);
0364             return entry;
0365         }
0366         entry = entry->next;
0367     }
0368     read_unlock_irqrestore(&mpc->egress_lock, flags);
0369 
0370     return NULL;
0371 }
0372 
0373 /* This can be called from any context since it saves CPU flags */
0374 static eg_cache_entry *eg_cache_get_by_vcc(struct atm_vcc *vcc,
0375                        struct mpoa_client *mpc)
0376 {
0377     unsigned long flags;
0378     eg_cache_entry *entry;
0379 
0380     read_lock_irqsave(&mpc->egress_lock, flags);
0381     entry = mpc->eg_cache;
0382     while (entry != NULL) {
0383         if (entry->shortcut == vcc) {
0384             refcount_inc(&entry->use);
0385             read_unlock_irqrestore(&mpc->egress_lock, flags);
0386             return entry;
0387         }
0388         entry = entry->next;
0389     }
0390     read_unlock_irqrestore(&mpc->egress_lock, flags);
0391 
0392     return NULL;
0393 }
0394 
0395 static eg_cache_entry *eg_cache_get_by_src_ip(__be32 ipaddr,
0396                           struct mpoa_client *mpc)
0397 {
0398     eg_cache_entry *entry;
0399 
0400     read_lock_irq(&mpc->egress_lock);
0401     entry = mpc->eg_cache;
0402     while (entry != NULL) {
0403         if (entry->latest_ip_addr == ipaddr) {
0404             refcount_inc(&entry->use);
0405             read_unlock_irq(&mpc->egress_lock);
0406             return entry;
0407         }
0408         entry = entry->next;
0409     }
0410     read_unlock_irq(&mpc->egress_lock);
0411 
0412     return NULL;
0413 }
0414 
0415 static void eg_cache_put(eg_cache_entry *entry)
0416 {
0417     if (refcount_dec_and_test(&entry->use)) {
0418         kfree_sensitive(entry);
0419     }
0420 }
0421 
0422 /*
0423  * This should be called with write lock on
0424  */
0425 static void eg_cache_remove_entry(eg_cache_entry *entry,
0426                   struct mpoa_client *client)
0427 {
0428     struct atm_vcc *vcc;
0429     struct k_message msg;
0430 
0431     vcc = entry->shortcut;
0432     dprintk("removing an egress entry.\n");
0433     if (entry->prev != NULL)
0434         entry->prev->next = entry->next;
0435     else
0436         client->eg_cache = entry->next;
0437     if (entry->next != NULL)
0438         entry->next->prev = entry->prev;
0439     client->eg_ops->put(entry);
0440     if (client->in_cache == NULL && client->eg_cache == NULL) {
0441         msg.type = STOP_KEEP_ALIVE_SM;
0442         msg_to_mpoad(&msg, client);
0443     }
0444 
0445     /* Check if the ingress side still uses this VCC */
0446     if (vcc != NULL) {
0447         in_cache_entry *in_entry = client->in_ops->get_by_vcc(vcc, client);
0448         if (in_entry != NULL) {
0449             client->in_ops->put(in_entry);
0450             return;
0451         }
0452         vcc_release_async(vcc, -EPIPE);
0453     }
0454 }
0455 
0456 static eg_cache_entry *eg_cache_add_entry(struct k_message *msg,
0457                       struct mpoa_client *client)
0458 {
0459     eg_cache_entry *entry = kzalloc(sizeof(eg_cache_entry), GFP_KERNEL);
0460 
0461     if (entry == NULL) {
0462         pr_info("out of memory\n");
0463         return NULL;
0464     }
0465 
0466     dprintk("adding an egress entry, ip = %pI4, this should be our IP\n",
0467         &msg->content.eg_info.eg_dst_ip);
0468 
0469     refcount_set(&entry->use, 1);
0470     dprintk("new_eg_cache_entry: about to lock\n");
0471     write_lock_irq(&client->egress_lock);
0472     entry->next = client->eg_cache;
0473     entry->prev = NULL;
0474     if (client->eg_cache != NULL)
0475         client->eg_cache->prev = entry;
0476     client->eg_cache = entry;
0477 
0478     memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN);
0479     entry->ctrl_info = msg->content.eg_info;
0480     entry->time = ktime_get_seconds();
0481     entry->entry_state = EGRESS_RESOLVED;
0482     dprintk("new_eg_cache_entry cache_id %u\n",
0483         ntohl(entry->ctrl_info.cache_id));
0484     dprintk("mps_ip = %pI4\n", &entry->ctrl_info.mps_ip);
0485     refcount_inc(&entry->use);
0486 
0487     write_unlock_irq(&client->egress_lock);
0488     dprintk("new_eg_cache_entry: unlocked\n");
0489 
0490     return entry;
0491 }
0492 
0493 static void update_eg_cache_entry(eg_cache_entry *entry, uint16_t holding_time)
0494 {
0495     entry->time = ktime_get_seconds();
0496     entry->entry_state = EGRESS_RESOLVED;
0497     entry->ctrl_info.holding_time = holding_time;
0498 }
0499 
0500 static void clear_expired(struct mpoa_client *client)
0501 {
0502     eg_cache_entry *entry, *next_entry;
0503     time64_t now;
0504     struct k_message msg;
0505 
0506     now = ktime_get_seconds();
0507 
0508     write_lock_irq(&client->egress_lock);
0509     entry = client->eg_cache;
0510     while (entry != NULL) {
0511         next_entry = entry->next;
0512         if ((now - entry->time) > entry->ctrl_info.holding_time) {
0513             msg.type = SND_EGRESS_PURGE;
0514             msg.content.eg_info = entry->ctrl_info;
0515             dprintk("egress_cache: holding time expired, cache_id = %u.\n",
0516                 ntohl(entry->ctrl_info.cache_id));
0517             msg_to_mpoad(&msg, client);
0518             client->eg_ops->remove_entry(entry, client);
0519         }
0520         entry = next_entry;
0521     }
0522     write_unlock_irq(&client->egress_lock);
0523 }
0524 
0525 static void eg_destroy_cache(struct mpoa_client *mpc)
0526 {
0527     write_lock_irq(&mpc->egress_lock);
0528     while (mpc->eg_cache != NULL)
0529         mpc->eg_ops->remove_entry(mpc->eg_cache, mpc);
0530     write_unlock_irq(&mpc->egress_lock);
0531 }
0532 
0533 
0534 static const struct in_cache_ops ingress_ops = {
0535     .add_entry = in_cache_add_entry,
0536     .get = in_cache_get,
0537     .get_with_mask = in_cache_get_with_mask,
0538     .get_by_vcc = in_cache_get_by_vcc,
0539     .put = in_cache_put,
0540     .remove_entry = in_cache_remove_entry,
0541     .cache_hit = cache_hit,
0542     .clear_count = clear_count_and_expired,
0543     .check_resolving = check_resolving_entries,
0544     .refresh = refresh_entries,
0545     .destroy_cache = in_destroy_cache
0546 };
0547 
0548 static const struct eg_cache_ops egress_ops = {
0549     .add_entry = eg_cache_add_entry,
0550     .get_by_cache_id = eg_cache_get_by_cache_id,
0551     .get_by_tag = eg_cache_get_by_tag,
0552     .get_by_vcc = eg_cache_get_by_vcc,
0553     .get_by_src_ip = eg_cache_get_by_src_ip,
0554     .put = eg_cache_put,
0555     .remove_entry = eg_cache_remove_entry,
0556     .update = update_eg_cache_entry,
0557     .clear_expired = clear_expired,
0558     .destroy_cache = eg_destroy_cache
0559 };
0560 
0561 void atm_mpoa_init_cache(struct mpoa_client *mpc)
0562 {
0563     mpc->in_ops = &ingress_ops;
0564     mpc->eg_ops = &egress_ops;
0565 }