xref: /openbmc/linux/net/atm/mpoa_caches.c (revision 453431a5)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds #include <linux/types.h>
31da177e4SLinus Torvalds #include <linux/atmmpc.h>
45a0e3ad6STejun Heo #include <linux/slab.h>
51da177e4SLinus Torvalds #include <linux/time.h>
61da177e4SLinus Torvalds 
71da177e4SLinus Torvalds #include "mpoa_caches.h"
81da177e4SLinus Torvalds #include "mpc.h"
91da177e4SLinus Torvalds 
101da177e4SLinus Torvalds /*
111da177e4SLinus Torvalds  * mpoa_caches.c: Implementation of ingress and egress cache
121da177e4SLinus Torvalds  * handling functions
131da177e4SLinus Torvalds  */
141da177e4SLinus Torvalds 
151da177e4SLinus Torvalds #if 0
16b50c2ea7SJoe Perches #define dprintk(format, args...)					\
17b50c2ea7SJoe Perches 	printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args)  /* debug */
181da177e4SLinus Torvalds #else
19bee67d34SJoe Perches #define dprintk(format, args...)					\
20b50c2ea7SJoe Perches 	do { if (0)							\
21b50c2ea7SJoe Perches 		printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args);\
22b50c2ea7SJoe Perches 	} while (0)
231da177e4SLinus Torvalds #endif
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds #if 0
26b50c2ea7SJoe Perches #define ddprintk(format, args...)					\
27b50c2ea7SJoe Perches 	printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args)  /* debug */
281da177e4SLinus Torvalds #else
29bee67d34SJoe Perches #define ddprintk(format, args...)					\
30b50c2ea7SJoe Perches 	do { if (0)							\
31b50c2ea7SJoe Perches 		printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args);\
32b50c2ea7SJoe Perches 	} while (0)
331da177e4SLinus Torvalds #endif
341da177e4SLinus Torvalds 
in_cache_get(__be32 dst_ip,struct mpoa_client * client)3530d492daSAl Viro static in_cache_entry *in_cache_get(__be32 dst_ip,
361da177e4SLinus Torvalds 				    struct mpoa_client *client)
371da177e4SLinus Torvalds {
381da177e4SLinus Torvalds 	in_cache_entry *entry;
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds 	read_lock_bh(&client->ingress_lock);
411da177e4SLinus Torvalds 	entry = client->in_cache;
421da177e4SLinus Torvalds 	while (entry != NULL) {
431da177e4SLinus Torvalds 		if (entry->ctrl_info.in_dst_ip == dst_ip) {
4493714912SReshetova, Elena 			refcount_inc(&entry->use);
451da177e4SLinus Torvalds 			read_unlock_bh(&client->ingress_lock);
461da177e4SLinus Torvalds 			return entry;
471da177e4SLinus Torvalds 		}
481da177e4SLinus Torvalds 		entry = entry->next;
491da177e4SLinus Torvalds 	}
501da177e4SLinus Torvalds 	read_unlock_bh(&client->ingress_lock);
511da177e4SLinus Torvalds 
521da177e4SLinus Torvalds 	return NULL;
531da177e4SLinus Torvalds }
541da177e4SLinus Torvalds 
in_cache_get_with_mask(__be32 dst_ip,struct mpoa_client * client,__be32 mask)5530d492daSAl Viro static in_cache_entry *in_cache_get_with_mask(__be32 dst_ip,
561da177e4SLinus Torvalds 					      struct mpoa_client *client,
5730d492daSAl Viro 					      __be32 mask)
581da177e4SLinus Torvalds {
591da177e4SLinus Torvalds 	in_cache_entry *entry;
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds 	read_lock_bh(&client->ingress_lock);
621da177e4SLinus Torvalds 	entry = client->in_cache;
631da177e4SLinus Torvalds 	while (entry != NULL) {
641da177e4SLinus Torvalds 		if ((entry->ctrl_info.in_dst_ip & mask) == (dst_ip & mask)) {
6593714912SReshetova, Elena 			refcount_inc(&entry->use);
661da177e4SLinus Torvalds 			read_unlock_bh(&client->ingress_lock);
671da177e4SLinus Torvalds 			return entry;
681da177e4SLinus Torvalds 		}
691da177e4SLinus Torvalds 		entry = entry->next;
701da177e4SLinus Torvalds 	}
711da177e4SLinus Torvalds 	read_unlock_bh(&client->ingress_lock);
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds 	return NULL;
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds }
761da177e4SLinus Torvalds 
in_cache_get_by_vcc(struct atm_vcc * vcc,struct mpoa_client * client)771da177e4SLinus Torvalds static in_cache_entry *in_cache_get_by_vcc(struct atm_vcc *vcc,
781da177e4SLinus Torvalds 					   struct mpoa_client *client)
791da177e4SLinus Torvalds {
801da177e4SLinus Torvalds 	in_cache_entry *entry;
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds 	read_lock_bh(&client->ingress_lock);
831da177e4SLinus Torvalds 	entry = client->in_cache;
841da177e4SLinus Torvalds 	while (entry != NULL) {
851da177e4SLinus Torvalds 		if (entry->shortcut == vcc) {
8693714912SReshetova, Elena 			refcount_inc(&entry->use);
871da177e4SLinus Torvalds 			read_unlock_bh(&client->ingress_lock);
881da177e4SLinus Torvalds 			return entry;
891da177e4SLinus Torvalds 		}
901da177e4SLinus Torvalds 		entry = entry->next;
911da177e4SLinus Torvalds 	}
921da177e4SLinus Torvalds 	read_unlock_bh(&client->ingress_lock);
931da177e4SLinus Torvalds 
941da177e4SLinus Torvalds 	return NULL;
951da177e4SLinus Torvalds }
961da177e4SLinus Torvalds 
in_cache_add_entry(__be32 dst_ip,struct mpoa_client * client)9730d492daSAl Viro static in_cache_entry *in_cache_add_entry(__be32 dst_ip,
981da177e4SLinus Torvalds 					  struct mpoa_client *client)
991da177e4SLinus Torvalds {
1002afe37cdSArnaldo Carvalho de Melo 	in_cache_entry *entry = kzalloc(sizeof(in_cache_entry), GFP_KERNEL);
1011da177e4SLinus Torvalds 
1021da177e4SLinus Torvalds 	if (entry == NULL) {
103bee67d34SJoe Perches 		pr_info("mpoa: mpoa_caches.c: new_in_cache_entry: out of memory\n");
1041da177e4SLinus Torvalds 		return NULL;
1051da177e4SLinus Torvalds 	}
1061da177e4SLinus Torvalds 
107b50c2ea7SJoe Perches 	dprintk("adding an ingress entry, ip = %pI4\n", &dst_ip);
1081da177e4SLinus Torvalds 
10993714912SReshetova, Elena 	refcount_set(&entry->use, 1);
110b50c2ea7SJoe Perches 	dprintk("new_in_cache_entry: about to lock\n");
1111da177e4SLinus Torvalds 	write_lock_bh(&client->ingress_lock);
1121da177e4SLinus Torvalds 	entry->next = client->in_cache;
1131da177e4SLinus Torvalds 	entry->prev = NULL;
1141da177e4SLinus Torvalds 	if (client->in_cache != NULL)
1151da177e4SLinus Torvalds 		client->in_cache->prev = entry;
1161da177e4SLinus Torvalds 	client->in_cache = entry;
1171da177e4SLinus Torvalds 
1181da177e4SLinus Torvalds 	memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN);
1191da177e4SLinus Torvalds 	entry->ctrl_info.in_dst_ip = dst_ip;
120d750dbdcSTina Ruchandani 	entry->time = ktime_get_seconds();
1211da177e4SLinus Torvalds 	entry->retry_time = client->parameters.mpc_p4;
1221da177e4SLinus Torvalds 	entry->count = 1;
1231da177e4SLinus Torvalds 	entry->entry_state = INGRESS_INVALID;
1241da177e4SLinus Torvalds 	entry->ctrl_info.holding_time = HOLDING_TIME_DEFAULT;
12593714912SReshetova, Elena 	refcount_inc(&entry->use);
1261da177e4SLinus Torvalds 
1271da177e4SLinus Torvalds 	write_unlock_bh(&client->ingress_lock);
128b50c2ea7SJoe Perches 	dprintk("new_in_cache_entry: unlocked\n");
1291da177e4SLinus Torvalds 
1301da177e4SLinus Torvalds 	return entry;
1311da177e4SLinus Torvalds }
1321da177e4SLinus Torvalds 
cache_hit(in_cache_entry * entry,struct mpoa_client * mpc)1331da177e4SLinus Torvalds static int cache_hit(in_cache_entry *entry, struct mpoa_client *mpc)
1341da177e4SLinus Torvalds {
1351da177e4SLinus Torvalds 	struct atm_mpoa_qos *qos;
1361da177e4SLinus Torvalds 	struct k_message msg;
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds 	entry->count++;
1391da177e4SLinus Torvalds 	if (entry->entry_state == INGRESS_RESOLVED && entry->shortcut != NULL)
1401da177e4SLinus Torvalds 		return OPEN;
1411da177e4SLinus Torvalds 
1421da177e4SLinus Torvalds 	if (entry->entry_state == INGRESS_REFRESHING) {
1431da177e4SLinus Torvalds 		if (entry->count > mpc->parameters.mpc_p1) {
1441da177e4SLinus Torvalds 			msg.type = SND_MPOA_RES_RQST;
1451da177e4SLinus Torvalds 			msg.content.in_info = entry->ctrl_info;
1461da177e4SLinus Torvalds 			memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN);
1471da177e4SLinus Torvalds 			qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip);
148bee67d34SJoe Perches 			if (qos != NULL)
149bee67d34SJoe Perches 				msg.qos = qos->qos;
1501da177e4SLinus Torvalds 			msg_to_mpoad(&msg, mpc);
151d750dbdcSTina Ruchandani 			entry->reply_wait = ktime_get_seconds();
1521da177e4SLinus Torvalds 			entry->entry_state = INGRESS_RESOLVING;
1531da177e4SLinus Torvalds 		}
1541da177e4SLinus Torvalds 		if (entry->shortcut != NULL)
1551da177e4SLinus Torvalds 			return OPEN;
1561da177e4SLinus Torvalds 		return CLOSED;
1571da177e4SLinus Torvalds 	}
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds 	if (entry->entry_state == INGRESS_RESOLVING && entry->shortcut != NULL)
1601da177e4SLinus Torvalds 		return OPEN;
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds 	if (entry->count > mpc->parameters.mpc_p1 &&
1631da177e4SLinus Torvalds 	    entry->entry_state == INGRESS_INVALID) {
164b50c2ea7SJoe Perches 		dprintk("(%s) threshold exceeded for ip %pI4, sending MPOA res req\n",
16521454aaaSHarvey Harrison 			mpc->dev->name, &entry->ctrl_info.in_dst_ip);
1661da177e4SLinus Torvalds 		entry->entry_state = INGRESS_RESOLVING;
1671da177e4SLinus Torvalds 		msg.type = SND_MPOA_RES_RQST;
1681da177e4SLinus Torvalds 		memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN);
1691da177e4SLinus Torvalds 		msg.content.in_info = entry->ctrl_info;
1701da177e4SLinus Torvalds 		qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip);
171bee67d34SJoe Perches 		if (qos != NULL)
172bee67d34SJoe Perches 			msg.qos = qos->qos;
1731da177e4SLinus Torvalds 		msg_to_mpoad(&msg, mpc);
174d750dbdcSTina Ruchandani 		entry->reply_wait = ktime_get_seconds();
1751da177e4SLinus Torvalds 	}
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds 	return CLOSED;
1781da177e4SLinus Torvalds }
1791da177e4SLinus Torvalds 
in_cache_put(in_cache_entry * entry)1801da177e4SLinus Torvalds static void in_cache_put(in_cache_entry *entry)
1811da177e4SLinus Torvalds {
18293714912SReshetova, Elena 	if (refcount_dec_and_test(&entry->use)) {
183453431a5SWaiman Long 		kfree_sensitive(entry);
1841da177e4SLinus Torvalds 	}
1851da177e4SLinus Torvalds }
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds /*
1881da177e4SLinus Torvalds  * This should be called with write lock on
1891da177e4SLinus Torvalds  */
in_cache_remove_entry(in_cache_entry * entry,struct mpoa_client * client)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... */
clear_count_and_expired(struct mpoa_client * client)2261da177e4SLinus Torvalds static void clear_count_and_expired(struct mpoa_client *client)
2271da177e4SLinus Torvalds {
2281da177e4SLinus Torvalds 	in_cache_entry *entry, *next_entry;
229d750dbdcSTina Ruchandani 	time64_t now;
2301da177e4SLinus Torvalds 
231d750dbdcSTina Ruchandani 	now = ktime_get_seconds();
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;
238d750dbdcSTina Ruchandani 		if ((now - entry->time) > entry->ctrl_info.holding_time) {
239b50c2ea7SJoe Perches 			dprintk("holding time expired, ip = %pI4\n",
24021454aaaSHarvey Harrison 				&entry->ctrl_info.in_dst_ip);
2411da177e4SLinus Torvalds 			client->in_ops->remove_entry(entry, client);
2421da177e4SLinus Torvalds 		}
2431da177e4SLinus Torvalds 		entry = next_entry;
2441da177e4SLinus Torvalds 	}
2451da177e4SLinus Torvalds 	write_unlock_bh(&client->ingress_lock);
2461da177e4SLinus Torvalds }
2471da177e4SLinus Torvalds 
2481da177e4SLinus Torvalds /* Call this every MPC-p4 seconds. */
check_resolving_entries(struct mpoa_client * client)2491da177e4SLinus Torvalds static void check_resolving_entries(struct mpoa_client *client)
2501da177e4SLinus Torvalds {
2511da177e4SLinus Torvalds 
2521da177e4SLinus Torvalds 	struct atm_mpoa_qos *qos;
2531da177e4SLinus Torvalds 	in_cache_entry *entry;
254d750dbdcSTina Ruchandani 	time64_t now;
2551da177e4SLinus Torvalds 	struct k_message msg;
2561da177e4SLinus Torvalds 
257d750dbdcSTina Ruchandani 	now = ktime_get_seconds();
2581da177e4SLinus Torvalds 
2591da177e4SLinus Torvalds 	read_lock_bh(&client->ingress_lock);
2601da177e4SLinus Torvalds 	entry = client->in_cache;
2611da177e4SLinus Torvalds 	while (entry != NULL) {
2621da177e4SLinus Torvalds 		if (entry->entry_state == INGRESS_RESOLVING) {
263d750dbdcSTina Ruchandani 
264d750dbdcSTina Ruchandani 			if ((now - entry->hold_down)
265d750dbdcSTina Ruchandani 					< client->parameters.mpc_p6) {
2661da177e4SLinus Torvalds 				entry = entry->next;	/* Entry in hold down */
2671da177e4SLinus Torvalds 				continue;
2681da177e4SLinus Torvalds 			}
269d750dbdcSTina Ruchandani 			if ((now - entry->reply_wait) > entry->retry_time) {
2701da177e4SLinus Torvalds 				entry->retry_time = MPC_C1 * (entry->retry_time);
271bee67d34SJoe Perches 				/*
272bee67d34SJoe Perches 				 * Retry time maximum exceeded,
273bee67d34SJoe Perches 				 * put entry in hold down.
274bee67d34SJoe Perches 				 */
2751da177e4SLinus Torvalds 				if (entry->retry_time > client->parameters.mpc_p5) {
276d750dbdcSTina Ruchandani 					entry->hold_down = ktime_get_seconds();
2771da177e4SLinus Torvalds 					entry->retry_time = client->parameters.mpc_p4;
2781da177e4SLinus Torvalds 					entry = entry->next;
2791da177e4SLinus Torvalds 					continue;
2801da177e4SLinus Torvalds 				}
2811da177e4SLinus Torvalds 				/* Ask daemon to send a resolution request. */
282d750dbdcSTina Ruchandani 				memset(&entry->hold_down, 0, sizeof(time64_t));
2831da177e4SLinus Torvalds 				msg.type = SND_MPOA_RES_RTRY;
2841da177e4SLinus Torvalds 				memcpy(msg.MPS_ctrl, client->mps_ctrl_addr, ATM_ESA_LEN);
2851da177e4SLinus Torvalds 				msg.content.in_info = entry->ctrl_info;
2861da177e4SLinus Torvalds 				qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip);
287bee67d34SJoe Perches 				if (qos != NULL)
288bee67d34SJoe Perches 					msg.qos = qos->qos;
2891da177e4SLinus Torvalds 				msg_to_mpoad(&msg, client);
290d750dbdcSTina Ruchandani 				entry->reply_wait = ktime_get_seconds();
2911da177e4SLinus Torvalds 			}
2921da177e4SLinus Torvalds 		}
2931da177e4SLinus Torvalds 		entry = entry->next;
2941da177e4SLinus Torvalds 	}
2951da177e4SLinus Torvalds 	read_unlock_bh(&client->ingress_lock);
2961da177e4SLinus Torvalds }
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds /* Call this every MPC-p5 seconds. */
refresh_entries(struct mpoa_client * client)2991da177e4SLinus Torvalds static void refresh_entries(struct mpoa_client *client)
3001da177e4SLinus Torvalds {
301d750dbdcSTina Ruchandani 	time64_t now;
3021da177e4SLinus Torvalds 	struct in_cache_entry *entry = client->in_cache;
3031da177e4SLinus Torvalds 
304b50c2ea7SJoe Perches 	ddprintk("refresh_entries\n");
305d750dbdcSTina Ruchandani 	now = ktime_get_seconds();
3061da177e4SLinus Torvalds 
3071da177e4SLinus Torvalds 	read_lock_bh(&client->ingress_lock);
3081da177e4SLinus Torvalds 	while (entry != NULL) {
3091da177e4SLinus Torvalds 		if (entry->entry_state == INGRESS_RESOLVED) {
3101da177e4SLinus Torvalds 			if (!(entry->refresh_time))
3111da177e4SLinus Torvalds 				entry->refresh_time = (2 * (entry->ctrl_info.holding_time))/3;
312d750dbdcSTina Ruchandani 			if ((now - entry->reply_wait) >
313bee67d34SJoe Perches 			    entry->refresh_time) {
314b50c2ea7SJoe Perches 				dprintk("refreshing an entry.\n");
3151da177e4SLinus Torvalds 				entry->entry_state = INGRESS_REFRESHING;
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds 			}
3181da177e4SLinus Torvalds 		}
3191da177e4SLinus Torvalds 		entry = entry->next;
3201da177e4SLinus Torvalds 	}
3211da177e4SLinus Torvalds 	read_unlock_bh(&client->ingress_lock);
3221da177e4SLinus Torvalds }
3231da177e4SLinus Torvalds 
in_destroy_cache(struct mpoa_client * mpc)3241da177e4SLinus Torvalds static void in_destroy_cache(struct mpoa_client *mpc)
3251da177e4SLinus Torvalds {
3261da177e4SLinus Torvalds 	write_lock_irq(&mpc->ingress_lock);
3271da177e4SLinus Torvalds 	while (mpc->in_cache != NULL)
3281da177e4SLinus Torvalds 		mpc->in_ops->remove_entry(mpc->in_cache, mpc);
3291da177e4SLinus Torvalds 	write_unlock_irq(&mpc->ingress_lock);
3301da177e4SLinus Torvalds }
3311da177e4SLinus Torvalds 
eg_cache_get_by_cache_id(__be32 cache_id,struct mpoa_client * mpc)332bee67d34SJoe Perches static eg_cache_entry *eg_cache_get_by_cache_id(__be32 cache_id,
333bee67d34SJoe Perches 						struct mpoa_client *mpc)
3341da177e4SLinus Torvalds {
3351da177e4SLinus Torvalds 	eg_cache_entry *entry;
3361da177e4SLinus Torvalds 
3371da177e4SLinus Torvalds 	read_lock_irq(&mpc->egress_lock);
3381da177e4SLinus Torvalds 	entry = mpc->eg_cache;
3391da177e4SLinus Torvalds 	while (entry != NULL) {
3401da177e4SLinus Torvalds 		if (entry->ctrl_info.cache_id == cache_id) {
341e00bdbefSReshetova, Elena 			refcount_inc(&entry->use);
3421da177e4SLinus Torvalds 			read_unlock_irq(&mpc->egress_lock);
3431da177e4SLinus Torvalds 			return entry;
3441da177e4SLinus Torvalds 		}
3451da177e4SLinus Torvalds 		entry = entry->next;
3461da177e4SLinus Torvalds 	}
3471da177e4SLinus Torvalds 	read_unlock_irq(&mpc->egress_lock);
3481da177e4SLinus Torvalds 
3491da177e4SLinus Torvalds 	return NULL;
3501da177e4SLinus Torvalds }
3511da177e4SLinus Torvalds 
3521da177e4SLinus Torvalds /* This can be called from any context since it saves CPU flags */
eg_cache_get_by_tag(__be32 tag,struct mpoa_client * mpc)35330d492daSAl Viro static eg_cache_entry *eg_cache_get_by_tag(__be32 tag, struct mpoa_client *mpc)
3541da177e4SLinus Torvalds {
3551da177e4SLinus Torvalds 	unsigned long flags;
3561da177e4SLinus Torvalds 	eg_cache_entry *entry;
3571da177e4SLinus Torvalds 
3581da177e4SLinus Torvalds 	read_lock_irqsave(&mpc->egress_lock, flags);
3591da177e4SLinus Torvalds 	entry = mpc->eg_cache;
3601da177e4SLinus Torvalds 	while (entry != NULL) {
3611da177e4SLinus Torvalds 		if (entry->ctrl_info.tag == tag) {
362e00bdbefSReshetova, Elena 			refcount_inc(&entry->use);
3631da177e4SLinus Torvalds 			read_unlock_irqrestore(&mpc->egress_lock, flags);
3641da177e4SLinus Torvalds 			return entry;
3651da177e4SLinus Torvalds 		}
3661da177e4SLinus Torvalds 		entry = entry->next;
3671da177e4SLinus Torvalds 	}
3681da177e4SLinus Torvalds 	read_unlock_irqrestore(&mpc->egress_lock, flags);
3691da177e4SLinus Torvalds 
3701da177e4SLinus Torvalds 	return NULL;
3711da177e4SLinus Torvalds }
3721da177e4SLinus Torvalds 
3731da177e4SLinus Torvalds /* 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)374bee67d34SJoe Perches static eg_cache_entry *eg_cache_get_by_vcc(struct atm_vcc *vcc,
375bee67d34SJoe Perches 					   struct mpoa_client *mpc)
3761da177e4SLinus Torvalds {
3771da177e4SLinus Torvalds 	unsigned long flags;
3781da177e4SLinus Torvalds 	eg_cache_entry *entry;
3791da177e4SLinus Torvalds 
3801da177e4SLinus Torvalds 	read_lock_irqsave(&mpc->egress_lock, flags);
3811da177e4SLinus Torvalds 	entry = mpc->eg_cache;
3821da177e4SLinus Torvalds 	while (entry != NULL) {
3831da177e4SLinus Torvalds 		if (entry->shortcut == vcc) {
384e00bdbefSReshetova, Elena 			refcount_inc(&entry->use);
3851da177e4SLinus Torvalds 			read_unlock_irqrestore(&mpc->egress_lock, flags);
3861da177e4SLinus Torvalds 			return entry;
3871da177e4SLinus Torvalds 		}
3881da177e4SLinus Torvalds 		entry = entry->next;
3891da177e4SLinus Torvalds 	}
3901da177e4SLinus Torvalds 	read_unlock_irqrestore(&mpc->egress_lock, flags);
3911da177e4SLinus Torvalds 
3921da177e4SLinus Torvalds 	return NULL;
3931da177e4SLinus Torvalds }
3941da177e4SLinus Torvalds 
eg_cache_get_by_src_ip(__be32 ipaddr,struct mpoa_client * mpc)395bee67d34SJoe Perches static eg_cache_entry *eg_cache_get_by_src_ip(__be32 ipaddr,
396bee67d34SJoe Perches 					      struct mpoa_client *mpc)
3971da177e4SLinus Torvalds {
3981da177e4SLinus Torvalds 	eg_cache_entry *entry;
3991da177e4SLinus Torvalds 
4001da177e4SLinus Torvalds 	read_lock_irq(&mpc->egress_lock);
4011da177e4SLinus Torvalds 	entry = mpc->eg_cache;
4021da177e4SLinus Torvalds 	while (entry != NULL) {
4031da177e4SLinus Torvalds 		if (entry->latest_ip_addr == ipaddr) {
404e00bdbefSReshetova, Elena 			refcount_inc(&entry->use);
4051da177e4SLinus Torvalds 			read_unlock_irq(&mpc->egress_lock);
4061da177e4SLinus Torvalds 			return entry;
4071da177e4SLinus Torvalds 		}
4081da177e4SLinus Torvalds 		entry = entry->next;
4091da177e4SLinus Torvalds 	}
4101da177e4SLinus Torvalds 	read_unlock_irq(&mpc->egress_lock);
4111da177e4SLinus Torvalds 
4121da177e4SLinus Torvalds 	return NULL;
4131da177e4SLinus Torvalds }
4141da177e4SLinus Torvalds 
eg_cache_put(eg_cache_entry * entry)4151da177e4SLinus Torvalds static void eg_cache_put(eg_cache_entry *entry)
4161da177e4SLinus Torvalds {
417e00bdbefSReshetova, Elena 	if (refcount_dec_and_test(&entry->use)) {
418453431a5SWaiman Long 		kfree_sensitive(entry);
4191da177e4SLinus Torvalds 	}
4201da177e4SLinus Torvalds }
4211da177e4SLinus Torvalds 
4221da177e4SLinus Torvalds /*
4231da177e4SLinus Torvalds  * This should be called with write lock on
4241da177e4SLinus Torvalds  */
eg_cache_remove_entry(eg_cache_entry * entry,struct mpoa_client * client)4251da177e4SLinus Torvalds static void eg_cache_remove_entry(eg_cache_entry *entry,
4261da177e4SLinus Torvalds 				  struct mpoa_client *client)
4271da177e4SLinus Torvalds {
4281da177e4SLinus Torvalds 	struct atm_vcc *vcc;
4291da177e4SLinus Torvalds 	struct k_message msg;
4301da177e4SLinus Torvalds 
4311da177e4SLinus Torvalds 	vcc = entry->shortcut;
432b50c2ea7SJoe Perches 	dprintk("removing an egress entry.\n");
4331da177e4SLinus Torvalds 	if (entry->prev != NULL)
4341da177e4SLinus Torvalds 		entry->prev->next = entry->next;
4351da177e4SLinus Torvalds 	else
4361da177e4SLinus Torvalds 		client->eg_cache = entry->next;
4371da177e4SLinus Torvalds 	if (entry->next != NULL)
4381da177e4SLinus Torvalds 		entry->next->prev = entry->prev;
4391da177e4SLinus Torvalds 	client->eg_ops->put(entry);
4401da177e4SLinus Torvalds 	if (client->in_cache == NULL && client->eg_cache == NULL) {
4411da177e4SLinus Torvalds 		msg.type = STOP_KEEP_ALIVE_SM;
4421da177e4SLinus Torvalds 		msg_to_mpoad(&msg, client);
4431da177e4SLinus Torvalds 	}
4441da177e4SLinus Torvalds 
4451da177e4SLinus Torvalds 	/* Check if the ingress side still uses this VCC */
4461da177e4SLinus Torvalds 	if (vcc != NULL) {
4471da177e4SLinus Torvalds 		in_cache_entry *in_entry = client->in_ops->get_by_vcc(vcc, client);
4481da177e4SLinus Torvalds 		if (in_entry != NULL) {
4491da177e4SLinus Torvalds 			client->in_ops->put(in_entry);
4501da177e4SLinus Torvalds 			return;
4511da177e4SLinus Torvalds 		}
4521da177e4SLinus Torvalds 		vcc_release_async(vcc, -EPIPE);
4531da177e4SLinus Torvalds 	}
4541da177e4SLinus Torvalds }
4551da177e4SLinus Torvalds 
eg_cache_add_entry(struct k_message * msg,struct mpoa_client * client)456bee67d34SJoe Perches static eg_cache_entry *eg_cache_add_entry(struct k_message *msg,
457bee67d34SJoe Perches 					  struct mpoa_client *client)
4581da177e4SLinus Torvalds {
4592afe37cdSArnaldo Carvalho de Melo 	eg_cache_entry *entry = kzalloc(sizeof(eg_cache_entry), GFP_KERNEL);
4601da177e4SLinus Torvalds 
4611da177e4SLinus Torvalds 	if (entry == NULL) {
462bee67d34SJoe Perches 		pr_info("out of memory\n");
4631da177e4SLinus Torvalds 		return NULL;
4641da177e4SLinus Torvalds 	}
4651da177e4SLinus Torvalds 
466b50c2ea7SJoe Perches 	dprintk("adding an egress entry, ip = %pI4, this should be our IP\n",
46721454aaaSHarvey Harrison 		&msg->content.eg_info.eg_dst_ip);
4681da177e4SLinus Torvalds 
469e00bdbefSReshetova, Elena 	refcount_set(&entry->use, 1);
470b50c2ea7SJoe Perches 	dprintk("new_eg_cache_entry: about to lock\n");
4711da177e4SLinus Torvalds 	write_lock_irq(&client->egress_lock);
4721da177e4SLinus Torvalds 	entry->next = client->eg_cache;
4731da177e4SLinus Torvalds 	entry->prev = NULL;
4741da177e4SLinus Torvalds 	if (client->eg_cache != NULL)
4751da177e4SLinus Torvalds 		client->eg_cache->prev = entry;
4761da177e4SLinus Torvalds 	client->eg_cache = entry;
4771da177e4SLinus Torvalds 
4781da177e4SLinus Torvalds 	memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN);
4791da177e4SLinus Torvalds 	entry->ctrl_info = msg->content.eg_info;
480d750dbdcSTina Ruchandani 	entry->time = ktime_get_seconds();
4811da177e4SLinus Torvalds 	entry->entry_state = EGRESS_RESOLVED;
482b50c2ea7SJoe Perches 	dprintk("new_eg_cache_entry cache_id %u\n",
483bee67d34SJoe Perches 		ntohl(entry->ctrl_info.cache_id));
484b50c2ea7SJoe Perches 	dprintk("mps_ip = %pI4\n", &entry->ctrl_info.mps_ip);
485e00bdbefSReshetova, Elena 	refcount_inc(&entry->use);
4861da177e4SLinus Torvalds 
4871da177e4SLinus Torvalds 	write_unlock_irq(&client->egress_lock);
488b50c2ea7SJoe Perches 	dprintk("new_eg_cache_entry: unlocked\n");
4891da177e4SLinus Torvalds 
4901da177e4SLinus Torvalds 	return entry;
4911da177e4SLinus Torvalds }
4921da177e4SLinus Torvalds 
update_eg_cache_entry(eg_cache_entry * entry,uint16_t holding_time)4931da177e4SLinus Torvalds static void update_eg_cache_entry(eg_cache_entry *entry, uint16_t holding_time)
4941da177e4SLinus Torvalds {
495d750dbdcSTina Ruchandani 	entry->time = ktime_get_seconds();
4961da177e4SLinus Torvalds 	entry->entry_state = EGRESS_RESOLVED;
4971da177e4SLinus Torvalds 	entry->ctrl_info.holding_time = holding_time;
4981da177e4SLinus Torvalds }
4991da177e4SLinus Torvalds 
clear_expired(struct mpoa_client * client)5001da177e4SLinus Torvalds static void clear_expired(struct mpoa_client *client)
5011da177e4SLinus Torvalds {
5021da177e4SLinus Torvalds 	eg_cache_entry *entry, *next_entry;
503d750dbdcSTina Ruchandani 	time64_t now;
5041da177e4SLinus Torvalds 	struct k_message msg;
5051da177e4SLinus Torvalds 
506d750dbdcSTina Ruchandani 	now = ktime_get_seconds();
5071da177e4SLinus Torvalds 
5081da177e4SLinus Torvalds 	write_lock_irq(&client->egress_lock);
5091da177e4SLinus Torvalds 	entry = client->eg_cache;
5101da177e4SLinus Torvalds 	while (entry != NULL) {
5111da177e4SLinus Torvalds 		next_entry = entry->next;
512d750dbdcSTina Ruchandani 		if ((now - entry->time) > entry->ctrl_info.holding_time) {
5131da177e4SLinus Torvalds 			msg.type = SND_EGRESS_PURGE;
5141da177e4SLinus Torvalds 			msg.content.eg_info = entry->ctrl_info;
515b50c2ea7SJoe Perches 			dprintk("egress_cache: holding time expired, cache_id = %u.\n",
516bee67d34SJoe Perches 				ntohl(entry->ctrl_info.cache_id));
5171da177e4SLinus Torvalds 			msg_to_mpoad(&msg, client);
5181da177e4SLinus Torvalds 			client->eg_ops->remove_entry(entry, client);
5191da177e4SLinus Torvalds 		}
5201da177e4SLinus Torvalds 		entry = next_entry;
5211da177e4SLinus Torvalds 	}
5221da177e4SLinus Torvalds 	write_unlock_irq(&client->egress_lock);
5231da177e4SLinus Torvalds }
5241da177e4SLinus Torvalds 
eg_destroy_cache(struct mpoa_client * mpc)5251da177e4SLinus Torvalds static void eg_destroy_cache(struct mpoa_client *mpc)
5261da177e4SLinus Torvalds {
5271da177e4SLinus Torvalds 	write_lock_irq(&mpc->egress_lock);
5281da177e4SLinus Torvalds 	while (mpc->eg_cache != NULL)
5291da177e4SLinus Torvalds 		mpc->eg_ops->remove_entry(mpc->eg_cache, mpc);
5301da177e4SLinus Torvalds 	write_unlock_irq(&mpc->egress_lock);
5311da177e4SLinus Torvalds }
5321da177e4SLinus Torvalds 
5331da177e4SLinus Torvalds 
5344dd191bbSJulia Lawall static const struct in_cache_ops ingress_ops = {
53599a5e178SKees Cook 	.add_entry = in_cache_add_entry,
53699a5e178SKees Cook 	.get = in_cache_get,
53799a5e178SKees Cook 	.get_with_mask = in_cache_get_with_mask,
53899a5e178SKees Cook 	.get_by_vcc = in_cache_get_by_vcc,
53999a5e178SKees Cook 	.put = in_cache_put,
54099a5e178SKees Cook 	.remove_entry = in_cache_remove_entry,
54199a5e178SKees Cook 	.cache_hit = cache_hit,
54299a5e178SKees Cook 	.clear_count = clear_count_and_expired,
54399a5e178SKees Cook 	.check_resolving = check_resolving_entries,
54499a5e178SKees Cook 	.refresh = refresh_entries,
54599a5e178SKees Cook 	.destroy_cache = in_destroy_cache
5461da177e4SLinus Torvalds };
5471da177e4SLinus Torvalds 
5484dd191bbSJulia Lawall static const struct eg_cache_ops egress_ops = {
54999a5e178SKees Cook 	.add_entry = eg_cache_add_entry,
55099a5e178SKees Cook 	.get_by_cache_id = eg_cache_get_by_cache_id,
55199a5e178SKees Cook 	.get_by_tag = eg_cache_get_by_tag,
55299a5e178SKees Cook 	.get_by_vcc = eg_cache_get_by_vcc,
55399a5e178SKees Cook 	.get_by_src_ip = eg_cache_get_by_src_ip,
55499a5e178SKees Cook 	.put = eg_cache_put,
55599a5e178SKees Cook 	.remove_entry = eg_cache_remove_entry,
55699a5e178SKees Cook 	.update = update_eg_cache_entry,
55799a5e178SKees Cook 	.clear_expired = clear_expired,
55899a5e178SKees Cook 	.destroy_cache = eg_destroy_cache
5591da177e4SLinus Torvalds };
5601da177e4SLinus Torvalds 
atm_mpoa_init_cache(struct mpoa_client * mpc)5611da177e4SLinus Torvalds void atm_mpoa_init_cache(struct mpoa_client *mpc)
5621da177e4SLinus Torvalds {
5631da177e4SLinus Torvalds 	mpc->in_ops = &ingress_ops;
5641da177e4SLinus Torvalds 	mpc->eg_ops = &egress_ops;
5651da177e4SLinus Torvalds }
566