From: Stefan Metzmacher Date: Mon, 8 Dec 2025 19:56:45 +0000 (+0100) Subject: smb: smbdirect: introduce smbdirect_netdev_rdma_capable_mode_type() X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=81a7a3a0faea7e8e64f83aa58e807a8ad329c97d;p=thirdparty%2Fkernel%2Flinux.git 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 Cc: Tom Talpey Cc: Long Li Cc: Namjae Jeon Cc: linux-cifs@vger.kernel.org Cc: samba-technical@lists.samba.org Signed-off-by: Stefan Metzmacher Acked-by: Namjae Jeon Signed-off-by: Steve French --- 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 @@ -24,6 +24,9 @@ struct smbdirect_mr_io; #include +__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);