Commit 81a7a3a0 authored by Stefan Metzmacher's avatar Stefan Metzmacher Committed by Steve French
Browse files

smb: smbdirect: introduce smbdirect_netdev_rdma_capable_mode_type()



This is basically a copy of ksmbd_rdma_capable_netdev() in the
server, but this also prints a message when a device is renamed.

The differences are:
- It uses rdma_for_each_port() instead of implementing the
  same logic again.
- It returns RDMA_NODE_{UNSPECIFIED,IB_CA,RNIC} values instead of bool

Cc: Steve French <smfrench@gmail.com>
Cc: Tom Talpey <tom@talpey.com>
Cc: Long Li <longli@microsoft.com>
Cc: Namjae Jeon <linkinjeon@kernel.org>
Cc: linux-cifs@vger.kernel.org
Cc: samba-technical@lists.samba.org
Signed-off-by: default avatarStefan Metzmacher <metze@samba.org>
Acked-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 50bdab9a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -14,4 +14,5 @@ smbdirect-y := \
	smbdirect_connect.o	\
	smbdirect_listen.o	\
	smbdirect_accept.o	\
	smbdirect_devices.o	\
	smbdirect_main.o
+277 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *   Copyright (C) 2017, Microsoft Corporation.
 *   Copyright (C) 2018, LG Electronics.
 *   Copyright (c) 2025 Stefan Metzmacher
 */

#include "smbdirect_internal.h"

static u8 smbdirect_ib_device_rdma_capable_node_type(struct ib_device *ib_dev)
{
	if (!smbdirect_frwr_is_supported(&ib_dev->attrs))
		return RDMA_NODE_UNSPECIFIED;

	switch (ib_dev->node_type) {
	case RDMA_NODE_IB_CA: /* Infiniband, RoCE v1 and v2 */
	case RDMA_NODE_RNIC:  /* iWarp */
		return ib_dev->node_type;
	}

	return RDMA_NODE_UNSPECIFIED;
}

static int smbdirect_ib_client_add(struct ib_device *ib_dev)
{
	u8 node_type = smbdirect_ib_device_rdma_capable_node_type(ib_dev);
	struct smbdirect_device *sdev;
	const char *node_str;
	const char *action;
	u32 pidx;

	switch (node_type) {
	case RDMA_NODE_IB_CA:
		node_str = "IB_CA";
		action = "added";
		break;
	case RDMA_NODE_RNIC:
		node_str = "RNIC";
		action = "added";
		break;
	case RDMA_NODE_UNSPECIFIED:
		node_str = "UNSPECIFIED";
		action = "ignored";
		break;
	default:
		node_str = "UNKNOWN";
		action = "ignored";
		node_type = RDMA_NODE_UNSPECIFIED;
		break;
	}

	pr_info("ib_dev[%.*s]: %s: %s %s=%u %s=0x%llx %s=0x%llx %s=0x%llx\n",
		IB_DEVICE_NAME_MAX,
		ib_dev->name,
		action,
		node_str,
		"max_fast_reg_page_list_len",
		ib_dev->attrs.max_fast_reg_page_list_len,
		"device_cap_flags",
		ib_dev->attrs.device_cap_flags,
		"kernel_cap_flags",
		ib_dev->attrs.kernel_cap_flags,
		"page_size_cap",
		ib_dev->attrs.page_size_cap);

	if (node_type == RDMA_NODE_UNSPECIFIED)
		return 0;

	pr_info("ib_dev[%.*s]: %s=%u %s=%u %s=%u %s=%u %s=%u %s=%u %s=%u %s=%u %s=%u\n",
		IB_DEVICE_NAME_MAX,
		ib_dev->name,
		"num_ports",
		rdma_end_port(ib_dev),
		"max_qp_rd_atom",
		ib_dev->attrs.max_qp_rd_atom,
		"max_qp_init_rd_atom",
		ib_dev->attrs.max_qp_init_rd_atom,
		"max_sgl_rd",
		ib_dev->attrs.max_sgl_rd,
		"max_sge_rd",
		ib_dev->attrs.max_sge_rd,
		"max_cqe",
		ib_dev->attrs.max_cqe,
		"max_qp_wr",
		ib_dev->attrs.max_qp_wr,
		"max_send_sge",
		ib_dev->attrs.max_send_sge,
		"max_recv_sge",
		ib_dev->attrs.max_recv_sge);

	rdma_for_each_port(ib_dev, pidx) {
		const struct ib_port_immutable *ib_pi =
			ib_port_immutable_read(ib_dev, pidx);
		u32 core_cap_flags = ib_pi ? ib_pi->core_cap_flags : 0;

		pr_info("ib_dev[%.*s]PORT[%u]: %s=%u %s=%u %s=%u %s=%u %s=%u %s=0x%x\n",
			IB_DEVICE_NAME_MAX,
			ib_dev->name,
			pidx,
			"iwarp",
			rdma_protocol_iwarp(ib_dev, pidx),
			"ib",
			rdma_protocol_ib(ib_dev, pidx),
			"roce",
			rdma_protocol_roce(ib_dev, pidx),
			"v1",
			rdma_protocol_roce_eth_encap(ib_dev, pidx),
			"v2",
			rdma_protocol_roce_udp_encap(ib_dev, pidx),
			"core_cap_flags",
			core_cap_flags);
	}

	sdev = kzalloc_obj(*sdev);
	if (!sdev)
		return -ENOMEM;
	sdev->ib_dev = ib_dev;
	snprintf(sdev->ib_name, ARRAY_SIZE(sdev->ib_name), "%.*s",
		 IB_DEVICE_NAME_MAX, ib_dev->name);

	write_lock(&smbdirect_globals.devices.lock);
	list_add(&sdev->list, &smbdirect_globals.devices.list);
	write_unlock(&smbdirect_globals.devices.lock);

	return 0;
}

