1 #include <linux/types.h>
2 #include <linux/atmmpc.h>
3 #include <linux/time.h>
5 #include "mpoa_caches.h"
9 * mpoa_caches.c: Implementation of ingress and egress cache
14 #define dprintk printk /* debug */
16 #define dprintk(format,args...)
20 #define ddprintk printk /* more debug */
22 #define ddprintk(format,args...)
25 static in_cache_entry
*in_cache_get(uint32_t dst_ip
,
26 struct mpoa_client
*client
)
28 in_cache_entry
*entry
;
30 read_lock_bh(&client
->ingress_lock
);
31 entry
= client
->in_cache
;
33 if( entry
->ctrl_info
.in_dst_ip
== dst_ip
){
34 atomic_inc(&entry
->use
);
35 read_unlock_bh(&client
->ingress_lock
);
40 read_unlock_bh(&client
->ingress_lock
);
45 static in_cache_entry
*in_cache_get_with_mask(uint32_t dst_ip
,
46 struct mpoa_client
*client
,
49 in_cache_entry
*entry
;
51 read_lock_bh(&client
->ingress_lock
);
52 entry
= client
->in_cache
;
54 if((entry
->ctrl_info
.in_dst_ip
& mask
) == (dst_ip
& mask
)){
55 atomic_inc(&entry
->use
);
56 read_unlock_bh(&client
->ingress_lock
);
61 read_unlock_bh(&client
->ingress_lock
);
67 static in_cache_entry
*in_cache_get_by_vcc(struct atm_vcc
*vcc
,
68 struct mpoa_client
*client
)
70 in_cache_entry
*entry
;
72 read_lock_bh(&client
->ingress_lock
);
73 entry
= client
->in_cache
;
75 if(entry
->shortcut
== vcc
) {
76 atomic_inc(&entry
->use
);
77 read_unlock_bh(&client
->ingress_lock
);
82 read_unlock_bh(&client
->ingress_lock
);
87 static in_cache_entry
*in_cache_add_entry(uint32_t dst_ip
,
88 struct mpoa_client
*client
)
90 unsigned char *ip
__attribute__ ((unused
)) = (unsigned char *)&dst_ip
;
91 in_cache_entry
* entry
= kmalloc(sizeof(in_cache_entry
), GFP_KERNEL
);
94 printk("mpoa: mpoa_caches.c: new_in_cache_entry: out of memory\n");
98 dprintk("mpoa: mpoa_caches.c: adding an ingress entry, ip = %u.%u.%u.%u\n", ip
[0], ip
[1], ip
[2], ip
[3]);
99 memset(entry
,0,sizeof(in_cache_entry
));
101 atomic_set(&entry
->use
, 1);
102 dprintk("mpoa: mpoa_caches.c: new_in_cache_entry: about to lock\n");
103 write_lock_bh(&client
->ingress_lock
);
104 entry
->next
= client
->in_cache
;
106 if (client
->in_cache
!= NULL
)
107 client
->in_cache
->prev
= entry
;
108 client
->in_cache
= entry
;
110 memcpy(entry
->MPS_ctrl_ATM_addr
, client
->mps_ctrl_addr
, ATM_ESA_LEN
);
111 entry
->ctrl_info
.in_dst_ip
= dst_ip
;
112 do_gettimeofday(&(entry
->tv
));
113 entry
->retry_time
= client
->parameters
.mpc_p4
;
115 entry
->entry_state
= INGRESS_INVALID
;
116 entry
->ctrl_info
.holding_time
= HOLDING_TIME_DEFAULT
;
117 atomic_inc(&entry
->use
);
119 write_unlock_bh(&client
->ingress_lock
);
120 dprintk("mpoa: mpoa_caches.c: new_in_cache_entry: unlocked\n");
125 static int cache_hit(in_cache_entry
*entry
, struct mpoa_client
*mpc
)
127 struct atm_mpoa_qos
*qos
;
128 struct k_message msg
;
131 if(entry
->entry_state
== INGRESS_RESOLVED
&& entry
->shortcut
!= NULL
)
134 if(entry
->entry_state
== INGRESS_REFRESHING
){
135 if(entry
->count
> mpc
->parameters
.mpc_p1
){
136 msg
.type
= SND_MPOA_RES_RQST
;
137 msg
.content
.in_info
= entry
->ctrl_info
;
138 memcpy(msg
.MPS_ctrl
, mpc
->mps_ctrl_addr
, ATM_ESA_LEN
);
139 qos
= atm_mpoa_search_qos(entry
->ctrl_info
.in_dst_ip
);
140 if (qos
!= NULL
) msg
.qos
= qos
->qos
;
141 msg_to_mpoad(&msg
, mpc
);
142 do_gettimeofday(&(entry
->reply_wait
));
143 entry
->entry_state
= INGRESS_RESOLVING
;
145 if(entry
->shortcut
!= NULL
)
150 if(entry
->entry_state
== INGRESS_RESOLVING
&& entry
->shortcut
!= NULL
)
153 if( entry
->count
> mpc
->parameters
.mpc_p1
&&
154 entry
->entry_state
== INGRESS_INVALID
){
155 unsigned char *ip
__attribute__ ((unused
)) =
156 (unsigned char *)&entry
->ctrl_info
.in_dst_ip
;
158 dprintk("mpoa: (%s) mpoa_caches.c: threshold exceeded for ip %u.%u.%u.%u, sending MPOA res req\n", mpc
->dev
->name
, ip
[0], ip
[1], ip
[2], ip
[3]);
159 entry
->entry_state
= INGRESS_RESOLVING
;
160 msg
.type
= SND_MPOA_RES_RQST
;
161 memcpy(msg
.MPS_ctrl
, mpc
->mps_ctrl_addr
, ATM_ESA_LEN
);
162 msg
.content
.in_info
= entry
->ctrl_info
;
163 qos
= atm_mpoa_search_qos(entry
->ctrl_info
.in_dst_ip
);
164 if (qos
!= NULL
) msg
.qos
= qos
->qos
;
165 msg_to_mpoad( &msg
, mpc
);
166 do_gettimeofday(&(entry
->reply_wait
));
172 static void in_cache_put(in_cache_entry
*entry
)
174 if (atomic_dec_and_test(&entry
->use
)) {
175 memset(entry
, 0, sizeof(in_cache_entry
));
183 * This should be called with write lock on
185 static void in_cache_remove_entry(in_cache_entry
*entry
,
186 struct mpoa_client
*client
)
189 struct k_message msg
;
192 vcc
= entry
->shortcut
;
193 ip
= (unsigned char *)&entry
->ctrl_info
.in_dst_ip
;
194 dprintk("mpoa: mpoa_caches.c: removing an ingress entry, ip = %u.%u.%u.%u\n",ip
[0], ip
[1], ip
[2], ip
[3]);
196 if (entry
->prev
!= NULL
)
197 entry
->prev
->next
= entry
->next
;
199 client
->in_cache
= entry
->next
;
200 if (entry
->next
!= NULL
)
201 entry
->next
->prev
= entry
->prev
;
202 client
->in_ops
->put(entry
);
203 if(client
->in_cache
== NULL
&& client
->eg_cache
== NULL
){
204 msg
.type
= STOP_KEEP_ALIVE_SM
;
205 msg_to_mpoad(&msg
,client
);
208 /* Check if the egress side still uses this VCC */
210 eg_cache_entry
*eg_entry
= client
->eg_ops
->get_by_vcc(vcc
, client
);
211 if (eg_entry
!= NULL
) {
212 client
->eg_ops
->put(eg_entry
);
215 vcc_release_async(vcc
, -EPIPE
);
222 /* Call this every MPC-p2 seconds... Not exactly correct solution,
223 but an easy one... */
224 static void clear_count_and_expired(struct mpoa_client
*client
)
227 in_cache_entry
*entry
, *next_entry
;
230 do_gettimeofday(&now
);
232 write_lock_bh(&client
->ingress_lock
);
233 entry
= client
->in_cache
;
234 while(entry
!= NULL
){
236 next_entry
= entry
->next
;
237 if((now
.tv_sec
- entry
->tv
.tv_sec
)
238 > entry
->ctrl_info
.holding_time
){
239 ip
= (unsigned char*)&entry
->ctrl_info
.in_dst_ip
;
240 dprintk("mpoa: mpoa_caches.c: holding time expired, ip = %u.%u.%u.%u\n", NIPQUAD(ip
));
241 client
->in_ops
->remove_entry(entry
, client
);
245 write_unlock_bh(&client
->ingress_lock
);
250 /* Call this every MPC-p4 seconds. */
251 static void check_resolving_entries(struct mpoa_client
*client
)
254 struct atm_mpoa_qos
*qos
;
255 in_cache_entry
*entry
;
257 struct k_message msg
;
259 do_gettimeofday( &now
);
261 read_lock_bh(&client
->ingress_lock
);
262 entry
= client
->in_cache
;
263 while( entry
!= NULL
){
264 if(entry
->entry_state
== INGRESS_RESOLVING
){
265 if(now
.tv_sec
- entry
->hold_down
.tv_sec
< client
->parameters
.mpc_p6
){
266 entry
= entry
->next
; /* Entry in hold down */
269 if( (now
.tv_sec
- entry
->reply_wait
.tv_sec
) >
271 entry
->retry_time
= MPC_C1
*( entry
->retry_time
);
272 if(entry
->retry_time
> client
->parameters
.mpc_p5
){
273 /* Retry time maximum exceeded, put entry in hold down. */
274 do_gettimeofday(&(entry
->hold_down
));
275 entry
->retry_time
= client
->parameters
.mpc_p4
;
279 /* Ask daemon to send a resolution request. */
280 memset(&(entry
->hold_down
),0,sizeof(struct timeval
));
281 msg
.type
= SND_MPOA_RES_RTRY
;
282 memcpy(msg
.MPS_ctrl
, client
->mps_ctrl_addr
, ATM_ESA_LEN
);
283 msg
.content
.in_info
= entry
->ctrl_info
;
284 qos
= atm_mpoa_search_qos(entry
->ctrl_info
.in_dst_ip
);
285 if (qos
!= NULL
) msg
.qos
= qos
->qos
;
286 msg_to_mpoad(&msg
, client
);
287 do_gettimeofday(&(entry
->reply_wait
));
292 read_unlock_bh(&client
->ingress_lock
);
295 /* Call this every MPC-p5 seconds. */
296 static void refresh_entries(struct mpoa_client
*client
)
299 struct in_cache_entry
*entry
= client
->in_cache
;
301 ddprintk("mpoa: mpoa_caches.c: refresh_entries\n");
302 do_gettimeofday(&now
);
304 read_lock_bh(&client
->ingress_lock
);
305 while( entry
!= NULL
){
306 if( entry
->entry_state
== INGRESS_RESOLVED
){
307 if(!(entry
->refresh_time
))
308 entry
->refresh_time
= (2*(entry
->ctrl_info
.holding_time
))/3;
309 if( (now
.tv_sec
- entry
->reply_wait
.tv_sec
) > entry
->refresh_time
){
310 dprintk("mpoa: mpoa_caches.c: refreshing an entry.\n");
311 entry
->entry_state
= INGRESS_REFRESHING
;
317 read_unlock_bh(&client
->ingress_lock
);
320 static void in_destroy_cache(struct mpoa_client
*mpc
)
322 write_lock_irq(&mpc
->ingress_lock
);
323 while(mpc
->in_cache
!= NULL
)
324 mpc
->in_ops
->remove_entry(mpc
->in_cache
, mpc
);
325 write_unlock_irq(&mpc
->ingress_lock
);
330 static eg_cache_entry
*eg_cache_get_by_cache_id(uint32_t cache_id
, struct mpoa_client
*mpc
)
332 eg_cache_entry
*entry
;
334 read_lock_irq(&mpc
->egress_lock
);
335 entry
= mpc
->eg_cache
;
336 while(entry
!= NULL
){
337 if(entry
->ctrl_info
.cache_id
== cache_id
){
338 atomic_inc(&entry
->use
);
339 read_unlock_irq(&mpc
->egress_lock
);
344 read_unlock_irq(&mpc
->egress_lock
);
349 /* This can be called from any context since it saves CPU flags */
350 static eg_cache_entry
*eg_cache_get_by_tag(uint32_t tag
, struct mpoa_client
*mpc
)
353 eg_cache_entry
*entry
;
355 read_lock_irqsave(&mpc
->egress_lock
, flags
);
356 entry
= mpc
->eg_cache
;
357 while (entry
!= NULL
){
358 if (entry
->ctrl_info
.tag
== tag
) {
359 atomic_inc(&entry
->use
);
360 read_unlock_irqrestore(&mpc
->egress_lock
, flags
);
365 read_unlock_irqrestore(&mpc
->egress_lock
, flags
);
370 /* This can be called from any context since it saves CPU flags */
371 static eg_cache_entry
*eg_cache_get_by_vcc(struct atm_vcc
*vcc
, struct mpoa_client
*mpc
)
374 eg_cache_entry
*entry
;
376 read_lock_irqsave(&mpc
->egress_lock
, flags
);
377 entry
= mpc
->eg_cache
;
378 while (entry
!= NULL
){
379 if (entry
->shortcut
== vcc
) {
380 atomic_inc(&entry
->use
);
381 read_unlock_irqrestore(&mpc
->egress_lock
, flags
);
386 read_unlock_irqrestore(&mpc
->egress_lock
, flags
);
391 static eg_cache_entry
*eg_cache_get_by_src_ip(uint32_t ipaddr
, struct mpoa_client
*mpc
)
393 eg_cache_entry
*entry
;
395 read_lock_irq(&mpc
->egress_lock
);
396 entry
= mpc
->eg_cache
;
397 while(entry
!= NULL
){
398 if(entry
->latest_ip_addr
== ipaddr
) {
399 atomic_inc(&entry
->use
);
400 read_unlock_irq(&mpc
->egress_lock
);
405 read_unlock_irq(&mpc
->egress_lock
);
410 static void eg_cache_put(eg_cache_entry
*entry
)
412 if (atomic_dec_and_test(&entry
->use
)) {
413 memset(entry
, 0, sizeof(eg_cache_entry
));
421 * This should be called with write lock on
423 static void eg_cache_remove_entry(eg_cache_entry
*entry
,
424 struct mpoa_client
*client
)
427 struct k_message msg
;
429 vcc
= entry
->shortcut
;
430 dprintk("mpoa: mpoa_caches.c: removing an egress entry.\n");
431 if (entry
->prev
!= NULL
)
432 entry
->prev
->next
= entry
->next
;
434 client
->eg_cache
= entry
->next
;
435 if (entry
->next
!= NULL
)
436 entry
->next
->prev
= entry
->prev
;
437 client
->eg_ops
->put(entry
);
438 if(client
->in_cache
== NULL
&& client
->eg_cache
== NULL
){
439 msg
.type
= STOP_KEEP_ALIVE_SM
;
440 msg_to_mpoad(&msg
,client
);
443 /* Check if the ingress side still uses this VCC */
445 in_cache_entry
*in_entry
= client
->in_ops
->get_by_vcc(vcc
, client
);
446 if (in_entry
!= NULL
) {
447 client
->in_ops
->put(in_entry
);
450 vcc_release_async(vcc
, -EPIPE
);
456 static eg_cache_entry
*eg_cache_add_entry(struct k_message
*msg
, struct mpoa_client
*client
)
459 eg_cache_entry
*entry
= kmalloc(sizeof(eg_cache_entry
), GFP_KERNEL
);
462 printk("mpoa: mpoa_caches.c: new_eg_cache_entry: out of memory\n");
466 ip
= (unsigned char *)&msg
->content
.eg_info
.eg_dst_ip
;
467 dprintk("mpoa: mpoa_caches.c: adding an egress entry, ip = %u.%u.%u.%u, this should be our IP\n", NIPQUAD(ip
));
468 memset(entry
, 0, sizeof(eg_cache_entry
));
470 atomic_set(&entry
->use
, 1);
471 dprintk("mpoa: mpoa_caches.c: new_eg_cache_entry: about to lock\n");
472 write_lock_irq(&client
->egress_lock
);
473 entry
->next
= client
->eg_cache
;
475 if (client
->eg_cache
!= NULL
)
476 client
->eg_cache
->prev
= entry
;
477 client
->eg_cache
= entry
;
479 memcpy(entry
->MPS_ctrl_ATM_addr
, client
->mps_ctrl_addr
, ATM_ESA_LEN
);
480 entry
->ctrl_info
= msg
->content
.eg_info
;
481 do_gettimeofday(&(entry
->tv
));
482 entry
->entry_state
= EGRESS_RESOLVED
;
483 dprintk("mpoa: mpoa_caches.c: new_eg_cache_entry cache_id %lu\n", ntohl(entry
->ctrl_info
.cache_id
));
484 ip
= (unsigned char *)&entry
->ctrl_info
.mps_ip
;
485 dprintk("mpoa: mpoa_caches.c: mps_ip = %u.%u.%u.%u\n", NIPQUAD(ip
));
486 atomic_inc(&entry
->use
);
488 write_unlock_irq(&client
->egress_lock
);
489 dprintk("mpoa: mpoa_caches.c: new_eg_cache_entry: unlocked\n");
494 static void update_eg_cache_entry(eg_cache_entry
* entry
, uint16_t holding_time
)
496 do_gettimeofday(&(entry
->tv
));
497 entry
->entry_state
= EGRESS_RESOLVED
;
498 entry
->ctrl_info
.holding_time
= holding_time
;
503 static void clear_expired(struct mpoa_client
*client
)
505 eg_cache_entry
*entry
, *next_entry
;
507 struct k_message msg
;
509 do_gettimeofday(&now
);
511 write_lock_irq(&client
->egress_lock
);
512 entry
= client
->eg_cache
;
513 while(entry
!= NULL
){
514 next_entry
= entry
->next
;
515 if((now
.tv_sec
- entry
->tv
.tv_sec
)
516 > entry
->ctrl_info
.holding_time
){
517 msg
.type
= SND_EGRESS_PURGE
;
518 msg
.content
.eg_info
= entry
->ctrl_info
;
519 dprintk("mpoa: mpoa_caches.c: egress_cache: holding time expired, cache_id = %lu.\n",ntohl(entry
->ctrl_info
.cache_id
));
520 msg_to_mpoad(&msg
, client
);
521 client
->eg_ops
->remove_entry(entry
, client
);
525 write_unlock_irq(&client
->egress_lock
);
530 static void eg_destroy_cache(struct mpoa_client
*mpc
)
532 write_lock_irq(&mpc
->egress_lock
);
533 while(mpc
->eg_cache
!= NULL
)
534 mpc
->eg_ops
->remove_entry(mpc
->eg_cache
, mpc
);
535 write_unlock_irq(&mpc
->egress_lock
);
542 static struct in_cache_ops ingress_ops
= {
543 in_cache_add_entry
, /* add_entry */
544 in_cache_get
, /* get */
545 in_cache_get_with_mask
, /* get_with_mask */
546 in_cache_get_by_vcc
, /* get_by_vcc */
547 in_cache_put
, /* put */
548 in_cache_remove_entry
, /* remove_entry */
549 cache_hit
, /* cache_hit */
550 clear_count_and_expired
, /* clear_count */
551 check_resolving_entries
, /* check_resolving */
552 refresh_entries
, /* refresh */
553 in_destroy_cache
/* destroy_cache */
556 static struct eg_cache_ops egress_ops
= {
557 eg_cache_add_entry
, /* add_entry */
558 eg_cache_get_by_cache_id
, /* get_by_cache_id */
559 eg_cache_get_by_tag
, /* get_by_tag */
560 eg_cache_get_by_vcc
, /* get_by_vcc */
561 eg_cache_get_by_src_ip
, /* get_by_src_ip */
562 eg_cache_put
, /* put */
563 eg_cache_remove_entry
, /* remove_entry */
564 update_eg_cache_entry
, /* update */
565 clear_expired
, /* clear_expired */
566 eg_destroy_cache
/* destroy_cache */
570 void atm_mpoa_init_cache(struct mpoa_client
*mpc
)
572 mpc
->in_ops
= &ingress_ops
;
573 mpc
->eg_ops
= &egress_ops
;