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