深入解析 ipoib_netlink.c:IPoIB 驱动的 Netlink 接口实现
引言
在 InfiniBand 网络中,IP over InfiniBand (IPoIB) 是一种将 IP 协议运行在 InfiniBand 网络上的技术。为了实现 IPoIB 设备的管理和配置,Linux 内核提供了 Netlink 接口,允许用户空间程序与内核中的 IPoIB 驱动进行交互。ipoib_netlink.c 文件是 IPoIB 驱动的一部分,主要负责实现与 Netlink 相关的操作,包括设备的创建、配置、信息获取等。
本文将对 ipoib_netlink.c 文件进行详细分析,探讨其实现的功能、数据结构以及关键函数的作用。
文件概述
ipoib_netlink.c 文件的主要功能是通过 Netlink 接口管理 IPoIB 设备。具体来说,它实现了以下功能:
-
IPoIB 设备的创建和配置:通过 Netlink 接口创建新的 IPoIB 子接口,并配置其属性(如 P_Key、模式、是否启用多播等)。
-
IPoIB 设备信息的获取:通过 Netlink 接口获取 IPoIB 设备的当前配置信息。
-
IPoIB 设备属性的修改:通过 Netlink 接口修改 IPoIB 设备的属性(如模式、多播设置等)。
代码分析
1. 头文件包含
#include <linux/netdevice.h> #include <linux/if_arp.h> /* For ARPHRD_xxx */ #include <linux/module.h> #include <net/rtnetlink.h> #include "ipoib.h"
-
linux/netdevice.h:提供网络设备相关的结构和函数。 -
linux/if_arp.h:定义了以太网地址类型,如ARPHRD_INFINIBAND。 -
linux/module.h:用于模块的初始化和退出。 -
net/rtnetlink.h:提供与 Netlink 相关的结构和函数,用于内核与用户空间的通信。 -
ipoib.h:自定义的 IPoIB 驱动头文件,包含驱动特定的定义和结构。
2. ipoib_policy 结构体
static const struct nla_policy ipoib_policy[IFLA_IPOIB_MAX + 1] = {
[IFLA_IPOIB_PKEY] = { .type = NLA_U16 },
[IFLA_IPOIB_MODE] = { .type = NLA_U16 },
[IFLA_IPOIB_UMCAST] = { .type = NLA_U16 },
};
ipoib_policy 结构体定义了 Netlink 属性的验证策略。ipoib_policy 数组指定了 IPoIB 设备支持的 Netlink 属性及其类型。例如,IFLA_IPOIB_PKEY 属性是一个 16 位无符号整数(NLA_U16)。
3. ipoib_fill_info 函数
static int ipoib_fill_info(struct sk_buff *skb, const struct net_device *dev)
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
u16 val;
if (nla_put_u16(skb, IFLA_IPOIB_PKEY, priv->pkey))
goto nla_put_failure;
val = test_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags);
if (nla_put_u16(skb, IFLA_IPOIB_MODE, val))
goto nla_put_failure;
val = test_bit(IPOIB_FLAG_UMCAST, &priv->flags);
if (nla_put_u16(skb, IFLA_IPOIB_UMCAST, val))
goto nla_put_failure;
return 0;
nla_put_failure:
return -EMSGSIZE;
}
ipoib_fill_info 函数用于将 IPoIB 设备的当前配置信息填充到 Netlink 消息中。它通过 nla_put_u16 函数将设备的 P_Key、模式和多播设置等信息添加到 sk_buff 中。如果添加失败,则返回 -EMSGSIZE。
4. ipoib_changelink 函数
static int ipoib_changelink(struct net_device *dev, struct nlattr *tb[],
struct nlattr *data[], struct netlink_ext_ack *extack)
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
u16 mode, umcast;
int ret = 0;
if (data[IFLA_IPOIB_MODE]) {
mode = nla_get_u16(data[IFLA_IPOIB_MODE]);
if (mode == IPOIB_MODE_DATAGRAM)
ret = priv->fp.ipoib_set_mode(dev, "datagram\n");
else if (mode == IPOIB_MODE_CONNECTED)
ret = priv->fp.ipoib_set_mode(dev, "connected\n");
else
ret = -EINVAL;
if (ret < 0)
goto out_err;
}
if (data[IFLA_IPOIB_UMCAST]) {
umcast = nla_get_u16(data[IFLA_IPOIB_UMCAST]);
ipoib_set_umcast(dev, umcast);
}
out_err:
return ret;
}
ipoib_changelink 函数用于修改 IPoIB 设备的属性。它根据传入的 Netlink 属性数据(data 参数)来设置设备的工作模式(IPOIB_MODE_DATAGRAM 或 IPOIB_MODE_CONNECTED)以及是否启用多播(umcast)。如果设置失败,则返回相应的错误码。
5. ipoib_new_child_link 函数
static int ipoib_new_child_link(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
{
struct net_device *pdev;
struct ipoib_dev_priv *ppriv;
struct ipoib_dev_priv *priv;
u16 child_pkey;
int err;
if (!tb[IFLA_LINK])
return -EINVAL;
pdev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK]));
if (!pdev || pdev->type != ARPHRD_INFINIBAND)
return -ENODEV;
ppriv = ipoib_priv(pdev);
if (test_bit(IPOIB_FLAG_SUBINTERFACE, &ppriv->flags)) {
ipoib_warn(ppriv, "child creation disallowed for child devices\n");
return -EINVAL;
}
if (!data || !data[IFLA_IPOIB_PKEY]) {
ipoib_dbg(ppriv, "no pkey specified, using parent pkey\n");
child_pkey = ppriv->pkey;
} else
child_pkey = nla_get_u16(data[IFLA_IPOIB_PKEY]);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
err = ipoib_set_fp_rss(priv, ppriv->ca);
if (err) {
ipoib_warn(ppriv, "failed to set pkey function pointers\n");
goto out;
}
err = ipoib_intf_init(ppriv->ca, ppriv->port, dev->name, dev, priv);
if (err) {
ipoib_warn(ppriv, "failed to initialize pkey device\n");
goto out;
}
err = __ipoib_vlan_add(ppriv, ipoib_priv(dev),
child_pkey, IPOIB_RTNL_CHILD);
if (err)
goto out;
if (data) {
err = ipoib_changelink(dev, tb, data, extack);
if (err) {
unregister_netdevice(dev);
return err;
}
}
return 0;
out:
kfree(priv);
return err;
}
ipoib_new_child_link 函数用于创建一个新的 IPoIB 子接口。它首先检查父设备是否存在并且类型是否正确,然后根据传入的 Netlink 属性数据来初始化子接口的 P_Key 和其他属性。如果初始化成功,则调用 ipoib_changelink 函数来进一步配置设备。
6. ipoib_get_size 函数
static size_t ipoib_get_size(const struct net_device *dev)
{
return nla_total_size(2) + /* IFLA_IPOIB_PKEY */
nla_total_size(2) + /* IFLA_IPOIB_MODE */
nla_total_size(2); /* IFLA_IPOIB_UMCAST */
}
ipoib_get_size 函数用于计算 IPoIB 设备的 Netlink 消息大小。它返回设备属性(P_Key、模式、多播设置)的总大小。
7. ipoib_link_ops 结构体
static struct rtnl_link_ops ipoib_link_ops __read_mostly = {
.kind = "ipoib",
.maxtype = IFLA_IPOIB_MAX,
.policy = ipoib_policy,
.priv_size = sizeof(struct ipoib_dev_priv),
.setup = ipoib_setup_common,
.newlink = ipoib_new_child_link,
.changelink = ipoib_changelink,
.get_size = ipoib_get_size,
.fill_info = ipoib_fill_info,
};
ipoib_link_ops 结构体定义了 IPoIB 设备的 Netlink 操作集。它指定了设备类型(kind)、属性策略(policy)、设备私有数据结构的大小(priv_size)以及各种操作函数(如 newlink、changelink、fill_info 等)。
8. ipoib_netlink_init 和 ipoib_netlink_fini 函数
int __init ipoib_netlink_init(void)
{
return rtnl_link_register(&ipoib_link_ops);
}
void __exit ipoib_netlink_fini(void)
{
rtnl_link_unregister(&ipoib_link_ops);
}
这两个函数分别用于在模块加载时注册和卸载时注销 IPoIB 的 Netlink 操作集。
9. 模块别名
MODULE_ALIAS_RTNL_LINK("ipoib");
MODULE_ALIAS_RTNL_LINK("ipoib") 为 rtnl 链接操作设置别名,以便在用户空间通过名称引用该接口类型。
总结
ipoib_netlink.c 文件的主要功能是通过 Netlink 接口管理 IPoIB 设备的创建、配置和信息获取。它定义了 IPoIB 设备的 Netlink 操作集,并实现了相关的操作函数。通过这些函数,用户空间程序可以通过 Netlink 接口与内核中的 IPoIB 驱动进行交互,从而配置和管理 IPoIB 设备。
该文件是 IPoIB 驱动的重要组成部分,确保了 IPoIB 设备在内核中的灵活管理和配置。通过 Netlink 接口,用户空间程序可以动态地创建、修改和查询 IPoIB 设备的状态,从而满足不同应用场景的需求。

