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 */ | ||||
| struct smc_net { | ||||
| 	struct smc_pnettable pnettable; | ||||
| 	struct smc_pnetids_ndev pnetids_ndev; | ||||
| }; | ||||
| #endif | ||||
|  |  | |||
|  | @ -29,6 +29,7 @@ | |||
| #include "smc_ism.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 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) | ||||
| }; | ||||
| 
 | ||||
| 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, | ||||
| 				 unsigned long event, void *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) { | ||||
| 	case NETDEV_REBOOT: | ||||
|  | @ -725,6 +831,17 @@ static int smc_pnet_netdev_event(struct notifier_block *this, | |||
| 	case NETDEV_REGISTER: | ||||
| 		smc_pnet_add_by_ndev(event_dev); | ||||
| 		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: | ||||
| 		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_pnettable *pnettable = &sn->pnettable; | ||||
| 	struct smc_pnetids_ndev *pnetids_ndev = &sn->pnetids_ndev; | ||||
| 
 | ||||
| 	INIT_LIST_HEAD(&pnettable->pnetlist); | ||||
| 	rwlock_init(&pnettable->lock); | ||||
| 	INIT_LIST_HEAD(&pnetids_ndev->list); | ||||
| 	rwlock_init(&pnetids_ndev->lock); | ||||
| 
 | ||||
| 	smc_pnet_create_pnetids_list(net); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -756,6 +878,7 @@ int __init smc_pnet_init(void) | |||
| 	rc = register_netdevice_notifier(&smc_netdev_notifier); | ||||
| 	if (rc) | ||||
| 		genl_unregister_family(&smc_pnet_nl_family); | ||||
| 
 | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
|  | @ -764,6 +887,7 @@ void smc_pnet_net_exit(struct net *net) | |||
| { | ||||
| 	/* flush pnet table */ | ||||
| 	smc_pnet_remove_by_pnetid(net, NULL); | ||||
| 	smc_pnet_destroy_pnetids_list(net); | ||||
| } | ||||
| 
 | ||||
| void smc_pnet_exit(void) | ||||
|  | @ -772,16 +896,11 @@ void smc_pnet_exit(void) | |||
| 	genl_unregister_family(&smc_pnet_nl_family); | ||||
| } | ||||
| 
 | ||||
| /* 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) | ||||
| static struct net_device *__pnet_find_base_ndev(struct net_device *ndev) | ||||
| { | ||||
| 	int i, nest_lvl; | ||||
| 
 | ||||
| 	rtnl_lock(); | ||||
| 	ASSERT_RTNL(); | ||||
| 	nest_lvl = ndev->lower_level; | ||||
| 	for (i = 0; i < nest_lvl; i++) { | ||||
| 		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; | ||||
| 		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(); | ||||
| 	return ndev; | ||||
| } | ||||
|  |  | |||
|  | @ -12,6 +12,8 @@ | |||
| #ifndef _SMC_PNET_H | ||||
| #define _SMC_PNET_H | ||||
| 
 | ||||
| #include <net/smc.h> | ||||
| 
 | ||||
| #if IS_ENABLED(CONFIG_HAVE_PNETID) | ||||
| #include <asm/pnet.h> | ||||
| #endif | ||||
|  | @ -31,6 +33,17 @@ struct smc_pnettable { | |||
| 	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, | ||||
| 					 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, | ||||
| 			    struct smc_init_info *ini, | ||||
| 			    struct smc_ib_device *known_dev); | ||||
| bool smc_pnet_is_ndev_pnetid(struct net *net, u8 *pnetid); | ||||
| #endif | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Ursula Braun
						Ursula Braun