Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[linux-2.6] / net / atm / mpoa_caches.c
1 #include <linux/types.h>
2 #include <linux/atmmpc.h>
3 #include <linux/time.h>
4
5 #include "mpoa_caches.h"
6 #include "mpc.h"
7
8 /*
9  * mpoa_caches.c: Implementation of ingress and egress cache
10  * handling functions
11  */
12
13 #if 0
14 #define dprintk printk    /* debug */
15 #else
16 #define dprintk(format,args...)
17 #endif
18
19 #if 0
20 #define ddprintk printk  /* more debug */
21 #else
22 #define ddprintk(format,args...)
23 #endif
24
25 static in_cache_entry *in_cache_get(__be32 dst_ip,
26                                     struct mpoa_client *client)
27 {
28         in_cache_entry *entry;
29
30         read_lock_bh(&client->ingress_lock);
31         entry = client->in_cache;
32         while(entry != NULL){
33                 if( entry->ctrl_info.in_dst_ip == dst_ip ){
34                         atomic_inc(&entry->use);
35                         read_unlock_bh(&client->ingress_lock);
36                         return entry;
37                 }
38                 entry = entry->next;
39         }
40         read_unlock_bh(&client->ingress_lock);
41
42         return NULL;
43 }
44
45 static in_cache_entry *in_cache_get_with_mask(__be32 dst_ip,
46                                               struct mpoa_client *client,
47                                               __be32 mask)
48 {
49         in_cache_entry *entry;
50
51         read_lock_bh(&client->ingress_lock);
52         entry = client->in_cache;
53         while(entry != NULL){
54                 if((entry->ctrl_info.in_dst_ip & mask)  == (dst_ip & mask )){
55                         atomic_inc(&entry->use);
56                         read_unlock_bh(&client->ingress_lock);
57                         return entry;
58                 }
59                 entry = entry->next;
60         }
61         read_unlock_bh(&client->ingress_lock);
62
63         return NULL;
64
65 }
66
67 static in_cache_entry *in_cache_get_by_vcc(struct atm_vcc *vcc,
68                                            struct mpoa_client *client )
69 {
70         in_cache_entry *entry;
71
72         read_lock_bh(&client->ingress_lock);
73         entry = client->in_cache;
74         while(entry != NULL){
75                 if(entry->shortcut == vcc) {
76                         atomic_inc(&entry->use);
77                         read_unlock_bh(&client->ingress_lock);
78                         return entry;
79                 }
80                 entry = entry->next;
81         }
82         read_unlock_bh(&client->ingress_lock);
83
84         return NULL;
85 }
86
87 static in_cache_entry *in_cache_add_entry(__be32 dst_ip,
88                                           struct mpoa_client *client)
89 {
90         in_cache_entry *entry = kzalloc(sizeof(in_cache_entry), GFP_KERNEL);
91
92         if (entry == NULL) {
93                 printk("mpoa: mpoa_caches.c: new_in_cache_entry: out of memory\n");
94                 return NULL;
95         }
96
97         dprintk("mpoa: mpoa_caches.c: adding an ingress entry, ip = %u.%u.%u.%u\n", NIPQUAD(dst_ip));
98
99         atomic_set(&entry->use, 1);
100         dprintk("mpoa: mpoa_caches.c: new_in_cache_entry: about to lock\n");
101         write_lock_bh(&client->ingress_lock);
102         entry->next = client->in_cache;
103         entry->prev = NULL;
104         if (client->in_cache != NULL)
105                 client->in_cache->prev = entry;
106         client->in_cache = entry;
107
108         memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN);
109         entry->ctrl_info.in_dst_ip = dst_ip;
110         do_gettimeofday(&(entry->tv));
111         entry->retry_time = client->parameters.mpc_p4;
112         entry->count = 1;
113         entry->entry_state = INGRESS_INVALID;
114         entry->ctrl_info.holding_time = HOLDING_TIME_DEFAULT;
115         atomic_inc(&entry->use);
116
117         write_unlock_bh(&client->ingress_lock);
118         dprintk("mpoa: mpoa_caches.c: new_in_cache_entry: unlocked\n");
119
120         return entry;
121 }
122
123 static int cache_hit(in_cache_entry *entry, struct mpoa_client *mpc)
124 {
125         struct atm_mpoa_qos *qos;
126         struct k_message msg;
127
128         entry->count++;
129         if(entry->entry_state == INGRESS_RESOLVED && entry->shortcut != NULL)
130                 return OPEN;
131
132         if(entry->entry_state == INGRESS_REFRESHING){
133                 if(entry->count > mpc->parameters.mpc_p1){
134                         msg.type = SND_MPOA_RES_RQST;
135                         msg.content.in_info = entry->ctrl_info;
136                         memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN);
137                         qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip);
138                         if (qos != NULL) msg.qos = qos->qos;
139                         msg_to_mpoad(&msg, mpc);
140                         do_gettimeofday(&(entry->reply_wait));
141                         entry->entry_state = INGRESS_RESOLVING;
142                 }
143                 if(entry->shortcut != NULL)
144                         return OPEN;
145                 return CLOSED;
146         }
147
148         if(entry->entry_state == INGRESS_RESOLVING && entry->shortcut != NULL)
149                 return OPEN;
150
151         if( entry->count > mpc->parameters.mpc_p1 &&
152             entry->entry_state == INGRESS_INVALID){
153                 dprintk("mpoa: (%s) mpoa_caches.c: threshold exceeded for ip %u.%u.%u.%u, sending MPOA res req\n", mpc->dev->name, NIPQUAD(entry->ctrl_info.in_dst_ip));
154                 entry->entry_state = INGRESS_RESOLVING;
155                 msg.type =  SND_MPOA_RES_RQST;
156                 memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN );
157                 msg.content.in_info = entry->ctrl_info;
158                 qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip);
159                 if (qos != NULL) msg.qos = qos->qos;
160                 msg_to_mpoad( &msg, mpc);
161                 do_gettimeofday(&(entry->reply_wait));
162         }
163
164         return CLOSED;
165 }
166
167 static void in_cache_put(in_cache_entry *entry)
168 {
169         if (atomic_dec_and_test(&entry->use)) {
170                 memset(entry, 0, sizeof(in_cache_entry));
171                 kfree(entry);
172         }
173
174         return;
175 }
176
177 /*
178  * This should be called with write lock on
179  */
180 static void in_cache_remove_entry(in_cache_entry *entry,
181                                   struct mpoa_client *client)
182 {
183         struct atm_vcc *vcc;
184         struct k_message msg;
185
186         vcc = entry->shortcut;
187         dprintk("mpoa: mpoa_caches.c: removing an ingress entry, ip = %u.%u.%u.%u\n",NIPQUAD(entry->ctrl_info.in_dst_ip));
188
189         if (entry->prev != NULL)
190                 entry->prev->next = entry->next;
191         else
192                 client->in_cache = entry->next;
193         if (entry->next != NULL)
194                 entry->next->prev = entry->prev;
195         client->in_ops->put(entry);
196         if(client->in_cache == NULL && client->eg_cache == NULL){
197                 msg.type = STOP_KEEP_ALIVE_SM;
198                 msg_to_mpoad(&msg,client);
199         }
200
201         /* Check if the egress side still uses this VCC */
202         if (vcc != NULL) {
203                 eg_cache_entry *eg_entry = client->eg_ops->get_by_vcc(vcc, client);
204                 if (eg_entry != NULL) {
205                         client->eg_ops->put(eg_entry);
206                         return;
207                 }
208                 vcc_release_async(vcc, -EPIPE);
209         }
210
211         return;
212 }
213
214
215 /* Call this every MPC-p2 seconds... Not exactly correct solution,
216    but an easy one... */
217 static void clear_count_and_expired(struct mpoa_client *client)
218 {
219         in_cache_entry *entry, *next_entry;
220         struct timeval now;
221
222         do_gettimeofday(&now);
223
224         write_lock_bh(&client->ingress_lock);
225         entry = client->in_cache;
226         while(entry != NULL){
227                 entry->count=0;
228                 next_entry = entry->next;
229                 if((now.tv_sec - entry->tv.tv_sec)
230                    > entry->ctrl_info.holding_time){
231                         dprintk("mpoa: mpoa_caches.c: holding time expired, ip = %u.%u.%u.%u\n", NIPQUAD(entry->ctrl_info.in_dst_ip));
232                         client->in_ops->remove_entry(entry, client);
233                 }
234                 entry = next_entry;
235         }
236         write_unlock_bh(&client->ingress_lock);
237
238         return;
239 }
240
241 /* Call this every MPC-p4 seconds. */
242 static void check_resolving_entries(struct mpoa_client *client)
243 {
244
245         struct atm_mpoa_qos *qos;
246         in_cache_entry *entry;
247         struct timeval now;
248         struct k_message msg;
249
250         do_gettimeofday( &now );
251
252         read_lock_bh(&client->ingress_lock);
253         entry = client->in_cache;
254         while( entry != NULL ){
255                 if(entry->entry_state == INGRESS_RESOLVING){
256                         if(now.tv_sec - entry->hold_down.tv_sec < client->parameters.mpc_p6){
257                                 entry = entry->next;                      /* Entry in hold down */
258                                 continue;
259                         }
260                         if( (now.tv_sec - entry->reply_wait.tv_sec) >
261                             entry->retry_time ){
262                                 entry->retry_time = MPC_C1*( entry->retry_time );
263                                 if(entry->retry_time > client->parameters.mpc_p5){
264                                         /* Retry time maximum exceeded, put entry in hold down. */
265                                         do_gettimeofday(&(entry->hold_down));
266                                         entry->retry_time = client->parameters.mpc_p4;
267                                         entry = entry->next;
268                                         continue;
269                                 }
270                                 /* Ask daemon to send a resolution request. */
271                                 memset(&(entry->hold_down),0,sizeof(struct timeval));
272                                 msg.type = SND_MPOA_RES_RTRY;
273                                 memcpy(msg.MPS_ctrl, client->mps_ctrl_addr, ATM_ESA_LEN);
274                                 msg.content.in_info = entry->ctrl_info;
275                                 qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip);
276                                 if (qos != NULL) msg.qos = qos->qos;
277                                 msg_to_mpoad(&msg, client);
278                                 do_gettimeofday(&(entry->reply_wait));
279                         }
280                 }
281                 entry = entry->next;
282         }
283         read_unlock_bh(&client->ingress_lock);
284 }
285
286 /* Call this every MPC-p5 seconds. */
287 static void refresh_entries(struct mpoa_client *client)
288 {
289         struct timeval now;
290         struct in_cache_entry *entry = client->in_cache;
291
292         ddprintk("mpoa: mpoa_caches.c: refresh_entries\n");
293         do_gettimeofday(&now);
294
295         read_lock_bh(&client->ingress_lock);
296         while( entry != NULL ){
297                 if( entry->entry_state == INGRESS_RESOLVED ){
298                         if(!(entry->refresh_time))
299                                 entry->refresh_time = (2*(entry->ctrl_info.holding_time))/3;
300                         if( (now.tv_sec - entry->reply_wait.tv_sec) > entry->refresh_time ){
301                                 dprintk("mpoa: mpoa_caches.c: refreshing an entry.\n");
302                                 entry->entry_state = INGRESS_REFRESHING;
303
304                         }
305                 }
306                 entry = entry->next;
307         }
308         read_unlock_bh(&client->ingress_lock);
309 }
310
311 static void in_destroy_cache(struct mpoa_client *mpc)
312 {
313         write_lock_irq(&mpc->ingress_lock);
314         while(mpc->in_cache != NULL)
315                 mpc->in_ops->remove_entry(mpc->in_cache, mpc);
316         write_unlock_irq(&mpc->ingress_lock);
317
318         return;
319 }
320
321 static eg_cache_entry *eg_cache_get_by_cache_id(__be32 cache_id, struct mpoa_client *mpc)
322 {
323         eg_cache_entry *entry;
324
325         read_lock_irq(&mpc->egress_lock);
326         entry = mpc->eg_cache;
327         while(entry != NULL){
328                 if(entry->ctrl_info.cache_id == cache_id){
329                         atomic_inc(&entry->use);
330                         read_unlock_irq(&mpc->egress_lock);
331                         return entry;
332                 }
333                 entry = entry->next;
334         }
335         read_unlock_irq(&mpc->egress_lock);
336
337         return NULL;
338 }
339
340 /* This can be called from any context since it saves CPU flags */
341 static eg_cache_entry *eg_cache_get_by_tag(__be32 tag, struct mpoa_client *mpc)
342 {
343         unsigned long flags;
344         eg_cache_entry *entry;
345
346         read_lock_irqsave(&mpc->egress_lock, flags);
347         entry = mpc->eg_cache;
348         while (entry != NULL){
349                 if (entry->ctrl_info.tag == tag) {
350                         atomic_inc(&entry->use);
351                         read_unlock_irqrestore(&mpc->egress_lock, flags);
352                         return entry;
353                 }
354                 entry = entry->next;
355         }
356         read_unlock_irqrestore(&mpc->egress_lock, flags);
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_vcc(struct atm_vcc *vcc, 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->shortcut == vcc) {
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 static eg_cache_entry *eg_cache_get_by_src_ip(__be32 ipaddr, struct mpoa_client *mpc)
383 {
384         eg_cache_entry *entry;
385
386         read_lock_irq(&mpc->egress_lock);
387         entry = mpc->eg_cache;
388         while(entry != NULL){
389                 if(entry->latest_ip_addr == ipaddr) {
390                         atomic_inc(&entry->use);
391                         read_unlock_irq(&mpc->egress_lock);
392                         return entry;
393                 }
394                 entry = entry->next;
395         }
396         read_unlock_irq(&mpc->egress_lock);
397
398         return NULL;
399 }
400
401 static void eg_cache_put(eg_cache_entry *entry)
402 {
403         if (atomic_dec_and_test(&entry->use)) {
404                 memset(entry, 0, sizeof(eg_cache_entry));
405                 kfree(entry);
406         }
407
408         return;
409 }
410
411 /*
412  * This should be called with write lock on
413  */
414 static void eg_cache_remove_entry(eg_cache_entry *entry,
415                                   struct mpoa_client *client)
416 {
417         struct atm_vcc *vcc;
418         struct k_message msg;
419
420         vcc = entry->shortcut;
421         dprintk("mpoa: mpoa_caches.c: removing an egress entry.\n");
422         if (entry->prev != NULL)
423                 entry->prev->next = entry->next;
424         else
425                 client->eg_cache = entry->next;
426         if (entry->next != NULL)
427                 entry->next->prev = entry->prev;
428         client->eg_ops->put(entry);
429         if(client->in_cache == NULL && client->eg_cache == NULL){
430                 msg.type = STOP_KEEP_ALIVE_SM;
431                 msg_to_mpoad(&msg,client);
432         }
433
434         /* Check if the ingress side still uses this VCC */
435         if (vcc != NULL) {
436                 in_cache_entry *in_entry = client->in_ops->get_by_vcc(vcc, client);
437                 if (in_entry != NULL) {
438                         client->in_ops->put(in_entry);
439                         return;
440                 }
441                 vcc_release_async(vcc, -EPIPE);
442         }
443
444         return;
445 }
446
447 static eg_cache_entry *eg_cache_add_entry(struct k_message *msg, struct mpoa_client *client)
448 {
449         eg_cache_entry *entry = kzalloc(sizeof(eg_cache_entry), GFP_KERNEL);
450
451         if (entry == NULL) {
452                 printk("mpoa: mpoa_caches.c: new_eg_cache_entry: out of memory\n");
453                 return NULL;
454         }
455
456         dprintk("mpoa: mpoa_caches.c: adding an egress entry, ip = %u.%u.%u.%u, this should be our IP\n", NIPQUAD(msg->content.eg_info.eg_dst_ip));
457
458         atomic_set(&entry->use, 1);
459         dprintk("mpoa: mpoa_caches.c: new_eg_cache_entry: about to lock\n");
460         write_lock_irq(&client->egress_lock);
461         entry->next = client->eg_cache;
462         entry->prev = NULL;
463         if (client->eg_cache != NULL)
464                 client->eg_cache->prev = entry;
465         client->eg_cache = entry;
466
467         memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN);
468         entry->ctrl_info = msg->content.eg_info;
469         do_gettimeofday(&(entry->tv));
470         entry->entry_state = EGRESS_RESOLVED;
471         dprintk("mpoa: mpoa_caches.c: new_eg_cache_entry cache_id %lu\n", ntohl(entry->ctrl_info.cache_id));
472         dprintk("mpoa: mpoa_caches.c: mps_ip = %u.%u.%u.%u\n",
473                 NIPQUAD(entry->ctrl_info.mps_ip));
474         atomic_inc(&entry->use);
475
476         write_unlock_irq(&client->egress_lock);
477         dprintk("mpoa: mpoa_caches.c: new_eg_cache_entry: unlocked\n");
478
479         return entry;
480 }
481
482 static void update_eg_cache_entry(eg_cache_entry * entry, uint16_t holding_time)
483 {
484         do_gettimeofday(&(entry->tv));
485         entry->entry_state = EGRESS_RESOLVED;
486         entry->ctrl_info.holding_time = holding_time;
487
488         return;
489 }
490
491 static void clear_expired(struct mpoa_client *client)
492 {
493         eg_cache_entry *entry, *next_entry;
494         struct timeval now;
495         struct k_message msg;
496
497         do_gettimeofday(&now);
498
499         write_lock_irq(&client->egress_lock);
500         entry = client->eg_cache;
501         while(entry != NULL){
502                 next_entry = entry->next;
503                 if((now.tv_sec - entry->tv.tv_sec)
504                    > entry->ctrl_info.holding_time){
505                         msg.type = SND_EGRESS_PURGE;
506                         msg.content.eg_info = entry->ctrl_info;
507                         dprintk("mpoa: mpoa_caches.c: egress_cache: holding time expired, cache_id = %lu.\n",ntohl(entry->ctrl_info.cache_id));
508                         msg_to_mpoad(&msg, client);
509                         client->eg_ops->remove_entry(entry, client);
510                 }
511                 entry = next_entry;
512         }
513         write_unlock_irq(&client->egress_lock);
514
515         return;
516 }
517
518 static void eg_destroy_cache(struct mpoa_client *mpc)
519 {
520         write_lock_irq(&mpc->egress_lock);
521         while(mpc->eg_cache != NULL)
522                 mpc->eg_ops->remove_entry(mpc->eg_cache, mpc);
523         write_unlock_irq(&mpc->egress_lock);
524
525         return;
526 }
527
528
529
530 static struct in_cache_ops ingress_ops = {
531         in_cache_add_entry,               /* add_entry       */
532         in_cache_get,                     /* get             */
533         in_cache_get_with_mask,           /* get_with_mask   */
534         in_cache_get_by_vcc,              /* get_by_vcc      */
535         in_cache_put,                     /* put             */
536         in_cache_remove_entry,            /* remove_entry    */
537         cache_hit,                        /* cache_hit       */
538         clear_count_and_expired,          /* clear_count     */
539         check_resolving_entries,          /* check_resolving */
540         refresh_entries,                  /* refresh         */
541         in_destroy_cache                  /* destroy_cache   */
542 };
543
544 static struct eg_cache_ops egress_ops = {
545         eg_cache_add_entry,               /* add_entry        */
546         eg_cache_get_by_cache_id,         /* get_by_cache_id  */
547         eg_cache_get_by_tag,              /* get_by_tag       */
548         eg_cache_get_by_vcc,              /* get_by_vcc       */
549         eg_cache_get_by_src_ip,           /* get_by_src_ip    */
550         eg_cache_put,                     /* put              */
551         eg_cache_remove_entry,            /* remove_entry     */
552         update_eg_cache_entry,            /* update           */
553         clear_expired,                    /* clear_expired    */
554         eg_destroy_cache                  /* destroy_cache    */
555 };
556
557
558 void atm_mpoa_init_cache(struct mpoa_client *mpc)
559 {
560         mpc->in_ops = &ingress_ops;
561         mpc->eg_ops = &egress_ops;
562
563         return;
564 }