xref: /openbmc/linux/net/atm/mpoa_caches.c (revision e00bdbef)
11da177e4SLinus Torvalds #include <linux/types.h>
21da177e4SLinus Torvalds #include <linux/atmmpc.h>
35a0e3ad6STejun Heo #include <linux/slab.h>
41da177e4SLinus Torvalds #include <linux/time.h>
51da177e4SLinus Torvalds 
61da177e4SLinus Torvalds #include "mpoa_caches.h"
71da177e4SLinus Torvalds #include "mpc.h"
81da177e4SLinus Torvalds 
91da177e4SLinus Torvalds /*
101da177e4SLinus Torvalds  * mpoa_caches.c: Implementation of ingress and egress cache
111da177e4SLinus Torvalds  * handling functions
121da177e4SLinus Torvalds  */
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds #if 0
15b50c2ea7SJoe Perches #define dprintk(format, args...)					\
16b50c2ea7SJoe Perches 	printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args)  /* debug */
171da177e4SLinus Torvalds #else
18bee67d34SJoe Perches #define dprintk(format, args...)					\
19b50c2ea7SJoe Perches 	do { if (0)							\
20b50c2ea7SJoe Perches 		printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args);\
21b50c2ea7SJoe Perches 	} while (0)
221da177e4SLinus Torvalds #endif
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds #if 0
25b50c2ea7SJoe Perches #define ddprintk(format, args...)					\
26b50c2ea7SJoe Perches 	printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args)  /* debug */
271da177e4SLinus Torvalds #else
28bee67d34SJoe Perches #define ddprintk(format, args...)					\
29b50c2ea7SJoe Perches 	do { if (0)							\
30b50c2ea7SJoe Perches 		printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args);\
31b50c2ea7SJoe Perches 	} while (0)
321da177e4SLinus Torvalds #endif
331da177e4SLinus Torvalds 
3430d492daSAl Viro static in_cache_entry *in_cache_get(__be32 dst_ip,
351da177e4SLinus Torvalds 				    struct mpoa_client *client)
361da177e4SLinus Torvalds {
371da177e4SLinus Torvalds 	in_cache_entry *entry;
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds 	read_lock_bh(&client->ingress_lock);
401da177e4SLinus Torvalds 	entry = client->in_cache;
411da177e4SLinus Torvalds 	while (entry != NULL) {
421da177e4SLinus Torvalds 		if (entry->ctrl_info.in_dst_ip == dst_ip) {
4393714912SReshetova, Elena 			refcount_inc(&entry->use);
441da177e4SLinus Torvalds 			read_unlock_bh(&client->ingress_lock);
451da177e4SLinus Torvalds 			return entry;
461da177e4SLinus Torvalds 		}
471da177e4SLinus Torvalds 		entry = entry->next;
481da177e4SLinus Torvalds 	}
491da177e4SLinus Torvalds 	read_unlock_bh(&client->ingress_lock);
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds 	return NULL;
521da177e4SLinus Torvalds }
531da177e4SLinus Torvalds 
5430d492daSAl Viro static in_cache_entry *in_cache_get_with_mask(__be32 dst_ip,
551da177e4SLinus Torvalds 					      struct mpoa_client *client,
5630d492daSAl Viro 					      __be32 mask)
571da177e4SLinus Torvalds {
581da177e4SLinus Torvalds 	in_cache_entry *entry;
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds 	read_lock_bh(&client->ingress_lock);
611da177e4SLinus Torvalds 	entry = client->in_cache;
621da177e4SLinus Torvalds 	while (entry != NULL) {
631da177e4SLinus Torvalds 		if ((entry->ctrl_info.in_dst_ip & mask) == (dst_ip & mask)) {
6493714912SReshetova, Elena 			refcount_inc(&entry->use);
651da177e4SLinus Torvalds 			read_unlock_bh(&client->ingress_lock);
661da177e4SLinus Torvalds 			return entry;
671da177e4SLinus Torvalds 		}
681da177e4SLinus Torvalds 		entry = entry->next;
691da177e4SLinus Torvalds 	}
701da177e4SLinus Torvalds 	read_unlock_bh(&client->ingress_lock);
711da177e4SLinus Torvalds 
721da177e4SLinus Torvalds 	return NULL;
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds }
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds static in_cache_entry *in_cache_get_by_vcc(struct atm_vcc *vcc,
771da177e4SLinus Torvalds 					   struct mpoa_client *client)
781da177e4SLinus Torvalds {
791da177e4SLinus Torvalds 	in_cache_entry *entry;
801da177e4SLinus Torvalds 
811da177e4SLinus Torvalds 	read_lock_bh(&client->ingress_lock);
821da177e4SLinus Torvalds 	entry = client->in_cache;
831da177e4SLinus Torvalds 	while (entry != NULL) {
841da177e4SLinus Torvalds 		if (entry->shortcut == vcc) {
8593714912SReshetova, Elena 			refcount_inc(&entry->use);
861da177e4SLinus Torvalds 			read_unlock_bh(&client->ingress_lock);
871da177e4SLinus Torvalds 			return entry;
881da177e4SLinus Torvalds 		}
891da177e4SLinus Torvalds 		entry = entry->next;
901da177e4SLinus Torvalds 	}
911da177e4SLinus Torvalds 	read_unlock_bh(&client->ingress_lock);
921da177e4SLinus Torvalds 
931da177e4SLinus Torvalds 	return NULL;
941da177e4SLinus Torvalds }
951da177e4SLinus Torvalds 
9630d492daSAl Viro static in_cache_entry *in_cache_add_entry(__be32 dst_ip,
971da177e4SLinus Torvalds 					  struct mpoa_client *client)
981da177e4SLinus Torvalds {
992afe37cdSArnaldo Carvalho de Melo 	in_cache_entry *entry = kzalloc(sizeof(in_cache_entry), GFP_KERNEL);
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds 	if (entry == NULL) {
102bee67d34SJoe Perches 		pr_info("mpoa: mpoa_caches.c: new_in_cache_entry: out of memory\n");
1031da177e4SLinus Torvalds 		return NULL;
1041da177e4SLinus Torvalds 	}
1051da177e4SLinus Torvalds 
106b50c2ea7SJoe Perches 	dprintk("adding an ingress entry, ip = %pI4\n", &dst_ip);
1071da177e4SLinus Torvalds 
10893714912SReshetova, Elena 	refcount_set(&entry->use, 1);
109b50c2ea7SJoe Perches 	dprintk("new_in_cache_entry: about to lock\n");
1101da177e4SLinus Torvalds 	write_lock_bh(&client->ingress_lock);
1111da177e4SLinus Torvalds 	entry->next = client->in_cache;
1121da177e4SLinus Torvalds 	entry->prev = NULL;
1131da177e4SLinus Torvalds 	if (client->in_cache != NULL)
1141da177e4SLinus Torvalds 		client->in_cache->prev = entry;
1151da177e4SLinus Torvalds 	client->in_cache = entry;
1161da177e4SLinus Torvalds 
1171da177e4SLinus Torvalds 	memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN);
1181da177e4SLinus Torvalds 	entry->ctrl_info.in_dst_ip = dst_ip;
1191da177e4SLinus Torvalds 	do_gettimeofday(&(entry->tv));
1201da177e4SLinus Torvalds 	entry->retry_time = client->parameters.mpc_p4;
1211da177e4SLinus Torvalds 	entry->count = 1;
1221da177e4SLinus Torvalds 	entry->entry_state = INGRESS_INVALID;
1231da177e4SLinus Torvalds 	entry->ctrl_info.holding_time = HOLDING_TIME_DEFAULT;
12493714912SReshetova, Elena 	refcount_inc(&entry->use);
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds 	write_unlock_bh(&client->ingress_lock);
127b50c2ea7SJoe Perches 	dprintk("new_in_cache_entry: unlocked\n");
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds 	return entry;
1301da177e4SLinus Torvalds }
1311da177e4SLinus Torvalds 
1321da177e4SLinus Torvalds static int cache_hit(in_cache_entry *entry, struct mpoa_client *mpc)
1331da177e4SLinus Torvalds {
1341da177e4SLinus Torvalds 	struct atm_mpoa_qos *qos;
1351da177e4SLinus Torvalds 	struct k_message msg;
1361da177e4SLinus Torvalds 
1371da177e4SLinus Torvalds 	entry->count++;
1381da177e4SLinus Torvalds 	if (entry->entry_state == INGRESS_RESOLVED && entry->shortcut != NULL)
1391da177e4SLinus Torvalds 		return OPEN;
1401da177e4SLinus Torvalds 
1411da177e4SLinus Torvalds 	if (entry->entry_state == INGRESS_REFRESHING) {
1421da177e4SLinus Torvalds 		if (entry->count > mpc->parameters.mpc_p1) {
1431da177e4SLinus Torvalds 			msg.type = SND_MPOA_RES_RQST;
1441da177e4SLinus Torvalds 			msg.content.in_info = entry->ctrl_info;
1451da177e4SLinus Torvalds 			memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN);
1461da177e4SLinus Torvalds 			qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip);
147bee67d34SJoe Perches 			if (qos != NULL)
148bee67d34SJoe Perches 				msg.qos = qos->qos;
1491da177e4SLinus Torvalds 			msg_to_mpoad(&msg, mpc);
1501da177e4SLinus Torvalds 			do_gettimeofday(&(entry->reply_wait));
1511da177e4SLinus Torvalds 			entry->entry_state = INGRESS_RESOLVING;
1521da177e4SLinus Torvalds 		}
1531da177e4SLinus Torvalds 		if (entry->shortcut != NULL)
1541da177e4SLinus Torvalds 			return OPEN;
1551da177e4SLinus Torvalds 		return CLOSED;
1561da177e4SLinus Torvalds 	}
1571da177e4SLinus Torvalds 
1581da177e4SLinus Torvalds 	if (entry->entry_state == INGRESS_RESOLVING && entry->shortcut != NULL)
1591da177e4SLinus Torvalds 		return OPEN;
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds 	if (entry->count > mpc->parameters.mpc_p1 &&
1621da177e4SLinus Torvalds 	    entry->entry_state == INGRESS_INVALID) {
163b50c2ea7SJoe Perches 		dprintk("(%s) threshold exceeded for ip %pI4, sending MPOA res req\n",
16421454aaaSHarvey Harrison 			mpc->dev->name, &entry->ctrl_info.in_dst_ip);
1651da177e4SLinus Torvalds 		entry->entry_state = INGRESS_RESOLVING;
1661da177e4SLinus Torvalds 		msg.type = SND_MPOA_RES_RQST;
1671da177e4SLinus Torvalds 		memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN);
1681da177e4SLinus Torvalds 		msg.content.in_info = entry->ctrl_info;
1691da177e4SLinus Torvalds 		qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip);
170bee67d34SJoe Perches 		if (qos != NULL)
171bee67d34SJoe Perches 			msg.qos = qos->qos;
1721da177e4SLinus Torvalds 		msg_to_mpoad(&msg, mpc);
1731da177e4SLinus Torvalds 		do_gettimeofday(&(entry->reply_wait));
1741da177e4SLinus Torvalds 	}
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds 	return CLOSED;
1771da177e4SLinus Torvalds }
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds static void in_cache_put(in_cache_entry *entry)
1801da177e4SLinus Torvalds {
18193714912SReshetova, Elena 	if (refcount_dec_and_test(&entry->use)) {
1821da177e4SLinus Torvalds 		memset(entry, 0, sizeof(in_cache_entry));
1831da177e4SLinus Torvalds 		kfree(entry);
1841da177e4SLinus Torvalds 	}
1851da177e4SLinus Torvalds }
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds /*
1881da177e4SLinus Torvalds  * This should be called with write lock on
1891da177e4SLinus Torvalds  */
1901da177e4SLinus Torvalds static void in_cache_remove_entry(in_cache_entry *entry,
1911da177e4SLinus Torvalds 				  struct mpoa_client *client)
1921da177e4SLinus Torvalds {
1931da177e4SLinus Torvalds 	struct atm_vcc *vcc;
1941da177e4SLinus Torvalds 	struct k_message msg;
1951da177e4SLinus Torvalds 
1961da177e4SLinus Torvalds 	vcc = entry->shortcut;
197b50c2ea7SJoe Perches 	dprintk("removing an ingress entry, ip = %pI4\n",
19821454aaaSHarvey Harrison 		&entry->ctrl_info.in_dst_ip);
1991da177e4SLinus Torvalds 
2001da177e4SLinus Torvalds 	if (entry->prev != NULL)
2011da177e4SLinus Torvalds 		entry->prev->next = entry->next;
2021da177e4SLinus Torvalds 	else
2031da177e4SLinus Torvalds 		client->in_cache = entry->next;
2041da177e4SLinus Torvalds 	if (entry->next != NULL)
2051da177e4SLinus Torvalds 		entry->next->prev = entry->prev;
2061da177e4SLinus Torvalds 	client->in_ops->put(entry);
2071da177e4SLinus Torvalds 	if (client->in_cache == NULL && client->eg_cache == NULL) {
2081da177e4SLinus Torvalds 		msg.type = STOP_KEEP_ALIVE_SM;
2091da177e4SLinus Torvalds 		msg_to_mpoad(&msg, client);
2101da177e4SLinus Torvalds 	}
2111da177e4SLinus Torvalds 
2121da177e4SLinus Torvalds 	/* Check if the egress side still uses this VCC */
2131da177e4SLinus Torvalds 	if (vcc != NULL) {
214bee67d34SJoe Perches 		eg_cache_entry *eg_entry = client->eg_ops->get_by_vcc(vcc,
215bee67d34SJoe Perches 								      client);
2161da177e4SLinus Torvalds 		if (eg_entry != NULL) {
2171da177e4SLinus Torvalds 			client->eg_ops->put(eg_entry);
2181da177e4SLinus Torvalds 			return;
2191da177e4SLinus Torvalds 		}
2201da177e4SLinus Torvalds 		vcc_release_async(vcc, -EPIPE);
2211da177e4SLinus Torvalds 	}
2221da177e4SLinus Torvalds }
2231da177e4SLinus Torvalds 
2241da177e4SLinus Torvalds /* Call this every MPC-p2 seconds... Not exactly correct solution,
2251da177e4SLinus Torvalds    but an easy one... */
2261da177e4SLinus Torvalds static void clear_count_and_expired(struct mpoa_client *client)
2271da177e4SLinus Torvalds {
2281da177e4SLinus Torvalds 	in_cache_entry *entry, *next_entry;
2291da177e4SLinus Torvalds 	struct timeval now;
2301da177e4SLinus Torvalds 
2311da177e4SLinus Torvalds 	do_gettimeofday(&now);
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds 	write_lock_bh(&client->ingress_lock);
2341da177e4SLinus Torvalds 	entry = client->in_cache;
2351da177e4SLinus Torvalds 	while (entry != NULL) {
2361da177e4SLinus Torvalds 		entry->count = 0;
2371da177e4SLinus Torvalds 		next_entry = entry->next;
2381da177e4SLinus Torvalds 		if ((now.tv_sec - entry->tv.tv_sec)
2391da177e4SLinus Torvalds 		   > entry->ctrl_info.holding_time) {
240b50c2ea7SJoe Perches 			dprintk("holding time expired, ip = %pI4\n",
24121454aaaSHarvey Harrison 				&entry->ctrl_info.in_dst_ip);
2421da177e4SLinus Torvalds 			client->in_ops->remove_entry(entry, client);
2431da177e4SLinus Torvalds 		}
2441da177e4SLinus Torvalds 		entry = next_entry;
2451da177e4SLinus Torvalds 	}
2461da177e4SLinus Torvalds 	write_unlock_bh(&client->ingress_lock);
2471da177e4SLinus Torvalds }
2481da177e4SLinus Torvalds 
2491da177e4SLinus Torvalds /* Call this every MPC-p4 seconds. */
2501da177e4SLinus Torvalds static void check_resolving_entries(struct mpoa_client *client)
2511da177e4SLinus Torvalds {
2521da177e4SLinus Torvalds 
2531da177e4SLinus Torvalds 	struct atm_mpoa_qos *qos;
2541da177e4SLinus Torvalds 	in_cache_entry *entry;
2551da177e4SLinus Torvalds 	struct timeval now;
2561da177e4SLinus Torvalds 	struct k_message msg;
2571da177e4SLinus Torvalds 
2581da177e4SLinus Torvalds 	do_gettimeofday(&now);
2591da177e4SLinus Torvalds 
2601da177e4SLinus Torvalds 	read_lock_bh(&client->ingress_lock);
2611da177e4SLinus Torvalds 	entry = client->in_cache;
2621da177e4SLinus Torvalds 	while (entry != NULL) {
2631da177e4SLinus Torvalds 		if (entry->entry_state == INGRESS_RESOLVING) {
264bee67d34SJoe Perches 			if ((now.tv_sec - entry->hold_down.tv_sec) <
265bee67d34SJoe Perches 			    client->parameters.mpc_p6) {
2661da177e4SLinus Torvalds 				entry = entry->next;	/* Entry in hold down */
2671da177e4SLinus Torvalds 				continue;
2681da177e4SLinus Torvalds 			}
2691da177e4SLinus Torvalds 			if ((now.tv_sec - entry->reply_wait.tv_sec) >
2701da177e4SLinus Torvalds 			    entry->retry_time) {
2711da177e4SLinus Torvalds 				entry->retry_time = MPC_C1 * (entry->retry_time);
272bee67d34SJoe Perches 				/*
273bee67d34SJoe Perches 				 * Retry time maximum exceeded,
274bee67d34SJoe Perches 				 * put entry in hold down.
275bee67d34SJoe Perches 				 */
2761da177e4SLinus Torvalds 				if (entry->retry_time > client->parameters.mpc_p5) {
2771da177e4SLinus Torvalds 					do_gettimeofday(&(entry->hold_down));
2781da177e4SLinus Torvalds 					entry->retry_time = client->parameters.mpc_p4;
2791da177e4SLinus Torvalds 					entry = entry->next;
2801da177e4SLinus Torvalds 					continue;
2811da177e4SLinus Torvalds 				}
2821da177e4SLinus Torvalds 				/* Ask daemon to send a resolution request. */
2831da177e4SLinus Torvalds 				memset(&(entry->hold_down), 0, sizeof(struct timeval));
2841da177e4SLinus Torvalds 				msg.type = SND_MPOA_RES_RTRY;
2851da177e4SLinus Torvalds 				memcpy(msg.MPS_ctrl, client->mps_ctrl_addr, ATM_ESA_LEN);
2861da177e4SLinus Torvalds 				msg.content.in_info = entry->ctrl_info;
2871da177e4SLinus Torvalds 				qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip);
288bee67d34SJoe Perches 				if (qos != NULL)
289bee67d34SJoe Perches 					msg.qos = qos->qos;
2901da177e4SLinus Torvalds 				msg_to_mpoad(&msg, client);
2911da177e4SLinus Torvalds 				do_gettimeofday(&(entry->reply_wait));
2921da177e4SLinus Torvalds 			}
2931da177e4SLinus Torvalds 		}
2941da177e4SLinus Torvalds 		entry = entry->next;
2951da177e4SLinus Torvalds 	}
2961da177e4SLinus Torvalds 	read_unlock_bh(&client->ingress_lock);
2971da177e4SLinus Torvalds }
2981da177e4SLinus Torvalds 
2991da177e4SLinus Torvalds /* Call this every MPC-p5 seconds. */
3001da177e4SLinus Torvalds static void refresh_entries(struct mpoa_client *client)
3011da177e4SLinus Torvalds {
3021da177e4SLinus Torvalds 	struct timeval now;
3031da177e4SLinus Torvalds 	struct in_cache_entry *entry = client->in_cache;
3041da177e4SLinus Torvalds 
305b50c2ea7SJoe Perches 	ddprintk("refresh_entries\n");
3061da177e4SLinus Torvalds 	do_gettimeofday(&now);
3071da177e4SLinus Torvalds 
3081da177e4SLinus Torvalds 	read_lock_bh(&client->ingress_lock);
3091da177e4SLinus Torvalds 	while (entry != NULL) {
3101da177e4SLinus Torvalds 		if (entry->entry_state == INGRESS_RESOLVED) {
3111da177e4SLinus Torvalds 			if (!(entry->refresh_time))
3121da177e4SLinus Torvalds 				entry->refresh_time = (2 * (entry->ctrl_info.holding_time))/3;
313bee67d34SJoe Perches 			if ((now.tv_sec - entry->reply_wait.tv_sec) >
314bee67d34SJoe Perches 			    entry->refresh_time) {
315b50c2ea7SJoe Perches 				dprintk("refreshing an entry.\n");
3161da177e4SLinus Torvalds 				entry->entry_state = INGRESS_REFRESHING;
3171da177e4SLinus Torvalds 
3181da177e4SLinus Torvalds 			}
3191da177e4SLinus Torvalds 		}
3201da177e4SLinus Torvalds 		entry = entry->next;
3211da177e4SLinus Torvalds 	}
3221da177e4SLinus Torvalds 	read_unlock_bh(&client->ingress_lock);
3231da177e4SLinus Torvalds }
3241da177e4SLinus Torvalds 
3251da177e4SLinus Torvalds static void in_destroy_cache(struct mpoa_client *mpc)
3261da177e4SLinus Torvalds {
3271da177e4SLinus Torvalds 	write_lock_irq(&mpc->ingress_lock);
3281da177e4SLinus Torvalds 	while (mpc->in_cache != NULL)
3291da177e4SLinus Torvalds 		mpc->in_ops->remove_entry(mpc->in_cache, mpc);
3301da177e4SLinus Torvalds 	write_unlock_irq(&mpc->ingress_lock);
3311da177e4SLinus Torvalds }
3321da177e4SLinus Torvalds 
333bee67d34SJoe Perches static eg_cache_entry *eg_cache_get_by_cache_id(__be32 cache_id,
334bee67d34SJoe Perches 						struct mpoa_client *mpc)
3351da177e4SLinus Torvalds {
3361da177e4SLinus Torvalds 	eg_cache_entry *entry;
3371da177e4SLinus Torvalds 
3381da177e4SLinus Torvalds 	read_lock_irq(&mpc->egress_lock);
3391da177e4SLinus Torvalds 	entry = mpc->eg_cache;
3401da177e4SLinus Torvalds 	while (entry != NULL) {
3411da177e4SLinus Torvalds 		if (entry->ctrl_info.cache_id == cache_id) {
342e00bdbefSReshetova, Elena 			refcount_inc(&entry->use);
3431da177e4SLinus Torvalds 			read_unlock_irq(&mpc->egress_lock);
3441da177e4SLinus Torvalds 			return entry;
3451da177e4SLinus Torvalds 		}
3461da177e4SLinus Torvalds 		entry = entry->next;
3471da177e4SLinus Torvalds 	}
3481da177e4SLinus Torvalds 	read_unlock_irq(&mpc->egress_lock);
3491da177e4SLinus Torvalds 
3501da177e4SLinus Torvalds 	return NULL;
3511da177e4SLinus Torvalds }
3521da177e4SLinus Torvalds 
3531da177e4SLinus Torvalds /* This can be called from any context since it saves CPU flags */
35430d492daSAl Viro static eg_cache_entry *eg_cache_get_by_tag(__be32 tag, struct mpoa_client *mpc)
3551da177e4SLinus Torvalds {
3561da177e4SLinus Torvalds 	unsigned long flags;
3571da177e4SLinus Torvalds 	eg_cache_entry *entry;
3581da177e4SLinus Torvalds 
3591da177e4SLinus Torvalds 	read_lock_irqsave(&mpc->egress_lock, flags);
3601da177e4SLinus Torvalds 	entry = mpc->eg_cache;
3611da177e4SLinus Torvalds 	while (entry != NULL) {
3621da177e4SLinus Torvalds 		if (entry->ctrl_info.tag == tag) {
363e00bdbefSReshetova, Elena 			refcount_inc(&entry->use);
3641da177e4SLinus Torvalds 			read_unlock_irqrestore(&mpc->egress_lock, flags);
3651da177e4SLinus Torvalds 			return entry;
3661da177e4SLinus Torvalds 		}
3671da177e4SLinus Torvalds 		entry = entry->next;
3681da177e4SLinus Torvalds 	}
3691da177e4SLinus Torvalds 	read_unlock_irqrestore(&mpc->egress_lock, flags);
3701da177e4SLinus Torvalds 
3711da177e4SLinus Torvalds 	return NULL;
3721da177e4SLinus Torvalds }
3731da177e4SLinus Torvalds 
3741da177e4SLinus Torvalds /* This can be called from any context since it saves CPU flags */
375bee67d34SJoe Perches static eg_cache_entry *eg_cache_get_by_vcc(struct atm_vcc *vcc,
376bee67d34SJoe Perches 					   struct mpoa_client *mpc)
3771da177e4SLinus Torvalds {
3781da177e4SLinus Torvalds 	unsigned long flags;
3791da177e4SLinus Torvalds 	eg_cache_entry *entry;
3801da177e4SLinus Torvalds 
3811da177e4SLinus Torvalds 	read_lock_irqsave(&mpc->egress_lock, flags);
3821da177e4SLinus Torvalds 	entry = mpc->eg_cache;
3831da177e4SLinus Torvalds 	while (entry != NULL) {
3841da177e4SLinus Torvalds 		if (entry->shortcut == vcc) {
385e00bdbefSReshetova, Elena 			refcount_inc(&entry->use);
3861da177e4SLinus Torvalds 			read_unlock_irqrestore(&mpc->egress_lock, flags);
3871da177e4SLinus Torvalds 			return entry;
3881da177e4SLinus Torvalds 		}
3891da177e4SLinus Torvalds 		entry = entry->next;
3901da177e4SLinus Torvalds 	}
3911da177e4SLinus Torvalds 	read_unlock_irqrestore(&mpc->egress_lock, flags);
3921da177e4SLinus Torvalds 
3931da177e4SLinus Torvalds 	return NULL;
3941da177e4SLinus Torvalds }
3951da177e4SLinus Torvalds 
396bee67d34SJoe Perches static eg_cache_entry *eg_cache_get_by_src_ip(__be32 ipaddr,
397bee67d34SJoe Perches 					      struct mpoa_client *mpc)
3981da177e4SLinus Torvalds {
3991da177e4SLinus Torvalds 	eg_cache_entry *entry;
4001da177e4SLinus Torvalds 
4011da177e4SLinus Torvalds 	read_lock_irq(&mpc->egress_lock);
4021da177e4SLinus Torvalds 	entry = mpc->eg_cache;
4031da177e4SLinus Torvalds 	while (entry != NULL) {
4041da177e4SLinus Torvalds 		if (entry->latest_ip_addr == ipaddr) {
405e00bdbefSReshetova, Elena 			refcount_inc(&entry->use);
4061da177e4SLinus Torvalds 			read_unlock_irq(&mpc->egress_lock);
4071da177e4SLinus Torvalds 			return entry;
4081da177e4SLinus Torvalds 		}
4091da177e4SLinus Torvalds 		entry = entry->next;
4101da177e4SLinus Torvalds 	}
4111da177e4SLinus Torvalds 	read_unlock_irq(&mpc->egress_lock);
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds 	return NULL;
4141da177e4SLinus Torvalds }
4151da177e4SLinus Torvalds 
4161da177e4SLinus Torvalds static void eg_cache_put(eg_cache_entry *entry)
4171da177e4SLinus Torvalds {
418e00bdbefSReshetova, Elena 	if (refcount_dec_and_test(&entry->use)) {
4191da177e4SLinus Torvalds 		memset(entry, 0, sizeof(eg_cache_entry));
4201da177e4SLinus Torvalds 		kfree(entry);
4211da177e4SLinus Torvalds 	}
4221da177e4SLinus Torvalds }
4231da177e4SLinus Torvalds 
4241da177e4SLinus Torvalds /*
4251da177e4SLinus Torvalds  * This should be called with write lock on
4261da177e4SLinus Torvalds  */
4271da177e4SLinus Torvalds static void eg_cache_remove_entry(eg_cache_entry *entry,
4281da177e4SLinus Torvalds 				  struct mpoa_client *client)
4291da177e4SLinus Torvalds {
4301da177e4SLinus Torvalds 	struct atm_vcc *vcc;
4311da177e4SLinus Torvalds 	struct k_message msg;
4321da177e4SLinus Torvalds 
4331da177e4SLinus Torvalds 	vcc = entry->shortcut;
434b50c2ea7SJoe Perches 	dprintk("removing an egress entry.\n");
4351da177e4SLinus Torvalds 	if (entry->prev != NULL)
4361da177e4SLinus Torvalds 		entry->prev->next = entry->next;
4371da177e4SLinus Torvalds 	else
4381da177e4SLinus Torvalds 		client->eg_cache = entry->next;
4391da177e4SLinus Torvalds 	if (entry->next != NULL)
4401da177e4SLinus Torvalds 		entry->next->prev = entry->prev;
4411da177e4SLinus Torvalds 	client->eg_ops->put(entry);
4421da177e4SLinus Torvalds 	if (client->in_cache == NULL && client->eg_cache == NULL) {
4431da177e4SLinus Torvalds 		msg.type = STOP_KEEP_ALIVE_SM;
4441da177e4SLinus Torvalds 		msg_to_mpoad(&msg, client);
4451da177e4SLinus Torvalds 	}
4461da177e4SLinus Torvalds 
4471da177e4SLinus Torvalds 	/* Check if the ingress side still uses this VCC */
4481da177e4SLinus Torvalds 	if (vcc != NULL) {
4491da177e4SLinus Torvalds 		in_cache_entry *in_entry = client->in_ops->get_by_vcc(vcc, client);
4501da177e4SLinus Torvalds 		if (in_entry != NULL) {
4511da177e4SLinus Torvalds 			client->in_ops->put(in_entry);
4521da177e4SLinus Torvalds 			return;
4531da177e4SLinus Torvalds 		}
4541da177e4SLinus Torvalds 		vcc_release_async(vcc, -EPIPE);
4551da177e4SLinus Torvalds 	}
4561da177e4SLinus Torvalds }
4571da177e4SLinus Torvalds 
458bee67d34SJoe Perches static eg_cache_entry *eg_cache_add_entry(struct k_message *msg,
459bee67d34SJoe Perches 					  struct mpoa_client *client)
4601da177e4SLinus Torvalds {
4612afe37cdSArnaldo Carvalho de Melo 	eg_cache_entry *entry = kzalloc(sizeof(eg_cache_entry), GFP_KERNEL);
4621da177e4SLinus Torvalds 
4631da177e4SLinus Torvalds 	if (entry == NULL) {
464bee67d34SJoe Perches 		pr_info("out of memory\n");
4651da177e4SLinus Torvalds 		return NULL;
4661da177e4SLinus Torvalds 	}
4671da177e4SLinus Torvalds 
468b50c2ea7SJoe Perches 	dprintk("adding an egress entry, ip = %pI4, this should be our IP\n",
46921454aaaSHarvey Harrison 		&msg->content.eg_info.eg_dst_ip);
4701da177e4SLinus Torvalds 
471e00bdbefSReshetova, Elena 	refcount_set(&entry->use, 1);
472b50c2ea7SJoe Perches 	dprintk("new_eg_cache_entry: about to lock\n");
4731da177e4SLinus Torvalds 	write_lock_irq(&client->egress_lock);
4741da177e4SLinus Torvalds 	entry->next = client->eg_cache;
4751da177e4SLinus Torvalds 	entry->prev = NULL;
4761da177e4SLinus Torvalds 	if (client->eg_cache != NULL)
4771da177e4SLinus Torvalds 		client->eg_cache->prev = entry;
4781da177e4SLinus Torvalds 	client->eg_cache = entry;
4791da177e4SLinus Torvalds 
4801da177e4SLinus Torvalds 	memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN);
4811da177e4SLinus Torvalds 	entry->ctrl_info = msg->content.eg_info;
4821da177e4SLinus Torvalds 	do_gettimeofday(&(entry->tv));
4831da177e4SLinus Torvalds 	entry->entry_state = EGRESS_RESOLVED;
484b50c2ea7SJoe Perches 	dprintk("new_eg_cache_entry cache_id %u\n",
485bee67d34SJoe Perches 		ntohl(entry->ctrl_info.cache_id));
486b50c2ea7SJoe Perches 	dprintk("mps_ip = %pI4\n", &entry->ctrl_info.mps_ip);
487e00bdbefSReshetova, Elena 	refcount_inc(&entry->use);
4881da177e4SLinus Torvalds 
4891da177e4SLinus Torvalds 	write_unlock_irq(&client->egress_lock);
490b50c2ea7SJoe Perches 	dprintk("new_eg_cache_entry: unlocked\n");
4911da177e4SLinus Torvalds 
4921da177e4SLinus Torvalds 	return entry;
4931da177e4SLinus Torvalds }
4941da177e4SLinus Torvalds 
4951da177e4SLinus Torvalds static void update_eg_cache_entry(eg_cache_entry *entry, uint16_t holding_time)
4961da177e4SLinus Torvalds {
4971da177e4SLinus Torvalds 	do_gettimeofday(&(entry->tv));
4981da177e4SLinus Torvalds 	entry->entry_state = EGRESS_RESOLVED;
4991da177e4SLinus Torvalds 	entry->ctrl_info.holding_time = holding_time;
5001da177e4SLinus Torvalds }
5011da177e4SLinus Torvalds 
5021da177e4SLinus Torvalds static void clear_expired(struct mpoa_client *client)
5031da177e4SLinus Torvalds {
5041da177e4SLinus Torvalds 	eg_cache_entry *entry, *next_entry;
5051da177e4SLinus Torvalds 	struct timeval now;
5061da177e4SLinus Torvalds 	struct k_message msg;
5071da177e4SLinus Torvalds 
5081da177e4SLinus Torvalds 	do_gettimeofday(&now);
5091da177e4SLinus Torvalds 
5101da177e4SLinus Torvalds 	write_lock_irq(&client->egress_lock);
5111da177e4SLinus Torvalds 	entry = client->eg_cache;
5121da177e4SLinus Torvalds 	while (entry != NULL) {
5131da177e4SLinus Torvalds 		next_entry = entry->next;
5141da177e4SLinus Torvalds 		if ((now.tv_sec - entry->tv.tv_sec)
5151da177e4SLinus Torvalds 		   > entry->ctrl_info.holding_time) {
5161da177e4SLinus Torvalds 			msg.type = SND_EGRESS_PURGE;
5171da177e4SLinus Torvalds 			msg.content.eg_info = entry->ctrl_info;
518b50c2ea7SJoe Perches 			dprintk("egress_cache: holding time expired, cache_id = %u.\n",
519bee67d34SJoe Perches 				ntohl(entry->ctrl_info.cache_id));
5201da177e4SLinus Torvalds 			msg_to_mpoad(&msg, client);
5211da177e4SLinus Torvalds 			client->eg_ops->remove_entry(entry, client);
5221da177e4SLinus Torvalds 		}
5231da177e4SLinus Torvalds 		entry = next_entry;
5241da177e4SLinus Torvalds 	}
5251da177e4SLinus Torvalds 	write_unlock_irq(&client->egress_lock);
5261da177e4SLinus Torvalds }
5271da177e4SLinus Torvalds 
5281da177e4SLinus Torvalds static void eg_destroy_cache(struct mpoa_client *mpc)
5291da177e4SLinus Torvalds {
5301da177e4SLinus Torvalds 	write_lock_irq(&mpc->egress_lock);
5311da177e4SLinus Torvalds 	while (mpc->eg_cache != NULL)
5321da177e4SLinus Torvalds 		mpc->eg_ops->remove_entry(mpc->eg_cache, mpc);
5331da177e4SLinus Torvalds 	write_unlock_irq(&mpc->egress_lock);
5341da177e4SLinus Torvalds }
5351da177e4SLinus Torvalds 
5361da177e4SLinus Torvalds 
5374dd191bbSJulia Lawall static const struct in_cache_ops ingress_ops = {
53899a5e178SKees Cook 	.add_entry = in_cache_add_entry,
53999a5e178SKees Cook 	.get = in_cache_get,
54099a5e178SKees Cook 	.get_with_mask = in_cache_get_with_mask,
54199a5e178SKees Cook 	.get_by_vcc = in_cache_get_by_vcc,
54299a5e178SKees Cook 	.put = in_cache_put,
54399a5e178SKees Cook 	.remove_entry = in_cache_remove_entry,
54499a5e178SKees Cook 	.cache_hit = cache_hit,
54599a5e178SKees Cook 	.clear_count = clear_count_and_expired,
54699a5e178SKees Cook 	.check_resolving = check_resolving_entries,
54799a5e178SKees Cook 	.refresh = refresh_entries,
54899a5e178SKees Cook 	.destroy_cache = in_destroy_cache
5491da177e4SLinus Torvalds };
5501da177e4SLinus Torvalds 
5514dd191bbSJulia Lawall static const struct eg_cache_ops egress_ops = {
55299a5e178SKees Cook 	.add_entry = eg_cache_add_entry,
55399a5e178SKees Cook 	.get_by_cache_id = eg_cache_get_by_cache_id,
55499a5e178SKees Cook 	.get_by_tag = eg_cache_get_by_tag,
55599a5e178SKees Cook 	.get_by_vcc = eg_cache_get_by_vcc,
55699a5e178SKees Cook 	.get_by_src_ip = eg_cache_get_by_src_ip,
55799a5e178SKees Cook 	.put = eg_cache_put,
55899a5e178SKees Cook 	.remove_entry = eg_cache_remove_entry,
55999a5e178SKees Cook 	.update = update_eg_cache_entry,
56099a5e178SKees Cook 	.clear_expired = clear_expired,
56199a5e178SKees Cook 	.destroy_cache = eg_destroy_cache
5621da177e4SLinus Torvalds };
5631da177e4SLinus Torvalds 
5641da177e4SLinus Torvalds void atm_mpoa_init_cache(struct mpoa_client *mpc)
5651da177e4SLinus Torvalds {
5661da177e4SLinus Torvalds 	mpc->in_ops = &ingress_ops;
5671da177e4SLinus Torvalds 	mpc->eg_ops = &egress_ops;
5681da177e4SLinus Torvalds }
569