Merge branch 'ht-delete-2.6.35' into release
[firefly-linux-kernel-4.4.55.git] / net / atm / mpoa_caches.c
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
34 static 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
54 static 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
76 static 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
96 static 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
132 static 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
179 static 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         return;
187 }
188
189 /*
190  * This should be called with write lock on
191  */
192 static void in_cache_remove_entry(in_cache_entry *entry,
193                                   struct mpoa_client *client)
194 {
195         struct atm_vcc *vcc;
196         struct k_message msg;
197
198         vcc = entry->shortcut;
199         dprintk("removing an ingress entry, ip = %pI4\n",
200                 &entry->ctrl_info.in_dst_ip);
201
202         if (entry->prev != NULL)
203                 entry->prev->next = entry->next;
204         else
205                 client->in_cache = entry->next;
206         if (entry->next != NULL)
207                 entry->next->prev = entry->prev;
208         client->in_ops->put(entry);
209         if (client->in_cache == NULL && client->eg_cache == NULL) {
210                 msg.type = STOP_KEEP_ALIVE_SM;
211                 msg_to_mpoad(&msg, client);
212         }
213
214         /* Check if the egress side still uses this VCC */
215         if (vcc != NULL) {
216                 eg_cache_entry *eg_entry = client->eg_ops->get_by_vcc(vcc,
217                                                                       client);
218                 if (eg_entry != NULL) {
219                         client->eg_ops->put(eg_entry);
220                         return;
221                 }
222                 vcc_release_async(vcc, -EPIPE);
223         }
224
225         return;
226 }
227
228 /* Call this every MPC-p2 seconds... Not exactly correct solution,
229    but an easy one... */
230 static void clear_count_and_expired(struct mpoa_client *client)
231 {
232         in_cache_entry *entry, *next_entry;
233         struct timeval now;
234
235         do_gettimeofday(&now);
236
237         write_lock_bh(&client->ingress_lock);
238         entry = client->in_cache;
239         while (entry != NULL) {
240                 entry->count = 0;
241                 next_entry = entry->next;
242                 if ((now.tv_sec - entry->tv.tv_sec)
243                    > entry->ctrl_info.holding_time) {
244                         dprintk("holding time expired, ip = %pI4\n",
245                                 &entry->ctrl_info.in_dst_ip);
246                         client->in_ops->remove_entry(entry, client);
247                 }
248                 entry = next_entry;
249         }
250         write_unlock_bh(&client->ingress_lock);
251
252         return;
253 }
254
255 /* Call this every MPC-p4 seconds. */
256 static void check_resolving_entries(struct mpoa_client *client)
257 {
258
259         struct atm_mpoa_qos *qos;
260         in_cache_entry *entry;
261         struct timeval now;
262         struct k_message msg;
263
264         do_gettimeofday(&now);
265
266         read_lock_bh(&client->ingress_lock);
267         entry = client->in_cache;
268         while (entry != NULL) {
269                 if (entry->entry_state == INGRESS_RESOLVING) {
270                         if ((now.tv_sec - entry->hold_down.tv_sec) <
271                             client->parameters.mpc_p6) {
272                                 entry = entry->next;    /* Entry in hold down */
273                                 continue;
274                         }
275                         if ((now.tv_sec - entry->reply_wait.tv_sec) >
276                             entry->retry_time) {
277                                 entry->retry_time = MPC_C1 * (entry->retry_time);
278                                 /*
279                                  * Retry time maximum exceeded,
280                                  * put entry in hold down.
281                                  */
282                                 if (entry->retry_time > client->parameters.mpc_p5) {
283                                         do_gettimeofday(&(entry->hold_down));
284                                         entry->retry_time = client->parameters.mpc_p4;
285                                         entry = entry->next;
286                                         continue;
287                                 }
288                                 /* Ask daemon to send a resolution request. */
289                                 memset(&(entry->hold_down), 0, sizeof(struct timeval));
290                                 msg.type = SND_MPOA_RES_RTRY;
291                                 memcpy(msg.MPS_ctrl, client->mps_ctrl_addr, ATM_ESA_LEN);
292                                 msg.content.in_info = entry->ctrl_info;
293                                 qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip);
294                                 if (qos != NULL)
295                                         msg.qos = qos->qos;
296                                 msg_to_mpoad(&msg, client);
297                                 do_gettimeofday(&(entry->reply_wait));
298                         }
299                 }
300                 entry = entry->next;
301         }
302         read_unlock_bh(&client->ingress_lock);
303 }
304
305 /* Call this every MPC-p5 seconds. */
306 static void refresh_entries(struct mpoa_client *client)
307 {
308         struct timeval now;
309         struct in_cache_entry *entry = client->in_cache;
310
311         ddprintk("refresh_entries\n");
312         do_gettimeofday(&now);
313
314         read_lock_bh(&client->ingress_lock);
315         while (entry != NULL) {
316                 if (entry->entry_state == INGRESS_RESOLVED) {
317                         if (!(entry->refresh_time))
318                                 entry->refresh_time = (2 * (entry->ctrl_info.holding_time))/3;
319                         if ((now.tv_sec - entry->reply_wait.tv_sec) >
320                             entry->refresh_time) {
321                                 dprintk("refreshing an entry.\n");
322                                 entry->entry_state = INGRESS_REFRESHING;
323
324                         }
325                 }
326                 entry = entry->next;
327         }
328         read_unlock_bh(&client->ingress_lock);
329 }
330
331 static void in_destroy_cache(struct mpoa_client *mpc)
332 {
333         write_lock_irq(&mpc->ingress_lock);
334         while (mpc->in_cache != NULL)
335                 mpc->in_ops->remove_entry(mpc->in_cache, mpc);
336         write_unlock_irq(&mpc->ingress_lock);
337
338         return;
339 }
340
341 static eg_cache_entry *eg_cache_get_by_cache_id(__be32 cache_id,
342                                                 struct mpoa_client *mpc)
343 {
344         eg_cache_entry *entry;
345
346         read_lock_irq(&mpc->egress_lock);
347         entry = mpc->eg_cache;
348         while (entry != NULL) {
349                 if (entry->ctrl_info.cache_id == cache_id) {
350                         atomic_inc(&entry->use);
351                         read_unlock_irq(&mpc->egress_lock);
352                         return entry;
353                 }
354                 entry = entry->next;
355         }
356         read_unlock_irq(&mpc->egress_lock);
357
358         return NULL;
359 }
360
361 /* This can be called from any context since it saves CPU flags */
362 static eg_cache_entry *eg_cache_get_by_tag(__be32 tag, struct mpoa_client *mpc)
363 {
364         unsigned long flags;
365         eg_cache_entry *entry;
366
367         read_lock_irqsave(&mpc->egress_lock, flags);
368         entry = mpc->eg_cache;
369         while (entry != NULL) {
370                 if (entry->ctrl_info.tag == tag) {
371                         atomic_inc(&entry->use);
372                         read_unlock_irqrestore(&mpc->egress_lock, flags);
373                         return entry;
374                 }
375                 entry = entry->next;
376         }
377         read_unlock_irqrestore(&mpc->egress_lock, flags);
378
379         return NULL;
380 }
381
382 /* This can be called from any context since it saves CPU flags */
383 static eg_cache_entry *eg_cache_get_by_vcc(struct atm_vcc *vcc,
384                                            struct mpoa_client *mpc)
385 {
386         unsigned long flags;
387         eg_cache_entry *entry;
388
389         read_lock_irqsave(&mpc->egress_lock, flags);
390         entry = mpc->eg_cache;
391         while (entry != NULL) {
392                 if (entry->shortcut == vcc) {
393                         atomic_inc(&entry->use);
394                         read_unlock_irqrestore(&mpc->egress_lock, flags);
395                         return entry;
396                 }
397                 entry = entry->next;
398         }
399         read_unlock_irqrestore(&mpc->egress_lock, flags);
400
401         return NULL;
402 }
403
404 static eg_cache_entry *eg_cache_get_by_src_ip(__be32 ipaddr,
405                                               struct mpoa_client *mpc)
406 {
407         eg_cache_entry *entry;
408
409         read_lock_irq(&mpc->egress_lock);
410         entry = mpc->eg_cache;
411         while (entry != NULL) {
412                 if (entry->latest_ip_addr == ipaddr) {
413                         atomic_inc(&entry->use);
414                         read_unlock_irq(&mpc->egress_lock);
415                         return entry;
416                 }
417                 entry = entry->next;
418         }
419         read_unlock_irq(&mpc->egress_lock);
420
421         return NULL;
422 }
423
424 static void eg_cache_put(eg_cache_entry *entry)
425 {
426         if (atomic_dec_and_test(&entry->use)) {
427                 memset(entry, 0, sizeof(eg_cache_entry));
428                 kfree(entry);
429         }
430
431         return;
432 }
433
434 /*
435  * This should be called with write lock on
436  */
437 static void eg_cache_remove_entry(eg_cache_entry *entry,
438                                   struct mpoa_client *client)
439 {
440         struct atm_vcc *vcc;
441         struct k_message msg;
442
443         vcc = entry->shortcut;
444         dprintk("removing an egress entry.\n");
445         if (entry->prev != NULL)
446                 entry->prev->next = entry->next;
447         else
448                 client->eg_cache = entry->next;
449         if (entry->next != NULL)
450                 entry->next->prev = entry->prev;
451         client->eg_ops->put(entry);
452         if (client->in_cache == NULL && client->eg_cache == NULL) {
453                 msg.type = STOP_KEEP_ALIVE_SM;
454                 msg_to_mpoad(&msg, client);
455         }
456
457         /* Check if the ingress side still uses this VCC */
458         if (vcc != NULL) {
459                 in_cache_entry *in_entry = client->in_ops->get_by_vcc(vcc, client);
460                 if (in_entry != NULL) {
461                         client->in_ops->put(in_entry);
462                         return;
463                 }
464                 vcc_release_async(vcc, -EPIPE);
465         }
466
467         return;
468 }
469
470 static eg_cache_entry *eg_cache_add_entry(struct k_message *msg,
471                                           struct mpoa_client *client)
472 {
473         eg_cache_entry *entry = kzalloc(sizeof(eg_cache_entry), GFP_KERNEL);
474
475         if (entry == NULL) {
476                 pr_info("out of memory\n");
477                 return NULL;
478         }
479
480         dprintk("adding an egress entry, ip = %pI4, this should be our IP\n",
481                 &msg->content.eg_info.eg_dst_ip);
482
483         atomic_set(&entry->use, 1);
484         dprintk("new_eg_cache_entry: about to lock\n");
485         write_lock_irq(&client->egress_lock);
486         entry->next = client->eg_cache;
487         entry->prev = NULL;
488         if (client->eg_cache != NULL)
489                 client->eg_cache->prev = entry;
490         client->eg_cache = entry;
491
492         memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN);
493         entry->ctrl_info = msg->content.eg_info;
494         do_gettimeofday(&(entry->tv));
495         entry->entry_state = EGRESS_RESOLVED;
496         dprintk("new_eg_cache_entry cache_id %u\n",
497                 ntohl(entry->ctrl_info.cache_id));
498         dprintk("mps_ip = %pI4\n", &entry->ctrl_info.mps_ip);
499         atomic_inc(&entry->use);
500
501         write_unlock_irq(&client->egress_lock);
502         dprintk("new_eg_cache_entry: unlocked\n");
503
504         return entry;
505 }
506
507 static void update_eg_cache_entry(eg_cache_entry *entry, uint16_t holding_time)
508 {
509         do_gettimeofday(&(entry->tv));
510         entry->entry_state = EGRESS_RESOLVED;
511         entry->ctrl_info.holding_time = holding_time;
512
513         return;
514 }
515
516 static void clear_expired(struct mpoa_client *client)
517 {
518         eg_cache_entry *entry, *next_entry;
519         struct timeval now;
520         struct k_message msg;
521
522         do_gettimeofday(&now);
523
524         write_lock_irq(&client->egress_lock);
525         entry = client->eg_cache;
526         while (entry != NULL) {
527                 next_entry = entry->next;
528                 if ((now.tv_sec - entry->tv.tv_sec)
529                    > entry->ctrl_info.holding_time) {
530                         msg.type = SND_EGRESS_PURGE;
531                         msg.content.eg_info = entry->ctrl_info;
532                         dprintk("egress_cache: holding time expired, cache_id = %u.\n",
533                                 ntohl(entry->ctrl_info.cache_id));
534                         msg_to_mpoad(&msg, client);
535                         client->eg_ops->remove_entry(entry, client);
536                 }
537                 entry = next_entry;
538         }
539         write_unlock_irq(&client->egress_lock);
540
541         return;
542 }
543
544 static void eg_destroy_cache(struct mpoa_client *mpc)
545 {
546         write_lock_irq(&mpc->egress_lock);
547         while (mpc->eg_cache != NULL)
548                 mpc->eg_ops->remove_entry(mpc->eg_cache, mpc);
549         write_unlock_irq(&mpc->egress_lock);
550
551         return;
552 }
553
554
555 static struct in_cache_ops ingress_ops = {
556         in_cache_add_entry,               /* add_entry       */
557         in_cache_get,                     /* get             */
558         in_cache_get_with_mask,           /* get_with_mask   */
559         in_cache_get_by_vcc,              /* get_by_vcc      */
560         in_cache_put,                     /* put             */
561         in_cache_remove_entry,            /* remove_entry    */
562         cache_hit,                        /* cache_hit       */
563         clear_count_and_expired,          /* clear_count     */
564         check_resolving_entries,          /* check_resolving */
565         refresh_entries,                  /* refresh         */
566         in_destroy_cache                  /* destroy_cache   */
567 };
568
569 static struct eg_cache_ops egress_ops = {
570         eg_cache_add_entry,               /* add_entry        */
571         eg_cache_get_by_cache_id,         /* get_by_cache_id  */
572         eg_cache_get_by_tag,              /* get_by_tag       */
573         eg_cache_get_by_vcc,              /* get_by_vcc       */
574         eg_cache_get_by_src_ip,           /* get_by_src_ip    */
575         eg_cache_put,                     /* put              */
576         eg_cache_remove_entry,            /* remove_entry     */
577         update_eg_cache_entry,            /* update           */
578         clear_expired,                    /* clear_expired    */
579         eg_destroy_cache                  /* destroy_cache    */
580 };
581
582
583 void atm_mpoa_init_cache(struct mpoa_client *mpc)
584 {
585         mpc->in_ops = &ingress_ops;
586         mpc->eg_ops = &egress_ops;
587
588         return;
589 }