Commit e888a2e8 authored by Ursula Braun's avatar Ursula Braun Committed by David S. Miller
Browse files

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: default avatarUrsula Braun <ubraun@linux.ibm.com>
Signed-off-by: default avatarKarsten Graul <kgraul@linux.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 8caaccf5
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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
+138 −7
Original line number Diff line number Diff line
@@ -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;
}
+14 −0
Original line number Diff line number Diff line
@@ -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