Merge remote branch 'origin' into secretlab/next-devicetree
[pandora-kernel.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
187 /*
188  * This should be called with write lock on
189  */
190 static 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... */
226 static 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. */
250 static 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. */
300 static 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
325 static 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
333 static 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 */
354 static 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 */
375 static 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
396 static 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
416 static 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  */
427 static 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
458 static 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
495 static 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
502 static 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
528 static 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
537 static 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
551 static 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
565 void atm_mpoa_init_cache(struct mpoa_client *mpc)
566 {
567         mpc->in_ops = &ingress_ops;
568         mpc->eg_ops = &egress_ops;
569 }