summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Metzmacher <metze@samba.org>2025-12-08 22:56:45 +0300
committerSteve French <stfrench@microsoft.com>2026-04-16 05:58:24 +0300
commit81a7a3a0faea7e8e64f83aa58e807a8ad329c97d (patch)
tree85fe4a07940f282e177862d096f3a3bae5b6935a
parent50bdab9ae45e6345eaa94adbaefaf1ce5a7e90a1 (diff)
downloadlinux-81a7a3a0faea7e8e64f83aa58e807a8ad329c97d.tar.xz
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: Stefan Metzmacher <metze@samba.org> Acked-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com>
-rw-r--r--fs/smb/common/smbdirect/Makefile1
-rw-r--r--fs/smb/common/smbdirect/smbdirect_devices.c277
-rw-r--r--fs/smb/common/smbdirect/smbdirect_internal.h18
-rw-r--r--fs/smb/common/smbdirect/smbdirect_main.c14
-rw-r--r--fs/smb/common/smbdirect/smbdirect_public.h3
5 files changed, 311 insertions, 2 deletions
diff --git a/fs/smb/common/smbdirect/Makefile b/fs/smb/common/smbdirect/Makefile
index b41271facfc3..423f533e1002 100644
--- a/fs/smb/common/smbdirect/Makefile
+++ b/fs/smb/common/smbdirect/Makefile
@@ -14,4 +14,5 @@ smbdirect-y := \
smbdirect_connect.o \
smbdirect_listen.o \
smbdirect_accept.o \
+ smbdirect_devices.o \
smbdirect_main.o
diff --git a/fs/smb/common/smbdirect/smbdirect_devices.c b/fs/smb/common/smbdirect/smbdirect_devices.c
new file mode 100644
index 000000000000..aaab99e9c045
--- /dev/null
+++ b/fs/smb/common/smbdirect/smbdirect_devices.c
@@ -0,0 +1,277 @@
+// 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);
+}
diff --git a/fs/smb/common/smbdirect/smbdirect_internal.h b/fs/smb/common/smbdirect/smbdirect_internal.h
index 901540d0cbbf..03a01fb1ab1c 100644
--- a/fs/smb/common/smbdirect/smbdirect_internal.h
+++ b/fs/smb/common/smbdirect/smbdirect_internal.h
@@ -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__ */
diff --git a/fs/smb/common/smbdirect/smbdirect_main.c b/fs/smb/common/smbdirect/smbdirect_main.c
index c61ae8d7f4f0..948964d3fa35 100644
--- a/fs/smb/common/smbdirect/smbdirect_main.c
+++ b/fs/smb/common/smbdirect/smbdirect_main.c
@@ -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");
diff --git a/fs/smb/common/smbdirect/smbdirect_public.h b/fs/smb/common/smbdirect/smbdirect_public.h
index c0144c5a808c..231ad7a9c6af 100644
--- a/fs/smb/common/smbdirect/smbdirect_public.h
+++ b/fs/smb/common/smbdirect/smbdirect_public.h
@@ -25,6 +25,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);
__SMBDIRECT_PUBLIC__