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 
in_cache_get(uint32_t dst_ip,struct mpoa_client * client)25 static in_cache_entry *in_cache_get(uint32_t 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 
in_cache_get_with_mask(uint32_t dst_ip,struct mpoa_client * client,uint32_t mask)45 static in_cache_entry *in_cache_get_with_mask(uint32_t dst_ip,
46 					      struct mpoa_client *client,
47 					      uint32_t 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 
in_cache_get_by_vcc(struct atm_vcc * vcc,struct mpoa_client * client)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 
in_cache_add_entry(uint32_t dst_ip,struct mpoa_client * client)87 static in_cache_entry *in_cache_add_entry(uint32_t dst_ip,
88 					  struct mpoa_client *client)
89 {
90 	unsigned char *ip __attribute__ ((unused)) = (unsigned char *)&dst_ip;
91 	in_cache_entry* entry = kmalloc(sizeof(in_cache_entry), GFP_KERNEL);
92 
93 	if (entry == NULL) {
94 		printk("mpoa: mpoa_caches.c: new_in_cache_entry: out of memory\n");
95 		return NULL;
96 	}
97 
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));
100 
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;
105 	entry->prev = NULL;
106 	if (client->in_cache != NULL)
107 		client->in_cache->prev = entry;
108 	client->in_cache = entry;
109 
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;
114 	entry->count = 1;
115 	entry->entry_state = INGRESS_INVALID;
116 	entry->ctrl_info.holding_time = HOLDING_TIME_DEFAULT;
117 	atomic_inc(&entry->use);
118 
119 	write_unlock_bh(&client->ingress_lock);
120 	dprintk("mpoa: mpoa_caches.c: new_in_cache_entry: unlocked\n");
121 
122 	return entry;
123 }
124 
cache_hit(in_cache_entry * entry,struct mpoa_client * mpc)125 static int cache_hit(in_cache_entry *entry, struct mpoa_client *mpc)
126 {
127 	struct atm_mpoa_qos *qos;
128 	struct k_message msg;
129 
130 	entry->count++;
131 	if(entry->entry_state == INGRESS_RESOLVED && entry->shortcut != NULL)
132 		return OPEN;
133 
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;
144 		}
145 		if(entry->shortcut != NULL)
146 			return OPEN;
147 		return CLOSED;
148 	}
149 
150 	if(entry->entry_state == INGRESS_RESOLVING && entry->shortcut != NULL)
151 		return OPEN;
152 
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;
157 
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));
167 	}
168 
169 	return CLOSED;
170 }
171 
in_cache_put(in_cache_entry * entry)172 static void in_cache_put(in_cache_entry *entry)
173 {
174 	if (atomic_dec_and_test(&entry->use)) {
175 		memset(entry, 0, sizeof(in_cache_entry));
176 		kfree(entry);
177 	}
178 
179 	return;
180 }
181 
182 /*
183  * This should be called with write lock on
184  */
in_cache_remove_entry(in_cache_entry * entry,struct mpoa_client * client)185 static void in_cache_remove_entry(in_cache_entry *entry,
186 				  struct mpoa_client *client)
187 {
188 	struct atm_vcc *vcc;
189 	struct k_message msg;
190 	unsigned char *ip;
191 
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]);
195 
196 	if (entry->prev != NULL)
197 		entry->prev->next = entry->next;
198 	else
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);
206 	}
207 
208 	/* Check if the egress side still uses this VCC */
209 	if (vcc != NULL) {
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);
213 			return;
214 		}
215 		vcc_release_async(vcc, -EPIPE);
216 	}
217 
218 	return;
219 }
220 
221 
222 /* Call this every MPC-p2 seconds... Not exactly correct solution,
223    but an easy one... */
clear_count_and_expired(struct mpoa_client * client)224 static void clear_count_and_expired(struct mpoa_client *client)
225 {
226 	unsigned char *ip;
227 	in_cache_entry *entry, *next_entry;
228 	struct timeval now;
229 
230 	do_gettimeofday(&now);
231 
232 	write_lock_bh(&client->ingress_lock);
233 	entry = client->in_cache;
234 	while(entry != NULL){
235 		entry->count=0;
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);
242 		}
243 		entry = next_entry;
244 	}
245 	write_unlock_bh(&client->ingress_lock);
246 
247 	return;
248 }
249 
250 /* Call this every MPC-p4 seconds. */
check_resolving_entries(struct mpoa_client * client)251 static void check_resolving_entries(struct mpoa_client *client)
252 {
253 
254 	struct atm_mpoa_qos *qos;
255 	in_cache_entry *entry;
256 	struct timeval now;
257 	struct k_message msg;
258 
259 	do_gettimeofday( &now );
260 
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 */
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 				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;
276 					entry = entry->next;
277 					continue;
278 				}
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));
288 			}
289 		}
290 		entry = entry->next;
291 	}
292 	read_unlock_bh(&client->ingress_lock);
293 }
294 
295 /* Call this every MPC-p5 seconds. */
refresh_entries(struct mpoa_client * client)296 static void refresh_entries(struct mpoa_client *client)
297 {
298 	struct timeval now;
299 	struct in_cache_entry *entry = client->in_cache;
300 
301 	ddprintk("mpoa: mpoa_caches.c: refresh_entries\n");
302 	do_gettimeofday(&now);
303 
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;
312 
313 			}
314 		}
315 		entry = entry->next;
316 	}
317 	read_unlock_bh(&client->ingress_lock);
318 }
319 
in_destroy_cache(struct mpoa_client * mpc)320 static void in_destroy_cache(struct mpoa_client *mpc)
321 {
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);
326 
327 	return;
328 }
329 
eg_cache_get_by_cache_id(uint32_t cache_id,struct mpoa_client * mpc)330 static eg_cache_entry *eg_cache_get_by_cache_id(uint32_t cache_id, struct mpoa_client *mpc)
331 {
332 	eg_cache_entry *entry;
333 
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);
340 			return entry;
341 		}
342 		entry = entry->next;
343 	}
344 	read_unlock_irq(&mpc->egress_lock);
345 
346 	return NULL;
347 }
348 
349 /* This can be called from any context since it saves CPU flags */
eg_cache_get_by_tag(uint32_t tag,struct mpoa_client * mpc)350 static eg_cache_entry *eg_cache_get_by_tag(uint32_t tag, struct mpoa_client *mpc)
351 {
352 	unsigned long flags;
353 	eg_cache_entry *entry;
354 
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);
361 			return entry;
362 		}
363 		entry = entry->next;
364 	}
365 	read_unlock_irqrestore(&mpc->egress_lock, flags);
366 
367 	return NULL;
368 }
369 
370 /* This can be called from any context since it saves CPU flags */
eg_cache_get_by_vcc(struct atm_vcc * vcc,struct mpoa_client * mpc)371 static eg_cache_entry *eg_cache_get_by_vcc(struct atm_vcc *vcc, struct mpoa_client *mpc)
372 {
373 	unsigned long flags;
374 	eg_cache_entry *entry;
375 
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);
382 			return entry;
383 		}
384 		entry = entry->next;
385 	}
386 	read_unlock_irqrestore(&mpc->egress_lock, flags);
387 
388 	return NULL;
389 }
390 
eg_cache_get_by_src_ip(uint32_t ipaddr,struct mpoa_client * mpc)391 static eg_cache_entry *eg_cache_get_by_src_ip(uint32_t ipaddr, struct mpoa_client *mpc)
392 {
393 	eg_cache_entry *entry;
394 
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);
401 			return entry;
402 		}
403 		entry = entry->next;
404 	}
405 	read_unlock_irq(&mpc->egress_lock);
406 
407 	return NULL;
408 }
409 
eg_cache_put(eg_cache_entry * entry)410 static void eg_cache_put(eg_cache_entry *entry)
411 {
412 	if (atomic_dec_and_test(&entry->use)) {
413 		memset(entry, 0, sizeof(eg_cache_entry));
414 		kfree(entry);
415 	}
416 
417 	return;
418 }
419 
420 /*
421  * This should be called with write lock on
422  */
eg_cache_remove_entry(eg_cache_entry * entry,struct mpoa_client * client)423 static void eg_cache_remove_entry(eg_cache_entry *entry,
424 				  struct mpoa_client *client)
425 {
426 	struct atm_vcc *vcc;
427 	struct k_message msg;
428 
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;
433 	else
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);
441 	}
442 
443 	/* Check if the ingress side still uses this VCC */
444 	if (vcc != NULL) {
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);
448 			return;
449 		}
450 		vcc_release_async(vcc, -EPIPE);
451 	}
452 
453 	return;
454 }
455 
eg_cache_add_entry(struct k_message * msg,struct mpoa_client * client)456 static eg_cache_entry *eg_cache_add_entry(struct k_message *msg, struct mpoa_client *client)
457 {
458 	unsigned char *ip;
459 	eg_cache_entry *entry = kmalloc(sizeof(eg_cache_entry), GFP_KERNEL);
460 
461 	if (entry == NULL) {
462 		printk("mpoa: mpoa_caches.c: new_eg_cache_entry: out of memory\n");
463 		return NULL;
464 	}
465 
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));
469 
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;
474 	entry->prev = NULL;
475 	if (client->eg_cache != NULL)
476 		client->eg_cache->prev = entry;
477 	client->eg_cache = entry;
478 
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);
487 
488 	write_unlock_irq(&client->egress_lock);
489 	dprintk("mpoa: mpoa_caches.c: new_eg_cache_entry: unlocked\n");
490 
491 	return entry;
492 }
493 
update_eg_cache_entry(eg_cache_entry * entry,uint16_t holding_time)494 static void update_eg_cache_entry(eg_cache_entry * entry, uint16_t holding_time)
495 {
496 	do_gettimeofday(&(entry->tv));
497 	entry->entry_state = EGRESS_RESOLVED;
498 	entry->ctrl_info.holding_time = holding_time;
499 
500 	return;
501 }
502 
clear_expired(struct mpoa_client * client)503 static void clear_expired(struct mpoa_client *client)
504 {
505 	eg_cache_entry *entry, *next_entry;
506 	struct timeval now;
507 	struct k_message msg;
508 
509 	do_gettimeofday(&now);
510 
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);
522 		}
523 		entry = next_entry;
524 	}
525 	write_unlock_irq(&client->egress_lock);
526 
527 	return;
528 }
529 
eg_destroy_cache(struct mpoa_client * mpc)530 static void eg_destroy_cache(struct mpoa_client *mpc)
531 {
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);
536 
537 	return;
538 }
539 
540 
541 
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   */
554 };
555 
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    */
567 };
568 
569 
atm_mpoa_init_cache(struct mpoa_client * mpc)570 void atm_mpoa_init_cache(struct mpoa_client *mpc)
571 {
572 	mpc->in_ops = &ingress_ops;
573 	mpc->eg_ops = &egress_ops;
574 
575 	return;
576 }
577