static void smbdirect_ib_client_remove(struct ib_device *ib_dev, void *client_data)
{
	struct smbdirect_device *sdev, *tmp;

	write_lock(&smbdirect_globals.devices.lock);
	list_for_each_entry_safe(sdev, tmp, &smbdirect_globals.devices.list, list) {
		if (sdev->ib_dev == ib_dev) {
			list_del(&sdev->list);
			pr_info("ib_dev[%.*s] removed\n",
				IB_DEVICE_NAME_MAX, sdev->ib_name);
			kfree(sdev);
			break;
		}
	}
	write_unlock(&smbdirect_globals.devices.lock);
}

static void smbdirect_ib_client_rename(struct ib_device *ib_dev, void *client_data)
{
	struct smbdirect_device *sdev;

	write_lock(&smbdirect_globals.devices.lock);
	list_for_each_entry(sdev, &smbdirect_globals.devices.list, list) {
		if (sdev->ib_dev == ib_dev) {
			pr_info("ib_dev[%.*s] renamed to [%.*s]\n",
				IB_DEVICE_NAME_MAX, sdev->ib_name,
				IB_DEVICE_NAME_MAX, ib_dev->name);
			snprintf(sdev->ib_name, ARRAY_SIZE(sdev->ib_name), "%.*s",
				 IB_DEVICE_NAME_MAX, ib_dev->name);
			break;
		}
	}
	write_unlock(&smbdirect_globals.devices.lock);
}

static struct ib_client smbdirect_ib_client = {
	.name	= "smbdirect_ib_client",
	.add	= smbdirect_ib_client_add,
	.remove	= smbdirect_ib_client_remove,
	.rename	= smbdirect_ib_client_rename,
};

static u8 smbdirect_netdev_find_rdma_capable_node_type(struct net_device *netdev)
{
	struct smbdirect_device *sdev;
	u8 node_type = RDMA_NODE_UNSPECIFIED;

	read_lock(&smbdirect_globals.devices.lock);
	list_for_each_entry(sdev, &smbdirect_globals.devices.list, list) {
		u32 pi;

		rdma_for_each_port(sdev->ib_dev, pi) {
			struct net_device *ndev;

			ndev = ib_device_get_netdev(sdev->ib_dev, pi);
			if (!ndev)
				continue;

			if (ndev == netdev) {
				dev_put(ndev);
				node_type = sdev->ib_dev->node_type;
				goto out;
			}
			dev_put(ndev);
		}
	}
out:
	read_unlock(&smbdirect_globals.devices.lock);

	if (node_type == RDMA_NODE_UNSPECIFIED) {
		struct ib_device *ibdev;

		ibdev = ib_device_get_by_netdev(netdev, RDMA_DRIVER_UNKNOWN);
		if (ibdev) {
			node_type = smbdirect_ib_device_rdma_capable_node_type(ibdev);
			ib_device_put(ibdev);
		}
	}

	return node_type;
}

/*
 * Returns RDMA_NODE_UNSPECIFIED when the netdev has
 * no support for smbdirect capable rdma.
 *
 * Otherwise RDMA_NODE_RNIC is returned for iwarp devices
 * and RDMA_NODE_IB_CA or Infiniband and RoCE (v1 and v2)
 */
