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