一、引言
在现代网络通信领域,InfiniBand over Ethernet (IPoIB) 驱动的高效性对于网络性能有着至关重要的影响。其中,接收方扩展 (RSS) 和传输方扩展 (TSS) 是提升网络性能的关键技术。ipoib_main_rss.c
文件作为IPoIB驱动中处理RSS和TSS的重要源码文件,蕴含着丰富的功能和复杂的逻辑。
二、版权与许可
文件开头的版权声明和许可证部分明确了代码的归属和使用权限。这不仅体现了软件开发中的知识产权保护意识,也为使用者提供了合法使用代码的依据。无论是遵循GNU General Public License (GPL) Version 2还是OpenIB.org BSD许可证,都确保了代码在开源环境下的有序传播和使用。
三、核心功能函数剖析
-
ipoib_set_mode_rss
函数- 此函数主要用于设置IPoIB设备的工作模式,即“connected”或“datagram”模式。它在网络设备的运行过程中起到了关键的配置调整作用。
- 在逻辑上,首先会判断当前设备的连接管理状态与输入模式是否匹配。如果切换到“connected”模式,需要检查设备对连接管理模式的支持情况。若支持,则会进行一系列操作,如设置标志位、更新设备特性(像禁用某些特性、设置最大传输单元MTU等)、处理发送队列标志以及刷新路径等。这一系列操作是为了确保设备在新的模式下能够正确运行。例如,当启用连接管理标志时,可能会因为某些特性不兼容而需要调整,像
dev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_TSO)
这样的操作就是为了适配新的模式。如果设备不支持连接管理模式却要切换到“connected”模式,则会返回错误码-EINVAL
。 - 同理,当切换到“datagram”模式时,会清除连接管理标志位,并根据设备的不同能力(如是否支持IP校验和等)来更新设备特性并设置合适的MTU。
-
ipoib_select_queue_sw_rss
函数- 该函数在发送队列的选择上发挥着重要作用。它通过多种条件判断来确定使用哪个发送队列。
- 对于广播或多播地址的情况,会直接返回指定的队列编号。在考虑连接管理模式时,如果启用了且处于管理启用状态,会基于远程QP进行哈希计算来确定队列编号。这一机制有助于在多队列环境下合理地分配数据包的发送任务。同时,还会检查是否支持TSS,如果不支持则返回特定队列编号,若支持则会进一步处理数据包头部并根据不同编译条件确定最终的发送队列编号。
-
ipoib_timeout_rss
函数- 主要用于处理网络传输超时的情况。当出现超时时,它会输出包含超时延迟时间的警告信息。
- 并且,它会遍历发送队列,检查队列是否停止以及获取队列的发送头和尾指针相关信息。最后调度相关的工作任务来进一步处理超时情况,这有助于及时发现和解决网络传输中的问题,保障网络的稳定性。
-
ipoib_get_stats_rss
函数- 负责获取网络设备的统计信息。它通过读取接收和发送队列的相关统计数据,如接收/发送的数据包数量、字节数、错误数、丢弃数等。
- 在获取这些数据的过程中,使用了读写锁来保证数据访问的一致性。然后将汇总后的统计信息更新到网络设备的统计结构体中并返回该结构体指针,为网络性能的分析和故障排查提供了重要依据。
四、设备初始化与反初始化相关函数
-
ipoib_dev_init_default_rss
函数- 进行网络设备的默认初始化操作。这包括为接收和发送队列分配内存并初始化相关数据结构。
- 例如,为每个队列分配相应的环形缓冲区等资源,并且添加NAPI相关的回调函数。同时,还会调用
ipoib_transport_dev_init_rss
进行传输层相关的初始化,并设置设备地址相关标志位来表示支持TSS等情况。如果在初始化过程中的任何环节出现内存分配失败等问题,会进行相应的资源清理并返回错误码-ENOMEM
,成功则返回0。
-
ipoib_dev_uninit_rss
函数- 执行网络设备的反初始化操作。它会清理之前初始化时所分配的各种资源,如邻居哈希表、Infiniband设备相关资源、工作队列等。
- 并且会将接收和发送队列指针置空,确保没有残留的资源占用,从而保证设备的正常关闭和资源的有效回收。
五、与其他模块的交互
ipoib_set_fp_rss
和ipoib_get_hca_features
函数ipoib_set_fp_rss
函数通过获取Infiniband设备的特性信息,来初始化函数指针,以支持接收端扩展和非RSS设备的不同功能函数调用。- 而
ipoib_get_hca_features
函数则是通过调用ib_exp_query_device
函数获取扩展的Infiniband设备属性信息,基于设备的核心数量、是否支持某些特性等来确定设备的多个队列相关参数,如接收和发送队列的最大数量、实际数量以及RSS和TSS相关的QP数量等,并将设备的能力标志位保存到对应的结构体成员中。这两个函数之间相互协作,为设备在不同特性下的功能实现奠定了基础。
六、系统文件操作与网络设备操作结构体
-
系统文件操作函数(如
get_rx_chan
、set_rx_chan
等)- 这些函数用于实现与sysfs文件系统交互的功能。它们通过
DEVICE_ATTR
宏定义了不同的设备属性,例如获取和设置接收通道数量、最大接收通道数量、发送通道数量、最大发送通道数量等。 - 在函数内部会进行参数合法性检查、调用
ipoib_reinit_rss
进行设备重新初始化等操作,并返回相应的结果,这对于用户在系统层面管理和监控IPoIB设备提供了便利。
- 这些函数用于实现与sysfs文件系统交互的功能。它们通过
-
网络设备操作结构体(如
ipoib_netdev_ops_pf_sw_tss
和ipoib_netdev_ops_vf_sw_tss
)- 这些结构体定义了不同情况下网络设备的操作函数集,包括初始化、反初始化、打开、关闭、发送数据、获取统计信息等操作对应的函数。
- 根据设备是否为虚拟功能设备以及最大发送队列数量等因素来选择合适的操作结构体,确保设备在不同配置下能够正确地进行各种操作。
七、总结
综上所述,ipoib_main_rss.c
文件在IPoIB驱动中扮演着不可或缺的角色。通过对RSS和TSS相关功能的全面实现,包括模式设置、队列选择、超时处理、统计信息获取、设备初始化与反初始化等多个方面,有效地提升了IPoIB设备在网络通信中的性能。各个函数之间紧密协作,并且与其他模块有着良好的交互关系,共同为构建高效、稳定的网络环境提供了有力的支持。
drivers\infiniband\ulp\ipoib\rss_tss\ipoib_main_rss.c
/*
* Copyright (c) 2004 Topspin Communications. All rights reserved.
* Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
* Copyright (c) 2004 Voltaire, Inc. 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.
*/
int ipoib_set_mode_rss(struct net_device *dev, const char *buf)
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
struct ipoib_send_ring *send_ring;
int i;
if ((test_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags) &&
!strcmp(buf, "connected\n")) ||
(!test_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags) &&
!strcmp(buf, "datagram\n"))) {
return 0;
}
/* flush paths if we switch modes so that connections are restarted */
if (!strcmp(buf, "connected\n")) {
if (IPOIB_CM_SUPPORTED(dev->dev_addr)) {
set_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags);
ipoib_warn(priv, "enabling connected mode "
"will cause multicast packet drops\n");
#if defined (HAVE_NETDEV_UPDATE_FEATURES) && defined (HAVE_NDO_FIX_FEATURES)
netdev_update_features(dev);
#else
dev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_TSO);
#endif
dev_set_mtu(dev, ipoib_cm_max_mtu(dev));
rtnl_unlock();
send_ring = priv->send_ring;
for (i = 0; i < priv->num_tx_queues; i++) {
send_ring->tx_wr.wr.send_flags &= ~IB_SEND_IP_CSUM;
send_ring->tx_wr.wr.opcode = IB_WR_SEND;
send_ring++;
}
ipoib_flush_paths(dev);
return (!rtnl_trylock()) ? -EBUSY : 0;
} else {
ipoib_warn(priv, "Setting Connected Mode failed, "
"not supported by this device");
return -EINVAL;
}
}
if (!strcmp(buf, "datagram\n")) {
clear_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags);
#if defined (HAVE_NETDEV_UPDATE_FEATURES) && defined (HAVE_NDO_FIX_FEATURES)
netdev_update_features(dev);
#else
if (priv->hca_caps & IB_DEVICE_UD_IP_CSUM)
dev->features |= NETIF_F_IP_CSUM;
if (priv->max_send_sge > 1)
dev->features |= NETIF_F_SG;
if (priv->hca_caps & IB_DEVICE_UD_TSO)
if (dev->features & (NETIF_F_IP_CSUM | NETIF_F_SG))
dev->features |= NETIF_F_TSO;
#endif
dev_set_mtu(dev, min(priv->mcast_mtu, dev->mtu));
rtnl_unlock();
ipoib_flush_paths(dev);
return (!rtnl_trylock()) ? -EBUSY : 0;
}
return -EINVAL;
}
#ifdef HAVE_NDO_SELECT_QUEUE_HAS_3_PARMS_NO_FALLBACK
u16 ipoib_select_queue_sw_rss(struct net_device *dev, struct sk_buff *skb,
struct net_device *sb_dev)
#elif defined(HAVE_NDO_SELECT_QUEUE_HAS_ACCEL_PRIV) || defined(HAVE_SELECT_QUEUE_FALLBACK_T)
u16 ipoib_select_queue_sw_rss(struct net_device *dev, struct sk_buff *skb,
#ifdef HAVE_SELECT_QUEUE_FALLBACK_T
#ifdef HAVE_SELECT_QUEUE_NET_DEVICE
struct net_device *sb_dev,
#else
void *accel_priv,
#endif /* HAVE_SELECT_QUEUE_NET_DEVICE */
select_queue_fallback_t fallback)
#else
void *accel_priv)
#endif
#else /* HAVE_NDO_SELECT_QUEUE_HAS_ACCEL_PRIV || HAVE_SELECT_QUEUE_FALLBACK_T */
u16 ipoib_select_queue_sw_rss(struct net_device *dev, struct sk_buff *skb)
#endif
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
struct ipoib_pseudo_header *phdr;
struct ipoib_header *header;
phdr = (struct ipoib_pseudo_header *) skb->data;
/* (BC/MC) use designated QDISC -> parent QP */
if (unlikely(phdr->hwaddr[4] == 0xff))
return priv->tss_qp_num;
/* is CM in use */
if (IPOIB_CM_SUPPORTED(phdr->hwaddr)) {
if (ipoib_cm_admin_enabled(dev)) {
/* use remote QP for hash, so we use the same ring */
u32 *d32 = (u32 *)phdr->hwaddr;
u32 hv = jhash_1word(*d32 & cpu_to_be32(0xFFFFFF), 0);
return hv % priv->tss_qp_num;
}
else
/* the ADMIN CM might be up until transmit, and
* we might transmit on CM QP not from it's
* designated ring */
phdr->hwaddr[0] &= ~IPOIB_FLAGS_RC;
}
/* Did neighbour advertise TSS support */
if (unlikely(!IPOIB_TSS_SUPPORTED(phdr->hwaddr)))
return priv->tss_qp_num;
/* We are after ipoib_hard_header so skb->data is O.K. */
header = (struct ipoib_header *) skb->data;
header->tss_qpn_mask_sz |= priv->tss_qpn_mask_sz;
/* don't use special ring in TX */
#ifdef HAVE_NDO_SELECT_QUEUE_HAS_3_PARMS_NO_FALLBACK
return netdev_pick_tx(dev, skb, NULL) % priv->tss_qp_num;
#elif defined( HAVE_SELECT_QUEUE_FALLBACK_T_3_PARAMS)
return fallback(dev, skb, NULL) % priv->tss_qp_num;
#else
return fallback(dev, skb) % priv->tss_qp_num;
#endif
}
#ifdef HAVE_NDO_TX_TIMEOUT_GET_2_PARAMS
static void ipoib_timeout_rss(struct net_device *dev, unsigned int txqueue)
#else
static void ipoib_timeout_rss(struct net_device *dev)
#endif
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
struct ipoib_send_ring *send_ring;
u16 index;
ipoib_warn(priv, "transmit timeout: latency %d msecs\n",
jiffies_to_msecs(jiffies - dev_trans_start(dev)));
for (index = 0; index < priv->num_tx_queues; index++) {
if (__netif_subqueue_stopped(dev, index)) {
send_ring = priv->send_ring + index;
ipoib_warn(priv, "queue (%d) stopped, tx_head %u, tx_tail %u\n",
index, send_ring->tx_head,
send_ring->tx_tail);
}
}
schedule_work(&priv->tx_timeout_work);
}
static struct net_device_stats *ipoib_get_stats_rss(struct net_device *dev)
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
struct net_device_stats *stats = &dev->stats;
struct net_device_stats local_stats;
int i;
if (!down_read_trylock(&priv->rings_rwsem))
return stats;
if (!priv->recv_ring || !priv->send_ring) {
up_read(&priv->rings_rwsem);
return stats;
}
memset(&local_stats, 0, sizeof(struct net_device_stats));
for (i = 0; i < priv->num_rx_queues; i++) {
struct ipoib_rx_ring_stats *rstats = &priv->recv_ring[i].stats;
local_stats.rx_packets += rstats->rx_packets;
local_stats.rx_bytes += rstats->rx_bytes;
local_stats.rx_errors += rstats->rx_errors;
local_stats.rx_dropped += rstats->rx_dropped;
}
for (i = 0; i < priv->num_tx_queues; i++) {
struct ipoib_tx_ring_stats *tstats = &priv->send_ring[i].stats;
local_stats.tx_packets += tstats->tx_packets;
local_stats.tx_bytes += tstats->tx_bytes;
local_stats.tx_errors += tstats->tx_errors;
local_stats.tx_dropped += tstats->tx_dropped;
}
up_read(&priv->rings_rwsem);
stats->rx_packets = local_stats.rx_packets;
stats->rx_bytes = local_stats.rx_bytes;
stats->rx_errors = local_stats.rx_errors;
stats->rx_dropped = local_stats.rx_dropped;
stats->tx_packets = local_stats.tx_packets;
stats->tx_bytes = local_stats.tx_bytes;
stats->tx_errors = local_stats.tx_errors;
stats->tx_dropped = local_stats.tx_dropped;
return stats;
}
static void ipoib_napi_add_rss(struct net_device *dev)
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
int i;
for (i = 0; i < priv->num_rx_queues; i++)
netif_napi_add(dev, &priv->recv_ring[i].napi,
ipoib_rx_poll_rss, IPOIB_NUM_WC);
for (i = 0; i < priv->num_tx_queues; i++)
netif_napi_add(dev, &priv->send_ring[i].napi,
ipoib_tx_poll_rss, MAX_SEND_CQE);
}
static void ipoib_napi_del_rss(struct net_device *dev)
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
int i;
for (i = 0; i < priv->num_rx_queues; i++)
netif_napi_del(&priv->recv_ring[i].napi);
for (i = 0; i < priv->num_tx_queues; i++)
netif_napi_del(&priv->send_ring[i].napi);
}
static struct ipoib_neigh *ipoib_neigh_ctor_rss(u8 *daddr,
struct net_device *dev)
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
struct ipoib_neigh *neigh;
neigh = kzalloc(sizeof *neigh, GFP_ATOMIC);
if (!neigh)
return NULL;
neigh->dev = dev;
memcpy(&neigh->daddr, daddr, sizeof(neigh->daddr));
skb_queue_head_init(&neigh->queue);
INIT_LIST_HEAD(&neigh->list);
ipoib_cm_set(neigh, NULL);
/* one ref on behalf of the caller */
atomic_set(&neigh->refcnt, 1);
/*
* ipoib_neigh_alloc can be called from neigh_add_path without
* the protection of spin lock or from ipoib_mcast_send under
* spin lock protection. thus there is a need to use atomic
*/
if (priv->tss_qp_num > 0)
neigh->index = atomic_inc_return(&priv->tx_ring_ind)
% priv->tss_qp_num;
else
neigh->index = 0;
return neigh;
}
#ifdef CONFIG_COMPAT_LRO_ENABLED_IPOIB
static void ipoib_lro_setup_rss(struct ipoib_recv_ring *recv_ring,
struct ipoib_dev_priv *priv)
{
recv_ring->lro.lro_mgr.max_aggr = IPOIB_LRO_MAX_AGGR;
recv_ring->lro.lro_mgr.max_desc = IPOIB_MAX_LRO_DESCRIPTORS;
recv_ring->lro.lro_mgr.lro_arr = recv_ring->lro.lro_desc;
recv_ring->lro.lro_mgr.get_skb_header = get_skb_hdr;
recv_ring->lro.lro_mgr.features = LRO_F_NAPI;
recv_ring->lro.lro_mgr.dev = priv->dev;
recv_ring->lro.lro_mgr.ip_summed_aggr = CHECKSUM_UNNECESSARY;
}
#endif
int ipoib_dev_init_default_rss(struct net_device *dev)
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
struct ib_device *ca = priv->ca;
struct ipoib_send_ring *send_ring;
struct ipoib_recv_ring *recv_ring;
int i, rx_allocated, tx_allocated;
unsigned long alloc_size;
down_write(&priv->rings_rwsem);
/* Multi queue initialization */
priv->recv_ring = kzalloc(priv->num_rx_queues * sizeof(*recv_ring),
GFP_KERNEL);
if (!priv->recv_ring) {
pr_warn("%s: failed to allocate RECV ring (%d entries)\n",
ca->name, priv->num_rx_queues);
goto out;
}
alloc_size = priv->recvq_size * sizeof(*recv_ring->rx_ring);
rx_allocated = 0;
recv_ring = priv->recv_ring;
for (i = 0; i < priv->num_rx_queues; i++) {
recv_ring->rx_ring = kzalloc(alloc_size, GFP_KERNEL);
if (!recv_ring->rx_ring) {
pr_warn("%s: failed to allocate RX ring (%d entries)\n",
priv->ca->name, priv->recvq_size);
goto out_recv_ring_cleanup;
}
recv_ring->dev = dev;
recv_ring->index = i;
#ifdef CONFIG_COMPAT_LRO_ENABLED_IPOIB
ipoib_lro_setup_rss(recv_ring, priv);
#endif
recv_ring++;
rx_allocated++;
}
priv->send_ring = kzalloc(priv->num_tx_queues * sizeof(*send_ring),
GFP_KERNEL);
if (!priv->send_ring) {
pr_warn("%s: failed to allocate SEND ring (%d entries)\n",
ca->name, priv->num_tx_queues);
goto out_recv_ring_cleanup;
}
alloc_size = priv->sendq_size * sizeof(*send_ring->tx_ring);
tx_allocated = 0;
send_ring = priv->send_ring;
for (i = 0; i < priv->num_tx_queues; i++) {
send_ring->tx_ring = vzalloc(alloc_size);
if (!send_ring->tx_ring) {
pr_warn("%s: failed to allocate TX ring (%d entries)\n",
ca->name, priv->sendq_size);
goto out_send_ring_cleanup;
}
send_ring->dev = dev;
send_ring->index = i;
/* priv->tx_head, tx_tail & tx_outstanding are already 0 */
atomic_set(&send_ring->tx_outstanding, 0);
send_ring++;
tx_allocated++;
}
ipoib_napi_add_rss(dev);
if (ipoib_transport_dev_init_rss(dev, priv->ca)) {
pr_warn("%s: ipoib_transport_dev_init_rss failed\n", priv->ca->name);
goto out_napi_delete;
}
up_write(&priv->rings_rwsem);
/*
* advertise that we are willing to accept from TSS sender
* note that this only indicates that this side is willing to accept
* TSS frames, it doesn't implies that it will use TSS since for
* transmission the peer should advertise TSS as well
*/
priv->dev->dev_addr[0] |= IPOIB_FLAGS_TSS;
priv->dev->dev_addr[1] = (priv->qp->qp_num >> 16) & 0xff;
priv->dev->dev_addr[2] = (priv->qp->qp_num >> 8) & 0xff;
priv->dev->dev_addr[3] = (priv->qp->qp_num) & 0xff;
return 0;
out_napi_delete:
ipoib_napi_del_rss(dev);
out_send_ring_cleanup:
for (i = 0; i < tx_allocated; i++)
vfree(priv->send_ring[i].tx_ring);
kfree(priv->send_ring);
out_recv_ring_cleanup:
for (i = 0; i < rx_allocated; i++)
kfree(priv->recv_ring[i].rx_ring);
kfree(priv->recv_ring);
out:
priv->send_ring = NULL;
priv->recv_ring = NULL;
up_write(&priv->rings_rwsem);
return -ENOMEM;
}
static void ipoib_dev_uninit_rss(struct net_device *dev)
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
if (dev->reg_state != NETREG_UNINITIALIZED)
ipoib_neigh_hash_uninit(dev);
ipoib_ib_dev_cleanup(dev);
/* no more works over the priv->wq */
if (priv->wq) {
flush_workqueue(priv->wq);
destroy_workqueue(priv->wq);
priv->wq = NULL;
}
}
static void ipoib_ndo_uninit_rss(struct net_device *dev)
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
ASSERT_RTNL();
/*
* ipoib_remove_one guarantees the children are removed before the
* parent, and that is the only place where a parent can be removed.
*/
#ifdef HAVE_NET_DEVICE_NEEDS_FREE_NETDEV
WARN_ON(!list_empty(&priv->child_intfs));
if (priv->parent) {
struct ipoib_dev_priv *ppriv = ipoib_priv(priv->parent);
down_write(&ppriv->vlan_rwsem);
list_del(&priv->list);
up_write(&ppriv->vlan_rwsem);
}
#endif
ipoib_dev_uninit_rss(dev);
if (priv->parent)
dev_put(priv->parent);
}
int ipoib_set_fp_rss(struct ipoib_dev_priv *priv, struct ib_device *hca)
{
int ret;
ret = ipoib_get_hca_features(priv, hca);
if (ret)
return ret;
/* Initialize function pointers for RSS and non-RSS devices */
ipoib_main_rss_init_fp(priv);
ipoib_cm_rss_init_fp(priv);
ipoib_ib_rss_init_fp(priv);
return 0;
}
static int ipoib_get_hca_features(struct ipoib_dev_priv *priv, struct ib_device *hca)
{
int num_cores, result;
struct ib_exp_device_attr exp_device_attr;
struct ib_udata uhw = {.outlen = 0, .inlen = 0};
result = ib_exp_query_device(hca, &exp_device_attr, &uhw);
if (result) {
ipoib_warn(priv, "%s: ib_exp_query_device failed (ret = %d)\n",
hca->name, result);
return result;
}
priv->hca_caps = hca->attrs.device_cap_flags;
priv->hca_caps_exp = exp_device_attr.device_cap_flags2;
num_cores = num_online_cpus();
if (num_cores == 1 || !(priv->hca_caps_exp & IB_EXP_DEVICE_QPG)) {
/* No additional QP, only one QP for RX & TX */
priv->rss_qp_num = 0;
priv->tss_qp_num = 0;
priv->max_rx_queues = 1;
priv->max_tx_queues = 1;
priv->num_rx_queues = 1;
priv->num_tx_queues = 1;
return 0;
}
num_cores = roundup_pow_of_two(num_cores);
if (priv->hca_caps_exp & IB_EXP_DEVICE_UD_RSS) {
int max_rss_tbl_sz;
max_rss_tbl_sz = exp_device_attr.max_rss_tbl_sz;
max_rss_tbl_sz = min(IPOIB_MAX_RX_QUEUES, max_rss_tbl_sz);
max_rss_tbl_sz = min(num_cores, max_rss_tbl_sz);
max_rss_tbl_sz = rounddown_pow_of_two(max_rss_tbl_sz);
priv->rss_qp_num = max_rss_tbl_sz;
priv->max_rx_queues = max_rss_tbl_sz;
} else {
/* No additional QP, only the parent QP for RX */
priv->rss_qp_num = 0;
priv->max_rx_queues = 1;
}
priv->num_rx_queues = priv->max_rx_queues;
priv->tss_qp_num = min(IPOIB_MAX_TX_QUEUES, num_cores);
/* TSS is not support by HW use the parent QP for ARP */
priv->max_tx_queues = priv->tss_qp_num + 1;
priv->num_tx_queues = priv->max_tx_queues;
return 0;
}
void ipoib_dev_uninit_default_rss(struct net_device *dev)
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
int i;
ipoib_transport_dev_cleanup_rss(dev);
ipoib_napi_del_rss(dev);
ipoib_cm_dev_cleanup(dev);
down_write(&priv->rings_rwsem);
for (i = 0; i < priv->num_tx_queues; i++)
vfree(priv->send_ring[i].tx_ring);
kfree(priv->send_ring);
for (i = 0; i < priv->num_rx_queues; i++)
kfree(priv->recv_ring[i].rx_ring);
kfree(priv->recv_ring);
priv->recv_ring = NULL;
priv->send_ring = NULL;
up_write(&priv->rings_rwsem);
}
int ipoib_reinit_rss(struct net_device *dev, int num_rx, int num_tx)
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
int flags;
int ret;
flags = dev->flags;
ipoib_stop(dev);
if (!test_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags))
ib_unregister_event_handler(&priv->event_handler);
ipoib_dev_uninit_rss(dev);
priv->num_rx_queues = num_rx;
priv->num_tx_queues = num_tx;
if (num_rx == 1)
priv->rss_qp_num = 0;
else
priv->rss_qp_num = num_rx;
priv->tss_qp_num = num_tx - 1;
netif_set_real_num_tx_queues(dev, num_tx);
netif_set_real_num_rx_queues(dev, num_rx);
/*
* prevent ipoib_ib_dev_init call ipoib_ib_dev_open
* let ipoib_open do it
*/
dev->flags &= ~IFF_UP;
ret = ipoib_dev_init(dev);
if (ret) {
pr_warn("%s: failed to reinitialize port %d (ret = %d)\n",
priv->ca->name, priv->port, ret);
return ret;
}
if (!test_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags)) {
ib_register_event_handler(&priv->event_handler);
}
dev->flags = flags;
/* if the device was up bring it up again */
if (flags & IFF_UP) {
ret = ipoib_open(dev);
if (ret)
pr_warn("%s: failed to reopen port %d (ret = %d)\n",
priv->ca->name, priv->port, ret);
}
return ret;
}
static ssize_t get_rx_chan(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ipoib_dev_priv *priv = ipoib_priv(to_net_dev(dev));
return sprintf(buf, "%d\n", priv->num_rx_queues);
}
static ssize_t set_rx_chan(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct net_device *ndev = to_net_dev(dev);
struct ipoib_dev_priv *priv = ipoib_priv(ndev);
int val, ret;
ret = sscanf(buf, "%d", &val);
if (ret != 1)
return -EINVAL;
if (val == 0 || val > priv->max_rx_queues) {
ipoib_warn(priv,
"Trying to set invalid rx_channels value (%d), max_rx_queues (%d)\n",
val, priv->max_rx_queues);
return -EINVAL;
}
/* Nothing to do ? */
if (val == priv->num_rx_queues)
return count;
if (!is_power_of_2(val)) {
ipoib_warn(priv,
"Trying to set invalid rx_channels value (%d), has to be power of 2\n",
val);
return -EINVAL;
}
if (!rtnl_trylock())
return restart_syscall();
ret = ipoib_reinit_rss(ndev, val, priv->num_tx_queues);
rtnl_unlock();
if (ret)
return ret;
return count;
}
static DEVICE_ATTR(rx_channels, S_IWUSR | S_IRUGO, get_rx_chan, set_rx_chan);
static ssize_t get_rx_max_channel(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ipoib_dev_priv *priv = ipoib_priv(to_net_dev(dev));
return sprintf(buf, "%d\n", priv->max_rx_queues);
}
static DEVICE_ATTR(rx_max_channels, S_IRUGO, get_rx_max_channel, NULL);
static ssize_t get_tx_chan(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ipoib_dev_priv *priv = ipoib_priv(to_net_dev(dev));
return sprintf(buf, "%d\n", priv->num_tx_queues);
}
static ssize_t set_tx_chan(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct net_device *ndev = to_net_dev(dev);
struct ipoib_dev_priv *priv = ipoib_priv(ndev);
int val, ret;
ret = sscanf(buf, "%d", &val);
if (ret != 1)
return -EINVAL;
if (val == 0 || val > priv->max_tx_queues) {
ipoib_warn(priv,
"Trying to set invalid tx_channels value (%d), max_tx_queues (%d)\n",
val, priv->max_tx_queues);
return -EINVAL;
}
/* Nothing to do ? */
if (val == priv->num_tx_queues)
return count;
/* 1 is always O.K. */
if (val > 1) {
/*
* with SW TSS tx_count = 1 + 2 ^ N.
* 2 is not allowed, makes no sense,
* if want to disable TSS use 1.
*/
if (!is_power_of_2(val - 1) || val == 2) {
ipoib_warn(priv,
"Trying to set invalid tx_channels value (%d), has to be (x^2 + 1)\n",
val);
return -EINVAL;
}
}
if (!rtnl_trylock())
return restart_syscall();
ret = ipoib_reinit_rss(ndev, priv->num_rx_queues, val);
rtnl_unlock();
if (ret)
return ret;
return count;
}
static DEVICE_ATTR(tx_channels, S_IWUSR | S_IRUGO, get_tx_chan, set_tx_chan);
static ssize_t get_tx_max_channel(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ipoib_dev_priv *priv = ipoib_priv(to_net_dev(dev));
return sprintf(buf, "%d\n", priv->max_tx_queues);
}
static DEVICE_ATTR(tx_max_channels, S_IRUGO, get_tx_max_channel, NULL);
int ipoib_set_rss_sysfs(struct ipoib_dev_priv *priv)
{
int ret;
ret = device_create_file(&priv->dev->dev, &dev_attr_tx_max_channels);
if (ret)
goto sysfs_failed;
ret = device_create_file(&priv->dev->dev, &dev_attr_rx_max_channels);
if (ret)
goto sysfs_failed;
ret = device_create_file(&priv->dev->dev, &dev_attr_tx_channels);
if (ret)
goto sysfs_failed;
ret = device_create_file(&priv->dev->dev, &dev_attr_rx_channels);
if (ret)
goto sysfs_failed;
sysfs_failed:
return ret;
}
static const struct net_device_ops ipoib_netdev_default_pf_rss = {
.ndo_init = ipoib_dev_init_default_rss,
.ndo_uninit = ipoib_dev_uninit_default_rss,
.ndo_open = ipoib_ib_dev_open_default_rss,
.ndo_stop = ipoib_ib_dev_stop_default_rss,
};
static const struct net_device_ops ipoib_netdev_ops_pf_sw_tss = {
.ndo_init = ipoib_ndo_init,
.ndo_uninit = ipoib_ndo_uninit_rss,
.ndo_open = ipoib_open,
.ndo_stop = ipoib_stop,
#ifdef HAVE_NDO_CHANGE_MTU_EXTENDED
.extended.ndo_change_mtu = ipoib_change_mtu,
#else
.ndo_change_mtu = ipoib_change_mtu,
#endif
#ifdef HAVE_NDO_FIX_FEATURES
.ndo_fix_features = ipoib_fix_features,
#endif
.ndo_start_xmit = ipoib_start_xmit,
.ndo_select_queue = ipoib_select_queue_sw_rss,
.ndo_tx_timeout = ipoib_timeout_rss,
.ndo_get_stats = ipoib_get_stats_rss,
.ndo_set_rx_mode = ipoib_set_mcast_list,
#ifdef HAVE_NDO_GET_IFLINK
.ndo_get_iflink = ipoib_get_iflink,
#endif
#ifdef HAVE_NETDEV_OPS_NDO_SET_VF_LINK_STATE
.ndo_set_vf_link_state = ipoib_set_vf_link_state,
#endif
#ifdef HAVE_NDO_SET_VF_MAC
.ndo_get_vf_config = ipoib_get_vf_config,
#endif
#ifdef HAVE_NDO_GET_VF_STATS
.ndo_get_vf_stats = ipoib_get_vf_stats,
#endif
#ifdef HAVE_NDO_SET_VF_GUID
.ndo_set_vf_guid = ipoib_set_vf_guid,
#endif
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 7, 0)
.ndo_set_mac_address = ipoib_set_mac,
#endif
#ifdef HAVE_NET_DEVICE_OPS_EXTENDED
.ndo_size = sizeof(struct net_device_ops),
#endif
};
static const struct net_device_ops ipoib_netdev_ops_vf_sw_tss = {
.ndo_init = ipoib_ndo_init,
.ndo_uninit = ipoib_ndo_uninit_rss,
.ndo_open = ipoib_open,
.ndo_stop = ipoib_stop,
#ifdef HAVE_NDO_CHANGE_MTU_EXTENDED
.extended.ndo_change_mtu = ipoib_change_mtu,
#else
.ndo_change_mtu = ipoib_change_mtu,
#endif
#ifdef HAVE_NDO_FIX_FEATURES
.ndo_fix_features = ipoib_fix_features,
#endif
.ndo_start_xmit = ipoib_start_xmit,
.ndo_select_queue = ipoib_select_queue_sw_rss,
.ndo_tx_timeout = ipoib_timeout_rss,
.ndo_get_stats = ipoib_get_stats_rss,
.ndo_set_rx_mode = ipoib_set_mcast_list,
#ifdef HAVE_NDO_GET_IFLINK
.ndo_get_iflink = ipoib_get_iflink,
#endif
};
const struct net_device_ops *ipoib_get_netdev_ops(struct ipoib_dev_priv *priv)
{
if (priv->hca_caps & IB_DEVICE_VIRTUAL_FUNCTION)
return priv->max_tx_queues > 1 ?
&ipoib_netdev_ops_vf_sw_tss : &ipoib_netdev_ops_vf;
else
return priv->max_tx_queues > 1 ?
&ipoib_netdev_ops_pf_sw_tss : &ipoib_netdev_ops_pf;
}
const struct net_device_ops *ipoib_get_rn_ops(struct ipoib_dev_priv *priv)
{
return priv->max_tx_queues > 1 ?
&ipoib_netdev_default_pf_rss : &ipoib_netdev_default_pf;
}
void ipoib_main_rss_init_fp(struct ipoib_dev_priv *priv)
{
if (priv->max_tx_queues > 1) {
priv->fp.ipoib_set_mode = ipoib_set_mode_rss;
priv->fp.ipoib_neigh_ctor = ipoib_neigh_ctor_rss;
} else {
priv->fp.ipoib_set_mode = ipoib_set_mode;
priv->fp.ipoib_neigh_ctor = ipoib_neigh_ctor;
}
}
ipoib_main_rss.c
是 InfiniBand 协议中的 IP over InfiniBand (IPoIB) 驱动的一部分,主要负责实现基于 RSS (Receive Side Scaling) 和 TSS (Transmit Side Scaling) 的多队列支持。RSS 和 TSS 是现代网络设备中常用的技术,用于在多核系统上提高网络吞吐量和性能。以下是对该文件的详细分析:
1. 文件概述
该文件主要实现了 IPoIB 驱动的多队列支持,特别是通过 RSS 和 TSS 技术来优化多核系统中的网络性能。文件中的代码主要涉及以下几个部分:
-
模式切换:支持在“connected mode”和“datagram mode”之间切换。
-
队列选择:通过
ipoib_select_queue_sw_rss
函数实现基于软件的多队列选择。 -
超时处理:通过
ipoib_timeout_rss
函数处理网络传输超时。 -
统计信息:通过
ipoib_get_stats_rss
函数获取网络设备的统计信息。 -
NAPI 支持:通过
ipoib_napi_add_rss
和ipoib_napi_del_rss
函数实现 NAPI (New API) 支持,用于高效处理网络数据包。 -
设备初始化与卸载:通过
ipoib_dev_init_default_rss
和ipoib_dev_uninit_rss
函数实现设备的初始化和卸载。 -
RSS/TSS 配置:通过
ipoib_set_fp_rss
和ipoib_get_hca_features
函数配置 RSS 和 TSS 相关的功能。
2. 关键函数分析
2.1 ipoib_set_mode_rss
该函数用于设置 IPoIB 设备的模式(connected mode 或 datagram mode)。根据传入的 buf
参数,函数会决定是否启用 connected mode。如果启用了 connected mode,函数会更新设备的特性(如 MTU、发送队列的配置等),并刷新路径以确保连接重新建立。
-
connected mode:启用 connected mode 时,函数会设置
IPOIB_FLAG_ADMIN_CM
标志,并更新设备的 MTU 和发送队列的配置。 -
datagram mode:在 datagram mode 下,函数会清除
IPOIB_FLAG_ADMIN_CM
标志,并恢复设备的默认特性。
2.2 ipoib_select_queue_sw_rss
该函数用于选择发送队列。它根据数据包的目标地址和设备的配置来决定使用哪个队列。如果设备支持 TSS(Transmit Side Scaling),函数会根据目标地址的哈希值来选择队列。
-
多队列支持:如果设备支持 TSS,函数会根据目标地址的哈希值来选择队列,从而实现负载均衡。
-
单队列支持:如果设备不支持 TSS,函数会返回默认的队列。
2.3 ipoib_timeout_rss
该函数处理网络传输超时。当网络传输超时时,函数会打印警告信息,并调度一个工作队列来处理超时事件。
-
超时处理:函数会检查每个发送队列的状态,并打印相关的警告信息。
-
工作队列:超时事件会被调度到一个工作队列中,以便后续处理。
2.4 ipoib_get_stats_rss
该函数用于获取网络设备的统计信息。它会遍历所有的接收和发送队列,并汇总每个队列的统计信息。
-
统计信息:函数会汇总每个队列的接收和发送数据包数量、字节数、错误数等信息。
2.5 ipoib_dev_init_default_rss
该函数用于初始化 IPoIB 设备的多队列支持。它会为每个接收和发送队列分配内存,并初始化相关的数据结构。
-
多队列初始化:函数会为每个接收和发送队列分配内存,并初始化相关的数据结构。
-
NAPI 支持:函数会为每个队列添加 NAPI 支持,以便高效处理网络数据包。
2.6 ipoib_dev_uninit_rss
该函数用于卸载 IPoIB 设备的多队列支持。它会释放为每个接收和发送队列分配的内存,并清理相关的数据结构。
-
内存释放:函数会释放为每个接收和发送队列分配的内存。
-
NAPI 支持:函数会删除每个队列的 NAPI 支持。
2.7 ipoib_set_fp_rss
该函数用于设置 RSS 和 TSS 相关的功能指针。根据设备的配置,函数会决定是否启用 RSS 和 TSS 支持。
-
功能指针:函数会根据设备的配置设置相关的功能指针,如
ipoib_set_mode
和ipoib_neigh_ctor
。
2.8 ipoib_get_hca_features
该函数用于获取 HCA (Host Channel Adapter) 的特性,并根据这些特性配置 RSS 和 TSS 支持。
-
HCA 特性:函数会查询 HCA 的特性,并根据这些特性决定是否启用 RSS 和 TSS 支持。
3. RSS 和 TSS 的支持
RSS 和 TSS 是现代网络设备中常用的技术,用于在多核系统上提高网络吞吐量和性能。RSS 通过将接收到的数据包分发到多个队列中,从而实现负载均衡。TSS 则通过将发送的数据包分发到多个队列中,从而提高发送性能。
-
RSS 支持:如果设备支持 RSS,函数会根据 HCA 的特性配置多个接收队列。
-
TSS 支持:如果设备支持 TSS,函数会根据 HCA 的特性配置多个发送队列。
4. 设备操作结构体
文件末尾定义了几个 net_device_ops
结构体,用于指定 IPoIB 设备的操作函数。这些结构体根据设备的配置(如是否支持 RSS 和 TSS)来决定使用哪些操作函数。
-
ipoib_netdev_default_pf_rss
:用于支持 RSS 的 PF (Physical Function) 设备。 -
ipoib_netdev_ops_pf_sw_tss
:用于支持 TSS 的 PF 设备。 -
ipoib_netdev_ops_vf_sw_tss
:用于支持 TSS 的 VF (Virtual Function) 设备。
5. 总结
ipoib_main_rss.c
文件实现了 IPoIB 驱动的多队列支持,特别是通过 RSS 和 TSS 技术来优化多核系统中的网络性能。文件中的代码涉及模式切换、队列选择、超时处理、统计信息、NAPI 支持、设备初始化与卸载等多个方面。通过这些功能,IPoIB 驱动能够在多核系统上实现高效的网络数据传输。
分析 ipoib_main_rss.c
文件的内容。这个文件属于 InfiniBand Verbs (IBV) 驱动中的 IP over InfiniBand (IPOIB) 部分,特别是与接收侧缩放 (RSS) 和传输侧缩放 (TSS) 相关的功能。以下是对文件内容的详细分析:
1. 版权和许可信息
文件开头包含了多个版权声明,涉及 Topspin Communications、Sun Microsystems 和 Voltaire, Inc.,并提供了两种许可选项:GNU General Public License (GPL) Version 2 和 OpenIB.org BSD license。
2. 主要功能函数
ipoib_set_mode_rss
- 功能:设置网络设备的工作模式为“connected”或“datagram”。
- 逻辑:
- 检查当前是否启用了连接管理 (Connected Mode, CM) 并验证输入模式是否匹配。
- 如果切换到“connected”模式:
- 启用连接管理标志。
- 更新网络设备的特性(如禁用某些特性,设置 MTU)。
- 刷新路径以重启连接。
- 如果切换到“datagram”模式:
- 禁用连接管理标志。
- 更新网络设备的特性(如启用 IP 校验和、散列分组等)。
- 设置 MTU 并刷新路径。
ipoib_select_queue_sw_rss
- 功能:选择传输队列,用于软件实现的 RSS(接收侧缩放)。
- 逻辑:
- 检查是否为广播或多播地址,如果是,则使用指定的队列。
- 检查是否启用了连接管理,如果是,则基于远程 QP 进行哈希选择队列。
- 检查邻居是否支持 TSS(传输侧缩放),如果支持,则使用 TSS 队列。
- 否则,使用默认队列。
ipoib_timeout_rss
- 功能:处理传输超时事件。
- 逻辑:
- 记录并警告传输延迟。
- 检查并报告停止的传输队列。
ipoib_get_stats_rss
- 功能:获取网络设备的统计信息。
- 逻辑:
- 读取接收和发送环的统计信息。
- 汇总并返回统计结果。
ipoib_napi_add_rss
和 ipoib_napi_del_rss
- 功能:添加和删除 NAPI 结构,用于网络设备的轮询机制。
ipoib_neigh_ctor_rss
- 功能:构造邻居结构,用于处理邻居缓存。
- 逻辑:
- 分配并初始化邻居结构。
- 根据 TSS 队列数量设置邻居的索引。
3. 初始化和清理函数
ipoib_dev_init_default_rss
- 功能:初始化网络设备的默认 RSS 和 TSS 配置。
- 逻辑:
- 分配接收和发送环的内存。
- 初始化 NAPI 结构。
- 设置设备地址以支持 TSS。
ipoib_dev_uninit_rss
- 功能:清理并释放网络设备的资源。
- 逻辑:
- 清理邻居哈希表。
- 释放工作队列和其他资源。
4. 系统调用和属性设置
get_rx_chan
和 set_rx_chan
- 功能:获取和设置接收通道数量。
- 逻辑:
- 验证输入值是否有效(必须是 2 的幂且不超过最大值)。
- 调整接收队列数量并重新初始化设备。
get_tx_chan
和 set_tx_chan
- 功能:获取和设置传输通道数量。
- 逻辑:
- 验证输入值是否有效(必须是 (x^2 + 1) 形式且不超过最大值)。
- 调整传输队列数量并重新初始化设备。
5. 网络设备操作结构
- 定义了多个
net_device_ops
结构体,用于不同的设备类型和配置。 - 根据设备的特性(如是否为虚拟功能)选择合适的操作结构体。
6. 函数指针初始化
ipoib_main_rss_init_fp
函数根据最大传输队列数量初始化函数指针。- 如果最大传输队列数量大于 1,则使用支持 RSS 和 TSS 的函数指针;否则,使用默认的函数指针。
总结
ipoib_main_rss.c
文件主要实现了 IPOIB 驱动中与 RSS 和 TSS 相关的功能,包括模式设置、队列选择、统计信息获取、初始化和清理等。通过这些功能,驱动能够有效地管理和优化网络数据包的处理,提高网络性能和可扩展性。
以下是对 drivers\infiniband\ulp\ipoib\rss_tss\ipoib_main_rss.c
文件内容的分析:
1. 版权与许可声明部分
文件开头包含了多个版权声明,表明该代码受到不同版权所有者版权保护,并且提供了两种许可选择,分别是 GNU General Public License (GPL) Version 2 和 OpenIB.org BSD 许可证,同时列出了在不同许可证下使用该软件需要满足的条件,例如对代码的分发、修改等相关要求以及版权声明和免责声明的保留等情况。
2. 功能函数分析
2.1 ipoib_set_mode_rss
函数
功能概述:
该函数用于设置 Infiniband over IP (IPoIB) 设备的工作模式(如 connected
模式或 datagram
模式),根据传入的参数和设备当前状态进行相应的配置调整,包括更新设备标志位、网络设备特性、最大传输单元(MTU)等,还涉及到对发送队列相关标志的处理以及路径的刷新等操作。若传入的模式参数不符合预期,则返回相应的错误码(如 -EINVAL
)。
- 关键逻辑流程:
首先进行模式合法性判断,如果满足特定的已设置管理连接模式(IPOIB_FLAG_ADMIN_CM
)与传入参数匹配的条件,则直接返回成功(0
)。对于切换到connected
模式的情况,会检查设备是否支持连接管理模式(IPOIB_CM_SUPPORTED
),若支持则进行一系列配置变更,如设置相关标志位、发出警告提示多播包可能丢弃、更新网络设备特性、设置 MTU、刷新路径等操作;若不支持则返回错误码-EINVAL
。而切换到datagram
模式时,也会相应地清除管理连接模式标志位,并根据设备的不同能力(如IB_DEVICE_UD_IP_CSUM
等)来更新网络设备的特性,同时设置合适的 MTU 并刷新路径等。
2.2 ipoib_select_queue_sw_rss
函数
- 功能概述:
用于选择合适的发送队列,根据不同的编译条件和传入的参数(如网络设备结构体、套接字缓冲区结构体等),结合设备的连接管理模式(CM
)状态、是否支持特定特性(如TSS
)等情况,通过不同的哈希算法或回退机制来确定具体使用的发送队列编号(QP
编号)。 - 关键逻辑流程:
根据数据包中的一些地址信息(如hwaddr
)来判断是否为广播 / 多播情况,若是则返回指定的队列编号(priv->tss_qp_num
)。接着判断是否启用了连接管理模式(CM
),若启用且处于管理启用状态,会基于远程QP
进行哈希计算来确定队列编号;若未启用CM
则进行相应的标志位处理。然后检查是否支持TSS
,若不支持则返回指定队列编号,若支持则进一步对数据包头部进行处理后,根据不同的编译条件通过不同方式(如netdev_pick_tx
或回退函数fallback
等)来确定最终的发送队列编号并返回。
2.3 ipoib_timeout_rss
函数
- 功能概述:
处理网络设备发送超时的情况,会输出相关的警告信息,提示超时的延迟时间,并且遍历发送队列,检查队列是否停止以及获取队列的发送头和尾指针相关信息,最后调度相关的工作任务(schedule_work
)来进一步处理超时情况。
2.4 ipoib_get_stats_rss
函数
- 功能概述:
用于获取网络设备的统计信息,通过读取设备的接收和发送队列的相关统计数据(如接收 / 发送的数据包数量、字节数、错误数、丢弃数等),汇总后更新到网络设备的统计结构体中并返回该结构体指针,过程中涉及到读写锁的使用来保证数据访问的一致性。
2.5 ipoib_napi_add_rss
和 ipoib_napi_del_rss
函数
- 功能概述:
ipoib_napi_add_rss
函数用于向网络设备添加 NAPI(New API for packet processing)相关的回调函数,针对接收和发送队列分别进行添加,以便在后续网络数据包的接收和发送处理中调用相应的轮询函数(如ipoib_rx_poll_rss
和ipoib_tx_poll_rss
)。ipoib_napi_del_rss
函数则相反,用于从网络设备中删除这些之前添加的 NAPI 相关回调函数。
2.6 ipoib_neigh_ctor_rss
函数
- 功能概述:
负责创建一个ipoib_neigh
结构体实例,用于表示网络邻居相关信息,进行内存分配、初始化结构体成员(如设备指针、目标地址、队列初始化、引用计数等),并根据设备的tss_qp_num
等情况来确定邻居对应的索引值,最后返回创建好的邻居结构体指针,若内存分配失败则返回NULL
。
2.7 ipoib_dev_init_default_rss
函数
- 功能概述:
进行网络设备的默认初始化操作,涉及到接收和发送队列的内存分配与初始化,包括为每个队列分配相应的环形缓冲区等资源,添加 NAPI 相关回调函数,调用ipoib_transport_dev_init_rss
进行传输层相关的初始化,设置设备地址相关标志位来表示支持TSS
等情况,若初始化过程中任何环节出现内存分配失败等问题,则进行相应的资源清理并返回错误码-ENOMEM
,成功则返回0
。
2.8 ipoib_dev_uninit_rss
函数
- 功能概述:
执行网络设备的反初始化操作,例如在设备状态不是未初始化状态时进行邻居哈希表的反初始化,清理 Infiniband 设备相关资源,停止并销毁工作队列等,释放相关的资源。
2.9 ipoib_ndo_uninit_rss
函数
- 功能概述:
进行网络设备操作(net_device_ops
)相关的反初始化,包含一些断言、根据设备层次结构关系进行相应的链表操作以及调用ipoib_dev_uninit_rss
进一步清理设备资源,还会对父设备进行相应的处理(如减少引用计数等)。
2.10 ipoib_set_fp_rss
函数
- 功能概述:
获取 Infiniband 设备(hca
)的特性信息,然后基于这些特性来初始化函数指针,用于支持接收端扩展(RSS)和非 RSS 设备的不同功能函数调用,调用了如ipoib_main_rss_init_fp
、ipoib_cm_rss_init_fp
和ipoib_ib_rss_init_fp
等函数来具体设置不同功能模块对应的函数指针。
2.11 ipoib_get_hca_features
函数
- 功能概述:
通过调用ib_exp_query_device
函数获取扩展的 Infiniband 设备属性信息,基于设备的核心数量、是否支持某些特性(如IB_EXP_DEVICE_QPG
、IB_EXP_DEVICE_UD_RSS
等)来确定设备的多个队列相关参数,如接收和发送队列的最大数量、实际数量以及 RSS 和 TSS 相关的QP
数量等,并将设备的能力标志位保存到对应的结构体成员中。
2.12 ipoib_dev_uninit_default_rss
函数
- 功能概述:
进行网络设备默认的反初始化操作,依次调用相关函数清理传输层、删除 NAPI 回调、清理连接管理相关资源、释放接收和发送队列的环形缓冲区内存等,将接收和发送队列指针置空,完成整个设备相关资源的释放和清理。
2.13 ipoib_reinit_rss
函数
- 功能概述:
重新初始化网络设备,先停止设备,根据需要注销或注册事件处理函数,进行设备的反初始化,然后更新接收和发送队列的数量参数,重新设置网络设备的一些标志位,调用ipoib_dev_init
进行初始化,再根据情况注册事件处理函数、恢复设备标志位,若设备之前处于开启状态则尝试重新打开设备,最后返回初始化过程中的错误码(若有)。
2.14 与 sysfs
相关的一组函数(如 get_rx_chan
、set_rx_chan
等)
- 功能概述:
这些函数用于实现与sysfs
文件系统交互的功能,通过DEVICE_ATTR
宏定义了不同的设备属性,例如获取和设置接收通道数量、最大接收通道数量、发送通道数量、最大发送通道数量等,函数内部会进行参数合法性检查、调用ipoib_reinit_rss
进行设备重新初始化等操作,并返回相应的结果(如成功写入的字节数或错误码)。
2.15 ipoib_set_rss_sysfs
函数
- 功能概述:
用于在sysfs
文件系统中创建与设备相关的属性文件,调用device_create_file
函数依次尝试创建发送和接收通道相关的最大通道数、通道数等属性文件,若创建过程中出现错误则跳转到相应的错误处理标签处并返回错误码。
2.16 与网络设备操作结构体相关的函数(如 ipoib_get_netdev_ops
、ipoib_get_rn_ops
、ipoib_main_rss_init_fp
等)
- 功能概述:
ipoib_get_netdev_ops
函数根据设备是否为虚拟功能设备以及最大发送队列数量来返回不同的网络设备操作结构体指针,用于定义设备在不同情况下的操作函数集(如初始化、反初始化、打开、关闭、发送数据、获取统计信息等操作对应的函数)。ipoib_get_rn_ops
函数同样基于最大发送队列数量来返回相应的网络设备操作结构体指针。ipoib_main_rss_init_fp
函数则根据最大发送队列数量来初始化一些功能函数指针,用于选择不同的ipoib_set_mode
和ipoib_neigh_ctor
实现函数,以适配不同的设备配置情况。
3. 整体逻辑关联
整个代码文件围绕着 Infiniband over IP (IPoIB) 设备的 RSS(Receive Side Scaling)和 TSS(Transport Selective Scheduling)相关功能展开,从设备的模式设置、队列选择、超时处理、统计信息获取、邻居结构体创建、设备初始化与反初始化、sysfs
交互以及网络设备操作函数集的配置等多个方面提供了一套完整的实现逻辑,各个函数之间相互协作,共同为 IPoIB 设备在支持 RSS 和 TSS 特性的场景下的正常运行和管理提供了功能支撑。不同的函数通过操作设备的结构体成员、调用底层的 Infiniband 相关函数以及遵循相应的规则和协议来实现对网络设备各方面行为的控制和数据的处理。
这个文件是InfiniBand over Ethernet (IPoIB) 驱动中用于处理接收方扩展 (RSS) 和传输方扩展 (TSS) 的源码文件。以下是对该文件内容的详细分析:
版权声明和许可证
-
文件开头包含版权声明,表明代码由Topspin Communications、Sun Microsystems、Voltaire等公司版权所有。
-
提供了两种许可证选择:GNU General Public License (GPL) Version 2 或 OpenIB.org BSD license,允许在满足特定条件下重新分发和使用代码。
函数 ipoib_set_mode_rss
-
功能:设置IPoIB设备的工作模式(连接模式或数据报模式)。
-
参数:
-
struct net_device *dev
:网络设备结构体指针。 -
const char *buf
:包含模式字符串的缓冲区。
-
-
逻辑:
-
检查当前模式和请求模式是否相同,相同则直接返回。
-
如果请求模式为“connected”,则检查设备是否支持连接模式,支持则设置相关标志并更新设备特性。
-
如果请求模式为“datagram”,则清除连接模式标志并更新设备特性。
-
更新设备的MTU(最大传输单元)。
-
刷新路径以重启连接。
-
返回状态码,成功返回0,失败返回错误码。
-
函数 ipoib_select_queue_sw_rss
-
功能:选择用于发送数据包的队列。
-
参数:
-
struct net_device *dev
:网络设备结构体指针。 -
struct sk_buff *skb
:套接字缓冲区指针。 -
其他参数根据宏定义可能不同。
-
-
逻辑:
-
获取设备私有数据结构体指针。
-
检查数据包是否为广播或多播,如果是则使用指定的队列。
-
检查是否使用连接模式,如果是则根据远程QP(队列对)进行哈希计算选择队列。
-
检查邻居是否支持TSS,支持则设置相关标志。
-
根据哈希值选择队列,返回队列索引。
-
函数 ipoib_timeout_rss
-
功能:处理传输超时。
-
参数:
-
struct net_device *dev
:网络设备结构体指针。 -
unsigned int txqueue
:传输队列索引(根据宏定义可能不存在)。
-
-
逻辑:
-
获取设备私有数据结构体指针。
-
打印超时警告信息,包括延迟时间。
-
遍历所有传输队列,检查是否停止,打印队列状态。
-
调度超时处理工作。
-
函数 ipoib_get_stats_rss
-
功能:获取网络设备的统计信息。
-
参数:
-
struct net_device *dev
:网络设备结构体指针。
-
-
逻辑:
-
获取设备私有数据结构体指针。
-
尝试获取读锁,失败则返回当前统计信息。
-
初始化本地统计信息结构体。
-
遍历所有接收和传输队列,累加统计信息。
-
释放读锁。
-
更新设备统计信息并返回。
-
函数 ipoib_napi_add_rss
和 ipoib_napi_del_rss
-
功能:添加和删除NAPI(New API)结构体,用于处理接收和传输队列的轮询。
-
参数:
-
struct net_device *dev
:网络设备结构体指针。
-
-
逻辑:
-
获取设备私有数据结构体指针。
-
遍历所有接收和传输队列,调用
netif_napi_add
或netif_napi_del
函数。
-
函数 ipoib_neigh_ctor_rss
-
功能:构造邻居结构体。
-
参数:
-
u8 *daddr
:目的地址。 -
struct net_device *dev
:网络设备结构体指针。
-
-
逻辑:
-
分配邻居结构体内存。
-
初始化邻居结构体,设置设备指针、目的地址、引用计数等。
-
如果支持TSS,设置邻居索引。
-
函数 ipoib_dev_init_default_rss
和 ipoib_dev_uninit_rss
-
功能:初始化和反初始化RSS相关的设备资源。
-
参数:
-
struct net_device *dev
:网络设备结构体指针。
-
-
逻辑:
-
获取设备私有数据结构体指针。
-
分配和初始化接收和传输队列。
-
添加NAPI结构体。
-
初始化传输设备。
-
反初始化时,释放资源,删除NAPI结构体,清理传输设备。
-
函数 ipoib_set_fp_rss
和 ipoib_get_hca_features
-
功能:设置功能指针和获取HCA(Host Channel Adapter)特性。
-
参数:
-
struct ipoib_dev_priv *priv
:设备私有数据结构体指针。 -
struct ib_device *hca
:IB设备结构体指针。
-
-
逻辑:
-
查询HCA特性,设置私有数据结构体中的特性标志。
-
根据特性初始化RSS和TSS相关的功能指针。
-
函数 ipoib_dev_uninit_default_rss
和 ipoib_reinit_rss
-
功能:反初始化默认RSS设备资源和重新初始化RSS。
-
参数:
-
struct net_device *dev
:网络设备结构体指针。 -
int num_rx
、int num_tx
:接收和传输队列数量。
-
-
逻辑:
-
清理传输设备,删除NAPI结构体,释放队列资源。
-
重新初始化设备,设置新的队列数量。
-
系统文件操作函数
-
功能:提供sysfs文件操作,用于动态调整接收和传输队列数量。
-
参数:
-
struct device *dev
:设备结构体指针。 -
struct device_attribute *attr
:设备属性结构体指针。 -
char *buf
:缓冲区指针。 -
size_t count
:缓冲区大小。
-
-
逻辑:
-
读取和设置接收和传输队列数量,验证输入值,调用重新初始化函数。
-
结构体 ipoib_netdev_ops_pf_sw_tss
和 ipoib_netdev_ops_vf_sw_tss
-
功能:定义网络设备操作结构体,包含各种网络操作函数指针。
-
逻辑:
-
根据设备特性(如是否支持虚拟功能)选择合适的操作结构体。
-
函数 ipoib_get_netdev_ops
和 ipoib_get_rn_ops
-
功能:获取网络设备操作结构体指针。
-
参数:
-
struct ipoib_dev_priv *priv
:设备私有数据结构体指针。
-
-
逻辑:
-
根据设备特性返回合适的操作结构体指针。
-
函数 ipoib_main_rss_init_fp
-
功能:初始化主RSS功能指针。
-
参数:
-
struct ipoib_dev_priv *priv
:设备私有数据结构体指针。
-
-
逻辑:
-
根据传输队列数量设置模式设置和邻居构造函数指针。
-
总结
这个文件主要实现了IPoIB驱动中RSS和TSS相关的功能,包括模式设置、队列选择、超时处理、统计信息获取、NAPI管理、邻居管理、设备初始化和反初始化等。通过这些功能,IPoIB驱动能够高效地管理和优化数据包的接收和传输,提高网络性能。
标签:struct,为例,dev,ipoib,IPOIB,rss,ring,privFrom: https://blog.csdn.net/eidolon_foot/article/details/145065105