u8 smbdirect_netdev_rdma_capable_node_type(struct net_device *netdev)
{
	struct net_device *lower_dev;
	struct list_head *iter;
	u8 node_type = RDMA_NODE_UNSPECIFIED;

	node_type = smbdirect_netdev_find_rdma_capable_node_type(netdev);
	if (node_type != RDMA_NODE_UNSPECIFIED)
		return node_type;

	/* check if netdev is bridge or VLAN */
	if (netif_is_bridge_master(netdev) || netdev->priv_flags & IFF_802_1Q_VLAN)
		netdev_for_each_lower_dev(netdev, lower_dev, iter) {
			node_type = smbdirect_netdev_find_rdma_capable_node_type(lower_dev);
			if (node_type != RDMA_NODE_UNSPECIFIED)
				return node_type;
		}

	/* check if netdev is IPoIB safely without layer violation */
	if (netdev->type == ARPHRD_INFINIBAND)
		return RDMA_NODE_IB_CA;

	return RDMA_NODE_UNSPECIFIED;
}
__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_netdev_rdma_capable_node_type);

__init int smbdirect_devices_init(void)
{
	int ret;

	rwlock_init(&smbdirect_globals.devices.lock);
	INIT_LIST_HEAD(&smbdirect_globals.devices.list);

	ret = ib_register_client(&smbdirect_ib_client);
	if (ret) {
		pr_crit("failed to ib_register_client: %d %1pe\n",
			ret, SMBDIRECT_DEBUG_ERR_PTR(ret));
		return ret;
	}

	return 0;
}

__exit void smbdirect_devices_exit(void)
{
	struct smbdirect_device *sdev, *tmp;

	/*
	 * On exist we just cleanup so that
	 * smbdirect_ib_client_remove() won't
	 * print removals of devices.
	 */
	write_lock(&smbdirect_globals.devices.lock);
	list_for_each_entry_safe(sdev, tmp, &smbdirect_globals.devices.list, list) {
		list_del(&sdev->list);
		kfree(sdev);
	}
	write_unlock(&smbdirect_globals.devices.lock);

	ib_unregister_client(&smbdirect_ib_client);
}
+18 −0
Original line number Diff line number Diff line
@@ -18,12 +18,27 @@

struct smbdirect_module_state {
	struct mutex mutex;

	struct {
		rwlock_t lock;
		struct list_head list;
	} devices;
};

extern struct smbdirect_module_state smbdirect_globals;

#include "smbdirect_socket.h"

struct smbdirect_device {
	struct list_head list;
	struct ib_device *ib_dev;
	/*
	 * copy of ib_dev->name,
	 * in order to print renames
	 */
	char ib_name[IB_DEVICE_NAME_MAX];
};

#ifdef SMBDIRECT_USE_INLINE_C_FILES
/* this is temporary while this file is included in others */
#define __SMBDIRECT_PRIVATE__ __maybe_unused static
@@ -143,4 +158,7 @@ void smbdirect_connection_destroy_mr_list(struct smbdirect_socket *sc);

void smbdirect_accept_negotiate_finish(struct smbdirect_socket *sc, u32 ntstatus);

__init int smbdirect_devices_init(void);
__exit void smbdirect_devices_exit(void);

#endif /* __FS_SMB_COMMON_SMBDIRECT_INTERNAL_H__ */
+12 −2
Original line number Diff line number Diff line
@@ -12,14 +12,24 @@ struct smbdirect_module_state smbdirect_globals = {

static __init int smbdirect_module_init(void)
{
	int ret;

	pr_notice("subsystem loading...\n");
	mutex_lock(&smbdirect_globals.mutex);

	/* TODO... */
	ret = smbdirect_devices_init();
	if (ret)
		goto devices_init_failed;

	mutex_unlock(&smbdirect_globals.mutex);
	pr_notice("subsystem loaded\n");
	return 0;

devices_init_failed:
	mutex_unlock(&smbdirect_globals.mutex);
	pr_crit("failed to loaded: %d (%1pe)\n",
		ret, SMBDIRECT_DEBUG_ERR_PTR(ret));
	return ret;
}

static __exit void smbdirect_module_exit(void)
@@ -27,7 +37,7 @@ static __exit void smbdirect_module_exit(void)
	pr_notice("subsystem unloading...\n");
	mutex_lock(&smbdirect_globals.mutex);

	/* TODO... */
	smbdirect_devices_exit();

	mutex_unlock(&smbdirect_globals.mutex);
	pr_notice("subsystem unloaded\n");
+3 −0
Original line number Diff line number Diff line
@@ -24,6 +24,9 @@ struct smbdirect_mr_io;

#include <rdma/rw.h>

__SMBDIRECT_PUBLIC__
u8 smbdirect_netdev_rdma_capable_node_type(struct net_device *netdev);

__SMBDIRECT_PUBLIC__
bool smbdirect_frwr_is_supported(const struct ib_device_attr *attrs);