drivers\infiniband\ulp\ipoib\ipoib_netlink.c
/*
* Copyright (c) 2012 Mellanox Technologies. - All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <linux/netdevice.h>
#include <linux/if_arp.h> /* For ARPHRD_xxx */
#include <linux/module.h>
#include <net/rtnetlink.h>
#include "ipoib.h"
static const struct nla_policy ipoib_policy[IFLA_IPOIB_MAX + 1] = {
[IFLA_IPOIB_PKEY] = { .type = NLA_U16 },
[IFLA_IPOIB_MODE] = { .type = NLA_U16 },
[IFLA_IPOIB_UMCAST] = { .type = NLA_U16 },
};
static int ipoib_fill_info(struct sk_buff *skb, const struct net_device *dev)
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
u16 val;
if (nla_put_u16(skb, IFLA_IPOIB_PKEY, priv->pkey))
goto nla_put_failure;
val = test_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags);
if (nla_put_u16(skb, IFLA_IPOIB_MODE, val))
goto nla_put_failure;
val = test_bit(IPOIB_FLAG_UMCAST, &priv->flags);
if (nla_put_u16(skb, IFLA_IPOIB_UMCAST, val))
goto nla_put_failure;
return 0;
nla_put_failure:
return -EMSGSIZE;
}
#if defined(HAVE_RTNL_LINK_OPS_NEWLINK_5_PARAMS)
static int ipoib_changelink(struct net_device *dev, struct nlattr *tb[],
struct nlattr *data[],
struct netlink_ext_ack *extack)
#else
static int ipoib_changelink(struct net_device *dev, struct nlattr *tb[],
struct nlattr *data[])
#endif
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
u16 mode, umcast;
int ret = 0;
if (data[IFLA_IPOIB_MODE]) {
mode = nla_get_u16(data[IFLA_IPOIB_MODE]);
if (mode == IPOIB_MODE_DATAGRAM)
ret = priv->fp.ipoib_set_mode(dev, "datagram\n");
else if (mode == IPOIB_MODE_CONNECTED)
ret = priv->fp.ipoib_set_mode(dev, "connected\n");
else
ret = -EINVAL;
if (ret < 0)
goto out_err;
}
if (data[IFLA_IPOIB_UMCAST]) {
umcast = nla_get_u16(data[IFLA_IPOIB_UMCAST]);
ipoib_set_umcast(dev, umcast);
}
out_err:
return ret;
}
#if defined(HAVE_RTNL_LINK_OPS_NEWLINK_5_PARAMS)
static int ipoib_new_child_link(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
#elif defined(HAVE_RTNL_LINK_OPS_NEWLINK_4_PARAMS)
static int ipoib_new_child_link(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[])
#else
static int ipoib_new_child_link(struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[])
#endif
{
struct net_device *pdev;
struct ipoib_dev_priv *ppriv;
struct ipoib_dev_priv *priv;
u16 child_pkey;
int err;
if (!tb[IFLA_LINK])
return -EINVAL;
#ifdef HAVE_RTNL_LINK_OPS_NEWLINK_4_PARAMS
pdev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK]));
#else
pdev = __dev_get_by_index(dev_net(dev), nla_get_u32(tb[IFLA_LINK]));
#endif
if (!pdev || pdev->type != ARPHRD_INFINIBAND)
return -ENODEV;
ppriv = ipoib_priv(pdev);
if (test_bit(IPOIB_FLAG_SUBINTERFACE, &ppriv->flags)) {
ipoib_warn(ppriv, "child creation disallowed for child devices\n");
return -EINVAL;
}
if (!data || !data[IFLA_IPOIB_PKEY]) {
ipoib_dbg(ppriv, "no pkey specified, using parent pkey\n");
child_pkey = ppriv->pkey;
} else
child_pkey = nla_get_u16(data[IFLA_IPOIB_PKEY]);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
err = ipoib_set_fp_rss(priv, ppriv->ca);
if (err) {
ipoib_warn(ppriv, "failed to set pkey function pointers\n");
goto out;
}
err = ipoib_intf_init(ppriv->ca, ppriv->port, dev->name, dev, priv);
if (err) {
ipoib_warn(ppriv, "failed to initialize pkey device\n");
goto out;
}
err = __ipoib_vlan_add(ppriv, ipoib_priv(dev),
child_pkey, IPOIB_RTNL_CHILD);
if (err)
goto out;
if (data) {
#if defined(HAVE_RTNL_LINK_OPS_NEWLINK_5_PARAMS)
err = ipoib_changelink(dev, tb, data, extack);
#else
err = ipoib_changelink(dev, tb, data);
#endif
if (err) {
unregister_netdevice(dev);
return err;
}
}
return 0;
out:
kfree(priv);
return err;
}
static size_t ipoib_get_size(const struct net_device *dev)
{
return nla_total_size(2) + /* IFLA_IPOIB_PKEY */
nla_total_size(2) + /* IFLA_IPOIB_MODE */
nla_total_size(2); /* IFLA_IPOIB_UMCAST */
}
static struct rtnl_link_ops ipoib_link_ops __read_mostly = {
.kind = "ipoib",
.maxtype = IFLA_IPOIB_MAX,
.policy = ipoib_policy,
.priv_size = sizeof(struct ipoib_dev_priv),
.setup = ipoib_setup_common,
.newlink = ipoib_new_child_link,
.changelink = ipoib_changelink,
.get_size = ipoib_get_size,
.fill_info = ipoib_fill_info,
};
struct rtnl_link_ops *ipoib_get_link_ops(void)
{
return &ipoib_link_ops;
}
int __init ipoib_netlink_init(void)
{
return rtnl_link_register(&ipoib_link_ops);
}
void __exit ipoib_netlink_fini(void)
{
rtnl_link_unregister(&ipoib_link_ops);
}
MODULE_ALIAS_RTNL_LINK("ipoib");

这个文件 ipoib_netlink.c 是 InfiniBand 协议中的 IP over InfiniBand (IPoIB) 驱动的一部分,主要用于处理与 Linux 内核的网络链接层(Netlink)相关的操作。Netlink 是 Linux 内核与用户空间进程之间进行通信的一种机制,通常用于配置网络设备、路由、策略路由等。
文件功能概述
该文件的主要功能是通过 Netlink 接口来管理和配置 IPoIB 设备。具体来说,它实现了以下功能:
-
IPoIB 设备的创建和配置:通过 Netlink 接口创建新的 IPoIB 子接口,并配置其属性(如 P_Key、模式、是否启用多播等)。
-
IPoIB 设备信息的获取:通过 Netlink 接口获取 IPoIB 设备的当前配置信息。
-
IPoIB 设备属性的修改:通过 Netlink 接口修改 IPoIB 设备的属性(如模式、多播设置等)。
代码分析
1. ipoib_policy 结构体
static const struct nla_policy ipoib_policy[IFLA_IPOIB_MAX + 1] = {
[IFLA_IPOIB_PKEY] = { .type = NLA_U16 },
[IFLA_IPOIB_MODE] = { .type = NLA_U16 },
[IFLA_IPOIB_UMCAST] = { .type = NLA_U16 },
};
这个结构体定义了 Netlink 属性的验证策略。ipoib_policy 数组指定了 IPoIB 设备支持的 Netlink 属性及其类型。例如,IFLA_IPOIB_PKEY 属性是一个 16 位无符号整数(NLA_U16)。
2. ipoib_fill_info 函数
static int ipoib_fill_info(struct sk_buff *skb, const struct net_device *dev)
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
u16 val;
if (nla_put_u16(skb, IFLA_IPOIB_PKEY, priv->pkey))
goto nla_put_failure;
val = test_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags);
if (nla_put_u16(skb, IFLA_IPOIB_MODE, val))
goto nla_put_failure;
val = test_bit(IPOIB_FLAG_UMCAST, &priv->flags);
if (nla_put_u16(skb, IFLA_IPOIB_UMCAST, val))
goto nla_put_failure;
return 0;
nla_put_failure:
return -EMSGSIZE;
}
这个函数用于将 IPoIB 设备的当前配置信息填充到 Netlink 消息中。它通过 nla_put_u16 函数将设备的 P_Key、模式和多播设置等信息添加到 sk_buff 中。如果添加失败,则返回 -EMSGSIZE。
3. ipoib_changelink 函数
static int ipoib_changelink(struct net_device *dev, struct nlattr *tb[],
struct nlattr *data[], struct netlink_ext_ack *extack)
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
u16 mode, umcast;
int ret = 0;
if (data[IFLA_IPOIB_MODE]) {
mode = nla_get_u16(data[IFLA_IPOIB_MODE]);
if (mode == IPOIB_MODE_DATAGRAM)
ret = priv->fp.ipoib_set_mode(dev, "datagram\n");
else if (mode == IPOIB_MODE_CONNECTED)
ret = priv->fp.ipoib_set_mode(dev, "connected\n");
else
ret = -EINVAL;
if (ret < 0)
goto out_err;
}
if (data[IFLA_IPOIB_UMCAST]) {
umcast = nla_get_u16(data[IFLA_IPOIB_UMCAST]);
ipoib_set_umcast(dev, umcast);
}
out_err:
return ret;
}
这个函数用于修改 IPoIB 设备的属性。它根据传入的 Netlink 属性数据(data 参数)来设置设备的工作模式(IPOIB_MODE_DATAGRAM 或 IPOIB_MODE_CONNECTED)以及是否启用多播(umcast)。如果设置失败,则返回相应的错误码。
4. ipoib_new_child_link 函数
static int ipoib_new_child_link(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
{
struct net_device *pdev;
struct ipoib_dev_priv *ppriv;
struct ipoib_dev_priv *priv;
u16 child_pkey;
int err;
if (!tb[IFLA_LINK])
return -EINVAL;
pdev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK]));
if (!pdev || pdev->type != ARPHRD_INFINIBAND)
return -ENODEV;
ppriv = ipoib_priv(pdev);
if (test_bit(IPOIB_FLAG_SUBINTERFACE, &ppriv->flags)) {
ipoib_warn(ppriv, "child creation disallowed for child devices\n");
return -EINVAL;
}
if (!data || !data[IFLA_IPOIB_PKEY]) {
ipoib_dbg(ppriv, "no pkey specified, using parent pkey\n");
child_pkey = ppriv->pkey;
} else
child_pkey = nla_get_u16(data[IFLA_IPOIB_PKEY]);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
err = ipoib_set_fp_rss(priv, ppriv->ca);
if (err) {
ipoib_warn(ppriv, "failed to set pkey function pointers\n");
goto out;
}
err = ipoib_intf_init(ppriv->ca, ppriv->port, dev->name, dev, priv);
if (err) {
ipoib_warn(ppriv, "failed to initialize pkey device\n");
goto out;
}
err = __ipoib_vlan_add(ppriv, ipoib_priv(dev),
child_pkey, IPOIB_RTNL_CHILD);
if (err)
goto out;
if (data) {
err = ipoib_changelink(dev, tb, data, extack);
if (err) {
unregister_netdevice(dev);
return err;
}
}
return 0;
out:
kfree(priv);
return err;
}
这个函数用于创建一个新的 IPoIB 子接口。它首先检查父设备是否存在并且类型是否正确,然后根据传入的 Netlink 属性数据来初始化子接口的 P_Key 和其他属性。如果初始化成功,则调用 ipoib_changelink 函数来进一步配置设备。
5. ipoib_get_size 函数
static size_t ipoib_get_size(const struct net_device *dev)
{
return nla_total_size(2) + /* IFLA_IPOIB_PKEY */
nla_total_size(2) + /* IFLA_IPOIB_MODE */
nla_total_size(2); /* IFLA_IPOIB_UMCAST */
}
这个函数用于计算 IPoIB 设备的 Netlink 消息大小。它返回设备属性(P_Key、模式、多播设置)的总大小。
6. ipoib_link_ops 结构体
static struct rtnl_link_ops ipoib_link_ops __read_mostly = {
.kind = "ipoib",
.maxtype = IFLA_IPOIB_MAX,
.policy = ipoib_policy,
.priv_size = sizeof(struct ipoib_dev_priv),
.setup = ipoib_setup_common,
.newlink = ipoib_new_child_link,
.changelink = ipoib_changelink,
.get_size = ipoib_get_size,
.fill_info = ipoib_fill_info,
};
这个结构体定义了 IPoIB 设备的 Netlink 操作集。它指定了设备类型(kind)、属性策略(policy)、设备私有数据结构的大小(priv_size)以及各种操作函数(如 newlink、changelink、fill_info 等)。
7. ipoib_netlink_init 和 ipoib_netlink_fini 函数
int __init ipoib_netlink_init(void)
{
return rtnl_link_register(&ipoib_link_ops);
}
void __exit ipoib_netlink_fini(void)
{
rtnl_link_unregister(&ipoib_link_ops);
}
这两个函数分别用于在模块加载时注册和卸载时注销 IPoIB 的 Netlink 操作集。
总结
ipoib_netlink.c 文件的主要功能是通过 Netlink 接口管理 IPoIB 设备的创建、配置和信息获取。它定义了 IPoIB 设备的 Netlink 操作集,并实现了相关的操作函数。通过这些函数,用户空间程序可以通过 Netlink 接口与内核中的 IPoIB 驱动进行交互,从而配置和管理 IPoIB 设备。

该文件是 InfiniBand IP over InfiniBand (IPOIB) 驱动的一部分,主要负责处理与网络命名空间(Netlink)相关的操作。以下是各个部分的详细解释:
1. 头文件包含
#include <linux/netdevice.h>
#include <linux/if_arp.h> /* For ARPHRD_xxx */
#include <linux/module.h>
#include <net/rtnetlink.h>
#include "ipoib.h"
linux/netdevice.h: 提供网络设备相关的结构和函数。linux/if_arp.h: 定义了以太网地址类型,如ARPHRD_INFINIBAND。linux/module.h: 用于模块的初始化和退出。net/rtnetlink.h: 提供与 Netlink 相关的结构和函数,用于内核与用户空间的通信。ipoib.h: 自定义的 IPOIB 驱动头文件,包含驱动特定的定义和结构。
2. NLA(Netlink Attribute)策略
static const struct nla_policy ipoib_policy[IFLA_IPOIB_MAX + 1] = {
[IFLA_IPOIB_PKEY] = { .type = NLA_U16 },
[IFLA_IPOIB_MODE] = { .type = NLA_U16 },
[IFLA_IPOIB_UMCAST] = { .type = NLA_U16 },
};
- 定义了 IPOIB 接口支持的 Netlink 属性及其类型:
IFLA_IPOIB_PKEY: 16 位 PKEY 值。IFLA_IPOIB_MODE: 16 位模式(如数据报模式或连接模式)。IFLA_IPOIB_UMCAST: 16 位是否启用多播。
3. 填充接口信息
static int ipoib_fill_info(struct sk_buff *skb, const struct net_device *dev)
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
u16 val;
if (nla_put_u16(skb, IFLA_IPOIB_PKEY, priv->pkey))
goto nla_put_failure;
val = test_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags);
if (nla_put_u16(skb, IFLA_IPOIB_MODE, val))
goto nla_put_failure;
val = test_bit(IPOIB_FLAG_UMCAST, &priv->flags);
if (nla_put_u16(skb, IFLA_IPOIB_UMCAST, val))
goto nla_put_failure;
return 0;
nla_put_failure:
return -EMSGSIZE;
}
- 功能: 将 IPOIB 接口的当前状态信息填充到 Netlink 消息中。
- 步骤:
- 获取设备的私有数据
priv。 - 将 PKEY、模式(通过标志位测试)和多播状态添加到 Netlink 消息中。
- 如果任何
nla_put_u16调用失败,返回错误。
- 获取设备的私有数据
4. 处理接口变更
static int ipoib_changelink(struct net_device *dev, struct nlattr *tb[],
struct nlattr *data[])
- 功能: 处理通过 Netlink 发送的接口配置变更。
- 步骤:
- 从
data中提取模式 (IFLA_IPOIB_MODE) 并调用相应的设置函数。 - 处理多播配置 (
IFLA_IPOIB_UMCAST)。
- 从
5. 创建子接口
static int ipoib_new_child_link(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[])
- 功能: 创建 IPOIB 的子接口(如 VLAN 或其他虚拟接口)。
- 步骤:
- 验证父接口是否存在且为 InfiniBand 类型。
- 分配并初始化子接口的私有数据。
- 设置函数指针和初始化接口。
- 调用
ipoib_changelink应用配置变更。
6. 获取接口信息的大小
static size_t ipoib_get_size(const struct net_device *dev)
{
return nla_total_size(2) + /* IFLA_IPOIB_PKEY */
nla_total_size(2) + /* IFLA_IPOIB_MODE */
nla_total_size(2); /* IFLA_IPOIB_UMCAST */
}
- 功能: 计算填充接口信息所需的 Netlink 消息大小。
7. 定义 rtnl_link_ops 结构
static struct rtnl_link_ops ipoib_link_ops __read_mostly = {
.kind = "ipoib",
.maxtype = IFLA_IPOIB_MAX,
.policy = ipoib_policy,
.priv_size = sizeof(struct ipoib_dev_priv),
.setup = ipoib_setup_common,
.newlink = ipoib_new_child_link,
.changelink = ipoib_changelink,
.get_size = ipoib_get_size,
.fill_info = ipoib_fill_info,
};
- 功能: 定义 IPOIB 接口的 Netlink 操作。
- 字段:
kind: 接口类型名称。maxtype: 支持的最大属性类型。policy: 属性验证策略。priv_size: 私有数据结构的大小。setup,newlink,changelink,get_size,fill_info: 各种操作的回调函数。
8. 注册和注销 rtnl_link_ops
struct rtnl_link_ops *ipoib_get_link_ops(void)
{
return &ipoib_link_ops;
}
int __init ipoib_netlink_init(void)
{
return rtnl_link_register(&ipoib_link_ops);
}
void __exit ipoib_netlink_fini(void)
{
rtnl_link_unregister(&ipoib_link_ops);
}
- 功能: 提供接口注册和注销的函数。
ipoib_netlink_init: 在模块初始化时注册ipoib_link_ops。ipoib_netlink_fini: 在模块退出时注销ipoib_link_ops。
9. 模块别名
MODULE_ALIAS_RTNL_LINK("ipoib");
- 功能: 为 rtnl 链接操作设置别名,以便在用户空间通过名称引用该接口类型。
总结
该文件主要实现了 IPOIB 接口在 Netlink 层的操作,包括接口信息的填充、配置变更的处理以及子接口的创建。通过定义 rtnl_link_ops 结构并注册相应的回调函数,驱动能够响应来自用户空间的配置请求,并管理 IPOIB 接口的状态和属性。

以下是对ipoib_netlink.c文件内容的分析:
文件概述
该文件是InfiniBand上的IP over InfiniBand(ipoib)驱动的一部分,主要实现了与网络链路相关的netlink接口功能,用于ipoib设备的创建、修改以及信息获取等操作,使得用户空间可以通过netlink与ipoib驱动进行交互,动态地管理ipoib网络设备。
包含的头文件
-
linux/netdevice.h:包含网络设备相关的定义和函数声明,如struct net_device等。 -
linux/if_arp.h:定义了地址解析协议相关的常量,如ARPHRD_xxx,用于标识网络设备的类型,其中ARPHRD_INFINIBAND表示InfiniBand设备。 -
linux/module.h:用于模块相关操作,如模块加载、卸载等。 -
net/rtnetlink.h:包含路由netlink相关的定义和函数声明,用于处理网络设备的创建、修改等请求。 -
ipoib.h:ipoib驱动的头文件,定义了ipoib相关的数据结构、常量和函数声明等。
定义的常量和数据结构
-
ipoib_policy:定义了一个nla_policy数组,用于指定ipoib设备属性的类型和长度限制。例如,IFLA_IPOIB_PKEY、IFLA_IPOIB_MODE和IFLA_IPOIB_UMCAST分别对应于pkey、模式和umcast属性,它们的类型都是NLA_U16,即16位无符号整数。 -
struct rtnl_link_ops ipoib_link_ops:定义了一个rtnl_link_ops结构体实例,用于注册ipoib设备的netlink操作。其中包含多个成员函数指针,指向ipoib驱动中实现的相应功能函数,如newlink用于创建新的ipoib子设备,changelink用于修改ipoib设备的属性,get_size和fill_info用于获取和填充ipoib设备的信息等。
主要函数
-
ipoib_fill_info:用于填充ipoib设备的信息到一个sk_buff中,以便通过netlink发送给用户空间。它会获取ipoib设备的pkey、模式和umcast属性,并使用nla_put_u16函数将这些属性添加到sk_buff中。如果在添加属性时发生错误(如空间不足),则会跳转到nla_put_failure标签,返回-EMSGSIZE错误码。 -
ipoib_changelink:用于修改ipoib设备的属性。它会根据传入的data数组中的属性,分别处理模式和umcast属性的修改。对于模式属性,会根据传入的模式值调用ipoib_set_mode函数设置ipoib设备的模式;对于umcast属性,会调用ipoib_set_umcast函数设置umcast值。如果在设置过程中发生错误,会返回相应的错误码。 -
ipoib_new_child_link:用于创建新的ipoib子设备。它会首先检查传入的参数,获取父设备和子设备的pkey等信息。然后,会为子设备分配私有数据结构ipoib_dev_priv,并调用相关函数初始化子设备,如设置功能指针、初始化接口等。最后,会调用__ipoib_vlan_add函数将子设备添加到父设备中,并根据传入的data数组调用ipoib_changelink函数设置子设备的属性。如果在创建过程中发生错误,会释放分配的资源并返回错误码。 -
ipoib_get_size:用于计算ipoib设备信息的大小。它会根据ipoib设备的属性数量和类型,计算出需要的空间大小,以便在sk_buff中分配足够的空间来存储设备信息。 -
ipoib_get_link_ops:返回ipoib_link_ops结构体的指针,供其他部分的代码使用。 -
ipoib_netlink_init和ipoib_netlink_fini:分别用于初始化和清理ipoib的netlink接口。在ipoib_netlink_init中,会调用rtnl_link_register函数注册ipoib_link_ops;在ipoib_netlink_fini中,会调用rtnl_link_unregister函数注销ipoib_link_ops。
模块相关宏
-
MODULE_ALIAS_RTNL_LINK("ipoib"):为ipoib模块指定一个别名,使得当用户空间通过netlink请求创建ipoib设备时,内核能够找到并加载ipoib模块。

以下是对 drivers\infiniband\ulp\ipoib\ipoib_netlink.c 文件内容的分析:
1. 版权与许可声明部分
开头部分是版权声明以及软件许可相关的说明,指出该软件可以在两种许可证(GNU General Public License (GPL) Version 2 或者 OpenIB.org BSD 许可证)下选择使用,并详细列出了在 BSD 许可证下使用该软件需满足的条件,比如对源代码和二进制形式再分发时要保留版权声明、遵循相应的免责条款等。
2. 头文件包含部分
#include <linux/netdevice.h>
#include <linux/if_arp.h> /* For ARPHRD_xxx */
#include <linux/module.h>
#include <net/rtnetlink.h>
#include "ipoib.h"
包含了一系列必要的头文件,像 <linux/netdevice.h> 用于处理网络设备相关的结构体和函数等;<linux/if_arp.h> 提供了与 ARP 相关的定义(此处用于获取 ARPHRD_xxx 相关内容);<linux/module.h> 常用于 Linux 内核模块相关的操作;<net/rtnetlink.h> 用于和网络链路相关的 Netlink 通信操作;"ipoib.h" 应该是和该 ipoib 模块自定义的一些结构体、函数声明等相关的头文件。
3. 网络属性策略定义部分
static const struct nla_policy ipoib_policy[IFLA_IPOIB_MAX + 1] = {
[IFLA_IPOIB_PKEY] = {.type = NLA_U16 },
[IFLA_IPOIB_MODE] = {.type = NLA_U16 },
[IFLA_IPOIB_UMCAST] = {.type = NLA_U16 },
};
定义了一个名为 ipoib_policy 的网络属性(Netlink Attributes,NLA)策略数组,用于规定 IFLA_IPOIB_ 开头的几种网络属性(如 PKEY、MODE、UMCAST)的数据类型为无符号 16 位整数(NLA_U16),这在后续处理网络链路相关属性配置时会起到类型检查等作用。
4. ipoib_fill_info 函数
static int ipoib_fill_info(struct sk_buff *skb, const struct net_device *dev)
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
u16 val;
if (nla_put_u16(skb, IFLA_IPOIB_PKEY, priv->pkey))
goto nla_put_failure;
val = test_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags);
if (nla_put_u16(skb, IFLA_IPOIB_MODE, val))
goto nla_put_failure;
val = test_bit(IPOIB_FLAG_UMCAST, &priv->flags);
if (nla_put_u16(skb, IFLA_IPOIB_UMCAST, val))
goto nla_put_failure;
return 0;
nla_put_failure:
return -EMSGSIZE;
}
这个函数的作用是将 ipoib 相关的设备信息填充到一个 sk_buff 结构体中,主要是将设备的私有数据(通过 ipoib_priv 函数获取)里的 pkey、根据相关标志位确定的 MODE 和 UMCAST 信息,以无符号 16 位整数的形式添加到 sk_buff 中,如果添加过程中出现问题(比如空间不足等情况,通过 nla_put_xxx 函数返回非零值判断),则跳转到错误处理部分返回 -EMSGSIZE 表示消息尺寸相关错误,否则返回 0 表示填充成功。
5. ipoib_changelink 函数
#if defined(HAVE_RTNL_LINK_OPS_NEWLINK_5_PARAMS)
static int ipoib_changelink(struct net_device *dev, struct nlattr *tb[],
struct nlattr *data[],
struct netlink_ext_ack *extack)
#else
static int ipoib_changelink(struct net_device *dev, struct nlattr *tb[],
struct nlattr *data[])
#endif
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
u16 mode, umcast;
int ret = 0;
if (data[IFLA_IPOIB_MODE]) {
mode = nla_get_u16(data[IFLA_IPOIB_MODE]);
if (mode == IPOIB_MODE_DATAGRAM)
ret = priv->fp.ipoib_set_mode(dev, "datagram\n");
else if (mode == IPOIB_MODE_CONNECTED)
ret = priv->fp.ipoib_set_mode(dev, "connected\n");
else
ret = -EINVAL;
if (ret < 0)
goto out_err;
}
if (data[IFLA_IPOIB_UMCAST]) {
umcast = nla_get_u16(data[IFLA_IPOIB_UMCAST]);
ipoib_set_umcast(dev, umcast);
}
out_err:
return ret;
}
这个函数用于处理网络链路属性更改操作。它首先从传入的网络属性数据中获取 IFLA_IPOIB_MODE 和 IFLA_IPOIB_UMCAST 相关的值(如果存在的话),对于 MODE,根据不同的值(DATAGRAM 或者 CONNECTED)调用相应的设备模式设置函数,若值不合法则返回错误;对于 UMCAST,则调用 ipoib_set_umcast 函数进行相应设置,最后返回操作的结果(成功或失败对应的返回值)。
6. ipoib_new_child_link 函数
#if defined(HAVE_RTNL_LINK_OPS_NEWLINK_5_PARAMS)
static int ipoib_new_child_link(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
#elif defined(HAVE_RTNL_LINK_OPS_NEWLINK_4_PARAMS)
static int ipoib_new_child_link(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[])
#else
static int ipoib_new_child_link(struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[])
#endif
{
// 一系列变量定义和初始化操作
//...
if (!tb[IFLA_LINK])
return -EINVAL;
#ifdef HAVE_RTNL_LINK_OPS_NEWLINK_4_PARAMS
pdev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK]));
#else
pdev = __dev_get_by_index(dev_net(dev), nla_get_u32(tb[IFLA_LINK]));
#endif
if (!pdev || pdev->type!= ARPHRD_INFINIBAND)
return -ENODEV;
// 后续一系列设备初始化、属性设置、添加 VLAN 等操作
//...
return 0;
}
这个函数用于创建一个新的子链路(ipoib 相关)。首先进行一些参数合法性检查,比如通过 IFLA_LINK 属性获取对应的父设备,判断父设备是否存在以及是否为 Infiniband 类型设备等。然后进行子设备相关的内存分配、函数指针设置、设备初始化、VLAN 添加等操作,过程中如果出现错误会进行相应的错误处理(如释放已分配内存等),最后返回操作结果(成功返回 0,失败返回对应错误码)。
7. ipoib_get_size 函数
static size_t ipoib_get_size(const struct net_device *dev)
{
return nla_total_size(2) + /* IFLA_IPOIB_PKEY */
nla_total_size(2) + /* IFLA_IPOIB_MODE */
nla_total_size(2); /* IFLA_IPOIB_UMCAST */
}
该函数用于计算 ipoib 相关网络设备信息所占用的空间大小,通过分别计算 IFLA_IPOIB_PKEY、IFLA_IPOIB_MODE 和 IFLA_IPOIB_UMCAST 这几个属性各自占用的空间(都调用 nla_total_size 来计算无符号 16 位整数类型属性占用空间)并求和返回总的空间大小。
8. ipoib_link_ops 结构体与相关函数
static struct rtnl_link_ops ipoib_link_ops __read_mostly = {
.kind = "ipoib",
.maxtype = IFLA_IPOIB_MAX,
.policy = ipoib_policy,
.priv_size = sizeof(struct ipoib_dev_priv),
.setup = ipoib_setup_common,
.newlink = ipoib_new_child_link,
.changelink = ipoib_changelink,
.get_size = ipoib_get_size,
.fill_info = ipoib_fill_info,
};
struct rtnl_link_ops *ipoib_get_link_ops(void)
{
return &ipoib_link_ops;
}
int __init ipoib_netlink_init(void)
{
return rtnl_link_register(&ipoib_link_ops);
}
void __exit ipoib_netlink_fini(void)
{
rtnl_link_unregister(&ipoib_link_ops);
}
MODULE_ALIAS_RTNL_LINK("ipoib");
- 定义了
ipoib_link_ops结构体,它是rtnl_link_ops类型,用于描述ipoib网络链路相关的操作集合,包括链路类型("ipoib")、最大属性类型(IFLA_IPOIB_MAX)、属性策略(ipoib_policy)、私有数据大小(sizeof(struct ipoib_dev_priv))以及前面介绍的各种链路操作函数(如创建新链路、更改链路属性、获取链路信息大小、填充链路信息等)。 ipoib_get_link_ops函数用于获取ipoib_link_ops结构体指针,方便外部模块调用相关链路操作集合。ipoib_netlink_init函数在模块初始化时被调用(通过__init标识),用于将ipoib_link_ops注册到内核的网络链路管理机制中,使得内核可以识别并使用ipoib相关的链路操作。ipoib_netlink_fini函数在模块退出时被调用(通过__exit标识),用于将之前注册的ipoib_link_ops从内核网络链路管理机制中注销,进行相应的清理工作。MODULE_ALIAS_RTNL_LINK("ipoib")则是给该模块添加一个别名,方便在相关链路操作场景下通过别名来引用该模块。
总体而言,这个文件主要实现了 ipoib 驱动在 Linux 内核中与网络链路相关的 Netlink 操作相关的功能,涵盖了链路信息填充、链路属性更改、子链路创建以及相关的初始化和注销操作等内容,是 ipoib 驱动在内核网络链路层面交互的重要实现部分。
更多推荐


所有评论(0)