forked from mirrors/linux
		
	net/smc: introduce list of pnetids for Ethernet devices
SMCD version 2 allows usage of ISM devices with hardware PNETID only, if an Ethernet net_device exists with the same hardware PNETID. This requires to maintain a list of pnetids belonging to Ethernet net_devices, which is covered by this patch. Signed-off-by: Ursula Braun <ubraun@linux.ibm.com> Signed-off-by: Karsten Graul <kgraul@linux.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									8caaccf521
								
							
						
					
					
						commit
						e888a2e833
					
				
					 3 changed files with 153 additions and 7 deletions
				
			
		|  | @ -16,5 +16,6 @@ extern unsigned int smc_net_id; | ||||||
| /* per-network namespace private data */ | /* per-network namespace private data */ | ||||||
| struct smc_net { | struct smc_net { | ||||||
| 	struct smc_pnettable pnettable; | 	struct smc_pnettable pnettable; | ||||||
|  | 	struct smc_pnetids_ndev pnetids_ndev; | ||||||
| }; | }; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -29,6 +29,7 @@ | ||||||
| #include "smc_ism.h" | #include "smc_ism.h" | ||||||
| #include "smc_core.h" | #include "smc_core.h" | ||||||
| 
 | 
 | ||||||
|  | static struct net_device *__pnet_find_base_ndev(struct net_device *ndev); | ||||||
| static struct net_device *pnet_find_base_ndev(struct net_device *ndev); | static struct net_device *pnet_find_base_ndev(struct net_device *ndev); | ||||||
| 
 | 
 | ||||||
| static const struct nla_policy smc_pnet_policy[SMC_PNETID_MAX + 1] = { | static const struct nla_policy smc_pnet_policy[SMC_PNETID_MAX + 1] = { | ||||||
|  | @ -712,10 +713,115 @@ static struct genl_family smc_pnet_nl_family __ro_after_init = { | ||||||
| 	.n_ops =  ARRAY_SIZE(smc_pnet_ops) | 	.n_ops =  ARRAY_SIZE(smc_pnet_ops) | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | bool smc_pnet_is_ndev_pnetid(struct net *net, u8 *pnetid) | ||||||
|  | { | ||||||
|  | 	struct smc_net *sn = net_generic(net, smc_net_id); | ||||||
|  | 	struct smc_pnetids_ndev_entry *pe; | ||||||
|  | 	bool rc = false; | ||||||
|  | 
 | ||||||
|  | 	read_lock(&sn->pnetids_ndev.lock); | ||||||
|  | 	list_for_each_entry(pe, &sn->pnetids_ndev.list, list) { | ||||||
|  | 		if (smc_pnet_match(pnetid, pe->pnetid)) { | ||||||
|  | 			rc = true; | ||||||
|  | 			goto unlock; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | unlock: | ||||||
|  | 	read_unlock(&sn->pnetids_ndev.lock); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int smc_pnet_add_pnetid(struct net *net, u8 *pnetid) | ||||||
|  | { | ||||||
|  | 	struct smc_net *sn = net_generic(net, smc_net_id); | ||||||
|  | 	struct smc_pnetids_ndev_entry *pe, *pi; | ||||||
|  | 
 | ||||||
|  | 	pe = kzalloc(sizeof(*pe), GFP_KERNEL); | ||||||
|  | 	if (!pe) | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 
 | ||||||
|  | 	write_lock(&sn->pnetids_ndev.lock); | ||||||
|  | 	list_for_each_entry(pi, &sn->pnetids_ndev.list, list) { | ||||||
|  | 		if (smc_pnet_match(pnetid, pe->pnetid)) { | ||||||
|  | 			refcount_inc(&pi->refcnt); | ||||||
|  | 			kfree(pe); | ||||||
|  | 			goto unlock; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	refcount_set(&pe->refcnt, 1); | ||||||
|  | 	memcpy(pe->pnetid, pnetid, SMC_MAX_PNETID_LEN); | ||||||
|  | 	list_add_tail(&pe->list, &sn->pnetids_ndev.list); | ||||||
|  | 
 | ||||||
|  | unlock: | ||||||
|  | 	write_unlock(&sn->pnetids_ndev.lock); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void smc_pnet_remove_pnetid(struct net *net, u8 *pnetid) | ||||||
|  | { | ||||||
|  | 	struct smc_net *sn = net_generic(net, smc_net_id); | ||||||
|  | 	struct smc_pnetids_ndev_entry *pe, *pe2; | ||||||
|  | 
 | ||||||
|  | 	write_lock(&sn->pnetids_ndev.lock); | ||||||
|  | 	list_for_each_entry_safe(pe, pe2, &sn->pnetids_ndev.list, list) { | ||||||
|  | 		if (smc_pnet_match(pnetid, pe->pnetid)) { | ||||||
|  | 			if (refcount_dec_and_test(&pe->refcnt)) { | ||||||
|  | 				list_del(&pe->list); | ||||||
|  | 				kfree(pe); | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	write_unlock(&sn->pnetids_ndev.lock); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void smc_pnet_add_base_pnetid(struct net *net, struct net_device *dev, | ||||||
|  | 				     u8 *ndev_pnetid) | ||||||
|  | { | ||||||
|  | 	struct net_device *base_dev; | ||||||
|  | 
 | ||||||
|  | 	base_dev = __pnet_find_base_ndev(dev); | ||||||
|  | 	if (base_dev->flags & IFF_UP && | ||||||
|  | 	    !smc_pnetid_by_dev_port(base_dev->dev.parent, base_dev->dev_port, | ||||||
|  | 				    ndev_pnetid)) { | ||||||
|  | 		/* add to PNETIDs list */ | ||||||
|  | 		smc_pnet_add_pnetid(net, ndev_pnetid); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* create initial list of netdevice pnetids */ | ||||||
|  | static void smc_pnet_create_pnetids_list(struct net *net) | ||||||
|  | { | ||||||
|  | 	u8 ndev_pnetid[SMC_MAX_PNETID_LEN]; | ||||||
|  | 	struct net_device *dev; | ||||||
|  | 
 | ||||||
|  | 	rtnl_lock(); | ||||||
|  | 	for_each_netdev(net, dev) | ||||||
|  | 		smc_pnet_add_base_pnetid(net, dev, ndev_pnetid); | ||||||
|  | 	rtnl_unlock(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* clean up list of netdevice pnetids */ | ||||||
|  | static void smc_pnet_destroy_pnetids_list(struct net *net) | ||||||
|  | { | ||||||
|  | 	struct smc_net *sn = net_generic(net, smc_net_id); | ||||||
|  | 	struct smc_pnetids_ndev_entry *pe, *temp_pe; | ||||||
|  | 
 | ||||||
|  | 	write_lock(&sn->pnetids_ndev.lock); | ||||||
|  | 	list_for_each_entry_safe(pe, temp_pe, &sn->pnetids_ndev.list, list) { | ||||||
|  | 		list_del(&pe->list); | ||||||
|  | 		kfree(pe); | ||||||
|  | 	} | ||||||
|  | 	write_unlock(&sn->pnetids_ndev.lock); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int smc_pnet_netdev_event(struct notifier_block *this, | static int smc_pnet_netdev_event(struct notifier_block *this, | ||||||
| 				 unsigned long event, void *ptr) | 				 unsigned long event, void *ptr) | ||||||
| { | { | ||||||
| 	struct net_device *event_dev = netdev_notifier_info_to_dev(ptr); | 	struct net_device *event_dev = netdev_notifier_info_to_dev(ptr); | ||||||
|  | 	struct net *net = dev_net(event_dev); | ||||||
|  | 	u8 ndev_pnetid[SMC_MAX_PNETID_LEN]; | ||||||
| 
 | 
 | ||||||
| 	switch (event) { | 	switch (event) { | ||||||
| 	case NETDEV_REBOOT: | 	case NETDEV_REBOOT: | ||||||
|  | @ -725,6 +831,17 @@ static int smc_pnet_netdev_event(struct notifier_block *this, | ||||||
| 	case NETDEV_REGISTER: | 	case NETDEV_REGISTER: | ||||||
| 		smc_pnet_add_by_ndev(event_dev); | 		smc_pnet_add_by_ndev(event_dev); | ||||||
| 		return NOTIFY_OK; | 		return NOTIFY_OK; | ||||||
|  | 	case NETDEV_UP: | ||||||
|  | 		smc_pnet_add_base_pnetid(net, event_dev, ndev_pnetid); | ||||||
|  | 		return NOTIFY_OK; | ||||||
|  | 	case NETDEV_DOWN: | ||||||
|  | 		event_dev = __pnet_find_base_ndev(event_dev); | ||||||
|  | 		if (!smc_pnetid_by_dev_port(event_dev->dev.parent, | ||||||
|  | 					    event_dev->dev_port, ndev_pnetid)) { | ||||||
|  | 			/* remove from PNETIDs list */ | ||||||
|  | 			smc_pnet_remove_pnetid(net, ndev_pnetid); | ||||||
|  | 		} | ||||||
|  | 		return NOTIFY_OK; | ||||||
| 	default: | 	default: | ||||||
| 		return NOTIFY_DONE; | 		return NOTIFY_DONE; | ||||||
| 	} | 	} | ||||||
|  | @ -739,9 +856,14 @@ int smc_pnet_net_init(struct net *net) | ||||||
| { | { | ||||||
| 	struct smc_net *sn = net_generic(net, smc_net_id); | 	struct smc_net *sn = net_generic(net, smc_net_id); | ||||||
| 	struct smc_pnettable *pnettable = &sn->pnettable; | 	struct smc_pnettable *pnettable = &sn->pnettable; | ||||||
|  | 	struct smc_pnetids_ndev *pnetids_ndev = &sn->pnetids_ndev; | ||||||
| 
 | 
 | ||||||
| 	INIT_LIST_HEAD(&pnettable->pnetlist); | 	INIT_LIST_HEAD(&pnettable->pnetlist); | ||||||
| 	rwlock_init(&pnettable->lock); | 	rwlock_init(&pnettable->lock); | ||||||
|  | 	INIT_LIST_HEAD(&pnetids_ndev->list); | ||||||
|  | 	rwlock_init(&pnetids_ndev->lock); | ||||||
|  | 
 | ||||||
|  | 	smc_pnet_create_pnetids_list(net); | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  | @ -756,6 +878,7 @@ int __init smc_pnet_init(void) | ||||||
| 	rc = register_netdevice_notifier(&smc_netdev_notifier); | 	rc = register_netdevice_notifier(&smc_netdev_notifier); | ||||||
| 	if (rc) | 	if (rc) | ||||||
| 		genl_unregister_family(&smc_pnet_nl_family); | 		genl_unregister_family(&smc_pnet_nl_family); | ||||||
|  | 
 | ||||||
| 	return rc; | 	return rc; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -764,6 +887,7 @@ void smc_pnet_net_exit(struct net *net) | ||||||
| { | { | ||||||
| 	/* flush pnet table */ | 	/* flush pnet table */ | ||||||
| 	smc_pnet_remove_by_pnetid(net, NULL); | 	smc_pnet_remove_by_pnetid(net, NULL); | ||||||
|  | 	smc_pnet_destroy_pnetids_list(net); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void smc_pnet_exit(void) | void smc_pnet_exit(void) | ||||||
|  | @ -772,16 +896,11 @@ void smc_pnet_exit(void) | ||||||
| 	genl_unregister_family(&smc_pnet_nl_family); | 	genl_unregister_family(&smc_pnet_nl_family); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* Determine one base device for stacked net devices.
 | static struct net_device *__pnet_find_base_ndev(struct net_device *ndev) | ||||||
|  * If the lower device level contains more than one devices |  | ||||||
|  * (for instance with bonding slaves), just the first device |  | ||||||
|  * is used to reach a base device. |  | ||||||
|  */ |  | ||||||
| static struct net_device *pnet_find_base_ndev(struct net_device *ndev) |  | ||||||
| { | { | ||||||
| 	int i, nest_lvl; | 	int i, nest_lvl; | ||||||
| 
 | 
 | ||||||
| 	rtnl_lock(); | 	ASSERT_RTNL(); | ||||||
| 	nest_lvl = ndev->lower_level; | 	nest_lvl = ndev->lower_level; | ||||||
| 	for (i = 0; i < nest_lvl; i++) { | 	for (i = 0; i < nest_lvl; i++) { | ||||||
| 		struct list_head *lower = &ndev->adj_list.lower; | 		struct list_head *lower = &ndev->adj_list.lower; | ||||||
|  | @ -791,6 +910,18 @@ static struct net_device *pnet_find_base_ndev(struct net_device *ndev) | ||||||
| 		lower = lower->next; | 		lower = lower->next; | ||||||
| 		ndev = netdev_lower_get_next(ndev, &lower); | 		ndev = netdev_lower_get_next(ndev, &lower); | ||||||
| 	} | 	} | ||||||
|  | 	return ndev; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Determine one base device for stacked net devices.
 | ||||||
|  |  * If the lower device level contains more than one devices | ||||||
|  |  * (for instance with bonding slaves), just the first device | ||||||
|  |  * is used to reach a base device. | ||||||
|  |  */ | ||||||
|  | static struct net_device *pnet_find_base_ndev(struct net_device *ndev) | ||||||
|  | { | ||||||
|  | 	rtnl_lock(); | ||||||
|  | 	ndev = __pnet_find_base_ndev(ndev); | ||||||
| 	rtnl_unlock(); | 	rtnl_unlock(); | ||||||
| 	return ndev; | 	return ndev; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -12,6 +12,8 @@ | ||||||
| #ifndef _SMC_PNET_H | #ifndef _SMC_PNET_H | ||||||
| #define _SMC_PNET_H | #define _SMC_PNET_H | ||||||
| 
 | 
 | ||||||
|  | #include <net/smc.h> | ||||||
|  | 
 | ||||||
| #if IS_ENABLED(CONFIG_HAVE_PNETID) | #if IS_ENABLED(CONFIG_HAVE_PNETID) | ||||||
| #include <asm/pnet.h> | #include <asm/pnet.h> | ||||||
| #endif | #endif | ||||||
|  | @ -31,6 +33,17 @@ struct smc_pnettable { | ||||||
| 	struct list_head pnetlist; | 	struct list_head pnetlist; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | struct smc_pnetids_ndev {	/* list of pnetids for net devices in UP state*/ | ||||||
|  | 	struct list_head	list; | ||||||
|  | 	rwlock_t		lock; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct smc_pnetids_ndev_entry { | ||||||
|  | 	struct list_head	list; | ||||||
|  | 	u8			pnetid[SMC_MAX_PNETID_LEN]; | ||||||
|  | 	refcount_t		refcnt; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| static inline int smc_pnetid_by_dev_port(struct device *dev, | static inline int smc_pnetid_by_dev_port(struct device *dev, | ||||||
| 					 unsigned short port, u8 *pnetid) | 					 unsigned short port, u8 *pnetid) | ||||||
| { | { | ||||||
|  | @ -52,4 +65,5 @@ int smc_pnetid_by_table_smcd(struct smcd_dev *smcd); | ||||||
| void smc_pnet_find_alt_roce(struct smc_link_group *lgr, | void smc_pnet_find_alt_roce(struct smc_link_group *lgr, | ||||||
| 			    struct smc_init_info *ini, | 			    struct smc_init_info *ini, | ||||||
| 			    struct smc_ib_device *known_dev); | 			    struct smc_ib_device *known_dev); | ||||||
|  | bool smc_pnet_is_ndev_pnetid(struct net *net, u8 *pnetid); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Ursula Braun
						Ursula Braun