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

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
...@@ -16,5 +16,6 @@ extern unsigned int smc_net_id; ...@@ -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 @@ ...@@ -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 = { ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ ...@@ -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 { ...@@ -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); ...@@ -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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment